summaryrefslogtreecommitdiffstats
path: root/xpcom
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /xpcom
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'xpcom')
-rw-r--r--xpcom/base/AvailableMemoryTracker.cpp447
-rw-r--r--xpcom/base/AvailableMemoryTracker.h30
-rw-r--r--xpcom/base/ClearOnShutdown.cpp45
-rw-r--r--xpcom/base/ClearOnShutdown.h124
-rw-r--r--xpcom/base/CodeAddressService.h198
-rw-r--r--xpcom/base/CountingAllocatorBase.h140
-rw-r--r--xpcom/base/CycleCollectedJSContext.cpp1717
-rw-r--r--xpcom/base/CycleCollectedJSContext.h494
-rw-r--r--xpcom/base/Debug.cpp23
-rw-r--r--xpcom/base/Debug.h21
-rw-r--r--xpcom/base/DebuggerOnGCRunnable.cpp47
-rw-r--r--xpcom/base/DebuggerOnGCRunnable.h35
-rw-r--r--xpcom/base/DeferredFinalize.cpp28
-rw-r--r--xpcom/base/DeferredFinalize.h33
-rw-r--r--xpcom/base/ErrorList.h1026
-rw-r--r--xpcom/base/ErrorNames.cpp84
-rw-r--r--xpcom/base/ErrorNames.h25
-rw-r--r--xpcom/base/HoldDropJSObjects.cpp68
-rw-r--r--xpcom/base/HoldDropJSObjects.h77
-rw-r--r--xpcom/base/JSObjectHolder.cpp9
-rw-r--r--xpcom/base/JSObjectHolder.h42
-rw-r--r--xpcom/base/LinuxUtils.cpp50
-rw-r--r--xpcom/base/LinuxUtils.h34
-rw-r--r--xpcom/base/LogModulePrefWatcher.cpp170
-rw-r--r--xpcom/base/LogModulePrefWatcher.h42
-rw-r--r--xpcom/base/Logging.cpp568
-rw-r--r--xpcom/base/Logging.h255
-rw-r--r--xpcom/base/MacHelpers.h18
-rw-r--r--xpcom/base/MacHelpers.mm41
-rw-r--r--xpcom/base/NSPRLogModulesParser.cpp54
-rw-r--r--xpcom/base/NSPRLogModulesParser.h22
-rw-r--r--xpcom/base/OwningNonNull.h198
-rw-r--r--xpcom/base/StaticMutex.h96
-rw-r--r--xpcom/base/StaticPtr.h270
-rw-r--r--xpcom/base/SystemMemoryReporter.cpp989
-rw-r--r--xpcom/base/SystemMemoryReporter.h29
-rw-r--r--xpcom/base/moz.build159
-rw-r--r--xpcom/base/nsAgg.h336
-rw-r--r--xpcom/base/nsAlgorithm.h75
-rw-r--r--xpcom/base/nsAllocator.h17
-rw-r--r--xpcom/base/nsAutoPtr.h454
-rw-r--r--xpcom/base/nsAutoRef.h672
-rw-r--r--xpcom/base/nsCom.h12
-rw-r--r--xpcom/base/nsConsoleMessage.cpp56
-rw-r--r--xpcom/base/nsConsoleMessage.h33
-rw-r--r--xpcom/base/nsConsoleService.cpp473
-rw-r--r--xpcom/base/nsConsoleService.h118
-rw-r--r--xpcom/base/nsCrashOnException.cpp44
-rw-r--r--xpcom/base/nsCrashOnException.h23
-rw-r--r--xpcom/base/nsCycleCollector.cpp4213
-rw-r--r--xpcom/base/nsCycleCollector.h72
-rw-r--r--xpcom/base/nsCycleCollectorTraceJSHelpers.cpp105
-rw-r--r--xpcom/base/nsDebugImpl.cpp607
-rw-r--r--xpcom/base/nsDebugImpl.h41
-rw-r--r--xpcom/base/nsDumpUtils.cpp513
-rw-r--r--xpcom/base/nsDumpUtils.h203
-rw-r--r--xpcom/base/nsError.h219
-rw-r--r--xpcom/base/nsErrorService.cpp50
-rw-r--r--xpcom/base/nsErrorService.h37
-rw-r--r--xpcom/base/nsGZFileWriter.cpp111
-rw-r--r--xpcom/base/nsGZFileWriter.h55
-rw-r--r--xpcom/base/nsIConsoleListener.idl18
-rw-r--r--xpcom/base/nsIConsoleMessage.idl43
-rw-r--r--xpcom/base/nsIConsoleService.idl56
-rw-r--r--xpcom/base/nsICycleCollectorListener.idl164
-rw-r--r--xpcom/base/nsIDebug2.idl82
-rw-r--r--xpcom/base/nsIErrorService.idl49
-rw-r--r--xpcom/base/nsIException.idl86
-rw-r--r--xpcom/base/nsIGZFileWriter.idl82
-rw-r--r--xpcom/base/nsIID.h10
-rw-r--r--xpcom/base/nsIInterfaceRequestor.idl36
-rw-r--r--xpcom/base/nsIMacUtils.idl32
-rw-r--r--xpcom/base/nsIMemory.idl78
-rw-r--r--xpcom/base/nsIMemoryInfoDumper.idl162
-rw-r--r--xpcom/base/nsIMemoryReporter.idl581
-rw-r--r--xpcom/base/nsIMessageLoop.idl36
-rw-r--r--xpcom/base/nsIMutable.idl22
-rw-r--r--xpcom/base/nsIProgrammingLanguage.idl25
-rw-r--r--xpcom/base/nsISecurityConsoleMessage.idl20
-rw-r--r--xpcom/base/nsISizeOf.h35
-rw-r--r--xpcom/base/nsIStatusReporter.idl90
-rw-r--r--xpcom/base/nsISupports.idl44
-rw-r--r--xpcom/base/nsISupportsBase.h85
-rw-r--r--xpcom/base/nsIUUIDGenerator.idl39
-rw-r--r--xpcom/base/nsIVersionComparator.idl49
-rw-r--r--xpcom/base/nsIWeakReference.idl74
-rw-r--r--xpcom/base/nsInterfaceRequestorAgg.cpp86
-rw-r--r--xpcom/base/nsInterfaceRequestorAgg.h38
-rw-r--r--xpcom/base/nsMacUtilsImpl.cpp147
-rw-r--r--xpcom/base/nsMacUtilsImpl.h42
-rw-r--r--xpcom/base/nsMemoryImpl.cpp184
-rw-r--r--xpcom/base/nsMemoryImpl.h55
-rw-r--r--xpcom/base/nsMemoryInfoDumper.cpp830
-rw-r--r--xpcom/base/nsMemoryInfoDumper.h46
-rw-r--r--xpcom/base/nsMemoryReporterManager.cpp2717
-rw-r--r--xpcom/base/nsMemoryReporterManager.h288
-rw-r--r--xpcom/base/nsMessageLoop.cpp172
-rw-r--r--xpcom/base/nsMessageLoop.h31
-rw-r--r--xpcom/base/nsObjCExceptions.h230
-rw-r--r--xpcom/base/nsQueryObject.h109
-rw-r--r--xpcom/base/nsSecurityConsoleMessage.cpp45
-rw-r--r--xpcom/base/nsSecurityConsoleMessage.h30
-rw-r--r--xpcom/base/nsSetDllDirectory.h45
-rw-r--r--xpcom/base/nsStatusReporterManager.cpp320
-rw-r--r--xpcom/base/nsStatusReporterManager.h41
-rw-r--r--xpcom/base/nsSystemInfo.cpp985
-rw-r--r--xpcom/base/nsSystemInfo.h66
-rw-r--r--xpcom/base/nsTraceRefcnt.cpp1319
-rw-r--r--xpcom/base/nsTraceRefcnt.h40
-rw-r--r--xpcom/base/nsUUIDGenerator.cpp177
-rw-r--r--xpcom/base/nsUUIDGenerator.h44
-rw-r--r--xpcom/base/nsVersionComparatorImpl.cpp22
-rw-r--r--xpcom/base/nsVersionComparatorImpl.h25
-rw-r--r--xpcom/base/nsWeakPtr.h15
-rw-r--r--xpcom/base/nsWindowsHelpers.h371
-rw-r--r--xpcom/base/nscore.h288
-rw-r--r--xpcom/base/nsrootidl.idl97
-rw-r--r--xpcom/build/BinaryPath.h188
-rw-r--r--xpcom/build/FileLocation.cpp224
-rw-r--r--xpcom/build/FileLocation.h134
-rw-r--r--xpcom/build/FrozenFunctions.cpp138
-rw-r--r--xpcom/build/IOInterposer.cpp582
-rw-r--r--xpcom/build/IOInterposer.h295
-rw-r--r--xpcom/build/IOInterposerPrivate.h167
-rw-r--r--xpcom/build/LateWriteChecks.cpp258
-rw-r--r--xpcom/build/LateWriteChecks.h60
-rw-r--r--xpcom/build/MainThreadIOLogger.cpp225
-rw-r--r--xpcom/build/MainThreadIOLogger.h19
-rw-r--r--xpcom/build/NSPRInterposer.cpp185
-rw-r--r--xpcom/build/NSPRInterposer.h28
-rw-r--r--xpcom/build/Omnijar.cpp188
-rw-r--r--xpcom/build/Omnijar.h160
-rw-r--r--xpcom/build/PoisonIOInterposer.h91
-rw-r--r--xpcom/build/PoisonIOInterposerBase.cpp291
-rw-r--r--xpcom/build/PoisonIOInterposerMac.cpp386
-rw-r--r--xpcom/build/PoisonIOInterposerStub.cpp16
-rw-r--r--xpcom/build/PoisonIOInterposerWin.cpp501
-rw-r--r--xpcom/build/ServiceList.h46
-rw-r--r--xpcom/build/Services.cpp71
-rw-r--r--xpcom/build/Services.h45
-rw-r--r--xpcom/build/XPCOM.h178
-rw-r--r--xpcom/build/XPCOMInit.cpp1126
-rw-r--r--xpcom/build/XPCOMModule.inc81
-rw-r--r--xpcom/build/XREChildData.h51
-rw-r--r--xpcom/build/XREShellData.h29
-rw-r--r--xpcom/build/mach_override.c789
-rw-r--r--xpcom/build/mach_override.h121
-rw-r--r--xpcom/build/moz.build102
-rw-r--r--xpcom/build/nsWindowsDllInterceptor.h1127
-rw-r--r--xpcom/build/nsXPCOM.h433
-rw-r--r--xpcom/build/nsXPCOMCID.h185
-rw-r--r--xpcom/build/nsXPCOMCIDInternal.h54
-rw-r--r--xpcom/build/nsXPCOMPrivate.h317
-rw-r--r--xpcom/build/nsXPCOMStrings.cpp366
-rw-r--r--xpcom/build/nsXREAppData.h164
-rw-r--r--xpcom/build/nsXULAppAPI.h538
-rw-r--r--xpcom/build/perfprobe.cpp242
-rw-r--r--xpcom/build/perfprobe.h204
-rw-r--r--xpcom/build/xpcom_alpha.def256
-rw-r--r--xpcom/build/xrecore.h25
-rw-r--r--xpcom/components/ManifestParser.cpp792
-rw-r--r--xpcom/components/ManifestParser.h22
-rw-r--r--xpcom/components/Module.h157
-rw-r--r--xpcom/components/ModuleLoader.h44
-rw-r--r--xpcom/components/ModuleUtils.h98
-rw-r--r--xpcom/components/moz.build55
-rw-r--r--xpcom/components/nsCategoryManager.cpp832
-rw-r--r--xpcom/components/nsCategoryManager.h146
-rw-r--r--xpcom/components/nsCategoryManagerUtils.h18
-rw-r--r--xpcom/components/nsComponentManager.cpp2083
-rw-r--r--xpcom/components/nsComponentManager.h363
-rw-r--r--xpcom/components/nsICategoryManager.idl69
-rw-r--r--xpcom/components/nsIClassInfo.idl87
-rw-r--r--xpcom/components/nsIComponentManager.idl106
-rw-r--r--xpcom/components/nsIComponentRegistrar.idl163
-rw-r--r--xpcom/components/nsIFactory.idl42
-rw-r--r--xpcom/components/nsIModule.idl82
-rw-r--r--xpcom/components/nsIServiceManager.idl72
-rw-r--r--xpcom/components/nsNativeModuleLoader.cpp220
-rw-r--r--xpcom/components/nsNativeModuleLoader.h39
-rw-r--r--xpcom/doc/README11
-rw-r--r--xpcom/ds/IncrementalTokenizer.cpp195
-rw-r--r--xpcom/ds/IncrementalTokenizer.h122
-rw-r--r--xpcom/ds/StickyTimeDuration.h267
-rw-r--r--xpcom/ds/Tokenizer.cpp738
-rw-r--r--xpcom/ds/Tokenizer.h446
-rw-r--r--xpcom/ds/moz.build105
-rw-r--r--xpcom/ds/nsArray.cpp242
-rw-r--r--xpcom/ds/nsArray.h76
-rw-r--r--xpcom/ds/nsAtomService.cpp25
-rw-r--r--xpcom/ds/nsAtomService.h25
-rw-r--r--xpcom/ds/nsAtomTable.cpp736
-rw-r--r--xpcom/ds/nsAtomTable.h19
-rw-r--r--xpcom/ds/nsCRT.cpp187
-rw-r--r--xpcom/ds/nsCRT.h186
-rw-r--r--xpcom/ds/nsCharSeparatedTokenizer.h200
-rw-r--r--xpcom/ds/nsCheapSets.h171
-rw-r--r--xpcom/ds/nsExpirationTracker.h570
-rw-r--r--xpcom/ds/nsHashPropertyBag.cpp284
-rw-r--r--xpcom/ds/nsHashPropertyBag.h57
-rw-r--r--xpcom/ds/nsIArray.idl91
-rw-r--r--xpcom/ds/nsIArrayExtensions.idl51
-rw-r--r--xpcom/ds/nsIAtom.idl166
-rw-r--r--xpcom/ds/nsIAtomService.idl35
-rw-r--r--xpcom/ds/nsICollection.idl67
-rw-r--r--xpcom/ds/nsIEnumerator.idl50
-rw-r--r--xpcom/ds/nsIHashable.idl24
-rw-r--r--xpcom/ds/nsIINIParser.idl58
-rw-r--r--xpcom/ds/nsIMutableArray.idl110
-rw-r--r--xpcom/ds/nsINIParserImpl.cpp142
-rw-r--r--xpcom/ds/nsINIParserImpl.h33
-rw-r--r--xpcom/ds/nsINIProcessor.js192
-rw-r--r--xpcom/ds/nsINIProcessor.manifest2
-rw-r--r--xpcom/ds/nsIObserver.idl38
-rw-r--r--xpcom/ds/nsIObserverService.idl77
-rw-r--r--xpcom/ds/nsIPersistentProperties.h14
-rw-r--r--xpcom/ds/nsIPersistentProperties2.idl63
-rw-r--r--xpcom/ds/nsIProperties.idl46
-rw-r--r--xpcom/ds/nsIProperty.idl25
-rw-r--r--xpcom/ds/nsIPropertyBag.idl30
-rw-r--r--xpcom/ds/nsIPropertyBag2.idl42
-rw-r--r--xpcom/ds/nsISerializable.idl32
-rw-r--r--xpcom/ds/nsISimpleEnumerator.idl46
-rw-r--r--xpcom/ds/nsIStringEnumerator.idl25
-rw-r--r--xpcom/ds/nsISupportsArray.idl60
-rw-r--r--xpcom/ds/nsISupportsIterators.idl292
-rw-r--r--xpcom/ds/nsISupportsPrimitives.idl235
-rw-r--r--xpcom/ds/nsIVariant.idl155
-rw-r--r--xpcom/ds/nsIWindowsRegKey.idl332
-rw-r--r--xpcom/ds/nsIWritablePropertyBag.idl27
-rw-r--r--xpcom/ds/nsIWritablePropertyBag2.idl22
-rw-r--r--xpcom/ds/nsMathUtils.h127
-rw-r--r--xpcom/ds/nsObserverList.cpp142
-rw-r--r--xpcom/ds/nsObserverList.h93
-rw-r--r--xpcom/ds/nsObserverService.cpp312
-rw-r--r--xpcom/ds/nsObserverService.h55
-rw-r--r--xpcom/ds/nsPersistentProperties.cpp666
-rw-r--r--xpcom/ds/nsPersistentProperties.h67
-rw-r--r--xpcom/ds/nsProperties.cpp99
-rw-r--r--xpcom/ds/nsProperties.h39
-rw-r--r--xpcom/ds/nsStaticAtom.h53
-rw-r--r--xpcom/ds/nsStaticNameTable.cpp201
-rw-r--r--xpcom/ds/nsStaticNameTable.h49
-rw-r--r--xpcom/ds/nsStringEnumerator.cpp262
-rw-r--r--xpcom/ds/nsStringEnumerator.h91
-rw-r--r--xpcom/ds/nsSupportsArray.cpp264
-rw-r--r--xpcom/ds/nsSupportsArray.h107
-rw-r--r--xpcom/ds/nsSupportsArrayEnumerator.cpp131
-rw-r--r--xpcom/ds/nsSupportsArrayEnumerator.h56
-rw-r--r--xpcom/ds/nsSupportsPrimitives.cpp849
-rw-r--r--xpcom/ds/nsSupportsPrimitives.h327
-rw-r--r--xpcom/ds/nsVariant.cpp2220
-rw-r--r--xpcom/ds/nsVariant.h229
-rw-r--r--xpcom/ds/nsWhitespaceTokenizer.h110
-rw-r--r--xpcom/ds/nsWindowsRegKey.cpp579
-rw-r--r--xpcom/ds/nsWindowsRegKey.h43
-rw-r--r--xpcom/glue/AppData.cpp95
-rw-r--r--xpcom/glue/AppData.h63
-rw-r--r--xpcom/glue/AutoRestore.h55
-rw-r--r--xpcom/glue/BlockingResourceBase.cpp511
-rw-r--r--xpcom/glue/BlockingResourceBase.h344
-rw-r--r--xpcom/glue/CondVar.h144
-rw-r--r--xpcom/glue/DeadlockDetector.h382
-rw-r--r--xpcom/glue/EnumeratedArrayCycleCollection.h43
-rw-r--r--xpcom/glue/FileUtils.cpp568
-rw-r--r--xpcom/glue/FileUtils.h220
-rw-r--r--xpcom/glue/GenericFactory.cpp27
-rw-r--r--xpcom/glue/GenericFactory.h43
-rw-r--r--xpcom/glue/GenericModule.cpp98
-rw-r--r--xpcom/glue/IntentionalCrash.h58
-rw-r--r--xpcom/glue/MainThreadUtils.h42
-rw-r--r--xpcom/glue/Monitor.h135
-rw-r--r--xpcom/glue/Mutex.h229
-rw-r--r--xpcom/glue/Observer.h83
-rw-r--r--xpcom/glue/PLDHashTable.cpp801
-rw-r--r--xpcom/glue/PLDHashTable.h621
-rw-r--r--xpcom/glue/ReentrantMonitor.h249
-rw-r--r--xpcom/glue/moz.build123
-rw-r--r--xpcom/glue/nsArrayEnumerator.cpp213
-rw-r--r--xpcom/glue/nsArrayEnumerator.h32
-rw-r--r--xpcom/glue/nsArrayUtils.cpp23
-rw-r--r--xpcom/glue/nsArrayUtils.h40
-rw-r--r--xpcom/glue/nsBaseHashtable.h270
-rw-r--r--xpcom/glue/nsCOMArray.cpp323
-rw-r--r--xpcom/glue/nsCOMArray.h473
-rw-r--r--xpcom/glue/nsCOMPtr.cpp128
-rw-r--r--xpcom/glue/nsCOMPtr.h1472
-rw-r--r--xpcom/glue/nsCRTGlue.cpp441
-rw-r--r--xpcom/glue/nsCRTGlue.h147
-rw-r--r--xpcom/glue/nsCategoryCache.cpp149
-rw-r--r--xpcom/glue/nsCategoryCache.h95
-rw-r--r--xpcom/glue/nsClassHashtable.h140
-rw-r--r--xpcom/glue/nsClassInfoImpl.cpp73
-rw-r--r--xpcom/glue/nsComponentManagerUtils.cpp301
-rw-r--r--xpcom/glue/nsComponentManagerUtils.h247
-rw-r--r--xpcom/glue/nsCycleCollectionNoteChild.h101
-rw-r--r--xpcom/glue/nsCycleCollectionNoteRootCallback.h31
-rw-r--r--xpcom/glue/nsCycleCollectionParticipant.cpp39
-rw-r--r--xpcom/glue/nsCycleCollectionParticipant.h852
-rw-r--r--xpcom/glue/nsCycleCollectionTraversalCallback.h62
-rw-r--r--xpcom/glue/nsDataHashtable.h58
-rw-r--r--xpcom/glue/nsDebug.h460
-rw-r--r--xpcom/glue/nsDeque.cpp361
-rw-r--r--xpcom/glue/nsDeque.h195
-rw-r--r--xpcom/glue/nsEnumeratorUtils.cpp291
-rw-r--r--xpcom/glue/nsEnumeratorUtils.h24
-rw-r--r--xpcom/glue/nsHashKeys.h660
-rw-r--r--xpcom/glue/nsIClassInfoImpl.h179
-rw-r--r--xpcom/glue/nsID.cpp133
-rw-r--r--xpcom/glue/nsID.h179
-rw-r--r--xpcom/glue/nsIInterfaceRequestorUtils.cpp33
-rw-r--r--xpcom/glue/nsIInterfaceRequestorUtils.h49
-rw-r--r--xpcom/glue/nsINIParser.cpp331
-rw-r--r--xpcom/glue/nsINIParser.h118
-rw-r--r--xpcom/glue/nsISupportsImpl.cpp27
-rw-r--r--xpcom/glue/nsISupportsImpl.h1090
-rw-r--r--xpcom/glue/nsISupportsUtils.h145
-rw-r--r--xpcom/glue/nsIWeakReferenceUtils.h102
-rw-r--r--xpcom/glue/nsInterfaceHashtable.h142
-rw-r--r--xpcom/glue/nsJSThingHashtable.h61
-rw-r--r--xpcom/glue/nsMemory.cpp53
-rw-r--r--xpcom/glue/nsMemory.h136
-rw-r--r--xpcom/glue/nsPointerHashKeys.h48
-rw-r--r--xpcom/glue/nsProxyRelease.cpp21
-rw-r--r--xpcom/glue/nsProxyRelease.h353
-rw-r--r--xpcom/glue/nsQuickSort.cpp187
-rw-r--r--xpcom/glue/nsQuickSort.h41
-rw-r--r--xpcom/glue/nsRefPtrHashtable.h191
-rw-r--r--xpcom/glue/nsServiceManagerUtils.h94
-rw-r--r--xpcom/glue/nsStringAPI.cpp1304
-rw-r--r--xpcom/glue/nsStringAPI.h1596
-rw-r--r--xpcom/glue/nsStringGlue.h24
-rw-r--r--xpcom/glue/nsTArray-inl.h463
-rw-r--r--xpcom/glue/nsTArray.cpp29
-rw-r--r--xpcom/glue/nsTArray.h2371
-rw-r--r--xpcom/glue/nsTArrayForwardDeclare.h36
-rw-r--r--xpcom/glue/nsTHashtable.h577
-rw-r--r--xpcom/glue/nsTObserverArray.cpp31
-rw-r--r--xpcom/glue/nsTObserverArray.h520
-rw-r--r--xpcom/glue/nsTPriorityQueue.h161
-rw-r--r--xpcom/glue/nsTWeakRef.h176
-rw-r--r--xpcom/glue/nsTextFormatter.cpp1394
-rw-r--r--xpcom/glue/nsTextFormatter.h80
-rw-r--r--xpcom/glue/nsThreadUtils.cpp472
-rw-r--r--xpcom/glue/nsThreadUtils.h1049
-rw-r--r--xpcom/glue/nsVersionComparator.cpp379
-rw-r--r--xpcom/glue/nsVersionComparator.h174
-rw-r--r--xpcom/glue/nsWeakReference.cpp164
-rw-r--r--xpcom/glue/nsWeakReference.h49
-rw-r--r--xpcom/glue/nsXPTCUtils.h45
-rw-r--r--xpcom/glue/objs.mozbuild48
-rw-r--r--xpcom/glue/standalone/moz.build58
-rw-r--r--xpcom/glue/standalone/nsXPCOMGlue.cpp927
-rw-r--r--xpcom/glue/standalone/nsXPCOMGlue.h49
-rw-r--r--xpcom/glue/standalone/staticruntime/moz.build50
-rw-r--r--xpcom/glue/staticruntime/moz.build48
-rw-r--r--xpcom/glue/tests/gtest/TestArray.cpp169
-rw-r--r--xpcom/glue/tests/gtest/TestFileUtils.cpp283
-rw-r--r--xpcom/glue/tests/gtest/TestGCPostBarriers.cpp140
-rw-r--r--xpcom/glue/tests/gtest/TestNsDeque.cpp342
-rw-r--r--xpcom/glue/tests/gtest/TestThreadUtils.cpp937
-rw-r--r--xpcom/glue/tests/gtest/moz.build22
-rw-r--r--xpcom/idl-parser/setup.py15
-rw-r--r--xpcom/idl-parser/xpidl/__init__.py0
-rw-r--r--xpcom/idl-parser/xpidl/header.py566
-rw-r--r--xpcom/idl-parser/xpidl/moz.build29
-rw-r--r--xpcom/idl-parser/xpidl/runtests.py114
-rw-r--r--xpcom/idl-parser/xpidl/typelib.py307
-rwxr-xr-xxpcom/idl-parser/xpidl/xpidl.py1465
-rw-r--r--xpcom/io/Base64.cpp645
-rw-r--r--xpcom/io/Base64.h73
-rw-r--r--xpcom/io/CocoaFileUtils.h35
-rw-r--r--xpcom/io/CocoaFileUtils.mm267
-rw-r--r--xpcom/io/FileUtilsWin.cpp75
-rw-r--r--xpcom/io/FileUtilsWin.h144
-rw-r--r--xpcom/io/SlicedInputStream.cpp209
-rw-r--r--xpcom/io/SlicedInputStream.h50
-rw-r--r--xpcom/io/SnappyCompressOutputStream.cpp256
-rw-r--r--xpcom/io/SnappyCompressOutputStream.h69
-rw-r--r--xpcom/io/SnappyFrameUtils.cpp258
-rw-r--r--xpcom/io/SnappyFrameUtils.h85
-rw-r--r--xpcom/io/SnappyUncompressInputStream.cpp362
-rw-r--r--xpcom/io/SnappyUncompressInputStream.h90
-rw-r--r--xpcom/io/SpecialSystemDirectory.cpp830
-rw-r--r--xpcom/io/SpecialSystemDirectory.h107
-rw-r--r--xpcom/io/crc32c.c154
-rw-r--r--xpcom/io/crc32c.h23
-rw-r--r--xpcom/io/moz.build140
-rw-r--r--xpcom/io/nsAnonymousTemporaryFile.cpp314
-rw-r--r--xpcom/io/nsAnonymousTemporaryFile.h31
-rw-r--r--xpcom/io/nsAppDirectoryServiceDefs.h118
-rw-r--r--xpcom/io/nsAppFileLocationProvider.cpp609
-rw-r--r--xpcom/io/nsAppFileLocationProvider.h55
-rw-r--r--xpcom/io/nsBinaryStream.cpp1016
-rw-r--r--xpcom/io/nsBinaryStream.h101
-rw-r--r--xpcom/io/nsDirectoryService.cpp766
-rw-r--r--xpcom/io/nsDirectoryService.h66
-rw-r--r--xpcom/io/nsDirectoryServiceAtomList.h98
-rw-r--r--xpcom/io/nsDirectoryServiceDefs.h168
-rw-r--r--xpcom/io/nsDirectoryServiceUtils.h31
-rw-r--r--xpcom/io/nsEscape.cpp633
-rw-r--r--xpcom/io/nsEscape.h224
-rw-r--r--xpcom/io/nsIAsyncInputStream.idl104
-rw-r--r--xpcom/io/nsIAsyncOutputStream.idl104
-rw-r--r--xpcom/io/nsIBinaryInputStream.idl119
-rw-r--r--xpcom/io/nsIBinaryOutputStream.idl90
-rw-r--r--xpcom/io/nsICloneableInputStream.idl22
-rw-r--r--xpcom/io/nsIConverterInputStream.idl40
-rw-r--r--xpcom/io/nsIConverterOutputStream.idl44
-rw-r--r--xpcom/io/nsIDirectoryEnumerator.idl34
-rw-r--r--xpcom/io/nsIDirectoryService.idl103
-rw-r--r--xpcom/io/nsIFile.idl521
-rw-r--r--xpcom/io/nsIIOUtil.idl34
-rw-r--r--xpcom/io/nsIInputStream.idl147
-rw-r--r--xpcom/io/nsIInputStreamTee.idl42
-rw-r--r--xpcom/io/nsILineInputStream.idl26
-rw-r--r--xpcom/io/nsILocalFile.idl17
-rw-r--r--xpcom/io/nsILocalFileMac.idl179
-rw-r--r--xpcom/io/nsILocalFileWin.idl121
-rw-r--r--xpcom/io/nsIMultiplexInputStream.idl55
-rw-r--r--xpcom/io/nsIOUtil.cpp32
-rw-r--r--xpcom/io/nsIOUtil.h27
-rw-r--r--xpcom/io/nsIObjectInputStream.idl53
-rw-r--r--xpcom/io/nsIObjectOutputStream.idl97
-rw-r--r--xpcom/io/nsIOutputStream.idl145
-rw-r--r--xpcom/io/nsIPipe.idl171
-rw-r--r--xpcom/io/nsISafeOutputStream.idl39
-rw-r--r--xpcom/io/nsIScriptableBase64Encoder.idl32
-rw-r--r--xpcom/io/nsIScriptableInputStream.idl67
-rw-r--r--xpcom/io/nsISeekableStream.idl74
-rw-r--r--xpcom/io/nsIStorageStream.idl69
-rw-r--r--xpcom/io/nsIStreamBufferAccess.idl88
-rw-r--r--xpcom/io/nsIStringStream.idl66
-rw-r--r--xpcom/io/nsIUnicharInputStream.idl95
-rw-r--r--xpcom/io/nsIUnicharLineInputStream.idl26
-rw-r--r--xpcom/io/nsIUnicharOutputStream.idl47
-rw-r--r--xpcom/io/nsInputStreamTee.cpp366
-rw-r--r--xpcom/io/nsLinebreakConverter.cpp488
-rw-r--r--xpcom/io/nsLinebreakConverter.h131
-rw-r--r--xpcom/io/nsLocalFile.h124
-rw-r--r--xpcom/io/nsLocalFileCommon.cpp328
-rw-r--r--xpcom/io/nsLocalFileUnix.cpp2715
-rw-r--r--xpcom/io/nsLocalFileUnix.h138
-rw-r--r--xpcom/io/nsLocalFileWin.cpp3787
-rw-r--r--xpcom/io/nsLocalFileWin.h123
-rw-r--r--xpcom/io/nsMultiplexInputStream.cpp835
-rw-r--r--xpcom/io/nsMultiplexInputStream.h30
-rw-r--r--xpcom/io/nsNativeCharsetUtils.cpp1044
-rw-r--r--xpcom/io/nsNativeCharsetUtils.h63
-rw-r--r--xpcom/io/nsPipe.h24
-rw-r--r--xpcom/io/nsPipe3.cpp2007
-rw-r--r--xpcom/io/nsScriptableBase64Encoder.cpp28
-rw-r--r--xpcom/io/nsScriptableBase64Encoder.h30
-rw-r--r--xpcom/io/nsScriptableInputStream.cpp134
-rw-r--r--xpcom/io/nsScriptableInputStream.h47
-rw-r--r--xpcom/io/nsSegmentedBuffer.cpp169
-rw-r--r--xpcom/io/nsSegmentedBuffer.h109
-rw-r--r--xpcom/io/nsStorageStream.cpp648
-rw-r--r--xpcom/io/nsStorageStream.h73
-rw-r--r--xpcom/io/nsStreamUtils.cpp957
-rw-r--r--xpcom/io/nsStreamUtils.h295
-rw-r--r--xpcom/io/nsStringStream.cpp452
-rw-r--r--xpcom/io/nsStringStream.h63
-rw-r--r--xpcom/io/nsUnicharInputStream.cpp398
-rw-r--r--xpcom/io/nsUnicharInputStream.h15
-rw-r--r--xpcom/io/nsWildCard.cpp481
-rw-r--r--xpcom/io/nsWildCard.h64
-rw-r--r--xpcom/libxpt/xptcall/porting.html17
-rw-r--r--xpcom/libxpt/xptcall/status.html17
-rw-r--r--xpcom/moz.build50
-rw-r--r--xpcom/reflect/moz.build8
-rw-r--r--xpcom/reflect/xptcall/README6
-rw-r--r--xpcom/reflect/xptcall/genstubs.pl88
-rw-r--r--xpcom/reflect/xptcall/md/moz.build12
-rw-r--r--xpcom/reflect/xptcall/md/test/README6
-rwxr-xr-xxpcom/reflect/xptcall/md/test/clean.bat5
-rw-r--r--xpcom/reflect/xptcall/md/test/invoke_test.cpp207
-rwxr-xr-xxpcom/reflect/xptcall/md/test/mk_invoke.bat9
-rwxr-xr-xxpcom/reflect/xptcall/md/test/mk_stub.bat9
-rw-r--r--xpcom/reflect/xptcall/md/test/moz.build9
-rw-r--r--xpcom/reflect/xptcall/md/test/stub_test.cpp213
-rw-r--r--xpcom/reflect/xptcall/md/unix/Makefile.in79
-rw-r--r--xpcom/reflect/xptcall/md/unix/moz.build327
-rw-r--r--xpcom/reflect/xptcall/md/unix/vtable_layout_x86.cpp66
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptc_gcc_x86_unix.h17
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp140
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_alpha_openbsd.cpp144
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp417
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_netbsd.cpp181
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_openbsd.cpp183
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_aarch64.s67
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf32.s145
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf64.s146
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips.S134
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips64.S122
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_pa32.s131
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_parisc_linux.s108
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S167
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix.s129
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix64.s128
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_ibmobj_aix.s124
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_linux.S98
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_netbsd.s95
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_openbsd.S94
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_rhapsody.s142
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc64_openbsd.s86
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_linux_GCC3.s53
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_netbsd.s55
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_openbsd.s55
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_GCC3.s52
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_SUNW.s56
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparcv9_solaris_SUNW.s85
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_solaris_SUNW.s55
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_darwin.cpp16
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp97
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf32.cpp132
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf64.cpp100
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_alpha.cpp144
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_m68k.cpp130
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390.cpp195
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390x.cpp190
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_mips.cpp99
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_mips64.cpp142
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_netbsd_m68k.cpp143
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_pa32.cpp149
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp97
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix.cpp74
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix64.cpp63
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_linux.cpp128
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_netbsd.cpp115
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_openbsd.cpp109
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_rhapsody.cpp113
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc64_openbsd.cpp69
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_netbsd.cpp131
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_openbsd.cpp128
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_solaris.cpp131
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_sparcv9_solaris.cpp73
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_solaris.cpp149
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp188
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_solaris.cpp67
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_aarch64.cpp219
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_alpha_openbsd.cpp189
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp238
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_arm_netbsd.cpp113
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_arm_openbsd.cpp205
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_aarch64.s39
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf32.s123
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf64.s124
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.S116
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.s.m475
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips64.S111
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_pa32.s68
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_parisc_linux.s73
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc64_linux.S112
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix.s.m4119
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix64.s.m497
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_darwin.s.m4114
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_linux.S77
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_netbsd.s70
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_openbsd.S72
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc64_openbsd.s50
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_netbsd.s49
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_openbsd.s49
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_solaris.s49
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparcv9_solaris.s50
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_64_solaris_SUNW.s63
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_solaris_SUNW.s78
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_darwin.cpp16
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_gcc_x86_unix.cpp139
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ipf32.cpp151
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ipf64.cpp154
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_linux_alpha.cpp187
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_linux_m68k.cpp98
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390.cpp183
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390x.cpp187
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_mips.cpp112
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_mips64.cpp185
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_netbsd_m68k.cpp115
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_pa32.cpp143
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp246
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix.cpp185
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix64.cpp172
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_linux.cpp220
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_netbsd.cpp185
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_openbsd.cpp202
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_rhapsody.cpp165
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_sparc64_openbsd.cpp104
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_netbsd.cpp117
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_openbsd.cpp120
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_solaris.cpp112
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_sparcv9_solaris.cpp101
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_darwin.cpp190
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_linux.cpp204
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_solaris.cpp139
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_x86_solaris.cpp77
-rw-r--r--xpcom/reflect/xptcall/md/win32/moz.build45
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcinvoke.cpp45
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64.asm107
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64_gnu.s110
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_msvc.asm63
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_64.cpp59
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_gnu.cpp106
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcstubs.cpp227
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcstubs_asm_x86_64.asm335
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64.cpp197
-rw-r--r--xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64_gnu.cpp297
-rw-r--r--xpcom/reflect/xptcall/moz.build23
-rw-r--r--xpcom/reflect/xptcall/porting.html216
-rw-r--r--xpcom/reflect/xptcall/status.html412
-rw-r--r--xpcom/reflect/xptcall/xptcall.cpp82
-rw-r--r--xpcom/reflect/xptcall/xptcall.h193
-rw-r--r--xpcom/reflect/xptcall/xptcprivate.h67
-rw-r--r--xpcom/reflect/xptcall/xptcstubsdecl.inc761
-rw-r--r--xpcom/reflect/xptcall/xptcstubsdef.inc252
-rw-r--r--xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp700
-rw-r--r--xpcom/reflect/xptinfo/ShimInterfaceInfo.h50
-rw-r--r--xpcom/reflect/xptinfo/TODO20
-rw-r--r--xpcom/reflect/xptinfo/XPTInterfaceInfoManager.h119
-rw-r--r--xpcom/reflect/xptinfo/moz.build37
-rw-r--r--xpcom/reflect/xptinfo/nsIInterfaceInfo.idl101
-rw-r--r--xpcom/reflect/xptinfo/nsIInterfaceInfoManager.idl28
-rw-r--r--xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp742
-rw-r--r--xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp242
-rw-r--r--xpcom/reflect/xptinfo/xptiTypelibGuts.cpp74
-rw-r--r--xpcom/reflect/xptinfo/xptiWorkingSet.cpp53
-rw-r--r--xpcom/reflect/xptinfo/xptinfo.h236
-rw-r--r--xpcom/reflect/xptinfo/xptiprivate.h394
-rw-r--r--xpcom/rust/nsstring/Cargo.toml8
-rw-r--r--xpcom/rust/nsstring/gtest/Cargo.toml12
-rw-r--r--xpcom/rust/nsstring/gtest/Test.cpp131
-rw-r--r--xpcom/rust/nsstring/gtest/moz.build12
-rw-r--r--xpcom/rust/nsstring/gtest/test.rs112
-rw-r--r--xpcom/rust/nsstring/src/lib.rs853
-rw-r--r--xpcom/string/README.html11
-rw-r--r--xpcom/string/crashtests/1113005-frame.html5
-rw-r--r--xpcom/string/crashtests/1113005.html2
-rw-r--r--xpcom/string/crashtests/394275-1.html9
-rw-r--r--xpcom/string/crashtests/395651-1.html31
-rw-r--r--xpcom/string/crashtests/crashtests.list3
-rw-r--r--xpcom/string/moz.build61
-rw-r--r--xpcom/string/nsAString.h62
-rw-r--r--xpcom/string/nsCharTraits.h587
-rw-r--r--xpcom/string/nsDependentString.cpp18
-rw-r--r--xpcom/string/nsDependentString.h23
-rw-r--r--xpcom/string/nsDependentSubstring.cpp18
-rw-r--r--xpcom/string/nsDependentSubstring.h22
-rw-r--r--xpcom/string/nsEmbedString.h18
-rw-r--r--xpcom/string/nsLiteralString.h37
-rw-r--r--xpcom/string/nsPrintfCString.h42
-rw-r--r--xpcom/string/nsPromiseFlatString.cpp17
-rw-r--r--xpcom/string/nsPromiseFlatString.h22
-rw-r--r--xpcom/string/nsReadableUtils.cpp1383
-rw-r--r--xpcom/string/nsReadableUtils.h428
-rw-r--r--xpcom/string/nsReadableUtilsImpl.h54
-rw-r--r--xpcom/string/nsReadableUtilsSSE2.cpp70
-rw-r--r--xpcom/string/nsString.cpp17
-rw-r--r--xpcom/string/nsString.h209
-rw-r--r--xpcom/string/nsStringBuffer.h160
-rw-r--r--xpcom/string/nsStringComparator.cpp39
-rw-r--r--xpcom/string/nsStringFwd.h64
-rw-r--r--xpcom/string/nsStringIterator.h268
-rw-r--r--xpcom/string/nsStringObsolete.cpp1053
-rw-r--r--xpcom/string/nsSubstring.cpp388
-rw-r--r--xpcom/string/nsSubstring.h12
-rw-r--r--xpcom/string/nsSubstringTuple.cpp20
-rw-r--r--xpcom/string/nsSubstringTuple.h22
-rw-r--r--xpcom/string/nsTDependentString.cpp25
-rw-r--r--xpcom/string/nsTDependentString.h106
-rw-r--r--xpcom/string/nsTDependentSubstring.cpp37
-rw-r--r--xpcom/string/nsTDependentSubstring.h124
-rw-r--r--xpcom/string/nsTLiteralString.h41
-rw-r--r--xpcom/string/nsTPromiseFlatString.cpp18
-rw-r--r--xpcom/string/nsTPromiseFlatString.h112
-rw-r--r--xpcom/string/nsTString.cpp47
-rw-r--r--xpcom/string/nsTString.h883
-rw-r--r--xpcom/string/nsTStringComparator.cpp50
-rw-r--r--xpcom/string/nsTStringObsolete.cpp700
-rw-r--r--xpcom/string/nsTSubstring.cpp1089
-rw-r--r--xpcom/string/nsTSubstring.h1186
-rw-r--r--xpcom/string/nsTSubstringTuple.cpp96
-rw-r--r--xpcom/string/nsTSubstringTuple.h84
-rw-r--r--xpcom/string/nsUTF8Utils.h742
-rw-r--r--xpcom/string/nsUTF8UtilsSSE2.cpp105
-rw-r--r--xpcom/string/nsXPCOMStrings.h748
-rw-r--r--xpcom/string/nsXPIDLString.h12
-rw-r--r--xpcom/string/string-template-def-char.h25
-rw-r--r--xpcom/string/string-template-def-unichar.h25
-rw-r--r--xpcom/string/string-template-undef.h26
-rw-r--r--xpcom/system/moz.build26
-rw-r--r--xpcom/system/nsIBlocklistService.idl140
-rw-r--r--xpcom/system/nsICrashReporter.idl135
-rw-r--r--xpcom/system/nsIDeviceSensors.idl60
-rw-r--r--xpcom/system/nsIGConfService.idl50
-rw-r--r--xpcom/system/nsIGIOService.idl82
-rw-r--r--xpcom/system/nsIGSettingsService.idl30
-rw-r--r--xpcom/system/nsIGeolocationProvider.idl83
-rw-r--r--xpcom/system/nsIHapticFeedback.idl22
-rw-r--r--xpcom/system/nsIPackageKitService.idl46
-rw-r--r--xpcom/system/nsIPlatformInfo.idl19
-rw-r--r--xpcom/system/nsIXULAppInfo.idl53
-rw-r--r--xpcom/system/nsIXULRuntime.idl176
-rw-r--r--xpcom/tests/Makefile.in13
-rw-r--r--xpcom/tests/NotXPCOMTest.idl23
-rw-r--r--xpcom/tests/RegFactory.cpp130
-rw-r--r--xpcom/tests/SizeTest01.cpp107
-rw-r--r--xpcom/tests/SizeTest02.cpp89
-rw-r--r--xpcom/tests/SizeTest03.cpp97
-rw-r--r--xpcom/tests/SizeTest04.cpp68
-rw-r--r--xpcom/tests/SizeTest05.cpp74
-rw-r--r--xpcom/tests/SizeTest06.cpp150
-rw-r--r--xpcom/tests/TestArguments.cpp25
-rw-r--r--xpcom/tests/TestBlockingProcess.cpp8
-rw-r--r--xpcom/tests/TestHarness.h292
-rw-r--r--xpcom/tests/TestPRIntN.cpp33
-rw-r--r--xpcom/tests/TestQuickReturn.cpp8
-rw-r--r--xpcom/tests/TestShutdown.cpp41
-rw-r--r--xpcom/tests/TestStackCrawl.cpp11
-rw-r--r--xpcom/tests/TestStreamUtils.cpp74
-rw-r--r--xpcom/tests/TestUnicodeArguments.cpp77
-rw-r--r--xpcom/tests/TestWinReg.js57
-rw-r--r--xpcom/tests/TestingAtomList.h6
-rw-r--r--xpcom/tests/bug656331_component/TestComponent.cpp32
-rw-r--r--xpcom/tests/bug656331_component/bug656331.manifest2
-rw-r--r--xpcom/tests/bug656331_component/moz.build26
-rw-r--r--xpcom/tests/component/TestComponent.cpp44
-rw-r--r--xpcom/tests/component/moz.build26
-rw-r--r--xpcom/tests/component/testcomponent.manifest4
-rw-r--r--xpcom/tests/component_no_aslr/Makefile.in8
-rw-r--r--xpcom/tests/component_no_aslr/TestComponent.cpp33
-rw-r--r--xpcom/tests/component_no_aslr/moz.build26
-rw-r--r--xpcom/tests/component_no_aslr/testcompnoaslr.manifest2
-rw-r--r--xpcom/tests/external/TestMinStringAPI.cpp1009
-rw-r--r--xpcom/tests/external/moz.build9
-rw-r--r--xpcom/tests/gtest/Helpers.cpp133
-rw-r--r--xpcom/tests/gtest/Helpers.h73
-rw-r--r--xpcom/tests/gtest/TestAllocReplacement.cpp175
-rw-r--r--xpcom/tests/gtest/TestAtoms.cpp153
-rw-r--r--xpcom/tests/gtest/TestAutoPtr.cpp220
-rw-r--r--xpcom/tests/gtest/TestAutoRef.cpp56
-rw-r--r--xpcom/tests/gtest/TestBase64.cpp291
-rw-r--r--xpcom/tests/gtest/TestCOMArray.cpp286
-rw-r--r--xpcom/tests/gtest/TestCOMPtr.cpp466
-rw-r--r--xpcom/tests/gtest/TestCOMPtrEq.cpp79
-rw-r--r--xpcom/tests/gtest/TestCRT.cpp86
-rw-r--r--xpcom/tests/gtest/TestCallTemplates.cpp104
-rw-r--r--xpcom/tests/gtest/TestCloneInputStream.cpp200
-rw-r--r--xpcom/tests/gtest/TestDeadlockDetector.cpp322
-rw-r--r--xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp170
-rw-r--r--xpcom/tests/gtest/TestEncoding.cpp109
-rw-r--r--xpcom/tests/gtest/TestEscapeURL.cpp69
-rw-r--r--xpcom/tests/gtest/TestExpirationTracker.cpp185
-rw-r--r--xpcom/tests/gtest/TestFile.cpp477
-rw-r--r--xpcom/tests/gtest/TestHashtables.cpp435
-rw-r--r--xpcom/tests/gtest/TestID.cpp36
-rw-r--r--xpcom/tests/gtest/TestNSPRLogModulesParser.cpp111
-rw-r--r--xpcom/tests/gtest/TestNsRefPtr.cpp479
-rw-r--r--xpcom/tests/gtest/TestObserverArray.cpp167
-rw-r--r--xpcom/tests/gtest/TestObserverService.cpp288
-rw-r--r--xpcom/tests/gtest/TestPLDHash.cpp368
-rw-r--r--xpcom/tests/gtest/TestPipes.cpp1097
-rw-r--r--xpcom/tests/gtest/TestPriorityQueue.cpp76
-rw-r--r--xpcom/tests/gtest/TestRacingServiceManager.cpp300
-rw-r--r--xpcom/tests/gtest/TestSTLWrappers.cpp78
-rw-r--r--xpcom/tests/gtest/TestSlicedInputStream.cpp266
-rw-r--r--xpcom/tests/gtest/TestSnappyStreams.cpp191
-rw-r--r--xpcom/tests/gtest/TestStateWatching.cpp46
-rw-r--r--xpcom/tests/gtest/TestStorageStream.cpp131
-rw-r--r--xpcom/tests/gtest/TestStringStream.cpp65
-rw-r--r--xpcom/tests/gtest/TestStrings.cpp982
-rw-r--r--xpcom/tests/gtest/TestSynchronization.cpp313
-rw-r--r--xpcom/tests/gtest/TestTArray.cpp206
-rw-r--r--xpcom/tests/gtest/TestTArray2.cpp1033
-rw-r--r--xpcom/tests/gtest/TestTextFormatter.cpp34
-rw-r--r--xpcom/tests/gtest/TestThreadPool.cpp124
-rw-r--r--xpcom/tests/gtest/TestThreadPoolListener.cpp209
-rw-r--r--xpcom/tests/gtest/TestThreadUtils.cpp378
-rw-r--r--xpcom/tests/gtest/TestThreads.cpp275
-rw-r--r--xpcom/tests/gtest/TestTimeStamp.cpp70
-rw-r--r--xpcom/tests/gtest/TestTimers.cpp437
-rw-r--r--xpcom/tests/gtest/TestTokenizer.cpp1134
-rw-r--r--xpcom/tests/gtest/TestUTF.cpp191
-rw-r--r--xpcom/tests/gtest/TestXPIDLString.cpp24
-rw-r--r--xpcom/tests/gtest/UTFStrings.h112
-rw-r--r--xpcom/tests/gtest/moz.build77
-rw-r--r--xpcom/tests/moz.build51
-rw-r--r--xpcom/tests/resources.h19
-rw-r--r--xpcom/tests/test.properties14
-rw-r--r--xpcom/tests/unit/bug725015.manifest3
-rw-r--r--xpcom/tests/unit/compmgr_warnings.manifest9
-rw-r--r--xpcom/tests/unit/data/SmallApp.app/Contents/Info.plist26
-rwxr-xr-xxpcom/tests/unit/data/SmallApp.app/Contents/MacOS/SmallAppbin0 -> 37988 bytes
-rw-r--r--xpcom/tests/unit/data/SmallApp.app/Contents/PkgInfo1
-rw-r--r--xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/InfoPlist.stringsbin0 -> 92 bytes
-rw-r--r--xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/designable.nib343
-rw-r--r--xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nibbin0 -> 3356 bytes
-rw-r--r--xpcom/tests/unit/data/bug121341-2.properties9
-rw-r--r--xpcom/tests/unit/data/bug121341.properties68
-rw-r--r--xpcom/tests/unit/data/child_process_directive_service.js21
-rw-r--r--xpcom/tests/unit/data/iniparser01-utf16leBOM.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser01-utf8BOM.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser01.ini0
-rw-r--r--xpcom/tests/unit/data/iniparser02-utf16leBOM.inibin0 -> 6 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser02-utf8BOM.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser02.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser03-utf16leBOM.inibin0 -> 10 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser03-utf8BOM.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser03.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser04-utf16leBOM.inibin0 -> 26 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser04-utf8BOM.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser04.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser05-utf16leBOM.inibin0 -> 34 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser05-utf8BOM.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser05.ini1
-rw-r--r--xpcom/tests/unit/data/iniparser06-utf16leBOM.inibin0 -> 30 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser06-utf8BOM.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser06.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser07-utf16leBOM.inibin0 -> 40 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser07-utf8BOM.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser07.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser08-utf16leBOM.inibin0 -> 42 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser08-utf8BOM.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser08.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser09-utf16leBOM.inibin0 -> 54 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser09-utf8BOM.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser09.ini2
-rw-r--r--xpcom/tests/unit/data/iniparser10-utf16leBOM.inibin0 -> 58 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser10-utf8BOM.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser10.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser11-utf16leBOM.inibin0 -> 76 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser11-utf8BOM.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser11.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser12-utf16leBOM.inibin0 -> 86 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser12-utf8BOM.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser12.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser13-utf16leBOM.inibin0 -> 94 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser13-utf8BOM.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser13.ini3
-rw-r--r--xpcom/tests/unit/data/iniparser14-utf16leBOM.inibin0 -> 160 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser14-utf8BOM.ini6
-rw-r--r--xpcom/tests/unit/data/iniparser14.ini6
-rw-r--r--xpcom/tests/unit/data/iniparser15-utf16leBOM.inibin0 -> 162 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser15-utf8BOM.ini6
-rw-r--r--xpcom/tests/unit/data/iniparser15.ini6
-rw-r--r--xpcom/tests/unit/data/iniparser16-utf16leBOM.inibin0 -> 210 bytes
-rw-r--r--xpcom/tests/unit/data/iniparser16-utf8BOM.ini13
-rw-r--r--xpcom/tests/unit/data/iniparser16.ini13
-rw-r--r--xpcom/tests/unit/data/main_process_directive_service.js21
-rw-r--r--xpcom/tests/unit/data/presentation.key/.typeAttributes.dict0
-rw-r--r--xpcom/tests/unit/data/presentation.key/Contents/PkgInfo1
-rw-r--r--xpcom/tests/unit/data/presentation.key/index.apxl.gzbin0 -> 83487 bytes
-rw-r--r--xpcom/tests/unit/data/presentation.key/thumbs/st0.tiffbin0 -> 16654 bytes
-rw-r--r--xpcom/tests/unit/data/process_directive.manifest5
-rw-r--r--xpcom/tests/unit/head_xpcom.js21
-rw-r--r--xpcom/tests/unit/test_bug121341.js71
-rw-r--r--xpcom/tests/unit/test_bug325418.js63
-rw-r--r--xpcom/tests/unit/test_bug332389.js19
-rw-r--r--xpcom/tests/unit/test_bug333505.js10
-rw-r--r--xpcom/tests/unit/test_bug364285-1.js51
-rw-r--r--xpcom/tests/unit/test_bug374754.js59
-rw-r--r--xpcom/tests/unit/test_bug476919.js27
-rw-r--r--xpcom/tests/unit/test_bug478086.js24
-rw-r--r--xpcom/tests/unit/test_bug656331.js39
-rw-r--r--xpcom/tests/unit/test_bug725015.js39
-rw-r--r--xpcom/tests/unit/test_bug745466.js6
-rw-r--r--xpcom/tests/unit/test_comp_no_aslr.js18
-rw-r--r--xpcom/tests/unit/test_compmgr_warnings.js71
-rw-r--r--xpcom/tests/unit/test_debugger_malloc_size_of.js34
-rw-r--r--xpcom/tests/unit/test_file_createUnique.js29
-rw-r--r--xpcom/tests/unit/test_file_equality.js43
-rw-r--r--xpcom/tests/unit/test_file_renameTo.js61
-rw-r--r--xpcom/tests/unit/test_hidden_files.js28
-rw-r--r--xpcom/tests/unit/test_home.js24
-rw-r--r--xpcom/tests/unit/test_iniProcessor.js288
-rw-r--r--xpcom/tests/unit/test_ioutil.js33
-rw-r--r--xpcom/tests/unit/test_localfile.js151
-rw-r--r--xpcom/tests/unit/test_mac_bundle.js18
-rw-r--r--xpcom/tests/unit/test_notxpcom_scriptable.js86
-rw-r--r--xpcom/tests/unit/test_nsIMutableArray.js184
-rw-r--r--xpcom/tests/unit/test_nsIProcess.js185
-rw-r--r--xpcom/tests/unit/test_nsIProcess_stress.js27
-rw-r--r--xpcom/tests/unit/test_pipe.js63
-rw-r--r--xpcom/tests/unit/test_process_directives.js25
-rw-r--r--xpcom/tests/unit/test_process_directives_child.js3
-rw-r--r--xpcom/tests/unit/test_seek_multiplex.js173
-rw-r--r--xpcom/tests/unit/test_storagestream.js162
-rw-r--r--xpcom/tests/unit/test_streams.js157
-rw-r--r--xpcom/tests/unit/test_stringstream.js23
-rw-r--r--xpcom/tests/unit/test_symlinks.js144
-rw-r--r--xpcom/tests/unit/test_systemInfo.js20
-rw-r--r--xpcom/tests/unit/test_versioncomparator.js59
-rw-r--r--xpcom/tests/unit/test_windows_cmdline_file.js21
-rw-r--r--xpcom/tests/unit/test_windows_registry.js205
-rw-r--r--xpcom/tests/unit/test_windows_shortcut.js279
-rw-r--r--xpcom/tests/unit/xpcomtest.manifest1
-rw-r--r--xpcom/tests/unit/xpcshell.ini79
-rw-r--r--xpcom/tests/windows/TestCOM.cpp158
-rw-r--r--xpcom/tests/windows/TestHelloXPLoop.cpp144
-rw-r--r--xpcom/tests/windows/TestNTFSPermissions.cpp286
-rw-r--r--xpcom/tests/windows/TestNtPathToDosPath.cpp193
-rw-r--r--xpcom/tests/windows/TestWinFileAttribs.cpp173
-rw-r--r--xpcom/tests/windows/moz.build16
-rw-r--r--xpcom/threads/AbstractThread.cpp192
-rw-r--r--xpcom/threads/AbstractThread.h111
-rw-r--r--xpcom/threads/BackgroundHangMonitor.cpp734
-rw-r--r--xpcom/threads/BackgroundHangMonitor.h246
-rw-r--r--xpcom/threads/HangAnnotations.cpp262
-rw-r--r--xpcom/threads/HangAnnotations.h104
-rw-r--r--xpcom/threads/HangMonitor.cpp434
-rw-r--r--xpcom/threads/HangMonitor.h58
-rw-r--r--xpcom/threads/LazyIdleThread.cpp624
-rw-r--r--xpcom/threads/LazyIdleThread.h226
-rw-r--r--xpcom/threads/LeakRefPtr.h52
-rw-r--r--xpcom/threads/MainThreadIdlePeriod.cpp76
-rw-r--r--xpcom/threads/MainThreadIdlePeriod.h28
-rw-r--r--xpcom/threads/MozPromise.h1067
-rw-r--r--xpcom/threads/SharedThreadPool.cpp224
-rw-r--r--xpcom/threads/SharedThreadPool.h129
-rw-r--r--xpcom/threads/StateMirroring.h378
-rw-r--r--xpcom/threads/StateWatching.h317
-rw-r--r--xpcom/threads/SyncRunnable.h129
-rw-r--r--xpcom/threads/TaskDispatcher.h276
-rw-r--r--xpcom/threads/TaskQueue.cpp271
-rw-r--r--xpcom/threads/TaskQueue.h203
-rw-r--r--xpcom/threads/ThreadStackHelper.cpp726
-rw-r--r--xpcom/threads/ThreadStackHelper.h147
-rw-r--r--xpcom/threads/ThrottledEventQueue.cpp446
-rw-r--r--xpcom/threads/ThrottledEventQueue.h94
-rw-r--r--xpcom/threads/TimerThread.cpp752
-rw-r--r--xpcom/threads/TimerThread.h115
-rw-r--r--xpcom/threads/moz.build89
-rw-r--r--xpcom/threads/nsEnvironment.cpp163
-rw-r--r--xpcom/threads/nsEnvironment.h36
-rw-r--r--xpcom/threads/nsEventQueue.cpp155
-rw-r--r--xpcom/threads/nsEventQueue.h123
-rw-r--r--xpcom/threads/nsICancelableRunnable.h38
-rw-r--r--xpcom/threads/nsIEnvironment.idl55
-rw-r--r--xpcom/threads/nsIEventTarget.idl127
-rw-r--r--xpcom/threads/nsIIdlePeriod.idl32
-rw-r--r--xpcom/threads/nsIIncrementalRunnable.h41
-rw-r--r--xpcom/threads/nsIProcess.idl99
-rw-r--r--xpcom/threads/nsIRunnable.idl27
-rw-r--r--xpcom/threads/nsISupportsPriority.idl45
-rw-r--r--xpcom/threads/nsIThread.idl149
-rw-r--r--xpcom/threads/nsIThreadInternal.idl135
-rw-r--r--xpcom/threads/nsIThreadManager.idl68
-rw-r--r--xpcom/threads/nsIThreadPool.idl88
-rw-r--r--xpcom/threads/nsITimer.idl244
-rw-r--r--xpcom/threads/nsMemoryPressure.cpp54
-rw-r--r--xpcom/threads/nsMemoryPressure.h77
-rw-r--r--xpcom/threads/nsProcess.h82
-rw-r--r--xpcom/threads/nsProcessCommon.cpp663
-rw-r--r--xpcom/threads/nsThread.cpp1500
-rw-r--r--xpcom/threads/nsThread.h284
-rw-r--r--xpcom/threads/nsThreadManager.cpp342
-rw-r--r--xpcom/threads/nsThreadManager.h89
-rw-r--r--xpcom/threads/nsThreadPool.cpp449
-rw-r--r--xpcom/threads/nsThreadPool.h65
-rw-r--r--xpcom/threads/nsThreadSyncDispatch.h50
-rw-r--r--xpcom/threads/nsTimerImpl.cpp658
-rw-r--r--xpcom/threads/nsTimerImpl.h207
-rw-r--r--xpcom/typelib/moz.build8
-rw-r--r--xpcom/typelib/xpt/moz.build40
-rw-r--r--xpcom/typelib/xpt/tools/moz.build13
-rw-r--r--xpcom/typelib/xpt/tools/runtests.py770
-rwxr-xr-xxpcom/typelib/xpt/tools/xpt.py1540
-rw-r--r--xpcom/typelib/xpt/xpt_arena.cpp196
-rw-r--r--xpcom/typelib/xpt/xpt_arena.h70
-rw-r--r--xpcom/typelib/xpt/xpt_struct.cpp432
-rw-r--r--xpcom/typelib/xpt/xpt_struct.h366
-rw-r--r--xpcom/typelib/xpt/xpt_xdr.cpp227
-rw-r--r--xpcom/typelib/xpt/xpt_xdr.h86
-rw-r--r--xpcom/windbgdlg/Makefile.in6
-rw-r--r--xpcom/windbgdlg/moz.build9
-rw-r--r--xpcom/windbgdlg/windbgdlg.cpp121
-rw-r--r--xpcom/xpcom-config.h.in24
-rw-r--r--xpcom/xpcom-private.h.in50
-rw-r--r--xpcom/xpidl/Makefile.in10
-rw-r--r--xpcom/xpidl/moz.build5
978 files changed, 191912 insertions, 0 deletions
diff --git a/xpcom/base/AvailableMemoryTracker.cpp b/xpcom/base/AvailableMemoryTracker.cpp
new file mode 100644
index 000000000..6272d89cf
--- /dev/null
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/AvailableMemoryTracker.h"
+
+#if defined(XP_WIN)
+#include "prinrval.h"
+#include "prenv.h"
+#include "nsIMemoryReporter.h"
+#include "nsMemoryPressure.h"
+#endif
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIRunnable.h"
+#include "nsISupports.h"
+#include "nsThreadUtils.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+
+#if defined(XP_WIN)
+# include "nsWindowsDllInterceptor.h"
+# include <windows.h>
+#endif
+
+#if defined(MOZ_MEMORY)
+# include "mozmemory.h"
+#endif // MOZ_MEMORY
+
+using namespace mozilla;
+
+namespace {
+
+#if defined(_M_IX86) && defined(XP_WIN)
+
+
+uint32_t sLowVirtualMemoryThreshold = 0;
+uint32_t sLowCommitSpaceThreshold = 0;
+uint32_t sLowPhysicalMemoryThreshold = 0;
+uint32_t sLowMemoryNotificationIntervalMS = 0;
+
+Atomic<uint32_t> sNumLowVirtualMemEvents;
+Atomic<uint32_t> sNumLowCommitSpaceEvents;
+Atomic<uint32_t> sNumLowPhysicalMemEvents;
+
+WindowsDllInterceptor sKernel32Intercept;
+WindowsDllInterceptor sGdi32Intercept;
+
+// Has Init() been called?
+bool sInitialized = false;
+
+// Has Activate() been called? The hooks don't do anything until this happens.
+bool sHooksActive = false;
+
+// Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
+// a lock!
+volatile bool sHasScheduledOneLowMemoryNotification = false;
+volatile PRIntervalTime sLastLowMemoryNotificationTime;
+
+// These are function pointers to the functions we wrap in Init().
+
+void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
+ DWORD aAllocationType, DWORD aProtect);
+
+void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
+ DWORD aDesiredAccess, DWORD aFileOffsetHigh,
+ DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
+
+HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
+ UINT aUsage, VOID** aBits,
+ HANDLE aSection, DWORD aOffset);
+
+/**
+ * Fire a memory pressure event if it's been long enough since the last one we
+ * fired.
+ */
+bool
+MaybeScheduleMemoryPressureEvent()
+{
+ // If this interval rolls over, we may fire an extra memory pressure
+ // event, but that's not a big deal.
+ PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
+ if (sHasScheduledOneLowMemoryNotification &&
+ PR_IntervalToMilliseconds(interval) < sLowMemoryNotificationIntervalMS) {
+
+ return false;
+ }
+
+ // There's a bit of a race condition here, since an interval may be a
+ // 64-bit number, and 64-bit writes aren't atomic on x86-32. But let's
+ // not worry about it -- the races only happen when we're already
+ // experiencing memory pressure and firing notifications, so the worst
+ // thing that can happen is that we fire two notifications when we
+ // should have fired only one.
+ sHasScheduledOneLowMemoryNotification = true;
+ sLastLowMemoryNotificationTime = PR_IntervalNow();
+
+ NS_DispatchEventualMemoryPressure(MemPressure_New);
+ return true;
+}
+
+void
+CheckMemAvailable()
+{
+ if (!sHooksActive) {
+ return;
+ }
+
+ MEMORYSTATUSEX stat;
+ stat.dwLength = sizeof(stat);
+ bool success = GlobalMemoryStatusEx(&stat);
+
+ if (success) {
+ // sLowVirtualMemoryThreshold is in MB, but ullAvailVirtual is in bytes.
+ if (stat.ullAvailVirtual < sLowVirtualMemoryThreshold * 1024 * 1024) {
+ // If we're running low on virtual memory, unconditionally schedule the
+ // notification. We'll probably crash if we run out of virtual memory,
+ // so don't worry about firing this notification too often.
+ ++sNumLowVirtualMemEvents;
+ NS_DispatchEventualMemoryPressure(MemPressure_New);
+ } else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
+ if (MaybeScheduleMemoryPressureEvent()) {
+ ++sNumLowCommitSpaceEvents;
+ }
+ } else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) {
+ if (MaybeScheduleMemoryPressureEvent()) {
+ ++sNumLowPhysicalMemEvents;
+ }
+ }
+ }
+}
+
+LPVOID WINAPI
+VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
+ DWORD aAllocationType,
+ DWORD aProtect)
+{
+ // It's tempting to see whether we have enough free virtual address space for
+ // this allocation and, if we don't, synchronously fire a low-memory
+ // notification to free some before we allocate.
+ //
+ // Unfortunately that doesn't work, principally because code doesn't expect a
+ // call to malloc could trigger a GC (or call into the other routines which
+ // are triggered by a low-memory notification).
+ //
+ // I think the best we can do here is try to allocate the memory and check
+ // afterwards how much free virtual address space we have. If we're running
+ // low, we schedule a low-memory notification to run as soon as possible.
+
+ LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
+
+ // Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
+ // virtual memory. Similarly, don't call CheckMemAvailable for MEM_COMMIT if
+ // we're not tracking low physical memory.
+ if ((sLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
+ (sLowPhysicalMemoryThreshold != 0 && aAllocationType & MEM_COMMIT)) {
+ CheckMemAvailable();
+ }
+
+ return result;
+}
+
+LPVOID WINAPI
+MapViewOfFileHook(HANDLE aFileMappingObject,
+ DWORD aDesiredAccess,
+ DWORD aFileOffsetHigh,
+ DWORD aFileOffsetLow,
+ SIZE_T aNumBytesToMap)
+{
+ LPVOID result = sMapViewOfFileOrig(aFileMappingObject, aDesiredAccess,
+ aFileOffsetHigh, aFileOffsetLow,
+ aNumBytesToMap);
+ CheckMemAvailable();
+ return result;
+}
+
+HBITMAP WINAPI
+CreateDIBSectionHook(HDC aDC,
+ const BITMAPINFO* aBitmapInfo,
+ UINT aUsage,
+ VOID** aBits,
+ HANDLE aSection,
+ DWORD aOffset)
+{
+ // There are a lot of calls to CreateDIBSection, so we make some effort not
+ // to CheckMemAvailable() for calls to CreateDIBSection which allocate only
+ // a small amount of memory.
+
+ // If aSection is non-null, CreateDIBSection won't allocate any new memory.
+ bool doCheck = false;
+ if (sHooksActive && !aSection && aBitmapInfo) {
+ uint16_t bitCount = aBitmapInfo->bmiHeader.biBitCount;
+ if (bitCount == 0) {
+ // MSDN says bitCount == 0 means that it figures out how many bits each
+ // pixel gets by examining the corresponding JPEG or PNG data. We'll just
+ // assume the worst.
+ bitCount = 32;
+ }
+
+ // |size| contains the expected allocation size in *bits*. Height may be
+ // negative (indicating the direction the DIB is drawn in), so we take the
+ // absolute value.
+ int64_t size = bitCount * aBitmapInfo->bmiHeader.biWidth *
+ aBitmapInfo->bmiHeader.biHeight;
+ if (size < 0) {
+ size *= -1;
+ }
+
+ // If we're allocating more than 1MB, check how much memory is left after
+ // the allocation.
+ if (size > 1024 * 1024 * 8) {
+ doCheck = true;
+ }
+ }
+
+ HBITMAP result = sCreateDIBSectionOrig(aDC, aBitmapInfo, aUsage, aBits,
+ aSection, aOffset);
+
+ if (doCheck) {
+ CheckMemAvailable();
+ }
+
+ return result;
+}
+
+static int64_t
+LowMemoryEventsVirtualDistinguishedAmount()
+{
+ return sNumLowVirtualMemEvents;
+}
+
+static int64_t
+LowMemoryEventsPhysicalDistinguishedAmount()
+{
+ return sNumLowPhysicalMemEvents;
+}
+
+class LowEventsReporter final : public nsIMemoryReporter
+{
+ ~LowEventsReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+ LowMemoryEventsVirtualDistinguishedAmount(),
+"Number of low-virtual-memory events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
+"virtual address space available (if zero, this behavior is disabled). The "
+"process will probably crash if it runs out of virtual address space, so "
+"this event is dire.");
+
+ MOZ_COLLECT_REPORT(
+ "low-memory-events/commit-space", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+ sNumLowCommitSpaceEvents,
+"Number of low-commit-space events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_commit_space_threshold_mb of "
+"commit space available (if zero, this behavior is disabled). Windows will "
+"likely kill the process if it runs out of commit space, so this event is "
+"dire.");
+
+ MOZ_COLLECT_REPORT(
+ "low-memory-events/physical", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+ LowMemoryEventsPhysicalDistinguishedAmount(),
+"Number of low-physical-memory events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_physical_memory_threshold_mb "
+"of physical memory available (if zero, this behavior is disabled). The "
+"machine will start to page if it runs out of physical memory. This may "
+"cause it to run slowly, but it shouldn't cause it to crash.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(LowEventsReporter, nsIMemoryReporter)
+
+#endif // defined(_M_IX86) && defined(XP_WIN)
+
+/**
+ * This runnable is executed in response to a memory-pressure event; we spin
+ * the event-loop when receiving the memory-pressure event in the hope that
+ * other observers will synchronously free some memory that we'll be able to
+ * purge here.
+ */
+class nsJemallocFreeDirtyPagesRunnable final : public nsIRunnable
+{
+ ~nsJemallocFreeDirtyPagesRunnable() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+};
+
+NS_IMPL_ISUPPORTS(nsJemallocFreeDirtyPagesRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+nsJemallocFreeDirtyPagesRunnable::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+#if defined(MOZ_MEMORY)
+ jemalloc_free_dirty_pages();
+#endif
+
+ return NS_OK;
+}
+
+/**
+ * The memory pressure watcher is used for listening to memory-pressure events
+ * and reacting upon them. We use one instance per process currently only for
+ * cleaning up dirty unused pages held by jemalloc.
+ */
+class nsMemoryPressureWatcher final : public nsIObserver
+{
+ ~nsMemoryPressureWatcher() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ void Init();
+
+private:
+ static bool sFreeDirtyPages;
+};
+
+NS_IMPL_ISUPPORTS(nsMemoryPressureWatcher, nsIObserver)
+
+bool nsMemoryPressureWatcher::sFreeDirtyPages = false;
+
+/**
+ * Initialize and subscribe to the memory-pressure events. We subscribe to the
+ * observer service in this method and not in the constructor because we need
+ * to hold a strong reference to 'this' before calling the observer service.
+ */
+void
+nsMemoryPressureWatcher::Init()
+{
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+
+ if (os) {
+ os->AddObserver(this, "memory-pressure", /* ownsWeak */ false);
+ }
+
+ Preferences::AddBoolVarCache(&sFreeDirtyPages, "memory.free_dirty_pages",
+ false);
+}
+
+/**
+ * Reacts to all types of memory-pressure events, launches a runnable to
+ * free dirty pages held by jemalloc.
+ */
+NS_IMETHODIMP
+nsMemoryPressureWatcher::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"), "Unknown topic");
+
+ if (sFreeDirtyPages) {
+ nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
+
+ NS_DispatchToMainThread(runnable);
+ }
+
+ return NS_OK;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace AvailableMemoryTracker {
+
+void
+Activate()
+{
+#if defined(_M_IX86) && defined(XP_WIN)
+ MOZ_ASSERT(sInitialized);
+ MOZ_ASSERT(!sHooksActive);
+
+ Preferences::AddUintVarCache(&sLowVirtualMemoryThreshold,
+ "memory.low_virtual_mem_threshold_mb", 256);
+ Preferences::AddUintVarCache(&sLowPhysicalMemoryThreshold,
+ "memory.low_physical_memory_threshold_mb", 0);
+ Preferences::AddUintVarCache(&sLowCommitSpaceThreshold,
+ "memory.low_commit_space_threshold_mb", 256);
+ Preferences::AddUintVarCache(&sLowMemoryNotificationIntervalMS,
+ "memory.low_memory_notification_interval_ms",
+ 10000);
+
+ RegisterStrongMemoryReporter(new LowEventsReporter());
+ RegisterLowMemoryEventsVirtualDistinguishedAmount(
+ LowMemoryEventsVirtualDistinguishedAmount);
+ RegisterLowMemoryEventsPhysicalDistinguishedAmount(
+ LowMemoryEventsPhysicalDistinguishedAmount);
+ sHooksActive = true;
+#endif
+
+ // This object is held alive by the observer service.
+ RefPtr<nsMemoryPressureWatcher> watcher = new nsMemoryPressureWatcher();
+ watcher->Init();
+}
+
+void
+Init()
+{
+ // Do nothing on x86-64, because nsWindowsDllInterceptor is not thread-safe
+ // on 64-bit. (On 32-bit, it's probably thread-safe.) Even if we run Init()
+ // before any other of our threads are running, another process may have
+ // started a remote thread which could call VirtualAlloc!
+ //
+ // Moreover, the benefit of this code is less clear when we're a 64-bit
+ // process, because we aren't going to run out of virtual memory, and the
+ // system is likely to have a fair bit of physical memory.
+
+#if defined(_M_IX86) && defined(XP_WIN)
+ // Don't register the hooks if we're a build instrumented for PGO: If we're
+ // an instrumented build, the compiler adds function calls all over the place
+ // which may call VirtualAlloc; this makes it hard to prevent
+ // VirtualAllocHook from reentering itself.
+ if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED")) {
+ sKernel32Intercept.Init("Kernel32.dll");
+ sKernel32Intercept.AddHook("VirtualAlloc",
+ reinterpret_cast<intptr_t>(VirtualAllocHook),
+ reinterpret_cast<void**>(&sVirtualAllocOrig));
+ sKernel32Intercept.AddHook("MapViewOfFile",
+ reinterpret_cast<intptr_t>(MapViewOfFileHook),
+ reinterpret_cast<void**>(&sMapViewOfFileOrig));
+
+ sGdi32Intercept.Init("Gdi32.dll");
+ sGdi32Intercept.AddHook("CreateDIBSection",
+ reinterpret_cast<intptr_t>(CreateDIBSectionHook),
+ reinterpret_cast<void**>(&sCreateDIBSectionOrig));
+ }
+
+ sInitialized = true;
+#endif
+}
+
+} // namespace AvailableMemoryTracker
+} // namespace mozilla
diff --git a/xpcom/base/AvailableMemoryTracker.h b/xpcom/base/AvailableMemoryTracker.h
new file mode 100644
index 000000000..33572f9c7
--- /dev/null
+++ b/xpcom/base/AvailableMemoryTracker.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AvailableMemoryTracker_h
+#define mozilla_AvailableMemoryTracker_h
+
+namespace mozilla {
+namespace AvailableMemoryTracker {
+
+// The AvailableMemoryTracker launches a memory pressure watcher on all
+// platforms to react to low-memory situations and on Windows it implements
+// the full functionality used to monitor how much memory is available.
+//
+// Init() must be called before any other threads have started, because it
+// modifies the in-memory implementations of some DLL functions in
+// non-thread-safe ways.
+//
+// The hooks don't do anything until Activate() is called. It's an error to
+// call Activate() without first calling Init().
+
+void Init();
+void Activate();
+
+} // namespace AvailableMemoryTracker
+} // namespace mozilla
+
+#endif // ifndef mozilla_AvailableMemoryTracker_h
diff --git a/xpcom/base/ClearOnShutdown.cpp b/xpcom/base/ClearOnShutdown.cpp
new file mode 100644
index 000000000..bfb3142bc
--- /dev/null
+++ b/xpcom/base/ClearOnShutdown.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ClearOnShutdown.h"
+
+namespace mozilla {
+namespace ClearOnShutdown_Internal {
+
+Array<StaticAutoPtr<ShutdownList>,
+ static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
+ShutdownPhase sCurrentShutdownPhase = ShutdownPhase::NotInShutdown;
+
+} // namespace ClearOnShutdown_Internal
+
+// Called when XPCOM is shutting down, after all shutdown notifications have
+// been sent and after all threads' event loops have been purged.
+void
+KillClearOnShutdown(ShutdownPhase aPhase)
+{
+ using namespace ClearOnShutdown_Internal;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ // Shutdown only goes one direction...
+ MOZ_ASSERT(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase));
+
+ // It's impossible to add an entry for a "past" phase; this is blocked in
+ // ClearOnShutdown, but clear them out anyways in case there are phases
+ // that weren't passed to KillClearOnShutdown.
+ for (size_t phase = static_cast<size_t>(ShutdownPhase::First);
+ phase <= static_cast<size_t>(aPhase);
+ phase++) {
+ if (sShutdownObservers[static_cast<size_t>(phase)]) {
+ while (ShutdownObserver* observer = sShutdownObservers[static_cast<size_t>(phase)]->popFirst()) {
+ observer->Shutdown();
+ delete observer;
+ }
+ sShutdownObservers[static_cast<size_t>(phase)] = nullptr;
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/ClearOnShutdown.h b/xpcom/base/ClearOnShutdown.h
new file mode 100644
index 000000000..5c39c281c
--- /dev/null
+++ b/xpcom/base/ClearOnShutdown.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ClearOnShutdown_h
+#define mozilla_ClearOnShutdown_h
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Array.h"
+#include "MainThreadUtils.h"
+
+/*
+ * This header exports one public method in the mozilla namespace:
+ *
+ * template<class SmartPtr>
+ * void ClearOnShutdown(SmartPtr *aPtr, aPhase=ShutdownPhase::ShutdownFinal)
+ *
+ * This function takes a pointer to a smart pointer and nulls the smart pointer
+ * on shutdown (and a particular phase of shutdown as needed). If a phase
+ * is specified, the ptr will be cleared at the start of that phase. Also,
+ * if a phase has already occurred when ClearOnShutdown() is called it will
+ * cause a MOZ_ASSERT. In case a phase is not explicitly cleared we will
+ * clear it on the next phase that occurs.
+ *
+ * This is useful if you have a global smart pointer object which you don't
+ * want to "leak" on shutdown.
+ *
+ * Although ClearOnShutdown will work with any smart pointer (i.e., nsCOMPtr,
+ * nsRefPtr, nsAutoPtr, StaticRefPtr, and StaticAutoPtr), you probably want to
+ * use it only with StaticRefPtr and StaticAutoPtr. There is no way to undo a
+ * call to ClearOnShutdown, so you can call it only on smart pointers which you
+ * know will live until the program shuts down. In practice, these are likely
+ * global variables, which should be Static{Ref,Auto}Ptr.
+ *
+ * ClearOnShutdown is currently main-thread only because we don't want to
+ * accidentally free an object from a different thread than the one it was
+ * created on.
+ */
+
+namespace mozilla {
+
+// Must be contiguous starting at 0
+enum class ShutdownPhase {
+ NotInShutdown = 0,
+ WillShutdown,
+ Shutdown,
+ ShutdownThreads,
+ ShutdownLoaders,
+ ShutdownFinal,
+ ShutdownPhase_Length, // never pass this value
+ First = WillShutdown, // for iteration
+ Last = ShutdownFinal
+};
+
+namespace ClearOnShutdown_Internal {
+
+class ShutdownObserver : public LinkedListElement<ShutdownObserver>
+{
+public:
+ virtual void Shutdown() = 0;
+ virtual ~ShutdownObserver()
+ {
+ }
+};
+
+template<class SmartPtr>
+class PointerClearer : public ShutdownObserver
+{
+public:
+ explicit PointerClearer(SmartPtr* aPtr)
+ : mPtr(aPtr)
+ {
+ }
+
+ virtual void Shutdown() override
+ {
+ if (mPtr) {
+ *mPtr = nullptr;
+ }
+ }
+
+private:
+ SmartPtr* mPtr;
+};
+
+typedef LinkedList<ShutdownObserver> ShutdownList;
+extern Array<StaticAutoPtr<ShutdownList>,
+ static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
+extern ShutdownPhase sCurrentShutdownPhase;
+
+} // namespace ClearOnShutdown_Internal
+
+template<class SmartPtr>
+inline void
+ClearOnShutdown(SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal)
+{
+ using namespace ClearOnShutdown_Internal;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
+
+ // Adding a ClearOnShutdown for a "past" phase is an error.
+ if (!(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase))) {
+ MOZ_ASSERT(false, "ClearOnShutdown for phase that already was cleared");
+ *aPtr = nullptr;
+ return;
+ }
+
+ if (!(sShutdownObservers[static_cast<size_t>(aPhase)])) {
+ sShutdownObservers[static_cast<size_t>(aPhase)] = new ShutdownList();
+ }
+ sShutdownObservers[static_cast<size_t>(aPhase)]->insertBack(new PointerClearer<SmartPtr>(aPtr));
+}
+
+// Called when XPCOM is shutting down, after all shutdown notifications have
+// been sent and after all threads' event loops have been purged.
+void KillClearOnShutdown(ShutdownPhase aPhase);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/CodeAddressService.h b/xpcom/base/CodeAddressService.h
new file mode 100644
index 000000000..7f91f93a6
--- /dev/null
+++ b/xpcom/base/CodeAddressService.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CodeAddressService_h__
+#define CodeAddressService_h__
+
+#include "mozilla/Assertions.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Types.h"
+
+#include "mozilla/StackWalk.h"
+
+namespace mozilla {
+
+// This class is used to print details about code locations.
+//
+// |StringTable| must implement an Intern() method that returns an interned
+// copy of the string that was passed in, as well as a standard
+// SizeOfExcludingThis() method.
+//
+// |StringAlloc| must implement |copy| and |free|. |copy| copies a string,
+// while |free| is used to free strings created by |copy|.
+//
+// |DescribeCodeAddressLock| is needed when the callers may be holding a lock
+// used by MozDescribeCodeAddress. |DescribeCodeAddressLock| must implement
+// static methods IsLocked(), Unlock() and Lock().
+template <class StringTable,
+ class StringAlloc,
+ class DescribeCodeAddressLock>
+class CodeAddressService
+{
+ // GetLocation() is the key function in this class. It's basically a wrapper
+ // around MozDescribeCodeAddress.
+ //
+ // However, MozDescribeCodeAddress is very slow on some platforms, and we
+ // have lots of repeated (i.e. same PC) calls to it. So we do some caching
+ // of results. Each cached result includes two strings (|mFunction| and
+ // |mLibrary|), so we also optimize them for space in the following ways.
+ //
+ // - The number of distinct library names is small, e.g. a few dozen. There
+ // is lots of repetition, especially of libxul. So we intern them in their
+ // own table, which saves space over duplicating them for each cache entry.
+ //
+ // - The number of distinct function names is much higher, so we duplicate
+ // them in each cache entry. That's more space-efficient than interning
+ // because entries containing single-occurrence function names are quickly
+ // overwritten, and their copies released. In addition, empty function
+ // names are common, so we use nullptr to represent them compactly.
+
+ StringTable mLibraryStrings;
+
+ struct Entry
+ {
+ const void* mPc;
+ char* mFunction; // owned by the Entry; may be null
+ const char* mLibrary; // owned by mLibraryStrings; never null
+ // in a non-empty entry is in use
+ ptrdiff_t mLOffset;
+ char* mFileName; // owned by the Entry; may be null
+ uint32_t mLineNo:31;
+ uint32_t mInUse:1; // is the entry used?
+
+ Entry()
+ : mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0),
+ mFileName(nullptr), mLineNo(0), mInUse(0)
+ {}
+
+ ~Entry()
+ {
+ // We don't free mLibrary because it's externally owned.
+ StringAlloc::free(mFunction);
+ StringAlloc::free(mFileName);
+ }
+
+ void Replace(const void* aPc, const char* aFunction,
+ const char* aLibrary, ptrdiff_t aLOffset,
+ const char* aFileName, unsigned long aLineNo)
+ {
+ mPc = aPc;
+
+ // Convert "" to nullptr. Otherwise, make a copy of the name.
+ StringAlloc::free(mFunction);
+ mFunction = !aFunction[0] ? nullptr : StringAlloc::copy(aFunction);
+ StringAlloc::free(mFileName);
+ mFileName = !aFileName[0] ? nullptr : StringAlloc::copy(aFileName);
+
+ mLibrary = aLibrary;
+ mLOffset = aLOffset;
+ mLineNo = aLineNo;
+
+ mInUse = 1;
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ // Don't measure mLibrary because it's externally owned.
+ size_t n = 0;
+ n += aMallocSizeOf(mFunction);
+ n += aMallocSizeOf(mFileName);
+ return n;
+ }
+ };
+
+ // A direct-mapped cache. When doing dmd::Analyze() just after starting
+ // desktop Firefox (which is similar to analyzing after a longer-running
+ // session, thanks to the limit on how many records we print), a cache with
+ // 2^24 entries (which approximates an infinite-entry cache) has a ~91% hit
+ // rate. A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB
+ // (on 32-bit platforms) or ~150 KiB (on 64-bit platforms).
+ static const size_t kNumEntries = 1 << 12;
+ static const size_t kMask = kNumEntries - 1;
+ Entry mEntries[kNumEntries];
+
+ size_t mNumCacheHits;
+ size_t mNumCacheMisses;
+
+public:
+ CodeAddressService()
+ : mEntries(), mNumCacheHits(0), mNumCacheMisses(0)
+ {
+ }
+
+ void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
+ size_t aBufLen)
+ {
+ MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
+
+ uint32_t index = HashGeneric(aPc) & kMask;
+ MOZ_ASSERT(index < kNumEntries);
+ Entry& entry = mEntries[index];
+
+ if (!entry.mInUse || entry.mPc != aPc) {
+ mNumCacheMisses++;
+
+ // MozDescribeCodeAddress can (on Linux) acquire a lock inside
+ // the shared library loader. Another thread might call malloc
+ // while holding that lock (when loading a shared library). So
+ // we have to exit the lock around this call. For details, see
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=363334#c3
+ MozCodeAddressDetails details;
+ {
+ DescribeCodeAddressLock::Unlock();
+ (void)MozDescribeCodeAddress(const_cast<void*>(aPc), &details);
+ DescribeCodeAddressLock::Lock();
+ }
+
+ const char* library = mLibraryStrings.Intern(details.library);
+ entry.Replace(aPc, details.function, library, details.loffset,
+ details.filename, details.lineno);
+
+ } else {
+ mNumCacheHits++;
+ }
+
+ MOZ_ASSERT(entry.mPc == aPc);
+
+ MozFormatCodeAddress(aBuf, aBufLen, aFrameNumber, entry.mPc,
+ entry.mFunction, entry.mLibrary, entry.mLOffset,
+ entry.mFileName, entry.mLineNo);
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = aMallocSizeOf(this);
+ for (uint32_t i = 0; i < kNumEntries; i++) {
+ n += mEntries[i].SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ n += mLibraryStrings.SizeOfExcludingThis(aMallocSizeOf);
+
+ return n;
+ }
+
+ size_t CacheCapacity() const { return kNumEntries; }
+
+ size_t CacheCount() const
+ {
+ size_t n = 0;
+ for (size_t i = 0; i < kNumEntries; i++) {
+ if (mEntries[i].mInUse) {
+ n++;
+ }
+ }
+ return n;
+ }
+
+ size_t NumCacheHits() const { return mNumCacheHits; }
+ size_t NumCacheMisses() const { return mNumCacheMisses; }
+};
+
+} // namespace mozilla
+
+#endif // CodeAddressService_h__
diff --git a/xpcom/base/CountingAllocatorBase.h b/xpcom/base/CountingAllocatorBase.h
new file mode 100644
index 000000000..fb4d2ffe8
--- /dev/null
+++ b/xpcom/base/CountingAllocatorBase.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CountingAllocatorBase_h
+#define CountingAllocatorBase_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "nsIMemoryReporter.h"
+
+namespace mozilla {
+
+// This CRTP class handles several details of wrapping allocators and should
+// be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
+// and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory
+// reporter for a particular third party library:
+//
+// class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
+// {
+// ...
+// NS_IMETHOD
+// CollectReports(nsIHandleReportCallback* aHandleReport,
+// nsISupports* aData, bool aAnonymize) override
+// {
+// MOZ_COLLECT_REPORT(
+// "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
+// MemoryAllocated(),
+// "A description of what we are reporting.");
+//
+// return NS_OK;
+// }
+// };
+//
+// ...somewhere later in the code...
+// SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
+// MyMemoryReporter::CountingFree);
+template<typename T>
+class CountingAllocatorBase
+{
+public:
+ CountingAllocatorBase()
+ {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount| being
+ // static.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ static size_t
+ MemoryAllocated()
+ {
+ return sAmount;
+ }
+
+ static void*
+ CountingMalloc(size_t size)
+ {
+ void* p = malloc(size);
+ sAmount += MallocSizeOfOnAlloc(p);
+ return p;
+ }
+
+ static void*
+ CountingCalloc(size_t nmemb, size_t size)
+ {
+ void* p = calloc(nmemb, size);
+ sAmount += MallocSizeOfOnAlloc(p);
+ return p;
+ }
+
+ static void*
+ CountingRealloc(void* p, size_t size)
+ {
+ size_t oldsize = MallocSizeOfOnFree(p);
+ void *pnew = realloc(p, size);
+ if (pnew) {
+ size_t newsize = MallocSizeOfOnAlloc(pnew);
+ sAmount += newsize - oldsize;
+ } else if (size == 0) {
+ // We asked for a 0-sized (re)allocation of some existing pointer
+ // and received NULL in return. 0-sized allocations are permitted
+ // to either return NULL or to allocate a unique object per call (!).
+ // For a malloc implementation that chooses the second strategy,
+ // that allocation may fail (unlikely, but possible).
+ //
+ // Given a NULL return value and an allocation size of 0, then, we
+ // don't know if that means the original pointer was freed or if
+ // the allocation of the unique object failed. If the original
+ // pointer was freed, then we have nothing to do here. If the
+ // allocation of the unique object failed, the original pointer is
+ // still valid and we ought to undo the decrement from above.
+ // However, we have no way of knowing how the underlying realloc
+ // implementation is behaving. Assuming that the original pointer
+ // was freed is the safest course of action. We do, however, need
+ // to note that we freed memory.
+ sAmount -= oldsize;
+ } else {
+ // realloc failed. The amount allocated hasn't changed.
+ }
+ return pnew;
+ }
+
+ // Some library code expects that realloc(x, 0) will free x, which is not
+ // the behavior of the version of jemalloc we're using, so this wrapped
+ // version of realloc is needed.
+ static void*
+ CountingFreeingRealloc(void* p, size_t size)
+ {
+ if (size == 0) {
+ CountingFree(p);
+ return nullptr;
+ }
+ return CountingRealloc(p, size);
+ }
+
+ static void
+ CountingFree(void* p)
+ {
+ sAmount -= MallocSizeOfOnFree(p);
+ free(p);
+ }
+
+private:
+ // |sAmount| can be (implicitly) accessed by multiple threads, so it
+ // must be thread-safe.
+ static Atomic<size_t> sAmount;
+
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+};
+
+} // namespace mozilla
+
+#endif // CountingAllocatorBase_h
diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp
new file mode 100644
index 000000000..87e123078
--- /dev/null
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -0,0 +1,1717 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// We're dividing JS objects into 3 categories:
+//
+// 1. "real" roots, held by the JS engine itself or rooted through the root
+// and lock JS APIs. Roots from this category are considered black in the
+// cycle collector, any cycle they participate in is uncollectable.
+//
+// 2. certain roots held by C++ objects that are guaranteed to be alive.
+// Roots from this category are considered black in the cycle collector,
+// and any cycle they participate in is uncollectable. These roots are
+// traced from TraceNativeBlackRoots.
+//
+// 3. all other roots held by C++ objects that participate in cycle
+// collection, held by us (see TraceNativeGrayRoots). Roots from this
+// category are considered grey in the cycle collector; whether or not
+// they are collected depends on the objects that hold them.
+//
+// Note that if a root is in multiple categories the fact that it is in
+// category 1 or 2 that takes precedence, so it will be considered black.
+//
+// During garbage collection we switch to an additional mark color (gray)
+// when tracing inside TraceNativeGrayRoots. This allows us to walk those
+// roots later on and add all objects reachable only from them to the
+// cycle collector.
+//
+// Phases:
+//
+// 1. marking of the roots in category 1 by having the JS GC do its marking
+// 2. marking of the roots in category 2 by having the JS GC call us back
+// (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
+// 3. marking of the roots in category 3 by TraceNativeGrayRoots using an
+// additional color (gray).
+// 4. end of GC, GC can sweep its heap
+//
+// At some later point, when the cycle collector runs:
+//
+// 5. walk gray objects and add them to the cycle collector, cycle collect
+//
+// JS objects that are part of cycles the cycle collector breaks will be
+// collected by the next JS GC.
+//
+// If WantAllTraces() is false the cycle collector will not traverse roots
+// from category 1 or any JS objects held by them. Any JS objects they hold
+// will already be marked by the JS GC and will thus be colored black
+// themselves. Any C++ objects they hold will have a missing (untraversed)
+// edge from the JS object to the C++ object and so it will be marked black
+// too. This decreases the number of objects that the cycle collector has to
+// deal with.
+// To improve debugging, if WantAllTraces() is true all JS objects are
+// traversed.
+
+#include "mozilla/CycleCollectedJSContext.h"
+#include <algorithm>
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/Move.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimelineConsumers.h"
+#include "mozilla/TimelineMarker.h"
+#include "mozilla/Unused.h"
+#include "mozilla/DebuggerOnGCRunnable.h"
+#include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseBinding.h"
+#include "mozilla/dom/PromiseDebugging.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "jsprf.h"
+#include "js/Debug.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollectionNoteRootCallback.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCycleCollector.h"
+#include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
+#include "nsWrapperCache.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+#include "nsIException.h"
+#include "nsIPlatformInfo.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "xpcpublic.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+struct DeferredFinalizeFunctionHolder
+{
+ DeferredFinalizeFunction run;
+ void* data;
+};
+
+class IncrementalFinalizeRunnable : public Runnable
+{
+ typedef AutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
+ typedef CycleCollectedJSContext::DeferredFinalizerTable DeferredFinalizerTable;
+
+ CycleCollectedJSContext* mContext;
+ DeferredFinalizeArray mDeferredFinalizeFunctions;
+ uint32_t mFinalizeFunctionToRun;
+ bool mReleasing;
+
+ static const PRTime SliceMillis = 5; /* ms */
+
+public:
+ IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
+ DeferredFinalizerTable& aFinalizerTable);
+ virtual ~IncrementalFinalizeRunnable();
+
+ void ReleaseNow(bool aLimited);
+
+ NS_DECL_NSIRUNNABLE
+};
+
+} // namespace mozilla
+
+struct NoteWeakMapChildrenTracer : public JS::CallbackTracer
+{
+ NoteWeakMapChildrenTracer(JSContext* aCx,
+ nsCycleCollectionNoteRootCallback& aCb)
+ : JS::CallbackTracer(aCx), mCb(aCb), mTracedAny(false), mMap(nullptr),
+ mKey(nullptr), mKeyDelegate(nullptr)
+ {
+ }
+ void onChild(const JS::GCCellPtr& aThing) override;
+ nsCycleCollectionNoteRootCallback& mCb;
+ bool mTracedAny;
+ JSObject* mMap;
+ JS::GCCellPtr mKey;
+ JSObject* mKeyDelegate;
+};
+
+void
+NoteWeakMapChildrenTracer::onChild(const JS::GCCellPtr& aThing)
+{
+ if (aThing.is<JSString>()) {
+ return;
+ }
+
+ if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
+ return;
+ }
+
+ if (AddToCCKind(aThing.kind())) {
+ mCb.NoteWeakMapping(mMap, mKey, mKeyDelegate, aThing);
+ mTracedAny = true;
+ } else {
+ JS::TraceChildren(this, aThing);
+ }
+}
+
+struct NoteWeakMapsTracer : public js::WeakMapTracer
+{
+ NoteWeakMapsTracer(JSContext* aCx, nsCycleCollectionNoteRootCallback& aCccb)
+ : js::WeakMapTracer(aCx), mCb(aCccb), mChildTracer(aCx, aCccb)
+ {
+ }
+ void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override;
+ nsCycleCollectionNoteRootCallback& mCb;
+ NoteWeakMapChildrenTracer mChildTracer;
+};
+
+void
+NoteWeakMapsTracer::trace(JSObject* aMap, JS::GCCellPtr aKey,
+ JS::GCCellPtr aValue)
+{
+ // If nothing that could be held alive by this entry is marked gray, return.
+ if ((!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
+ MOZ_LIKELY(!mCb.WantAllTraces())) {
+ if (!aValue || !JS::GCThingIsMarkedGray(aValue) || aValue.is<JSString>()) {
+ return;
+ }
+ }
+
+ // The cycle collector can only properly reason about weak maps if it can
+ // reason about the liveness of their keys, which in turn requires that
+ // the key can be represented in the cycle collector graph. All existing
+ // uses of weak maps use either objects or scripts as keys, which are okay.
+ MOZ_ASSERT(AddToCCKind(aKey.kind()));
+
+ // As an emergency fallback for non-debug builds, if the key is not
+ // representable in the cycle collector graph, we treat it as marked. This
+ // can cause leaks, but is preferable to ignoring the binding, which could
+ // cause the cycle collector to free live objects.
+ if (!AddToCCKind(aKey.kind())) {
+ aKey = nullptr;
+ }
+
+ JSObject* kdelegate = nullptr;
+ if (aKey.is<JSObject>()) {
+ kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
+ }
+
+ if (AddToCCKind(aValue.kind())) {
+ mCb.NoteWeakMapping(aMap, aKey, kdelegate, aValue);
+ } else {
+ mChildTracer.mTracedAny = false;
+ mChildTracer.mMap = aMap;
+ mChildTracer.mKey = aKey;
+ mChildTracer.mKeyDelegate = kdelegate;
+
+ if (!aValue.is<JSString>()) {
+ JS::TraceChildren(&mChildTracer, aValue);
+ }
+
+ // The delegate could hold alive the key, so report something to the CC
+ // if we haven't already.
+ if (!mChildTracer.mTracedAny &&
+ aKey && JS::GCThingIsMarkedGray(aKey) && kdelegate) {
+ mCb.NoteWeakMapping(aMap, aKey, kdelegate, nullptr);
+ }
+ }
+}
+
+// This is based on the logic in FixWeakMappingGrayBitsTracer::trace.
+struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
+{
+ explicit FixWeakMappingGrayBitsTracer(JSContext* aCx)
+ : js::WeakMapTracer(aCx)
+ {
+ }
+
+ void
+ FixAll()
+ {
+ do {
+ mAnyMarked = false;
+ js::TraceWeakMaps(this);
+ } while (mAnyMarked);
+ }
+
+ void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override
+ {
+ // If nothing that could be held alive by this entry is marked gray, return.
+ bool keyMightNeedMarking = aKey && JS::GCThingIsMarkedGray(aKey);
+ bool valueMightNeedMarking = aValue && JS::GCThingIsMarkedGray(aValue) &&
+ aValue.kind() != JS::TraceKind::String;
+ if (!keyMightNeedMarking && !valueMightNeedMarking) {
+ return;
+ }
+
+ if (!AddToCCKind(aKey.kind())) {
+ aKey = nullptr;
+ }
+
+ if (keyMightNeedMarking && aKey.is<JSObject>()) {
+ JSObject* kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
+ if (kdelegate && !JS::ObjectIsMarkedGray(kdelegate) &&
+ (!aMap || !JS::ObjectIsMarkedGray(aMap)))
+ {
+ if (JS::UnmarkGrayGCThingRecursively(aKey)) {
+ mAnyMarked = true;
+ }
+ }
+ }
+
+ if (aValue && JS::GCThingIsMarkedGray(aValue) &&
+ (!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
+ (!aMap || !JS::ObjectIsMarkedGray(aMap)) &&
+ aValue.kind() != JS::TraceKind::Shape) {
+ if (JS::UnmarkGrayGCThingRecursively(aValue)) {
+ mAnyMarked = true;
+ }
+ }
+ }
+
+ MOZ_INIT_OUTSIDE_CTOR bool mAnyMarked;
+};
+
+static void
+CheckParticipatesInCycleCollection(JS::GCCellPtr aThing, const char* aName,
+ void* aClosure)
+{
+ bool* cycleCollectionEnabled = static_cast<bool*>(aClosure);
+
+ if (*cycleCollectionEnabled) {
+ return;
+ }
+
+ if (AddToCCKind(aThing.kind()) && JS::GCThingIsMarkedGray(aThing)) {
+ *cycleCollectionEnabled = true;
+ }
+}
+
+NS_IMETHODIMP
+JSGCThingParticipant::Traverse(void* aPtr,
+ nsCycleCollectionTraversalCallback& aCb)
+{
+ auto runtime = reinterpret_cast<CycleCollectedJSContext*>(
+ reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSContext,
+ mGCThingCycleCollectorGlobal));
+
+ JS::GCCellPtr cellPtr(aPtr, JS::GCThingTraceKind(aPtr));
+ runtime->TraverseGCThing(CycleCollectedJSContext::TRAVERSE_FULL, cellPtr, aCb);
+ return NS_OK;
+}
+
+// NB: This is only used to initialize the participant in
+// CycleCollectedJSContext. It should never be used directly.
+static JSGCThingParticipant sGCThingCycleCollectorGlobal;
+
+NS_IMETHODIMP
+JSZoneParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
+{
+ auto runtime = reinterpret_cast<CycleCollectedJSContext*>(
+ reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSContext,
+ mJSZoneCycleCollectorGlobal));
+
+ MOZ_ASSERT(!aCb.WantAllTraces());
+ JS::Zone* zone = static_cast<JS::Zone*>(aPtr);
+
+ runtime->TraverseZone(zone, aCb);
+ return NS_OK;
+}
+
+struct TraversalTracer : public JS::CallbackTracer
+{
+ TraversalTracer(JSContext* aCx, nsCycleCollectionTraversalCallback& aCb)
+ : JS::CallbackTracer(aCx, DoNotTraceWeakMaps), mCb(aCb)
+ {
+ }
+ void onChild(const JS::GCCellPtr& aThing) override;
+ nsCycleCollectionTraversalCallback& mCb;
+};
+
+void
+TraversalTracer::onChild(const JS::GCCellPtr& aThing)
+{
+ // Don't traverse non-gray objects, unless we want all traces.
+ if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
+ return;
+ }
+
+ /*
+ * This function needs to be careful to avoid stack overflow. Normally, when
+ * AddToCCKind is true, the recursion terminates immediately as we just add
+ * |thing| to the CC graph. So overflow is only possible when there are long
+ * or cyclic chains of non-AddToCCKind GC things. Places where this can occur
+ * use special APIs to handle such chains iteratively.
+ */
+ if (AddToCCKind(aThing.kind())) {
+ if (MOZ_UNLIKELY(mCb.WantDebugInfo())) {
+ char buffer[200];
+ getTracingEdgeName(buffer, sizeof(buffer));
+ mCb.NoteNextEdgeName(buffer);
+ }
+ mCb.NoteJSChild(aThing);
+ } else if (aThing.is<js::Shape>()) {
+ // The maximum depth of traversal when tracing a Shape is unbounded, due to
+ // the parent pointers on the shape.
+ JS_TraceShapeCycleCollectorChildren(this, aThing);
+ } else if (aThing.is<js::ObjectGroup>()) {
+ // The maximum depth of traversal when tracing an ObjectGroup is unbounded,
+ // due to information attached to the groups which can lead other groups to
+ // be traced.
+ JS_TraceObjectGroupCycleCollectorChildren(this, aThing);
+ } else if (!aThing.is<JSString>()) {
+ JS::TraceChildren(this, aThing);
+ }
+}
+
+static void
+NoteJSChildGrayWrapperShim(void* aData, JS::GCCellPtr aThing)
+{
+ TraversalTracer* trc = static_cast<TraversalTracer*>(aData);
+ trc->onChild(aThing);
+}
+
+/*
+ * The cycle collection participant for a Zone is intended to produce the same
+ * results as if all of the gray GCthings in a zone were merged into a single node,
+ * except for self-edges. This avoids the overhead of representing all of the GCthings in
+ * the zone in the cycle collector graph, which should be much faster if many of
+ * the GCthings in the zone are gray.
+ *
+ * Zone merging should not always be used, because it is a conservative
+ * approximation of the true cycle collector graph that can incorrectly identify some
+ * garbage objects as being live. For instance, consider two cycles that pass through a
+ * zone, where one is garbage and the other is live. If we merge the entire
+ * zone, the cycle collector will think that both are alive.
+ *
+ * We don't have to worry about losing track of a garbage cycle, because any such garbage
+ * cycle incorrectly identified as live must contain at least one C++ to JS edge, and
+ * XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure
+ * C++ garbage cycles, which must always be properly identified, because we clear the
+ * purple buffer during every CC, which may contain the last reference to a garbage
+ * cycle.)
+ */
+
+// NB: This is only used to initialize the participant in
+// CycleCollectedJSContext. It should never be used directly.
+static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
+
+static
+void JSObjectsTenuredCb(JSContext* aContext, void* aData)
+{
+ static_cast<CycleCollectedJSContext*>(aData)->JSObjectsTenured();
+}
+
+bool
+mozilla::GetBuildId(JS::BuildIdCharVector* aBuildID)
+{
+ nsCOMPtr<nsIPlatformInfo> info = do_GetService("@mozilla.org/xre/app-info;1");
+ if (!info) {
+ return false;
+ }
+
+ nsCString buildID;
+ nsresult rv = info->GetPlatformBuildID(buildID);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (!aBuildID->resize(buildID.Length())) {
+ return false;
+ }
+
+ for (size_t i = 0; i < buildID.Length(); i++) {
+ (*aBuildID)[i] = buildID[i];
+ }
+
+ return true;
+}
+
+CycleCollectedJSContext::CycleCollectedJSContext()
+ : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal)
+ , mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
+ , mJSContext(nullptr)
+ , mPrevGCSliceCallback(nullptr)
+ , mPrevGCNurseryCollectionCallback(nullptr)
+ , mJSHolders(256)
+ , mDoingStableStates(false)
+ , mDisableMicroTaskCheckpoint(false)
+ , mOutOfMemoryState(OOMState::OK)
+ , mLargeAllocationFailureState(OOMState::OK)
+{
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ mOwningThread = thread.forget().downcast<nsThread>().take();
+ MOZ_RELEASE_ASSERT(mOwningThread);
+}
+
+CycleCollectedJSContext::~CycleCollectedJSContext()
+{
+ // If the allocation failed, here we are.
+ if (!mJSContext) {
+ return;
+ }
+
+ MOZ_ASSERT(!mDeferredFinalizerTable.Count());
+
+ // Last chance to process any events.
+ ProcessMetastableStateQueue(mBaseRecursionDepth);
+ MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
+
+ ProcessStableStateQueue();
+ MOZ_ASSERT(mStableStateEvents.IsEmpty());
+
+ // Clear mPendingException first, since it might be cycle collected.
+ mPendingException = nullptr;
+
+ MOZ_ASSERT(mDebuggerPromiseMicroTaskQueue.empty());
+ MOZ_ASSERT(mPromiseMicroTaskQueue.empty());
+
+#ifdef SPIDERMONKEY_PROMISE
+ mUncaughtRejections.reset();
+ mConsumedRejections.reset();
+#endif // SPIDERMONKEY_PROMISE
+
+ JS_DestroyContext(mJSContext);
+ mJSContext = nullptr;
+ nsCycleCollector_forgetJSContext();
+
+ mozilla::dom::DestroyScriptSettings();
+
+ mOwningThread->SetScriptObserver(nullptr);
+ NS_RELEASE(mOwningThread);
+}
+
+static void
+MozCrashWarningReporter(JSContext*, JSErrorReport*)
+{
+ MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
+}
+
+nsresult
+CycleCollectedJSContext::Initialize(JSContext* aParentContext,
+ uint32_t aMaxBytes,
+ uint32_t aMaxNurseryBytes)
+{
+ MOZ_ASSERT(!mJSContext);
+
+ mOwningThread->SetScriptObserver(this);
+ // The main thread has a base recursion depth of 0, workers of 1.
+ mBaseRecursionDepth = RecursionDepth();
+
+ mozilla::dom::InitScriptSettings();
+ mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentContext);
+ if (!mJSContext) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_GetCurrentThread()->SetCanInvokeJS(true);
+
+ if (!JS_AddExtraGCRootsTracer(mJSContext, TraceBlackJS, this)) {
+ MOZ_CRASH("JS_AddExtraGCRootsTracer failed");
+ }
+ JS_SetGrayGCRootsTracer(mJSContext, TraceGrayJS, this);
+ JS_SetGCCallback(mJSContext, GCCallback, this);
+ mPrevGCSliceCallback = JS::SetGCSliceCallback(mJSContext, GCSliceCallback);
+
+ if (NS_IsMainThread()) {
+ // We would like to support all threads here, but the way timeline consumers
+ // are set up currently, you can either add a marker for one specific
+ // docshell, or for every consumer globally. We would like to add a marker
+ // for every consumer observing anything on this thread, but that is not
+ // currently possible. For now, add global markers only when we are on the
+ // main thread, since the UI for this tracing data only displays data
+ // relevant to the main-thread.
+ mPrevGCNurseryCollectionCallback = JS::SetGCNurseryCollectionCallback(
+ mJSContext, GCNurseryCollectionCallback);
+ }
+
+ JS_SetObjectsTenuredCallback(mJSContext, JSObjectsTenuredCb, this);
+ JS::SetOutOfMemoryCallback(mJSContext, OutOfMemoryCallback, this);
+ JS::SetLargeAllocationFailureCallback(mJSContext,
+ LargeAllocationFailureCallback, this);
+ JS_SetDestroyZoneCallback(mJSContext, XPCStringConvert::FreeZoneCache);
+ JS_SetSweepZoneCallback(mJSContext, XPCStringConvert::ClearZoneCache);
+ JS::SetBuildIdOp(mJSContext, GetBuildId);
+ JS::SetWarningReporter(mJSContext, MozCrashWarningReporter);
+#ifdef MOZ_CRASHREPORTER
+ js::AutoEnterOOMUnsafeRegion::setAnnotateOOMAllocationSizeCallback(
+ CrashReporter::AnnotateOOMAllocationSize);
+#endif
+
+ static js::DOMCallbacks DOMcallbacks = {
+ InstanceClassHasProtoAtDepth
+ };
+ SetDOMCallbacks(mJSContext, &DOMcallbacks);
+ js::SetScriptEnvironmentPreparer(mJSContext, &mEnvironmentPreparer);
+
+ JS::SetGetIncumbentGlobalCallback(mJSContext, GetIncumbentGlobalCallback);
+
+#ifdef SPIDERMONKEY_PROMISE
+ JS::SetEnqueuePromiseJobCallback(mJSContext, EnqueuePromiseJobCallback, this);
+ JS::SetPromiseRejectionTrackerCallback(mJSContext, PromiseRejectionTrackerCallback, this);
+ mUncaughtRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
+ mConsumedRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
+#endif // SPIDERMONKEY_PROMISE
+
+ JS::dbg::SetDebuggerMallocSizeOf(mJSContext, moz_malloc_size_of);
+
+ nsCycleCollector_registerJSContext(this);
+
+ return NS_OK;
+}
+
+size_t
+CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t n = 0;
+
+ // We're deliberately not measuring anything hanging off the entries in
+ // mJSHolders.
+ n += mJSHolders.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ return n;
+}
+
+void
+CycleCollectedJSContext::UnmarkSkippableJSHolders()
+{
+ for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
+ void* holder = iter.Key();
+ nsScriptObjectTracer*& tracer = iter.Data();
+ tracer->CanSkip(holder, true);
+ }
+}
+
+void
+CycleCollectedJSContext::DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const
+{
+ if (!aCb.WantDebugInfo()) {
+ aCb.DescribeGCedNode(aIsMarked, "JS Object");
+ return;
+ }
+
+ char name[72];
+ uint64_t compartmentAddress = 0;
+ if (aThing.is<JSObject>()) {
+ JSObject* obj = &aThing.as<JSObject>();
+ compartmentAddress = (uint64_t)js::GetObjectCompartment(obj);
+ const js::Class* clasp = js::GetObjectClass(obj);
+
+ // Give the subclass a chance to do something
+ if (DescribeCustomObjects(obj, clasp, name)) {
+ // Nothing else to do!
+ } else if (js::IsFunctionObject(obj)) {
+ JSFunction* fun = JS_GetObjectFunction(obj);
+ JSString* str = JS_GetFunctionDisplayId(fun);
+ if (str) {
+ JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(str);
+ nsAutoString chars;
+ AssignJSFlatString(chars, flat);
+ NS_ConvertUTF16toUTF8 fname(chars);
+ SprintfLiteral(name, "JS Object (Function - %s)", fname.get());
+ } else {
+ SprintfLiteral(name, "JS Object (Function)");
+ }
+ } else {
+ SprintfLiteral(name, "JS Object (%s)", clasp->name);
+ }
+ } else {
+ SprintfLiteral(name, "JS %s", JS::GCTraceKindToAscii(aThing.kind()));
+ }
+
+ // Disable printing global for objects while we figure out ObjShrink fallout.
+ aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
+}
+
+void
+CycleCollectedJSContext::NoteGCThingJSChildren(JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const
+{
+ MOZ_ASSERT(mJSContext);
+ TraversalTracer trc(mJSContext, aCb);
+ JS::TraceChildren(&trc, aThing);
+}
+
+void
+CycleCollectedJSContext::NoteGCThingXPCOMChildren(const js::Class* aClasp,
+ JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const
+{
+ MOZ_ASSERT(aClasp);
+ MOZ_ASSERT(aClasp == js::GetObjectClass(aObj));
+
+ if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) {
+ // Nothing else to do!
+ return;
+ }
+ // XXX This test does seem fragile, we should probably whitelist classes
+ // that do hold a strong reference, but that might not be possible.
+ else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
+ aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
+ aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
+ } else {
+ const DOMJSClass* domClass = GetDOMClass(aObj);
+ if (domClass) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
+ // It's possible that our object is an unforgeable holder object, in
+ // which case it doesn't actually have a C++ DOM object associated with
+ // it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
+ // that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
+ if (domClass->mDOMObjectIsISupports) {
+ aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
+ } else if (domClass->mParticipant) {
+ aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
+ domClass->mParticipant);
+ }
+ }
+ }
+}
+
+void
+CycleCollectedJSContext::TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb)
+{
+ bool isMarkedGray = JS::GCThingIsMarkedGray(aThing);
+
+ if (aTs == TRAVERSE_FULL) {
+ DescribeGCThing(!isMarkedGray, aThing, aCb);
+ }
+
+ // If this object is alive, then all of its children are alive. For JS objects,
+ // the black-gray invariant ensures the children are also marked black. For C++
+ // objects, the ref count from this object will keep them alive. Thus we don't
+ // need to trace our children, unless we are debugging using WantAllTraces.
+ if (!isMarkedGray && !aCb.WantAllTraces()) {
+ return;
+ }
+
+ if (aTs == TRAVERSE_FULL) {
+ NoteGCThingJSChildren(aThing, aCb);
+ }
+
+ if (aThing.is<JSObject>()) {
+ JSObject* obj = &aThing.as<JSObject>();
+ NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, aCb);
+ }
+}
+
+struct TraverseObjectShimClosure
+{
+ nsCycleCollectionTraversalCallback& cb;
+ CycleCollectedJSContext* self;
+};
+
+void
+CycleCollectedJSContext::TraverseZone(JS::Zone* aZone,
+ nsCycleCollectionTraversalCallback& aCb)
+{
+ MOZ_ASSERT(mJSContext);
+
+ /*
+ * We treat the zone as being gray. We handle non-gray GCthings in the
+ * zone by not reporting their children to the CC. The black-gray invariant
+ * ensures that any JS children will also be non-gray, and thus don't need to be
+ * added to the graph. For C++ children, not representing the edge from the
+ * non-gray JS GCthings to the C++ object will keep the child alive.
+ *
+ * We don't allow zone merging in a WantAllTraces CC, because then these
+ * assumptions don't hold.
+ */
+ aCb.DescribeGCedNode(false, "JS Zone");
+
+ /*
+ * Every JS child of everything in the zone is either in the zone
+ * or is a cross-compartment wrapper. In the former case, we don't need to
+ * represent these edges in the CC graph because JS objects are not ref counted.
+ * In the latter case, the JS engine keeps a map of these wrappers, which we
+ * iterate over. Edges between compartments in the same zone will add
+ * unnecessary loop edges to the graph (bug 842137).
+ */
+ TraversalTracer trc(mJSContext, aCb);
+ js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
+
+ /*
+ * To find C++ children of things in the zone, we scan every JS Object in
+ * the zone. Only JS Objects can have C++ children.
+ */
+ TraverseObjectShimClosure closure = { aCb, this };
+ js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
+}
+
+/* static */ void
+CycleCollectedJSContext::TraverseObjectShim(void* aData, JS::GCCellPtr aThing)
+{
+ TraverseObjectShimClosure* closure =
+ static_cast<TraverseObjectShimClosure*>(aData);
+
+ MOZ_ASSERT(aThing.is<JSObject>());
+ closure->self->TraverseGCThing(CycleCollectedJSContext::TRAVERSE_CPP,
+ aThing, closure->cb);
+}
+
+void
+CycleCollectedJSContext::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb)
+{
+ // NB: This is here just to preserve the existing XPConnect order. I doubt it
+ // would hurt to do this after the JS holders.
+ TraverseAdditionalNativeRoots(aCb);
+
+ for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
+ void* holder = iter.Key();
+ nsScriptObjectTracer*& tracer = iter.Data();
+
+ bool noteRoot = false;
+ if (MOZ_UNLIKELY(aCb.WantAllTraces())) {
+ noteRoot = true;
+ } else {
+ tracer->Trace(holder,
+ TraceCallbackFunc(CheckParticipatesInCycleCollection),
+ &noteRoot);
+ }
+
+ if (noteRoot) {
+ aCb.NoteNativeRoot(holder, tracer);
+ }
+ }
+}
+
+/* static */ void
+CycleCollectedJSContext::TraceBlackJS(JSTracer* aTracer, void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ self->TraceNativeBlackRoots(aTracer);
+}
+
+/* static */ void
+CycleCollectedJSContext::TraceGrayJS(JSTracer* aTracer, void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ // Mark these roots as gray so the CC can walk them later.
+ self->TraceNativeGrayRoots(aTracer);
+}
+
+/* static */ void
+CycleCollectedJSContext::GCCallback(JSContext* aContext,
+ JSGCStatus aStatus,
+ void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ MOZ_ASSERT(aContext == self->Context());
+
+ self->OnGC(aStatus);
+}
+
+/* static */ void
+CycleCollectedJSContext::GCSliceCallback(JSContext* aContext,
+ JS::GCProgress aProgress,
+ const JS::GCDescription& aDesc)
+{
+ CycleCollectedJSContext* self = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(self->Context() == aContext);
+
+ if (aProgress == JS::GC_CYCLE_END) {
+ JS::gcreason::Reason reason = aDesc.reason_;
+ Unused <<
+ NS_WARN_IF(NS_FAILED(DebuggerOnGCRunnable::Enqueue(aContext, aDesc)) &&
+ reason != JS::gcreason::SHUTDOWN_CC &&
+ reason != JS::gcreason::DESTROY_RUNTIME &&
+ reason != JS::gcreason::XPCONNECT_SHUTDOWN);
+ }
+
+ if (self->mPrevGCSliceCallback) {
+ self->mPrevGCSliceCallback(aContext, aProgress, aDesc);
+ }
+}
+
+class MinorGCMarker : public TimelineMarker
+{
+private:
+ JS::gcreason::Reason mReason;
+
+public:
+ MinorGCMarker(MarkerTracingType aTracingType,
+ JS::gcreason::Reason aReason)
+ : TimelineMarker("MinorGC",
+ aTracingType,
+ MarkerStackRequest::NO_STACK)
+ , mReason(aReason)
+ {
+ MOZ_ASSERT(aTracingType == MarkerTracingType::START ||
+ aTracingType == MarkerTracingType::END);
+ }
+
+ MinorGCMarker(JS::GCNurseryProgress aProgress,
+ JS::gcreason::Reason aReason)
+ : TimelineMarker("MinorGC",
+ aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
+ ? MarkerTracingType::START
+ : MarkerTracingType::END,
+ MarkerStackRequest::NO_STACK)
+ , mReason(aReason)
+ { }
+
+ virtual void
+ AddDetails(JSContext* aCx,
+ dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ auto reason = JS::gcreason::ExplainReason(mReason);
+ aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));
+ }
+ }
+
+ virtual UniquePtr<AbstractTimelineMarker>
+ Clone() override
+ {
+ auto clone = MakeUnique<MinorGCMarker>(GetTracingType(), mReason);
+ clone->SetCustomTime(GetTime());
+ return UniquePtr<AbstractTimelineMarker>(Move(clone));
+ }
+};
+
+/* static */ void
+CycleCollectedJSContext::GCNurseryCollectionCallback(JSContext* aContext,
+ JS::GCNurseryProgress aProgress,
+ JS::gcreason::Reason aReason)
+{
+ CycleCollectedJSContext* self = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(self->Context() == aContext);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (timelines && !timelines->IsEmpty()) {
+ UniquePtr<AbstractTimelineMarker> abstractMarker(
+ MakeUnique<MinorGCMarker>(aProgress, aReason));
+ timelines->AddMarkerForAllObservedDocShells(abstractMarker);
+ }
+
+ if (self->mPrevGCNurseryCollectionCallback) {
+ self->mPrevGCNurseryCollectionCallback(aContext, aProgress, aReason);
+ }
+}
+
+
+/* static */ void
+CycleCollectedJSContext::OutOfMemoryCallback(JSContext* aContext,
+ void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ MOZ_ASSERT(aContext == self->Context());
+
+ self->OnOutOfMemory();
+}
+
+/* static */ void
+CycleCollectedJSContext::LargeAllocationFailureCallback(void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ self->OnLargeAllocationFailure();
+}
+
+class PromiseJobRunnable final : public Runnable
+{
+public:
+ PromiseJobRunnable(JS::HandleObject aCallback, JS::HandleObject aAllocationSite,
+ nsIGlobalObject* aIncumbentGlobal)
+ : mCallback(new PromiseJobCallback(aCallback, aAllocationSite, aIncumbentGlobal))
+ {
+ }
+
+ virtual ~PromiseJobRunnable()
+ {
+ }
+
+protected:
+ NS_IMETHOD
+ Run() override
+ {
+ nsIGlobalObject* global = xpc::NativeGlobal(mCallback->CallbackPreserveColor());
+ if (global && !global->IsDying()) {
+ mCallback->Call("promise callback");
+ }
+ return NS_OK;
+ }
+
+private:
+ RefPtr<PromiseJobCallback> mCallback;
+};
+
+/* static */
+JSObject*
+CycleCollectedJSContext::GetIncumbentGlobalCallback(JSContext* aCx)
+{
+ nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal();
+ if (global) {
+ return global->GetGlobalJSObject();
+ }
+ return nullptr;
+}
+
+/* static */
+bool
+CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
+ JS::HandleObject aJob,
+ JS::HandleObject aAllocationSite,
+ JS::HandleObject aIncumbentGlobal,
+ void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+ MOZ_ASSERT(aCx == self->Context());
+ MOZ_ASSERT(Get() == self);
+
+ nsIGlobalObject* global = nullptr;
+ if (aIncumbentGlobal) {
+ global = xpc::NativeGlobal(aIncumbentGlobal);
+ }
+ nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
+ self->DispatchToMicroTask(runnable.forget());
+ return true;
+}
+
+#ifdef SPIDERMONKEY_PROMISE
+/* static */
+void
+CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
+ JS::HandleObject aPromise,
+ PromiseRejectionHandlingState state,
+ void* aData)
+{
+#ifdef DEBUG
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+#endif // DEBUG
+ MOZ_ASSERT(aCx == self->Context());
+ MOZ_ASSERT(Get() == self);
+
+ if (state == PromiseRejectionHandlingState::Unhandled) {
+ PromiseDebugging::AddUncaughtRejection(aPromise);
+ } else {
+ PromiseDebugging::AddConsumedRejection(aPromise);
+ }
+}
+#endif // SPIDERMONKEY_PROMISE
+
+struct JsGcTracer : public TraceCallbacks
+{
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+};
+
+void
+mozilla::TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer)
+{
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(aHolder, &participant);
+ participant->Trace(aHolder, JsGcTracer(), aTracer);
+}
+
+void
+CycleCollectedJSContext::TraceNativeGrayRoots(JSTracer* aTracer)
+{
+ MOZ_ASSERT(mJSContext);
+
+ // NB: This is here just to preserve the existing XPConnect order. I doubt it
+ // would hurt to do this after the JS holders.
+ TraceAdditionalNativeGrayRoots(aTracer);
+
+ for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
+ void* holder = iter.Key();
+ nsScriptObjectTracer*& tracer = iter.Data();
+ tracer->Trace(holder, JsGcTracer(), aTracer);
+ }
+}
+
+void
+CycleCollectedJSContext::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
+{
+ MOZ_ASSERT(mJSContext);
+ mJSHolders.Put(aHolder, aTracer);
+}
+
+struct ClearJSHolder : public TraceCallbacks
+{
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const override
+ {
+ aPtr->setUndefined();
+ }
+
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char*, void*) const override
+ {
+ *aPtr = JSID_VOID;
+ }
+
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+};
+
+void
+CycleCollectedJSContext::RemoveJSHolder(void* aHolder)
+{
+ MOZ_ASSERT(mJSContext);
+
+ nsScriptObjectTracer* tracer = mJSHolders.Get(aHolder);
+ if (!tracer) {
+ return;
+ }
+ tracer->Trace(aHolder, ClearJSHolder(), nullptr);
+ mJSHolders.Remove(aHolder);
+}
+
+#ifdef DEBUG
+bool
+CycleCollectedJSContext::IsJSHolder(void* aHolder)
+{
+ MOZ_ASSERT(mJSContext);
+ return mJSHolders.Get(aHolder, nullptr);
+}
+
+static void
+AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
+{
+ MOZ_ASSERT(!aGCThing);
+}
+
+void
+CycleCollectedJSContext::AssertNoObjectsToTrace(void* aPossibleJSHolder)
+{
+ MOZ_ASSERT(mJSContext);
+
+ nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
+ if (tracer) {
+ tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
+ }
+}
+#endif
+
+already_AddRefed<nsIException>
+CycleCollectedJSContext::GetPendingException() const
+{
+ MOZ_ASSERT(mJSContext);
+
+ nsCOMPtr<nsIException> out = mPendingException;
+ return out.forget();
+}
+
+void
+CycleCollectedJSContext::SetPendingException(nsIException* aException)
+{
+ MOZ_ASSERT(mJSContext);
+ mPendingException = aException;
+}
+
+std::queue<nsCOMPtr<nsIRunnable>>&
+CycleCollectedJSContext::GetPromiseMicroTaskQueue()
+{
+ MOZ_ASSERT(mJSContext);
+ return mPromiseMicroTaskQueue;
+}
+
+std::queue<nsCOMPtr<nsIRunnable>>&
+CycleCollectedJSContext::GetDebuggerPromiseMicroTaskQueue()
+{
+ MOZ_ASSERT(mJSContext);
+ return mDebuggerPromiseMicroTaskQueue;
+}
+
+nsCycleCollectionParticipant*
+CycleCollectedJSContext::GCThingParticipant()
+{
+ MOZ_ASSERT(mJSContext);
+ return &mGCThingCycleCollectorGlobal;
+}
+
+nsCycleCollectionParticipant*
+CycleCollectedJSContext::ZoneParticipant()
+{
+ MOZ_ASSERT(mJSContext);
+ return &mJSZoneCycleCollectorGlobal;
+}
+
+nsresult
+CycleCollectedJSContext::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
+{
+ MOZ_ASSERT(mJSContext);
+
+ TraverseNativeRoots(aCb);
+
+ NoteWeakMapsTracer trc(mJSContext, aCb);
+ js::TraceWeakMaps(&trc);
+
+ return NS_OK;
+}
+
+bool
+CycleCollectedJSContext::UsefulToMergeZones() const
+{
+ return false;
+}
+
+void
+CycleCollectedJSContext::FixWeakMappingGrayBits() const
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSContext),
+ "Don't call FixWeakMappingGrayBits during a GC.");
+ FixWeakMappingGrayBitsTracer fixer(mJSContext);
+ fixer.FixAll();
+}
+
+bool
+CycleCollectedJSContext::AreGCGrayBitsValid() const
+{
+ MOZ_ASSERT(mJSContext);
+ return js::AreGCGrayBitsValid(mJSContext);
+}
+
+void
+CycleCollectedJSContext::GarbageCollect(uint32_t aReason) const
+{
+ MOZ_ASSERT(mJSContext);
+
+ MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
+ JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
+
+ JS::PrepareForFullGC(mJSContext);
+ JS::GCForReason(mJSContext, GC_NORMAL, gcreason);
+}
+
+void
+CycleCollectedJSContext::JSObjectsTenured()
+{
+ MOZ_ASSERT(mJSContext);
+
+ for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
+ nsWrapperCache* cache = iter.Get();
+ JSObject* wrapper = cache->GetWrapperPreserveColor();
+ MOZ_ASSERT(wrapper);
+ if (!JS::ObjectIsTenured(wrapper)) {
+ MOZ_ASSERT(!cache->PreservingWrapper());
+ const JSClass* jsClass = js::GetObjectJSClass(wrapper);
+ jsClass->doFinalize(nullptr, wrapper);
+ }
+ }
+
+#ifdef DEBUG
+for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
+ MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));
+}
+#endif
+
+ mNurseryObjects.Clear();
+ mPreservedNurseryObjects.Clear();
+}
+
+void
+CycleCollectedJSContext::NurseryWrapperAdded(nsWrapperCache* aCache)
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_ASSERT(aCache);
+ MOZ_ASSERT(aCache->GetWrapperPreserveColor());
+ MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
+ mNurseryObjects.InfallibleAppend(aCache);
+}
+
+void
+CycleCollectedJSContext::NurseryWrapperPreserved(JSObject* aWrapper)
+{
+ MOZ_ASSERT(mJSContext);
+
+ mPreservedNurseryObjects.InfallibleAppend(
+ JS::PersistentRooted<JSObject*>(mJSContext, aWrapper));
+}
+
+void
+CycleCollectedJSContext::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing)
+{
+ MOZ_ASSERT(mJSContext);
+
+ void* thingArray = nullptr;
+ bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
+
+ thingArray = aAppendFunc(thingArray, aThing);
+ if (!hadThingArray) {
+ mDeferredFinalizerTable.Put(aFunc, thingArray);
+ }
+}
+
+void
+CycleCollectedJSContext::DeferredFinalize(nsISupports* aSupports)
+{
+ MOZ_ASSERT(mJSContext);
+
+ typedef DeferredFinalizerImpl<nsISupports> Impl;
+ DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize,
+ aSupports);
+}
+
+void
+CycleCollectedJSContext::DumpJSHeap(FILE* aFile)
+{
+ js::DumpHeap(Context(), aFile, js::CollectNurseryBeforeDump);
+}
+
+void
+CycleCollectedJSContext::ProcessStableStateQueue()
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_RELEASE_ASSERT(!mDoingStableStates);
+ mDoingStableStates = true;
+
+ for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
+ nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
+ event->Run();
+ }
+
+ mStableStateEvents.Clear();
+ mDoingStableStates = false;
+}
+
+void
+CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_RELEASE_ASSERT(!mDoingStableStates);
+ mDoingStableStates = true;
+
+ nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
+
+ for (uint32_t i = 0; i < localQueue.Length(); ++i)
+ {
+ RunInMetastableStateData& data = localQueue[i];
+ if (data.mRecursionDepth != aRecursionDepth) {
+ continue;
+ }
+
+ {
+ nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
+ runnable->Run();
+ }
+
+ localQueue.RemoveElementAt(i--);
+ }
+
+ // If the queue has events in it now, they were added from something we called,
+ // so they belong at the end of the queue.
+ localQueue.AppendElements(mMetastableStateEvents);
+ localQueue.SwapElements(mMetastableStateEvents);
+ mDoingStableStates = false;
+}
+
+void
+CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
+{
+ MOZ_ASSERT(mJSContext);
+
+ // See HTML 6.1.4.2 Processing model
+
+ // Execute any events that were waiting for a microtask to complete.
+ // This is not (yet) in the spec.
+ ProcessMetastableStateQueue(aRecursionDepth);
+
+ // Step 4.1: Execute microtasks.
+ if (!mDisableMicroTaskCheckpoint) {
+ if (NS_IsMainThread()) {
+ nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
+ Promise::PerformMicroTaskCheckpoint();
+ } else {
+ Promise::PerformWorkerMicroTaskCheckpoint();
+ }
+ }
+
+ // Step 4.2 Execute any events that were waiting for a stable state.
+ ProcessStableStateQueue();
+}
+
+void
+CycleCollectedJSContext::AfterProcessMicrotask()
+{
+ MOZ_ASSERT(mJSContext);
+ AfterProcessMicrotask(RecursionDepth());
+}
+
+void
+CycleCollectedJSContext::AfterProcessMicrotask(uint32_t aRecursionDepth)
+{
+ MOZ_ASSERT(mJSContext);
+
+ // Between microtasks, execute any events that were waiting for a microtask
+ // to complete.
+ ProcessMetastableStateQueue(aRecursionDepth);
+}
+
+uint32_t
+CycleCollectedJSContext::RecursionDepth()
+{
+ return mOwningThread->RecursionDepth();
+}
+
+void
+CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
+{
+ MOZ_ASSERT(mJSContext);
+ mStableStateEvents.AppendElement(Move(aRunnable));
+}
+
+void
+CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
+{
+ MOZ_ASSERT(mJSContext);
+
+ RunInMetastableStateData data;
+ data.mRunnable = aRunnable;
+
+ MOZ_ASSERT(mOwningThread);
+ data.mRecursionDepth = RecursionDepth();
+
+ // There must be an event running to get here.
+#ifndef MOZ_WIDGET_COCOA
+ MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
+#else
+ // XXX bug 1261143
+ // Recursion depth should be greater than mBaseRecursionDepth,
+ // or the runnable will stay in the queue forever.
+ if (data.mRecursionDepth <= mBaseRecursionDepth) {
+ data.mRecursionDepth = mBaseRecursionDepth + 1;
+ }
+#endif
+
+ mMetastableStateEvents.AppendElement(Move(data));
+}
+
+IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
+ DeferredFinalizerTable& aFinalizers)
+ : mContext(aCx)
+ , mFinalizeFunctionToRun(0)
+ , mReleasing(false)
+{
+ for (auto iter = aFinalizers.Iter(); !iter.Done(); iter.Next()) {
+ DeferredFinalizeFunction& function = iter.Key();
+ void*& data = iter.Data();
+
+ DeferredFinalizeFunctionHolder* holder =
+ mDeferredFinalizeFunctions.AppendElement();
+ holder->run = function;
+ holder->data = data;
+
+ iter.Remove();
+ }
+}
+
+IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
+{
+ MOZ_ASSERT(this != mContext->mFinalizeRunnable);
+}
+
+void
+IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
+{
+ if (mReleasing) {
+ NS_WARNING("Re-entering ReleaseNow");
+ return;
+ }
+ {
+ mozilla::AutoRestore<bool> ar(mReleasing);
+ mReleasing = true;
+ MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
+ "We should have at least ReleaseSliceNow to run");
+ MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
+ "No more finalizers to run?");
+
+ TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
+ TimeStamp started = TimeStamp::Now();
+ bool timeout = false;
+ do {
+ const DeferredFinalizeFunctionHolder& function =
+ mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
+ if (aLimited) {
+ bool done = false;
+ while (!timeout && !done) {
+ /*
+ * We don't want to read the clock too often, so we try to
+ * release slices of 100 items.
+ */
+ done = function.run(100, function.data);
+ timeout = TimeStamp::Now() - started >= sliceTime;
+ }
+ if (done) {
+ ++mFinalizeFunctionToRun;
+ }
+ if (timeout) {
+ break;
+ }
+ } else {
+ while (!function.run(UINT32_MAX, function.data));
+ ++mFinalizeFunctionToRun;
+ }
+ } while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
+ }
+
+ if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
+ MOZ_ASSERT(mContext->mFinalizeRunnable == this);
+ mDeferredFinalizeFunctions.Clear();
+ // NB: This may delete this!
+ mContext->mFinalizeRunnable = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+IncrementalFinalizeRunnable::Run()
+{
+ if (mContext->mFinalizeRunnable != this) {
+ /* These items were already processed synchronously in JSGC_END. */
+ MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
+ return NS_OK;
+ }
+
+ TimeStamp start = TimeStamp::Now();
+ ReleaseNow(true);
+
+ if (mDeferredFinalizeFunctions.Length()) {
+ nsresult rv = NS_DispatchToCurrentThread(this);
+ if (NS_FAILED(rv)) {
+ ReleaseNow(false);
+ }
+ }
+
+ uint32_t duration = (uint32_t)((TimeStamp::Now() - start).ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC, duration);
+
+ return NS_OK;
+}
+
+void
+CycleCollectedJSContext::FinalizeDeferredThings(DeferredFinalizeType aType)
+{
+ MOZ_ASSERT(mJSContext);
+
+ /*
+ * If the previous GC created a runnable to finalize objects
+ * incrementally, and if it hasn't finished yet, finish it now. We
+ * don't want these to build up. We also don't want to allow any
+ * existing incremental finalize runnables to run after a
+ * non-incremental GC, since they are often used to detect leaks.
+ */
+ if (mFinalizeRunnable) {
+ mFinalizeRunnable->ReleaseNow(false);
+ if (mFinalizeRunnable) {
+ // If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
+ // we need to just continue processing it.
+ return;
+ }
+ }
+
+ if (mDeferredFinalizerTable.Count() == 0) {
+ return;
+ }
+
+ mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
+ mDeferredFinalizerTable);
+
+ // Everything should be gone now.
+ MOZ_ASSERT(mDeferredFinalizerTable.Count() == 0);
+
+ if (aType == FinalizeIncrementally) {
+ NS_DispatchToCurrentThread(mFinalizeRunnable);
+ } else {
+ mFinalizeRunnable->ReleaseNow(false);
+ MOZ_ASSERT(!mFinalizeRunnable);
+ }
+}
+
+void
+CycleCollectedJSContext::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
+ OOMState aNewState)
+{
+ MOZ_ASSERT(mJSContext);
+
+ *aStatePtr = aNewState;
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
+ ? NS_LITERAL_CSTRING("JSOutOfMemory")
+ : NS_LITERAL_CSTRING("JSLargeAllocationFailure"),
+ aNewState == OOMState::Reporting
+ ? NS_LITERAL_CSTRING("Reporting")
+ : aNewState == OOMState::Reported
+ ? NS_LITERAL_CSTRING("Reported")
+ : NS_LITERAL_CSTRING("Recovered"));
+#endif
+}
+
+void
+CycleCollectedJSContext::OnGC(JSGCStatus aStatus)
+{
+ MOZ_ASSERT(mJSContext);
+
+ switch (aStatus) {
+ case JSGC_BEGIN:
+ nsCycleCollector_prepareForGarbageCollection();
+ mZonesWaitingForGC.Clear();
+ break;
+ case JSGC_END: {
+#ifdef MOZ_CRASHREPORTER
+ if (mOutOfMemoryState == OOMState::Reported) {
+ AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Recovered);
+ }
+ if (mLargeAllocationFailureState == OOMState::Reported) {
+ AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Recovered);
+ }
+#endif
+
+ // Do any deferred finalization of native objects.
+ FinalizeDeferredThings(JS::WasIncrementalGC(mJSContext) ? FinalizeIncrementally :
+ FinalizeNow);
+ break;
+ }
+ default:
+ MOZ_CRASH();
+ }
+
+ CustomGCCallback(aStatus);
+}
+
+void
+CycleCollectedJSContext::OnOutOfMemory()
+{
+ MOZ_ASSERT(mJSContext);
+
+ AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
+ CustomOutOfMemoryCallback();
+ AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
+}
+
+void
+CycleCollectedJSContext::OnLargeAllocationFailure()
+{
+ MOZ_ASSERT(mJSContext);
+
+ AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reporting);
+ CustomLargeAllocationFailureCallback();
+ AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
+}
+
+void
+CycleCollectedJSContext::PrepareWaitingZonesForGC()
+{
+ if (mZonesWaitingForGC.Count() == 0) {
+ JS::PrepareForFullGC(Context());
+ } else {
+ for (auto iter = mZonesWaitingForGC.Iter(); !iter.Done(); iter.Next()) {
+ JS::PrepareZoneForGC(iter.Get()->GetKey());
+ }
+ mZonesWaitingForGC.Clear();
+ }
+}
+
+void
+CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable)
+{
+ RefPtr<nsIRunnable> runnable(aRunnable);
+
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(runnable);
+
+ mPromiseMicroTaskQueue.push(runnable.forget());
+}
+
+void
+CycleCollectedJSContext::EnvironmentPreparer::invoke(JS::HandleObject scope,
+ js::ScriptEnvironmentPreparer::Closure& closure)
+{
+ nsIGlobalObject* global = xpc::NativeGlobal(scope);
+
+ // Not much we can do if we simply don't have a usable global here...
+ NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
+
+ AutoEntryScript aes(global, "JS-engine-initiated execution");
+
+ MOZ_ASSERT(!JS_IsExceptionPending(aes.cx()));
+
+ DebugOnly<bool> ok = closure(aes.cx());
+
+ MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aes.cx()));
+
+ // The AutoEntryScript will check for JS_IsExceptionPending on the
+ // JSContext and report it as needed as it comes off the stack.
+}
diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h
new file mode 100644
index 000000000..9415634b8
--- /dev/null
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CycleCollectedJSContext_h__
+#define mozilla_CycleCollectedJSContext_h__
+
+#include <queue>
+
+#include "mozilla/DeferredFinalize.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/SegmentedVector.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+
+class nsCycleCollectionNoteRootCallback;
+class nsIException;
+class nsIRunnable;
+class nsThread;
+class nsWrapperCache;
+
+namespace js {
+struct Class;
+} // namespace js
+
+namespace mozilla {
+
+class JSGCThingParticipant: public nsCycleCollectionParticipant
+{
+public:
+ NS_IMETHOD_(void) Root(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Root on GC things");
+ }
+
+ NS_IMETHOD_(void) Unlink(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) Unroot(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) override
+ {
+ MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
+ }
+
+ NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
+ override;
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSGCThingParticipant)
+};
+
+class JSZoneParticipant : public nsCycleCollectionParticipant
+{
+public:
+ constexpr JSZoneParticipant(): nsCycleCollectionParticipant()
+ {
+ }
+
+ NS_IMETHOD_(void) Root(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Root on GC things");
+ }
+
+ NS_IMETHOD_(void) Unlink(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) Unroot(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) DeleteCycleCollectable(void*) override
+ {
+ MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
+ }
+
+ NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
+ override;
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
+};
+
+class IncrementalFinalizeRunnable;
+
+// Contains various stats about the cycle collection.
+struct CycleCollectorResults
+{
+ CycleCollectorResults()
+ {
+ // Initialize here so when we increment mNumSlices the first time we're
+ // not using uninitialized memory.
+ Init();
+ }
+
+ void Init()
+ {
+ mForcedGC = false;
+ mMergedZones = false;
+ mAnyManual = false;
+ mVisitedRefCounted = 0;
+ mVisitedGCed = 0;
+ mFreedRefCounted = 0;
+ mFreedGCed = 0;
+ mFreedJSZones = 0;
+ mNumSlices = 1;
+ // mNumSlices is initialized to one, because we call Init() after the
+ // per-slice increment of mNumSlices has already occurred.
+ }
+
+ bool mForcedGC;
+ bool mMergedZones;
+ bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown.
+ uint32_t mVisitedRefCounted;
+ uint32_t mVisitedGCed;
+ uint32_t mFreedRefCounted;
+ uint32_t mFreedGCed;
+ uint32_t mFreedJSZones;
+ uint32_t mNumSlices;
+};
+
+class CycleCollectedJSContext
+{
+ friend class JSGCThingParticipant;
+ friend class JSZoneParticipant;
+ friend class IncrementalFinalizeRunnable;
+protected:
+ CycleCollectedJSContext();
+ virtual ~CycleCollectedJSContext();
+
+ MOZ_IS_CLASS_INIT
+ nsresult Initialize(JSContext* aParentContext,
+ uint32_t aMaxBytes,
+ uint32_t aMaxNurseryBytes);
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ void UnmarkSkippableJSHolders();
+
+ virtual void
+ TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
+ virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
+
+ virtual void CustomGCCallback(JSGCStatus aStatus) {}
+ virtual void CustomOutOfMemoryCallback() {}
+ virtual void CustomLargeAllocationFailureCallback() {}
+
+ std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
+ std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
+
+private:
+ void
+ DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const;
+
+ virtual bool
+ DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
+ char (&aName)[72]) const
+ {
+ return false; // We did nothing.
+ }
+
+ void
+ NoteGCThingJSChildren(JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const;
+
+ void
+ NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const;
+
+ virtual bool
+ NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const
+ {
+ return false; // We did nothing.
+ }
+
+ enum TraverseSelect {
+ TRAVERSE_CPP,
+ TRAVERSE_FULL
+ };
+
+ void
+ TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb);
+
+ void
+ TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
+
+ static void
+ TraverseObjectShim(void* aData, JS::GCCellPtr aThing);
+
+ void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
+
+ static void TraceBlackJS(JSTracer* aTracer, void* aData);
+ static void TraceGrayJS(JSTracer* aTracer, void* aData);
+ static void GCCallback(JSContext* aContext, JSGCStatus aStatus, void* aData);
+ static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
+ const JS::GCDescription& aDesc);
+ static void GCNurseryCollectionCallback(JSContext* aContext,
+ JS::GCNurseryProgress aProgress,
+ JS::gcreason::Reason aReason);
+ static void OutOfMemoryCallback(JSContext* aContext, void* aData);
+ static void LargeAllocationFailureCallback(void* aData);
+ static bool ContextCallback(JSContext* aCx, unsigned aOperation,
+ void* aData);
+ static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
+ static bool EnqueuePromiseJobCallback(JSContext* aCx,
+ JS::HandleObject aJob,
+ JS::HandleObject aAllocationSite,
+ JS::HandleObject aIncumbentGlobal,
+ void* aData);
+#ifdef SPIDERMONKEY_PROMISE
+ static void PromiseRejectionTrackerCallback(JSContext* aCx,
+ JS::HandleObject aPromise,
+ PromiseRejectionHandlingState state,
+ void* aData);
+#endif // SPIDERMONKEY_PROMISE
+
+ virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
+ void TraceNativeGrayRoots(JSTracer* aTracer);
+
+ void AfterProcessMicrotask(uint32_t aRecursionDepth);
+public:
+ void ProcessStableStateQueue();
+private:
+ void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
+
+public:
+ enum DeferredFinalizeType {
+ FinalizeIncrementally,
+ FinalizeNow,
+ };
+
+ void FinalizeDeferredThings(DeferredFinalizeType aType);
+
+ // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
+ // crash reports. Here are the values that can appear in the reports:
+ enum class OOMState : uint32_t {
+ // The condition has never happened. No entry appears in the crash report.
+ OK,
+
+ // We are currently reporting the given condition.
+ //
+ // Suppose a crash report contains "JSLargeAllocationFailure:
+ // Reporting". This means we crashed while executing memory-pressure
+ // observers, trying to shake loose some memory. The large allocation in
+ // question did not return null: it is still on the stack. Had we not
+ // crashed, it would have been retried.
+ Reporting,
+
+ // The condition has been reported since the last GC.
+ //
+ // If a crash report contains "JSOutOfMemory: Reported", that means a small
+ // allocation failed, and then we crashed, probably due to buggy
+ // error-handling code that ran after allocation returned null.
+ //
+ // This contrasts with "Reporting" which means that no error-handling code
+ // had executed yet.
+ Reported,
+
+ // The condition has happened, but a GC cycle ended since then.
+ //
+ // GC is taken as a proxy for "we've been banging on the heap a good bit
+ // now and haven't crashed; the OOM was probably handled correctly".
+ Recovered
+ };
+
+private:
+ void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
+ void OnGC(JSGCStatus aStatus);
+ void OnOutOfMemory();
+ void OnLargeAllocationFailure();
+
+public:
+ void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
+ void RemoveJSHolder(void* aHolder);
+#ifdef DEBUG
+ bool IsJSHolder(void* aHolder);
+ void AssertNoObjectsToTrace(void* aPossibleJSHolder);
+#endif
+
+ already_AddRefed<nsIException> GetPendingException() const;
+ void SetPendingException(nsIException* aException);
+
+ std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
+ std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
+
+ nsCycleCollectionParticipant* GCThingParticipant();
+ nsCycleCollectionParticipant* ZoneParticipant();
+
+ nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
+ virtual bool UsefulToMergeZones() const;
+ void FixWeakMappingGrayBits() const;
+ bool AreGCGrayBitsValid() const;
+ void GarbageCollect(uint32_t aReason) const;
+
+ void NurseryWrapperAdded(nsWrapperCache* aCache);
+ void NurseryWrapperPreserved(JSObject* aWrapper);
+ void JSObjectsTenured();
+
+ void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing);
+ void DeferredFinalize(nsISupports* aSupports);
+
+ void DumpJSHeap(FILE* aFile);
+
+ virtual void PrepareForForgetSkippable() = 0;
+ virtual void BeginCycleCollectionCallback() = 0;
+ virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
+ virtual void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) = 0;
+
+ JSContext* Context() const
+ {
+ MOZ_ASSERT(mJSContext);
+ return mJSContext;
+ }
+
+ JS::RootingContext* RootingCx() const
+ {
+ MOZ_ASSERT(mJSContext);
+ return JS::RootingContext::get(mJSContext);
+ }
+
+ bool MicroTaskCheckpointDisabled() const
+ {
+ return mDisableMicroTaskCheckpoint;
+ }
+
+ void DisableMicroTaskCheckpoint(bool aDisable)
+ {
+ mDisableMicroTaskCheckpoint = aDisable;
+ }
+
+ class MOZ_RAII AutoDisableMicroTaskCheckpoint
+ {
+ public:
+ AutoDisableMicroTaskCheckpoint()
+ : mCCJSCX(CycleCollectedJSContext::Get())
+ {
+ mOldValue = mCCJSCX->MicroTaskCheckpointDisabled();
+ mCCJSCX->DisableMicroTaskCheckpoint(true);
+ }
+
+ ~AutoDisableMicroTaskCheckpoint()
+ {
+ mCCJSCX->DisableMicroTaskCheckpoint(mOldValue);
+ }
+
+ CycleCollectedJSContext* mCCJSCX;
+ bool mOldValue;
+ };
+
+protected:
+ JSContext* MaybeContext() const { return mJSContext; }
+
+public:
+ // nsThread entrypoints
+ virtual void BeforeProcessTask(bool aMightBlock) { };
+ virtual void AfterProcessTask(uint32_t aRecursionDepth);
+
+ // microtask processor entry point
+ void AfterProcessMicrotask();
+
+ uint32_t RecursionDepth();
+
+ // Run in stable state (call through nsContentUtils)
+ void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
+ // This isn't in the spec at all yet, but this gets the behavior we want for IDB.
+ // Runs after the current microtask completes.
+ void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
+
+ // Get the current thread's CycleCollectedJSContext. Returns null if there
+ // isn't one.
+ static CycleCollectedJSContext* Get();
+
+ // Add aZone to the set of zones waiting for a GC.
+ void AddZoneWaitingForGC(JS::Zone* aZone)
+ {
+ mZonesWaitingForGC.PutEntry(aZone);
+ }
+
+ // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
+ // since the last GC or since the last call to PrepareWaitingZonesForGC(),
+ // whichever was most recent. If there were no such zones, prepare for a
+ // full GC.
+ void PrepareWaitingZonesForGC();
+
+ // Queue an async microtask to the current main or worker thread.
+ virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable);
+
+ // Storage for watching rejected promises waiting for some client to
+ // consume their rejection.
+#ifdef SPIDERMONKEY_PROMISE
+ // Promises in this list have been rejected in the last turn of the
+ // event loop without the rejection being handled.
+ // Note that this can contain nullptrs in place of promises removed because
+ // they're consumed before it'd be reported.
+ JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mUncaughtRejections;
+
+ // Promises in this list have previously been reported as rejected
+ // (because they were in the above list), but the rejection was handled
+ // in the last turn of the event loop.
+ JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
+#else
+ // We store values as `nsISupports` to avoid adding compile-time dependencies
+ // from xpcom to dom/promise, but they can really only have a single concrete
+ // type.
+ nsTArray<nsCOMPtr<nsISupports /* Promise */>> mUncaughtRejections;
+ nsTArray<nsCOMPtr<nsISupports /* Promise */ >> mConsumedRejections;
+#endif // SPIDERMONKEY_PROMISE
+ nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
+
+private:
+ JSGCThingParticipant mGCThingCycleCollectorGlobal;
+
+ JSZoneParticipant mJSZoneCycleCollectorGlobal;
+
+ JSContext* mJSContext;
+
+ JS::GCSliceCallback mPrevGCSliceCallback;
+ JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
+
+ nsDataHashtable<nsPtrHashKey<void>, nsScriptObjectTracer*> mJSHolders;
+
+ typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
+ DeferredFinalizerTable;
+ DeferredFinalizerTable mDeferredFinalizerTable;
+
+ RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
+
+ nsCOMPtr<nsIException> mPendingException;
+ nsThread* mOwningThread; // Manual refcounting to avoid include hell.
+
+ struct RunInMetastableStateData
+ {
+ nsCOMPtr<nsIRunnable> mRunnable;
+ uint32_t mRecursionDepth;
+ };
+
+ nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
+ nsTArray<RunInMetastableStateData> mMetastableStateEvents;
+ uint32_t mBaseRecursionDepth;
+ bool mDoingStableStates;
+
+ bool mDisableMicroTaskCheckpoint;
+
+ OOMState mOutOfMemoryState;
+ OOMState mLargeAllocationFailureState;
+
+ static const size_t kSegmentSize = 512;
+ SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
+ mNurseryObjects;
+ SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
+ InfallibleAllocPolicy>
+ mPreservedNurseryObjects;
+
+ nsTHashtable<nsPtrHashKey<JS::Zone>> mZonesWaitingForGC;
+
+ struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
+ void invoke(JS::HandleObject scope, Closure& closure) override;
+ };
+ EnvironmentPreparer mEnvironmentPreparer;
+};
+
+void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
+
+// Returns true if the JS::TraceKind is one the cycle collector cares about.
+inline bool AddToCCKind(JS::TraceKind aKind)
+{
+ return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope;
+}
+
+bool
+GetBuildId(JS::BuildIdCharVector* aBuildID);
+
+} // namespace mozilla
+
+#endif // mozilla_CycleCollectedJSContext_h__
diff --git a/xpcom/base/Debug.cpp b/xpcom/base/Debug.cpp
new file mode 100644
index 000000000..cc5272da0
--- /dev/null
+++ b/xpcom/base/Debug.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Debug.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#ifdef XP_WIN
+
+void
+mozilla::PrintToDebugger(const char* aStr)
+{
+ if (::IsDebuggerPresent()) {
+ ::OutputDebugStringA(aStr);
+ }
+}
+
+#endif
diff --git a/xpcom/base/Debug.h b/xpcom/base/Debug.h
new file mode 100644
index 000000000..2479799ae
--- /dev/null
+++ b/xpcom/base/Debug.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Debug_h__
+#define mozilla_Debug_h__
+
+namespace mozilla {
+
+#ifdef XP_WIN
+
+// Print aStr to a debugger if the debugger is attached.
+void PrintToDebugger(const char* aStr);
+
+#endif
+
+} // namespace mozilla
+
+#endif // mozilla_Debug_h__
diff --git a/xpcom/base/DebuggerOnGCRunnable.cpp b/xpcom/base/DebuggerOnGCRunnable.cpp
new file mode 100644
index 000000000..96f4cddcb
--- /dev/null
+++ b/xpcom/base/DebuggerOnGCRunnable.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebuggerOnGCRunnable.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/Move.h"
+#include "js/Debug.h"
+
+namespace mozilla {
+
+/* static */ nsresult
+DebuggerOnGCRunnable::Enqueue(JSContext* aCx, const JS::GCDescription& aDesc)
+{
+ auto gcEvent = aDesc.toGCEvent(aCx);
+ if (!gcEvent) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ RefPtr<DebuggerOnGCRunnable> runOnGC =
+ new DebuggerOnGCRunnable(Move(gcEvent));
+ return NS_DispatchToCurrentThread(runOnGC);
+}
+
+NS_IMETHODIMP
+DebuggerOnGCRunnable::Run()
+{
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ if (!JS::dbg::FireOnGarbageCollectionHook(jsapi.cx(), Move(mGCData))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+nsresult
+DebuggerOnGCRunnable::Cancel()
+{
+ mGCData = nullptr;
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/DebuggerOnGCRunnable.h b/xpcom/base/DebuggerOnGCRunnable.h
new file mode 100644
index 000000000..8f9621613
--- /dev/null
+++ b/xpcom/base/DebuggerOnGCRunnable.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DebuggerOnGCRunnable_h
+#define mozilla_DebuggerOnGCRunnable_h
+
+#include "nsThreadUtils.h"
+#include "js/GCAPI.h"
+#include "mozilla/Move.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+// Runnable to fire the SpiderMonkey Debugger API's onGarbageCollection hook.
+class DebuggerOnGCRunnable : public CancelableRunnable
+{
+ JS::dbg::GarbageCollectionEvent::Ptr mGCData;
+
+ explicit DebuggerOnGCRunnable(JS::dbg::GarbageCollectionEvent::Ptr&& aGCData)
+ : mGCData(Move(aGCData))
+ { }
+
+public:
+ static nsresult Enqueue(JSContext* aCx, const JS::GCDescription& aDesc);
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+} // namespace mozilla
+
+#endif // ifdef mozilla_dom_DebuggerOnGCRunnable_h
diff --git a/xpcom/base/DeferredFinalize.cpp b/xpcom/base/DeferredFinalize.cpp
new file mode 100644
index 000000000..b9a71b81c
--- /dev/null
+++ b/xpcom/base/DeferredFinalize.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DeferredFinalize.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CycleCollectedJSContext.h"
+
+void
+mozilla::DeferredFinalize(nsISupports* aSupports)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->DeferredFinalize(aSupports);
+}
+
+void
+mozilla::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->DeferredFinalize(aAppendFunc, aFunc, aThing);
+}
diff --git a/xpcom/base/DeferredFinalize.h b/xpcom/base/DeferredFinalize.h
new file mode 100644
index 000000000..7d9c58881
--- /dev/null
+++ b/xpcom/base/DeferredFinalize.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DeferredFinalize_h
+#define mozilla_DeferredFinalize_h
+
+class nsISupports;
+
+namespace mozilla {
+
+// Called back from DeferredFinalize. Should add 'thing' to the array of smart
+// pointers in 'pointers', creating the array if 'pointers' is null, and return
+// the array.
+typedef void* (*DeferredFinalizeAppendFunction)(void* aPointers, void* aThing);
+
+// Called to finalize a number of objects. Slice is the number of objects to
+// finalize. The return value indicates whether it finalized all objects in the
+// buffer. If it returns true, the function will not be called again, so the
+// function should free aData.
+typedef bool (*DeferredFinalizeFunction)(uint32_t aSlice, void* aData);
+
+void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing);
+
+void DeferredFinalize(nsISupports* aSupports);
+
+} // namespace mozilla
+
+#endif // mozilla_DeferredFinalize_h
diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h
new file mode 100644
index 000000000..cfa461fe4
--- /dev/null
+++ b/xpcom/base/ErrorList.h
@@ -0,0 +1,1026 @@
+// IWYU pragma: private, include "nsError.h"
+/* Helper file for nsError.h, via preprocessor magic */
+ /* Standard "it worked" return value */
+ ERROR(NS_OK, 0),
+
+ /* ======================================================================= */
+ /* Core errors, not part of any modules */
+ /* ======================================================================= */
+ ERROR(NS_ERROR_BASE, 0xC1F30000),
+ /* Returned when an instance is not initialized */
+ ERROR(NS_ERROR_NOT_INITIALIZED, NS_ERROR_BASE + 1),
+ /* Returned when an instance is already initialized */
+ ERROR(NS_ERROR_ALREADY_INITIALIZED, NS_ERROR_BASE + 2),
+ /* Returned by a not implemented function */
+ ERROR(NS_ERROR_NOT_IMPLEMENTED, 0x80004001),
+ /* Returned when a given interface is not supported. */
+ ERROR(NS_NOINTERFACE, 0x80004002),
+ ERROR(NS_ERROR_NO_INTERFACE, NS_NOINTERFACE),
+ /* Returned when a function aborts */
+ ERROR(NS_ERROR_ABORT, 0x80004004),
+ /* Returned when a function fails */
+ ERROR(NS_ERROR_FAILURE, 0x80004005),
+ /* Returned when an unexpected error occurs */
+ ERROR(NS_ERROR_UNEXPECTED, 0x8000ffff),
+ /* Returned when a memory allocation fails */
+ ERROR(NS_ERROR_OUT_OF_MEMORY, 0x8007000e),
+ /* Returned when an illegal value is passed */
+ ERROR(NS_ERROR_ILLEGAL_VALUE, 0x80070057),
+ ERROR(NS_ERROR_INVALID_ARG, NS_ERROR_ILLEGAL_VALUE),
+ ERROR(NS_ERROR_INVALID_POINTER, NS_ERROR_INVALID_ARG),
+ ERROR(NS_ERROR_NULL_POINTER, NS_ERROR_INVALID_ARG),
+ /* Returned when a class doesn't allow aggregation */
+ ERROR(NS_ERROR_NO_AGGREGATION, 0x80040110),
+ /* Returned when an operation can't complete due to an unavailable resource */
+ ERROR(NS_ERROR_NOT_AVAILABLE, 0x80040111),
+ /* Returned when a class is not registered */
+ ERROR(NS_ERROR_FACTORY_NOT_REGISTERED, 0x80040154),
+ /* Returned when a class cannot be registered, but may be tried again later */
+ ERROR(NS_ERROR_FACTORY_REGISTER_AGAIN, 0x80040155),
+ /* Returned when a dynamically loaded factory couldn't be found */
+ ERROR(NS_ERROR_FACTORY_NOT_LOADED, 0x800401f8),
+ /* Returned when a factory doesn't support signatures */
+ ERROR(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT, NS_ERROR_BASE + 0x101),
+ /* Returned when a factory already is registered */
+ ERROR(NS_ERROR_FACTORY_EXISTS, NS_ERROR_BASE + 0x100),
+
+
+ /* ======================================================================= */
+ /* 1: NS_ERROR_MODULE_XPCOM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_XPCOM
+ /* Result codes used by nsIVariant */
+ ERROR(NS_ERROR_CANNOT_CONVERT_DATA, FAILURE(1)),
+ ERROR(NS_ERROR_OBJECT_IS_IMMUTABLE, FAILURE(2)),
+ ERROR(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA, FAILURE(3)),
+ /* Result code used by nsIThreadManager */
+ ERROR(NS_ERROR_NOT_SAME_THREAD, FAILURE(4)),
+ /* Various operations are not permitted during XPCOM shutdown and will fail
+ * with this exception. */
+ ERROR(NS_ERROR_ILLEGAL_DURING_SHUTDOWN, FAILURE(30)),
+ ERROR(NS_ERROR_SERVICE_NOT_AVAILABLE, FAILURE(22)),
+
+ ERROR(NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA, SUCCESS(1)),
+ /* Used by nsCycleCollectionParticipant */
+ ERROR(NS_SUCCESS_INTERRUPTED_TRAVERSE, SUCCESS(2)),
+ /* DEPRECATED */
+ ERROR(NS_ERROR_SERVICE_NOT_FOUND, SUCCESS(22)),
+ /* DEPRECATED */
+ ERROR(NS_ERROR_SERVICE_IN_USE, SUCCESS(23)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 2: NS_ERROR_MODULE_BASE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_BASE
+ /* I/O Errors */
+
+ /* Stream closed */
+ ERROR(NS_BASE_STREAM_CLOSED, FAILURE(2)),
+ /* Error from the operating system */
+ ERROR(NS_BASE_STREAM_OSERROR, FAILURE(3)),
+ /* Illegal arguments */
+ ERROR(NS_BASE_STREAM_ILLEGAL_ARGS, FAILURE(4)),
+ /* For unichar streams */
+ ERROR(NS_BASE_STREAM_NO_CONVERTER, FAILURE(5)),
+ /* For unichar streams */
+ ERROR(NS_BASE_STREAM_BAD_CONVERSION, FAILURE(6)),
+ ERROR(NS_BASE_STREAM_WOULD_BLOCK, FAILURE(7)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 3: NS_ERROR_MODULE_GFX */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_GFX
+ /* no printer available (e.g. cannot find _any_ printer) */
+ ERROR(NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE, FAILURE(1)),
+ /* _specified_ (by name) printer not found */
+ ERROR(NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND, FAILURE(2)),
+ /* print-to-file: could not open output file */
+ ERROR(NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE, FAILURE(3)),
+ /* print: starting document */
+ ERROR(NS_ERROR_GFX_PRINTER_STARTDOC, FAILURE(4)),
+ /* print: ending document */
+ ERROR(NS_ERROR_GFX_PRINTER_ENDDOC, FAILURE(5)),
+ /* print: starting page */
+ ERROR(NS_ERROR_GFX_PRINTER_STARTPAGE, FAILURE(6)),
+ /* The document is still being loaded */
+ ERROR(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY, FAILURE(7)),
+
+ /* Font cmap is strangely structured - avoid this font! */
+ ERROR(NS_ERROR_GFX_CMAP_MALFORMED, FAILURE(51)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 4: NS_ERROR_MODULE_WIDGET */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_WIDGET
+ /* Used by:
+ * - nsIWidget::NotifyIME()
+ * - nsIWidget::OnWindowedPluginKeyEvent()
+ * Returned when the notification or the event is handled and it's consumed
+ * by somebody. */
+ ERROR(NS_SUCCESS_EVENT_CONSUMED, SUCCESS(1)),
+ /* Used by:
+ * - nsIWidget::OnWindowedPluginKeyEvent()
+ * Returned when the event is handled correctly but the result will be
+ * notified asynchronously. */
+ ERROR(NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 6: NS_ERROR_MODULE_NETWORK */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_NETWORK
+ /* General async request error codes:
+ *
+ * These error codes are commonly passed through callback methods to indicate
+ * the status of some requested async request.
+ *
+ * For example, see nsIRequestObserver::onStopRequest.
+ */
+
+ /* The async request completed successfully. */
+ ERROR(NS_BINDING_SUCCEEDED, NS_OK),
+
+ /* The async request failed for some unknown reason. */
+ ERROR(NS_BINDING_FAILED, FAILURE(1)),
+ /* The async request failed because it was aborted by some user action. */
+ ERROR(NS_BINDING_ABORTED, FAILURE(2)),
+ /* The async request has been "redirected" to a different async request.
+ * (e.g., an HTTP redirect occurred).
+ *
+ * This error code is used with load groups to notify the load group observer
+ * when a request in the load group is redirected to another request. */
+ ERROR(NS_BINDING_REDIRECTED, FAILURE(3)),
+ /* The async request has been "retargeted" to a different "handler."
+ *
+ * This error code is used with load groups to notify the load group observer
+ * when a request in the load group is removed from the load group and added
+ * to a different load group. */
+ ERROR(NS_BINDING_RETARGETED, FAILURE(4)),
+
+ /* Miscellaneous error codes: These errors are not typically passed via
+ * onStopRequest. */
+
+ /* The URI is malformed. */
+ ERROR(NS_ERROR_MALFORMED_URI, FAILURE(10)),
+ /* The requested action could not be completed while the object is busy.
+ * Implementations of nsIChannel::asyncOpen will commonly return this error
+ * if the channel has already been opened (and has not yet been closed). */
+ ERROR(NS_ERROR_IN_PROGRESS, FAILURE(15)),
+ /* Returned from nsIChannel::asyncOpen to indicate that OnDataAvailable will
+ * not be called because there is no content available. This is used by
+ * helper app style protocols (e.g., mailto). XXX perhaps this should be a
+ * success code. */
+ ERROR(NS_ERROR_NO_CONTENT, FAILURE(17)),
+ /* The URI scheme corresponds to an unknown protocol handler. */
+ ERROR(NS_ERROR_UNKNOWN_PROTOCOL, FAILURE(18)),
+ /* The content encoding of the source document was incorrect, for example
+ * returning a plain HTML document advertised as Content-Encoding: gzip */
+ ERROR(NS_ERROR_INVALID_CONTENT_ENCODING, FAILURE(27)),
+ /* A transport level corruption was found in the source document. for example
+ * a document with a calculated checksum that does not match the Content-MD5
+ * http header. */
+ ERROR(NS_ERROR_CORRUPTED_CONTENT, FAILURE(29)),
+ /* A content signature verification failed for some reason. This can be either
+ * an actual verification error, or any other error that led to the fact that
+ * a content signature that was expected couldn't be verified. */
+ ERROR(NS_ERROR_INVALID_SIGNATURE, FAILURE(58)),
+ /* While parsing for the first component of a header field using syntax as in
+ * Content-Disposition or Content-Type, the first component was found to be
+ * empty, such as in: Content-Disposition: ; filename=foo */
+ ERROR(NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY, FAILURE(34)),
+ /* Returned from nsIChannel::asyncOpen when trying to open the channel again
+ * (reopening is not supported). */
+ ERROR(NS_ERROR_ALREADY_OPENED, FAILURE(73)),
+
+ /* Connectivity error codes: */
+
+ /* The connection is already established. XXX unused - consider removing. */
+ ERROR(NS_ERROR_ALREADY_CONNECTED, FAILURE(11)),
+ /* The connection does not exist. XXX unused - consider removing. */
+ ERROR(NS_ERROR_NOT_CONNECTED, FAILURE(12)),
+ /* The connection attempt failed, for example, because no server was
+ * listening at specified host:port. */
+ ERROR(NS_ERROR_CONNECTION_REFUSED, FAILURE(13)),
+ /* The connection was lost due to a timeout error. */
+ ERROR(NS_ERROR_NET_TIMEOUT, FAILURE(14)),
+ /* The requested action could not be completed while the networking library
+ * is in the offline state. */
+ ERROR(NS_ERROR_OFFLINE, FAILURE(16)),
+ /* The requested action was prohibited because it would have caused the
+ * networking library to establish a connection to an unsafe or otherwise
+ * banned port. */
+ ERROR(NS_ERROR_PORT_ACCESS_NOT_ALLOWED, FAILURE(19)),
+ /* The connection was established, but no data was ever received. */
+ ERROR(NS_ERROR_NET_RESET, FAILURE(20)),
+ /* The connection was established, but the data transfer was interrupted. */
+ ERROR(NS_ERROR_NET_INTERRUPT, FAILURE(71)),
+ /* The connection attempt to a proxy failed. */
+ ERROR(NS_ERROR_PROXY_CONNECTION_REFUSED, FAILURE(72)),
+ /* A transfer was only partially done when it completed. */
+ ERROR(NS_ERROR_NET_PARTIAL_TRANSFER, FAILURE(76)),
+ /* HTTP/2 detected invalid TLS configuration */
+ ERROR(NS_ERROR_NET_INADEQUATE_SECURITY, FAILURE(82)),
+
+ /* XXX really need to better rationalize these error codes. are consumers of
+ * necko really expected to know how to discern the meaning of these?? */
+ /* This request is not resumable, but it was tried to resume it, or to
+ * request resume-specific data. */
+ ERROR(NS_ERROR_NOT_RESUMABLE, FAILURE(25)),
+ /* The request failed as a result of a detected redirection loop. */
+ ERROR(NS_ERROR_REDIRECT_LOOP, FAILURE(31)),
+ /* It was attempted to resume the request, but the entity has changed in the
+ * meantime. */
+ ERROR(NS_ERROR_ENTITY_CHANGED, FAILURE(32)),
+ /* The request failed because the content type returned by the server was not
+ * a type expected by the channel (for nested channels such as the JAR
+ * channel). */
+ ERROR(NS_ERROR_UNSAFE_CONTENT_TYPE, FAILURE(74)),
+ /* The request failed because the user tried to access to a remote XUL
+ * document from a website that is not in its white-list. */
+ ERROR(NS_ERROR_REMOTE_XUL, FAILURE(75)),
+ /* The request resulted in an error page being displayed. */
+ ERROR(NS_ERROR_LOAD_SHOWED_ERRORPAGE, FAILURE(77)),
+
+
+ /* FTP specific error codes: */
+
+ ERROR(NS_ERROR_FTP_LOGIN, FAILURE(21)),
+ ERROR(NS_ERROR_FTP_CWD, FAILURE(22)),
+ ERROR(NS_ERROR_FTP_PASV, FAILURE(23)),
+ ERROR(NS_ERROR_FTP_PWD, FAILURE(24)),
+ ERROR(NS_ERROR_FTP_LIST, FAILURE(28)),
+
+ /* DNS specific error codes: */
+
+ /* The lookup of a hostname failed. This generally refers to the hostname
+ * from the URL being loaded. */
+ ERROR(NS_ERROR_UNKNOWN_HOST, FAILURE(30)),
+ /* A low or medium priority DNS lookup failed because the pending queue was
+ * already full. High priorty (the default) always makes room */
+ ERROR(NS_ERROR_DNS_LOOKUP_QUEUE_FULL, FAILURE(33)),
+ /* The lookup of a proxy hostname failed. If a channel is configured to
+ * speak to a proxy server, then it will generate this error if the proxy
+ * hostname cannot be resolved. */
+ ERROR(NS_ERROR_UNKNOWN_PROXY_HOST, FAILURE(42)),
+
+
+ /* Socket specific error codes: */
+
+ /* The specified socket type does not exist. */
+ ERROR(NS_ERROR_UNKNOWN_SOCKET_TYPE, FAILURE(51)),
+ /* The specified socket type could not be created. */
+ ERROR(NS_ERROR_SOCKET_CREATE_FAILED, FAILURE(52)),
+ /* The operating system doesn't support the given type of address. */
+ ERROR(NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED, FAILURE(53)),
+ /* The address to which we tried to bind the socket was busy. */
+ ERROR(NS_ERROR_SOCKET_ADDRESS_IN_USE, FAILURE(54)),
+
+ /* Cache specific error codes: */
+ ERROR(NS_ERROR_CACHE_KEY_NOT_FOUND, FAILURE(61)),
+ ERROR(NS_ERROR_CACHE_DATA_IS_STREAM, FAILURE(62)),
+ ERROR(NS_ERROR_CACHE_DATA_IS_NOT_STREAM, FAILURE(63)),
+ ERROR(NS_ERROR_CACHE_WAIT_FOR_VALIDATION, FAILURE(64)),
+ ERROR(NS_ERROR_CACHE_ENTRY_DOOMED, FAILURE(65)),
+ ERROR(NS_ERROR_CACHE_READ_ACCESS_DENIED, FAILURE(66)),
+ ERROR(NS_ERROR_CACHE_WRITE_ACCESS_DENIED, FAILURE(67)),
+ ERROR(NS_ERROR_CACHE_IN_USE, FAILURE(68)),
+ /* Error passed through onStopRequest if the document could not be fetched
+ * from the cache. */
+ ERROR(NS_ERROR_DOCUMENT_NOT_CACHED, FAILURE(70)),
+
+ /* Effective TLD Service specific error codes: */
+
+ /* The requested number of domain levels exceeds those present in the host
+ * string. */
+ ERROR(NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS, FAILURE(80)),
+ /* The host string is an IP address. */
+ ERROR(NS_ERROR_HOST_IS_IP_ADDRESS, FAILURE(81)),
+
+
+ /* StreamLoader specific result codes: */
+
+ /* Result code returned by nsIStreamLoaderObserver to indicate that the
+ * observer is taking over responsibility for the data buffer, and the loader
+ * should NOT free it. */
+ ERROR(NS_SUCCESS_ADOPTED_DATA, SUCCESS(90)),
+
+ /* FTP */
+ ERROR(NS_NET_STATUS_BEGIN_FTP_TRANSACTION, SUCCESS(27)),
+ ERROR(NS_NET_STATUS_END_FTP_TRANSACTION, SUCCESS(28)),
+
+ /* This success code may be returned by nsIAuthModule::getNextToken to
+ * indicate that the authentication is finished and thus there's no need
+ * to call getNextToken again. */
+ ERROR(NS_SUCCESS_AUTH_FINISHED, SUCCESS(40)),
+
+ /* These are really not "results", they're statuses, used by nsITransport and
+ * friends. This is abuse of nsresult, but we'll put up with it for now. */
+ /* nsITransport */
+ ERROR(NS_NET_STATUS_READING, FAILURE(8)),
+ ERROR(NS_NET_STATUS_WRITING, FAILURE(9)),
+
+ /* nsISocketTransport */
+ ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
+ ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
+ ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
+ ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
+ ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
+ ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
+ ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
+
+ /* nsIInterceptedChannel */
+ /* Generic error for non-specific failures during service worker interception */
+ ERROR(NS_ERROR_INTERCEPTION_FAILED, FAILURE(100)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 7: NS_ERROR_MODULE_PLUGINS */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_PLUGINS
+ ERROR(NS_ERROR_PLUGINS_PLUGINSNOTCHANGED, FAILURE(1000)),
+ ERROR(NS_ERROR_PLUGIN_DISABLED, FAILURE(1001)),
+ ERROR(NS_ERROR_PLUGIN_BLOCKLISTED, FAILURE(1002)),
+ ERROR(NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, FAILURE(1003)),
+ ERROR(NS_ERROR_PLUGIN_CLICKTOPLAY, FAILURE(1004)),
+ ERROR(NS_PLUGIN_INIT_PENDING, SUCCESS(1005)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 8: NS_ERROR_MODULE_LAYOUT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_LAYOUT
+ /* Return code for nsITableLayout */
+ ERROR(NS_TABLELAYOUT_CELL_NOT_FOUND, SUCCESS(0)),
+ /* Return code for nsFrame::GetNextPrevLineFromeBlockFrame */
+ ERROR(NS_POSITION_BEFORE_TABLE, SUCCESS(3)),
+ /** Return codes for nsPresState::GetProperty() */
+ /* Returned if the property exists */
+ ERROR(NS_STATE_PROPERTY_EXISTS, NS_OK),
+ /* Returned if the property does not exist */
+ ERROR(NS_STATE_PROPERTY_NOT_THERE, SUCCESS(5)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 9: NS_ERROR_MODULE_HTMLPARSER */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_HTMLPARSER
+ ERROR(NS_ERROR_HTMLPARSER_CONTINUE, NS_OK),
+
+ ERROR(NS_ERROR_HTMLPARSER_EOF, FAILURE(1000)),
+ ERROR(NS_ERROR_HTMLPARSER_UNKNOWN, FAILURE(1001)),
+ ERROR(NS_ERROR_HTMLPARSER_CANTPROPAGATE, FAILURE(1002)),
+ ERROR(NS_ERROR_HTMLPARSER_CONTEXTMISMATCH, FAILURE(1003)),
+ ERROR(NS_ERROR_HTMLPARSER_BADFILENAME, FAILURE(1004)),
+ ERROR(NS_ERROR_HTMLPARSER_BADURL, FAILURE(1005)),
+ ERROR(NS_ERROR_HTMLPARSER_INVALIDPARSERCONTEXT, FAILURE(1006)),
+ ERROR(NS_ERROR_HTMLPARSER_INTERRUPTED, FAILURE(1007)),
+ ERROR(NS_ERROR_HTMLPARSER_BLOCK, FAILURE(1008)),
+ ERROR(NS_ERROR_HTMLPARSER_BADTOKENIZER, FAILURE(1009)),
+ ERROR(NS_ERROR_HTMLPARSER_BADATTRIBUTE, FAILURE(1010)),
+ ERROR(NS_ERROR_HTMLPARSER_UNRESOLVEDDTD, FAILURE(1011)),
+ ERROR(NS_ERROR_HTMLPARSER_MISPLACEDTABLECONTENT, FAILURE(1012)),
+ ERROR(NS_ERROR_HTMLPARSER_BADDTD, FAILURE(1013)),
+ ERROR(NS_ERROR_HTMLPARSER_BADCONTEXT, FAILURE(1014)),
+ ERROR(NS_ERROR_HTMLPARSER_STOPPARSING, FAILURE(1015)),
+ ERROR(NS_ERROR_HTMLPARSER_UNTERMINATEDSTRINGLITERAL, FAILURE(1016)),
+ ERROR(NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP, FAILURE(1017)),
+ ERROR(NS_ERROR_HTMLPARSER_FAKE_ENDTAG, FAILURE(1018)),
+ ERROR(NS_ERROR_HTMLPARSER_INVALID_COMMENT, FAILURE(1019)),
+
+ ERROR(NS_HTMLTOKENS_NOT_AN_ENTITY, SUCCESS(2000)),
+ ERROR(NS_HTMLPARSER_VALID_META_CHARSET, SUCCESS(3000)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 10: NS_ERROR_MODULE_RDF */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_RDF
+ /* Returned from nsIRDFDataSource::Assert() and Unassert() if the assertion
+ * (or unassertion was accepted by the datasource */
+ ERROR(NS_RDF_ASSERTION_ACCEPTED, NS_OK),
+ /* Returned from nsIRDFCursor::Advance() if the cursor has no more
+ * elements to enumerate */
+ ERROR(NS_RDF_CURSOR_EMPTY, SUCCESS(1)),
+ /* Returned from nsIRDFDataSource::GetSource() and GetTarget() if the
+ * source/target has no value */
+ ERROR(NS_RDF_NO_VALUE, SUCCESS(2)),
+ /* Returned from nsIRDFDataSource::Assert() and Unassert() if the assertion
+ * (or unassertion) was rejected by the datasource; i.e., the datasource was
+ * not willing to record the statement. */
+ ERROR(NS_RDF_ASSERTION_REJECTED, SUCCESS(3)),
+ /* Return this from rdfITripleVisitor to stop cycling */
+ ERROR(NS_RDF_STOP_VISIT, SUCCESS(4)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 11: NS_ERROR_MODULE_UCONV */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_UCONV
+ ERROR(NS_ERROR_UCONV_NOCONV, FAILURE(1)),
+ ERROR(NS_ERROR_UDEC_ILLEGALINPUT, FAILURE(14)),
+
+ ERROR(NS_SUCCESS_USING_FALLBACK_LOCALE, SUCCESS(2)),
+ ERROR(NS_OK_UDEC_EXACTLENGTH, SUCCESS(11)),
+ ERROR(NS_OK_UDEC_MOREINPUT, SUCCESS(12)),
+ ERROR(NS_OK_UDEC_MOREOUTPUT, SUCCESS(13)),
+ ERROR(NS_OK_UDEC_NOBOMFOUND, SUCCESS(14)),
+ ERROR(NS_OK_UENC_EXACTLENGTH, SUCCESS(33)),
+ ERROR(NS_OK_UENC_MOREOUTPUT, SUCCESS(34)),
+ ERROR(NS_ERROR_UENC_NOMAPPING, SUCCESS(35)),
+ ERROR(NS_OK_UENC_MOREINPUT, SUCCESS(36)),
+
+ /* BEGIN DEPRECATED */
+ ERROR(NS_EXACT_LENGTH, NS_OK_UDEC_EXACTLENGTH),
+ ERROR(NS_PARTIAL_MORE_INPUT, NS_OK_UDEC_MOREINPUT),
+ ERROR(NS_PARTIAL_MORE_OUTPUT, NS_OK_UDEC_MOREOUTPUT),
+ ERROR(NS_ERROR_ILLEGAL_INPUT, NS_ERROR_UDEC_ILLEGALINPUT),
+ /* END DEPRECATED */
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 13: NS_ERROR_MODULE_FILES */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_FILES
+ ERROR(NS_ERROR_FILE_UNRECOGNIZED_PATH, FAILURE(1)),
+ ERROR(NS_ERROR_FILE_UNRESOLVABLE_SYMLINK, FAILURE(2)),
+ ERROR(NS_ERROR_FILE_EXECUTION_FAILED, FAILURE(3)),
+ ERROR(NS_ERROR_FILE_UNKNOWN_TYPE, FAILURE(4)),
+ ERROR(NS_ERROR_FILE_DESTINATION_NOT_DIR, FAILURE(5)),
+ ERROR(NS_ERROR_FILE_TARGET_DOES_NOT_EXIST, FAILURE(6)),
+ ERROR(NS_ERROR_FILE_COPY_OR_MOVE_FAILED, FAILURE(7)),
+ ERROR(NS_ERROR_FILE_ALREADY_EXISTS, FAILURE(8)),
+ ERROR(NS_ERROR_FILE_INVALID_PATH, FAILURE(9)),
+ ERROR(NS_ERROR_FILE_DISK_FULL, FAILURE(10)),
+ ERROR(NS_ERROR_FILE_CORRUPTED, FAILURE(11)),
+ ERROR(NS_ERROR_FILE_NOT_DIRECTORY, FAILURE(12)),
+ ERROR(NS_ERROR_FILE_IS_DIRECTORY, FAILURE(13)),
+ ERROR(NS_ERROR_FILE_IS_LOCKED, FAILURE(14)),
+ ERROR(NS_ERROR_FILE_TOO_BIG, FAILURE(15)),
+ ERROR(NS_ERROR_FILE_NO_DEVICE_SPACE, FAILURE(16)),
+ ERROR(NS_ERROR_FILE_NAME_TOO_LONG, FAILURE(17)),
+ ERROR(NS_ERROR_FILE_NOT_FOUND, FAILURE(18)),
+ ERROR(NS_ERROR_FILE_READ_ONLY, FAILURE(19)),
+ ERROR(NS_ERROR_FILE_DIR_NOT_EMPTY, FAILURE(20)),
+ ERROR(NS_ERROR_FILE_ACCESS_DENIED, FAILURE(21)),
+
+ ERROR(NS_SUCCESS_FILE_DIRECTORY_EMPTY, SUCCESS(1)),
+ /* Result codes used by nsIDirectoryServiceProvider2 */
+ ERROR(NS_SUCCESS_AGGREGATE_RESULT, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 14: NS_ERROR_MODULE_DOM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM
+ /* XXX If you add a new DOM error code, also add an error string to
+ * dom/base/domerr.msg */
+
+ /* Standard DOM error codes: http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */
+ ERROR(NS_ERROR_DOM_INDEX_SIZE_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_WRONG_DOCUMENT_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_INVALID_CHARACTER_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR, FAILURE(7)),
+ ERROR(NS_ERROR_DOM_NOT_FOUND_ERR, FAILURE(8)),
+ ERROR(NS_ERROR_DOM_NOT_SUPPORTED_ERR, FAILURE(9)),
+ ERROR(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR, FAILURE(10)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_ERR, FAILURE(11)),
+ ERROR(NS_ERROR_DOM_SYNTAX_ERR, FAILURE(12)),
+ ERROR(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, FAILURE(13)),
+ ERROR(NS_ERROR_DOM_NAMESPACE_ERR, FAILURE(14)),
+ ERROR(NS_ERROR_DOM_INVALID_ACCESS_ERR, FAILURE(15)),
+ ERROR(NS_ERROR_DOM_TYPE_MISMATCH_ERR, FAILURE(17)),
+ ERROR(NS_ERROR_DOM_SECURITY_ERR, FAILURE(18)),
+ ERROR(NS_ERROR_DOM_NETWORK_ERR, FAILURE(19)),
+ ERROR(NS_ERROR_DOM_ABORT_ERR, FAILURE(20)),
+ ERROR(NS_ERROR_DOM_URL_MISMATCH_ERR, FAILURE(21)),
+ ERROR(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR, FAILURE(22)),
+ ERROR(NS_ERROR_DOM_TIMEOUT_ERR, FAILURE(23)),
+ ERROR(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR, FAILURE(24)),
+ ERROR(NS_ERROR_DOM_DATA_CLONE_ERR, FAILURE(25)),
+ /* XXX Should be JavaScript native errors */
+ ERROR(NS_ERROR_TYPE_ERR, FAILURE(26)),
+ ERROR(NS_ERROR_RANGE_ERR, FAILURE(27)),
+ /* StringEncoding API errors from http://wiki.whatwg.org/wiki/StringEncoding */
+ ERROR(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR, FAILURE(28)),
+ ERROR(NS_ERROR_DOM_INVALID_POINTER_ERR, FAILURE(29)),
+ /* WebCrypto API errors from http://www.w3.org/TR/WebCryptoAPI/ */
+ ERROR(NS_ERROR_DOM_UNKNOWN_ERR, FAILURE(30)),
+ ERROR(NS_ERROR_DOM_DATA_ERR, FAILURE(31)),
+ ERROR(NS_ERROR_DOM_OPERATION_ERR, FAILURE(32)),
+ /* https://heycam.github.io/webidl/#notallowederror */
+ ERROR(NS_ERROR_DOM_NOT_ALLOWED_ERR, FAILURE(33)),
+ /* DOM error codes defined by us */
+ ERROR(NS_ERROR_DOM_SECMAN_ERR, FAILURE(1001)),
+ ERROR(NS_ERROR_DOM_WRONG_TYPE_ERR, FAILURE(1002)),
+ ERROR(NS_ERROR_DOM_NOT_OBJECT_ERR, FAILURE(1003)),
+ ERROR(NS_ERROR_DOM_NOT_XPC_OBJECT_ERR, FAILURE(1004)),
+ ERROR(NS_ERROR_DOM_NOT_NUMBER_ERR, FAILURE(1005)),
+ ERROR(NS_ERROR_DOM_NOT_BOOLEAN_ERR, FAILURE(1006)),
+ ERROR(NS_ERROR_DOM_NOT_FUNCTION_ERR, FAILURE(1007)),
+ ERROR(NS_ERROR_DOM_TOO_FEW_PARAMETERS_ERR, FAILURE(1008)),
+ ERROR(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN, FAILURE(1009)),
+ ERROR(NS_ERROR_DOM_PROP_ACCESS_DENIED, FAILURE(1010)),
+ ERROR(NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED, FAILURE(1011)),
+ ERROR(NS_ERROR_DOM_BAD_URI, FAILURE(1012)),
+ ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
+ ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
+ ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
+
+ /* A way to represent uncatchable exceptions */
+ ERROR(NS_ERROR_UNCATCHABLE_EXCEPTION, FAILURE(1016)),
+
+ /* An nsresult value to use in ErrorResult to indicate that we want to throw
+ a DOMException */
+ ERROR(NS_ERROR_DOM_DOMEXCEPTION, FAILURE(1017)),
+
+ /* An nsresult value to use in ErrorResult to indicate that we
+ * should just rethrow whatever is on the JSContext (which might be
+ * nothing if an uncatchable exception was thrown).
+ */
+ ERROR(NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT, FAILURE(1018)),
+
+ ERROR(NS_ERROR_DOM_MALFORMED_URI, FAILURE(1019)),
+ ERROR(NS_ERROR_DOM_INVALID_HEADER_NAME, FAILURE(1020)),
+
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT, FAILURE(1021)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED, FAILURE(1022)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING, FAILURE(1023)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE, FAILURE(1024)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML, FAILURE(1025)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT, FAILURE(1026)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC, FAILURE(1027)),
+ ERROR(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC, FAILURE(1028)),
+
+ /* May be used to indicate when e.g. setting a property value didn't
+ * actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
+ * the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
+ */
+ ERROR(NS_SUCCESS_DOM_NO_OPERATION, SUCCESS(1)),
+
+ /*
+ * A success code that indicates that evaluating a string of JS went
+ * just fine except it threw an exception. Only for legacy use by
+ * nsJSUtils.
+ */
+ ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW, SUCCESS(2)),
+
+ /*
+ * A success code that indicates that evaluating a string of JS went
+ * just fine except it was killed by an uncatchable exception.
+ * Only for legacy use by nsJSUtils.
+ */
+ ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE, SUCCESS(3)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 15: NS_ERROR_MODULE_IMGLIB */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_IMGLIB
+ ERROR(NS_IMAGELIB_SUCCESS_LOAD_FINISHED, SUCCESS(0)),
+ ERROR(NS_IMAGELIB_CHANGING_OWNER, SUCCESS(1)),
+
+ ERROR(NS_IMAGELIB_ERROR_FAILURE, FAILURE(5)),
+ ERROR(NS_IMAGELIB_ERROR_NO_DECODER, FAILURE(6)),
+ ERROR(NS_IMAGELIB_ERROR_NOT_FINISHED, FAILURE(7)),
+ ERROR(NS_IMAGELIB_ERROR_NO_ENCODER, FAILURE(9)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 17: NS_ERROR_MODULE_EDITOR */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_EDITOR
+ ERROR(NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND, SUCCESS(1)),
+ ERROR(NS_SUCCESS_EDITOR_FOUND_TARGET, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 18: NS_ERROR_MODULE_XPCONNECT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_XPCONNECT
+ ERROR(NS_ERROR_XPC_NOT_ENOUGH_ARGS, FAILURE(1)),
+ ERROR(NS_ERROR_XPC_NEED_OUT_OBJECT, FAILURE(2)),
+ ERROR(NS_ERROR_XPC_CANT_SET_OUT_VAL, FAILURE(3)),
+ ERROR(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, FAILURE(4)),
+ ERROR(NS_ERROR_XPC_CANT_GET_INTERFACE_INFO, FAILURE(5)),
+ ERROR(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, FAILURE(6)),
+ ERROR(NS_ERROR_XPC_CANT_GET_METHOD_INFO, FAILURE(7)),
+ ERROR(NS_ERROR_XPC_UNEXPECTED, FAILURE(8)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_JS, FAILURE(9)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_NATIVE, FAILURE(10)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF, FAILURE(11)),
+ ERROR(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, FAILURE(12)),
+ ERROR(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN, FAILURE(13)),
+ ERROR(NS_ERROR_XPC_CANT_DEFINE_PROP_ON_WN, FAILURE(14)),
+ ERROR(NS_ERROR_XPC_CANT_WATCH_WN_STATIC, FAILURE(15)),
+ ERROR(NS_ERROR_XPC_CANT_EXPORT_WN_STATIC, FAILURE(16)),
+ ERROR(NS_ERROR_XPC_SCRIPTABLE_CALL_FAILED, FAILURE(17)),
+ ERROR(NS_ERROR_XPC_SCRIPTABLE_CTOR_FAILED, FAILURE(18)),
+ ERROR(NS_ERROR_XPC_CANT_CALL_WO_SCRIPTABLE, FAILURE(19)),
+ ERROR(NS_ERROR_XPC_CANT_CTOR_WO_SCRIPTABLE, FAILURE(20)),
+ ERROR(NS_ERROR_XPC_CI_RETURNED_FAILURE, FAILURE(21)),
+ ERROR(NS_ERROR_XPC_GS_RETURNED_FAILURE, FAILURE(22)),
+ ERROR(NS_ERROR_XPC_BAD_CID, FAILURE(23)),
+ ERROR(NS_ERROR_XPC_BAD_IID, FAILURE(24)),
+ ERROR(NS_ERROR_XPC_CANT_CREATE_WN, FAILURE(25)),
+ ERROR(NS_ERROR_XPC_JS_THREW_EXCEPTION, FAILURE(26)),
+ ERROR(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT, FAILURE(27)),
+ ERROR(NS_ERROR_XPC_JS_THREW_JS_OBJECT, FAILURE(28)),
+ ERROR(NS_ERROR_XPC_JS_THREW_NULL, FAILURE(29)),
+ ERROR(NS_ERROR_XPC_JS_THREW_STRING, FAILURE(30)),
+ ERROR(NS_ERROR_XPC_JS_THREW_NUMBER, FAILURE(31)),
+ ERROR(NS_ERROR_XPC_JAVASCRIPT_ERROR, FAILURE(32)),
+ ERROR(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, FAILURE(33)),
+ ERROR(NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY, FAILURE(34)),
+ ERROR(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, FAILURE(35)),
+ ERROR(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY, FAILURE(36)),
+ ERROR(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, FAILURE(37)),
+ ERROR(NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING, FAILURE(38)),
+ ERROR(NS_ERROR_XPC_SECURITY_MANAGER_VETO, FAILURE(39)),
+ ERROR(NS_ERROR_XPC_INTERFACE_NOT_SCRIPTABLE, FAILURE(40)),
+ ERROR(NS_ERROR_XPC_INTERFACE_NOT_FROM_NSISUPPORTS, FAILURE(41)),
+ ERROR(NS_ERROR_XPC_CANT_GET_JSOBJECT_OF_DOM_OBJECT, FAILURE(42)),
+ ERROR(NS_ERROR_XPC_CANT_SET_READ_ONLY_CONSTANT, FAILURE(43)),
+ ERROR(NS_ERROR_XPC_CANT_SET_READ_ONLY_ATTRIBUTE, FAILURE(44)),
+ ERROR(NS_ERROR_XPC_CANT_SET_READ_ONLY_METHOD, FAILURE(45)),
+ ERROR(NS_ERROR_XPC_CANT_ADD_PROP_TO_WRAPPED_NATIVE, FAILURE(46)),
+ ERROR(NS_ERROR_XPC_CALL_TO_SCRIPTABLE_FAILED, FAILURE(47)),
+ ERROR(NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED, FAILURE(48)),
+ ERROR(NS_ERROR_XPC_BAD_ID_STRING, FAILURE(49)),
+ ERROR(NS_ERROR_XPC_BAD_INITIALIZER_NAME, FAILURE(50)),
+ ERROR(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, FAILURE(51)),
+ ERROR(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, FAILURE(52)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL, FAILURE(53)),
+ ERROR(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, FAILURE(54)),
+ /* any new errors here should have an associated entry added in xpc.msg */
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 19: NS_ERROR_MODULE_PROFILE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_PROFILE
+ ERROR(NS_ERROR_LAUNCHED_CHILD_PROCESS, FAILURE(200)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 21: NS_ERROR_MODULE_SECURITY */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SECURITY
+ /* Error code for CSP */
+ ERROR(NS_ERROR_CSP_FORM_ACTION_VIOLATION, FAILURE(98)),
+ ERROR(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION, FAILURE(99)),
+
+ /* Error code for Sub-Resource Integrity */
+ ERROR(NS_ERROR_SRI_CORRUPT, FAILURE(200)),
+ ERROR(NS_ERROR_SRI_DISABLED, FAILURE(201)),
+ ERROR(NS_ERROR_SRI_NOT_ELIGIBLE, FAILURE(202)),
+ ERROR(NS_ERROR_SRI_UNEXPECTED_HASH_TYPE, FAILURE(203)),
+ ERROR(NS_ERROR_SRI_IMPORT, FAILURE(204)),
+
+ /* CMS specific nsresult error codes. Note: the numbers used here correspond
+ * to the values in nsICMSMessageErrors.idl. */
+ ERROR(NS_ERROR_CMS_VERIFY_NOT_SIGNED, FAILURE(1024)),
+ ERROR(NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO, FAILURE(1025)),
+ ERROR(NS_ERROR_CMS_VERIFY_BAD_DIGEST, FAILURE(1026)),
+ ERROR(NS_ERROR_CMS_VERIFY_NOCERT, FAILURE(1028)),
+ ERROR(NS_ERROR_CMS_VERIFY_UNTRUSTED, FAILURE(1029)),
+ ERROR(NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED, FAILURE(1031)),
+ ERROR(NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, FAILURE(1032)),
+ ERROR(NS_ERROR_CMS_VERIFY_BAD_SIGNATURE, FAILURE(1033)),
+ ERROR(NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH, FAILURE(1034)),
+ ERROR(NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO, FAILURE(1035)),
+ ERROR(NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO, FAILURE(1036)),
+ ERROR(NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE, FAILURE(1037)),
+ ERROR(NS_ERROR_CMS_VERIFY_HEADER_MISMATCH, FAILURE(1038)),
+ ERROR(NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED, FAILURE(1039)),
+ ERROR(NS_ERROR_CMS_VERIFY_CERT_WITHOUT_ADDRESS, FAILURE(1040)),
+ ERROR(NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG, FAILURE(1056)),
+ ERROR(NS_ERROR_CMS_ENCRYPT_INCOMPLETE, FAILURE(1057)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 22: NS_ERROR_MODULE_DOM_XPATH */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_XPATH
+ /* DOM error codes from http://www.w3.org/TR/DOM-Level-3-XPath/ */
+ ERROR(NS_ERROR_DOM_INVALID_EXPRESSION_ERR, FAILURE(51)),
+ ERROR(NS_ERROR_DOM_TYPE_ERR, FAILURE(52)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 24: NS_ERROR_MODULE_URILOADER */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_URILOADER
+ ERROR(NS_ERROR_WONT_HANDLE_CONTENT, FAILURE(1)),
+ /* The load has been cancelled because it was found on a malware or phishing
+ * blacklist. */
+ ERROR(NS_ERROR_MALWARE_URI, FAILURE(30)),
+ ERROR(NS_ERROR_PHISHING_URI, FAILURE(31)),
+ ERROR(NS_ERROR_TRACKING_URI, FAILURE(34)),
+ ERROR(NS_ERROR_UNWANTED_URI, FAILURE(35)),
+ ERROR(NS_ERROR_BLOCKED_URI, FAILURE(37)),
+ /* Used when "Save Link As..." doesn't see the headers quickly enough to
+ * choose a filename. See nsContextMenu.js. */
+ ERROR(NS_ERROR_SAVE_LINK_AS_TIMEOUT, FAILURE(32)),
+ /* Used when the data from a channel has already been parsed and cached so it
+ * doesn't need to be reparsed from the original source. */
+ ERROR(NS_ERROR_PARSED_DATA_CACHED, FAILURE(33)),
+
+ /* This success code indicates that a refresh header was found and
+ * successfully setup. */
+ ERROR(NS_REFRESHURI_HEADER_FOUND, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 25: NS_ERROR_MODULE_CONTENT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_CONTENT
+ /* Error codes for image loading */
+ ERROR(NS_ERROR_IMAGE_SRC_CHANGED, FAILURE(4)),
+ ERROR(NS_ERROR_IMAGE_BLOCKED, FAILURE(5)),
+ /* Error codes for content policy blocking */
+ ERROR(NS_ERROR_CONTENT_BLOCKED, FAILURE(6)),
+ ERROR(NS_ERROR_CONTENT_BLOCKED_SHOW_ALT, FAILURE(7)),
+ /* Success variations of content policy blocking */
+ ERROR(NS_PROPTABLE_PROP_NOT_THERE, FAILURE(10)),
+ /* Error code for XBL */
+ ERROR(NS_ERROR_XBL_BLOCKED, FAILURE(15)),
+ /* Error code for when the content process crashed */
+ ERROR(NS_ERROR_CONTENT_CRASHED, FAILURE(16)),
+
+ /* XXX this is not really used */
+ ERROR(NS_HTML_STYLE_PROPERTY_NOT_THERE, SUCCESS(2)),
+ ERROR(NS_CONTENT_BLOCKED, SUCCESS(8)),
+ ERROR(NS_CONTENT_BLOCKED_SHOW_ALT, SUCCESS(9)),
+ ERROR(NS_PROPTABLE_PROP_OVERWRITTEN, SUCCESS(11)),
+ /* Error codes for FindBroadcaster in XULDocument.cpp */
+ ERROR(NS_FINDBROADCASTER_NOT_FOUND, SUCCESS(12)),
+ ERROR(NS_FINDBROADCASTER_FOUND, SUCCESS(13)),
+ ERROR(NS_FINDBROADCASTER_AWAIT_OVERLAYS, SUCCESS(14)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 27: NS_ERROR_MODULE_XSLT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_XSLT
+ ERROR(NS_ERROR_XPATH_INVALID_ARG, NS_ERROR_INVALID_ARG),
+
+ ERROR(NS_ERROR_XSLT_PARSE_FAILURE, FAILURE(1)),
+ ERROR(NS_ERROR_XPATH_PARSE_FAILURE, FAILURE(2)),
+ ERROR(NS_ERROR_XSLT_ALREADY_SET, FAILURE(3)),
+ ERROR(NS_ERROR_XSLT_EXECUTION_FAILURE, FAILURE(4)),
+ ERROR(NS_ERROR_XPATH_UNKNOWN_FUNCTION, FAILURE(5)),
+ ERROR(NS_ERROR_XSLT_BAD_RECURSION, FAILURE(6)),
+ ERROR(NS_ERROR_XSLT_BAD_VALUE, FAILURE(7)),
+ ERROR(NS_ERROR_XSLT_NODESET_EXPECTED, FAILURE(8)),
+ ERROR(NS_ERROR_XSLT_ABORTED, FAILURE(9)),
+ ERROR(NS_ERROR_XSLT_NETWORK_ERROR, FAILURE(10)),
+ ERROR(NS_ERROR_XSLT_WRONG_MIME_TYPE, FAILURE(11)),
+ ERROR(NS_ERROR_XSLT_LOAD_RECURSION, FAILURE(12)),
+ ERROR(NS_ERROR_XPATH_BAD_ARGUMENT_COUNT, FAILURE(13)),
+ ERROR(NS_ERROR_XPATH_BAD_EXTENSION_FUNCTION, FAILURE(14)),
+ ERROR(NS_ERROR_XPATH_PAREN_EXPECTED, FAILURE(15)),
+ ERROR(NS_ERROR_XPATH_INVALID_AXIS, FAILURE(16)),
+ ERROR(NS_ERROR_XPATH_NO_NODE_TYPE_TEST, FAILURE(17)),
+ ERROR(NS_ERROR_XPATH_BRACKET_EXPECTED, FAILURE(18)),
+ ERROR(NS_ERROR_XPATH_INVALID_VAR_NAME, FAILURE(19)),
+ ERROR(NS_ERROR_XPATH_UNEXPECTED_END, FAILURE(20)),
+ ERROR(NS_ERROR_XPATH_OPERATOR_EXPECTED, FAILURE(21)),
+ ERROR(NS_ERROR_XPATH_UNCLOSED_LITERAL, FAILURE(22)),
+ ERROR(NS_ERROR_XPATH_BAD_COLON, FAILURE(23)),
+ ERROR(NS_ERROR_XPATH_BAD_BANG, FAILURE(24)),
+ ERROR(NS_ERROR_XPATH_ILLEGAL_CHAR, FAILURE(25)),
+ ERROR(NS_ERROR_XPATH_BINARY_EXPECTED, FAILURE(26)),
+ ERROR(NS_ERROR_XSLT_LOAD_BLOCKED_ERROR, FAILURE(27)),
+ ERROR(NS_ERROR_XPATH_INVALID_EXPRESSION_EVALUATED, FAILURE(28)),
+ ERROR(NS_ERROR_XPATH_UNBALANCED_CURLY_BRACE, FAILURE(29)),
+ ERROR(NS_ERROR_XSLT_BAD_NODE_NAME, FAILURE(30)),
+ ERROR(NS_ERROR_XSLT_VAR_ALREADY_SET, FAILURE(31)),
+ ERROR(NS_ERROR_XSLT_CALL_TO_KEY_NOT_ALLOWED, FAILURE(32)),
+
+ ERROR(NS_XSLT_GET_NEW_HANDLER, SUCCESS(1)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 28: NS_ERROR_MODULE_IPC */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_IPC
+ // Initial creation of a Transport object failed internally for unknown reasons.
+ ERROR(NS_ERROR_TRANSPORT_INIT, FAILURE(1)),
+ // Generic error related to duplicating handle failures.
+ ERROR(NS_ERROR_DUPLICATE_HANDLE, FAILURE(2)),
+ // Bridging: failure trying to open the connection to the parent
+ ERROR(NS_ERROR_BRIDGE_OPEN_PARENT, FAILURE(3)),
+ // Bridging: failure trying to open the connection to the child
+ ERROR(NS_ERROR_BRIDGE_OPEN_CHILD, FAILURE(4)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 29: NS_ERROR_MODULE_SVG */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SVG
+ /* SVG DOM error codes from http://www.w3.org/TR/SVG11/svgdom.html */
+ ERROR(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR, FAILURE(0)),
+ /* Yes, the spec says "INVERTABLE", not "INVERTIBLE" */
+ ERROR(NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE, FAILURE(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 30: NS_ERROR_MODULE_STORAGE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_STORAGE
+ /* To add additional errors to Storage, please append entries to the bottom
+ * of the list in the following format:
+ * NS_ERROR_STORAGE_YOUR_ERR, FAILURE(n)
+ * where n is the next unique positive integer. You must also add an entry
+ * to js/xpconnect/src/xpc.msg under the code block beginning with the
+ * comment 'storage related codes (from mozStorage.h)', in the following
+ * format: 'XPC_MSG_DEF(NS_ERROR_STORAGE_YOUR_ERR, "brief description of your
+ * error")' */
+ ERROR(NS_ERROR_STORAGE_BUSY, FAILURE(1)),
+ ERROR(NS_ERROR_STORAGE_IOERR, FAILURE(2)),
+ ERROR(NS_ERROR_STORAGE_CONSTRAINT, FAILURE(3)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 32: NS_ERROR_MODULE_DOM_FILE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_FILE
+ ERROR(NS_ERROR_DOM_FILE_NOT_FOUND_ERR, FAILURE(0)),
+ ERROR(NS_ERROR_DOM_FILE_NOT_READABLE_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_FILE_ABORT_ERR, FAILURE(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 33: NS_ERROR_MODULE_DOM_INDEXEDDB */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_INDEXEDDB
+ /* IndexedDB error codes http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html */
+ ERROR(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_DATA_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR, FAILURE(6)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR, FAILURE(7)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, FAILURE(8)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR, FAILURE(9)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_TIMEOUT_ERR, FAILURE(10)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR, FAILURE(11)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_VERSION_ERR, FAILURE(12)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR, FAILURE(1001)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 34: NS_ERROR_MODULE_DOM_FILEHANDLE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_FILEHANDLE
+ ERROR(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR, FAILURE(6)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 35: NS_ERROR_MODULE_SIGNED_JAR */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SIGNED_JAR
+ ERROR(NS_ERROR_SIGNED_JAR_NOT_SIGNED, FAILURE(1)),
+ ERROR(NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, FAILURE(2)),
+ ERROR(NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, FAILURE(3)),
+ ERROR(NS_ERROR_SIGNED_JAR_ENTRY_MISSING, FAILURE(4)),
+ ERROR(NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, FAILURE(5)),
+ ERROR(NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE, FAILURE(6)),
+ ERROR(NS_ERROR_SIGNED_JAR_ENTRY_INVALID, FAILURE(7)),
+ ERROR(NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, FAILURE(8)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 36: NS_ERROR_MODULE_DOM_FILESYSTEM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_FILESYSTEM
+ ERROR(NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR, FAILURE(6)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 38: NS_ERROR_MODULE_SIGNED_APP */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SIGNED_APP
+ ERROR(NS_ERROR_SIGNED_APP_MANIFEST_INVALID, FAILURE(1)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 39: NS_ERROR_MODULE_DOM_ANIM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_ANIM
+ ERROR(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR, FAILURE(1)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 40: NS_ERROR_MODULE_DOM_PUSH */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_PUSH
+ ERROR(NS_ERROR_DOM_PUSH_INVALID_REGISTRATION_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_PUSH_DENIED_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_PUSH_ABORT_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_PUSH_INVALID_KEY_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR, FAILURE(6)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 41: NS_ERROR_MODULE_DOM_MEDIA */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_MEDIA
+ /* HTMLMediaElement API errors from https://html.spec.whatwg.org/multipage/embedded-content.html#media-elements */
+ ERROR(NS_ERROR_DOM_MEDIA_ABORT_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, FAILURE(3)),
+
+ /* HTMLMediaElement internal decoding error */
+ ERROR(NS_ERROR_DOM_MEDIA_DECODE_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_MEDIA_FATAL_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_MEDIA_METADATA_ERR, FAILURE(6)),
+ ERROR(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR, FAILURE(7)),
+ ERROR(NS_ERROR_DOM_MEDIA_END_OF_STREAM, FAILURE(8)),
+ ERROR(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, FAILURE(9)),
+ ERROR(NS_ERROR_DOM_MEDIA_CANCELED, FAILURE(10)),
+ ERROR(NS_ERROR_DOM_MEDIA_MEDIASINK_ERR, FAILURE(11)),
+ ERROR(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, FAILURE(12)),
+ ERROR(NS_ERROR_DOM_MEDIA_CDM_ERR, FAILURE(13)),
+ ERROR(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, FAILURE(14)),
+
+ /* Internal platform-related errors */
+ ERROR(NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR, FAILURE(101)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 51: NS_ERROR_MODULE_GENERAL */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_GENERAL
+ /* Error code used internally by the incremental downloader to cancel the
+ * network channel when the download is already complete. */
+ ERROR(NS_ERROR_DOWNLOAD_COMPLETE, FAILURE(1)),
+ /* Error code used internally by the incremental downloader to cancel the
+ * network channel when the response to a range request is 200 instead of
+ * 206. */
+ ERROR(NS_ERROR_DOWNLOAD_NOT_PARTIAL, FAILURE(2)),
+ ERROR(NS_ERROR_UNORM_MOREOUTPUT, FAILURE(33)),
+
+ ERROR(NS_ERROR_DOCSHELL_REQUEST_REJECTED, FAILURE(1001)),
+ /* This is needed for displaying an error message when navigation is
+ * attempted on a document when printing The value arbitrary as long as it
+ * doesn't conflict with any of the other values in the errors in
+ * DisplayLoadError */
+ ERROR(NS_ERROR_DOCUMENT_IS_PRINTMODE, FAILURE(2001)),
+
+ ERROR(NS_SUCCESS_DONT_FIXUP, SUCCESS(1)),
+ /* This success code may be returned by nsIAppStartup::Run to indicate that
+ * the application should be restarted. This condition corresponds to the
+ * case in which nsIAppStartup::Quit was called with the eRestart flag. */
+ ERROR(NS_SUCCESS_RESTART_APP, SUCCESS(1)),
+ ERROR(NS_SUCCESS_RESTART_APP_NOT_SAME_PROFILE, SUCCESS(3)),
+ ERROR(NS_SUCCESS_UNORM_NOTFOUND, SUCCESS(17)),
+
+
+ /* a11y */
+ /* raised when current pivot's position is needed but it's not in the tree */
+ ERROR(NS_ERROR_NOT_IN_TREE, FAILURE(38)),
+
+ /* see nsTextEquivUtils */
+ ERROR(NS_OK_NO_NAME_CLAUSE_HANDLED, SUCCESS(34))
+#undef MODULE
diff --git a/xpcom/base/ErrorNames.cpp b/xpcom/base/ErrorNames.cpp
new file mode 100644
index 000000000..165a1a0fc
--- /dev/null
+++ b/xpcom/base/ErrorNames.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ErrorNames.h"
+#include "nsString.h"
+#include "prerror.h"
+
+namespace {
+
+struct ErrorEntry
+{
+ nsresult value;
+ const char * name;
+};
+
+#undef ERROR
+#define ERROR(key, val) {key, #key}
+
+const ErrorEntry errors[] = {
+ #include "ErrorList.h"
+};
+
+#undef ERROR
+
+} // unnamed namespace
+
+namespace mozilla {
+
+void
+GetErrorName(nsresult rv, nsACString& name)
+{
+ for (size_t i = 0; i < ArrayLength(errors); ++i) {
+ if (errors[i].value == rv) {
+ name.AssignASCII(errors[i].name);
+ return;
+ }
+ }
+
+ bool isSecurityError = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY;
+
+ // NS_ERROR_MODULE_SECURITY is the only module that is "allowed" to
+ // synthesize nsresult error codes that are not listed in ErrorList.h. (The
+ // NS_ERROR_MODULE_SECURITY error codes are synthesized from NSPR error
+ // codes.)
+ MOZ_ASSERT(isSecurityError);
+
+ name.AssignASCII(NS_SUCCEEDED(rv) ? "NS_ERROR_GENERATE_SUCCESS("
+ : "NS_ERROR_GENERATE_FAILURE(");
+
+ if (isSecurityError) {
+ name.AppendASCII("NS_ERROR_MODULE_SECURITY");
+ } else {
+ // This should never happen given the assertion above, so we don't bother
+ // trying to print a symbolic name for the module here.
+ name.AppendInt(NS_ERROR_GET_MODULE(rv));
+ }
+
+ name.AppendASCII(", ");
+
+ const char * nsprName = nullptr;
+ if (isSecurityError) {
+ // Invert the logic from NSSErrorsService::GetXPCOMFromNSSError
+ PRErrorCode nsprCode
+ = -1 * static_cast<PRErrorCode>(NS_ERROR_GET_CODE(rv));
+ nsprName = PR_ErrorToName(nsprCode);
+
+ // All NSPR error codes defined by NSPR or NSS should have a name mapping.
+ MOZ_ASSERT(nsprName);
+ }
+
+ if (nsprName) {
+ name.AppendASCII(nsprName);
+ } else {
+ name.AppendInt(NS_ERROR_GET_CODE(rv));
+ }
+
+ name.AppendASCII(")");
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/ErrorNames.h b/xpcom/base/ErrorNames.h
new file mode 100644
index 000000000..9fdba7ace
--- /dev/null
+++ b/xpcom/base/ErrorNames.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ErrorNames_h
+#define mozilla_ErrorNames_h
+
+#include "nsError.h"
+
+class nsACString;
+
+namespace mozilla {
+
+// Maps the given nsresult to its symbolic name. For example,
+// GetErrorName(NS_OK, name) will result in name == "NS_OK".
+// When the symbolic name is unknown, name will be of the form
+// "NS_ERROR_GENERATE_SUCCESS(<module>, <code>)" or
+// "NS_ERROR_GENERATE_FAILURE(<module>, <code>)".
+void GetErrorName(nsresult rv, nsACString& name);
+
+} // namespace mozilla
+
+#endif // mozilla_ErrorNames_h
diff --git a/xpcom/base/HoldDropJSObjects.cpp b/xpcom/base/HoldDropJSObjects.cpp
new file mode 100644
index 000000000..eeecc7121
--- /dev/null
+++ b/xpcom/base/HoldDropJSObjects.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/HoldDropJSObjects.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CycleCollectedJSContext.h"
+
+namespace mozilla {
+namespace cyclecollector {
+
+void
+HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->AddJSHolder(aHolder, aTracer);
+}
+
+void
+HoldJSObjectsImpl(nsISupports* aHolder)
+{
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(aHolder, &participant);
+ MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
+ MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
+ "The result of QIing a JS holder should be the same as ToSupports");
+
+ HoldJSObjectsImpl(aHolder, participant);
+}
+
+void
+DropJSObjectsImpl(void* aHolder)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->RemoveJSHolder(aHolder);
+}
+
+void
+DropJSObjectsImpl(nsISupports* aHolder)
+{
+#ifdef DEBUG
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(aHolder, &participant);
+ MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
+ MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
+ "The result of QIing a JS holder should be the same as ToSupports");
+#endif
+ DropJSObjectsImpl(static_cast<void*>(aHolder));
+}
+
+} // namespace cyclecollector
+
+#ifdef DEBUG
+bool
+IsJSHolder(void* aHolder)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ return cx->IsJSHolder(aHolder);
+}
+#endif
+
+} // namespace mozilla
diff --git a/xpcom/base/HoldDropJSObjects.h b/xpcom/base/HoldDropJSObjects.h
new file mode 100644
index 000000000..1a500a94a
--- /dev/null
+++ b/xpcom/base/HoldDropJSObjects.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_HoldDropJSObjects_h
+#define mozilla_HoldDropJSObjects_h
+
+#include "mozilla/TypeTraits.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsISupports;
+class nsScriptObjectTracer;
+
+// Only HoldJSObjects and DropJSObjects should be called directly.
+
+namespace mozilla {
+namespace cyclecollector {
+
+void HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer);
+void HoldJSObjectsImpl(nsISupports* aHolder);
+void DropJSObjectsImpl(void* aHolder);
+void DropJSObjectsImpl(nsISupports* aHolder);
+
+} // namespace cyclecollector
+
+
+template<class T, bool isISupports = IsBaseOf<nsISupports, T>::value>
+struct HoldDropJSObjectsHelper
+{
+ static void Hold(T* aHolder)
+ {
+ cyclecollector::HoldJSObjectsImpl(aHolder,
+ NS_CYCLE_COLLECTION_PARTICIPANT(T));
+ }
+ static void Drop(T* aHolder)
+ {
+ cyclecollector::DropJSObjectsImpl(aHolder);
+ }
+};
+
+template<class T>
+struct HoldDropJSObjectsHelper<T, true>
+{
+ static void Hold(T* aHolder)
+ {
+ cyclecollector::HoldJSObjectsImpl(ToSupports(aHolder));
+ }
+ static void Drop(T* aHolder)
+ {
+ cyclecollector::DropJSObjectsImpl(ToSupports(aHolder));
+ }
+};
+
+
+template<class T>
+void
+HoldJSObjects(T* aHolder)
+{
+ HoldDropJSObjectsHelper<T>::Hold(aHolder);
+}
+
+template<class T>
+void
+DropJSObjects(T* aHolder)
+{
+ HoldDropJSObjectsHelper<T>::Drop(aHolder);
+}
+
+#ifdef DEBUG
+bool IsJSHolder(void* aHolder);
+#endif
+
+} // namespace mozilla
+
+#endif // mozilla_HoldDropJSObjects_h
diff --git a/xpcom/base/JSObjectHolder.cpp b/xpcom/base/JSObjectHolder.cpp
new file mode 100644
index 000000000..5bcc3cabb
--- /dev/null
+++ b/xpcom/base/JSObjectHolder.cpp
@@ -0,0 +1,9 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "JSObjectHolder.h"
+
+NS_IMPL_ISUPPORTS(mozilla::JSObjectHolder, nsISupports)
diff --git a/xpcom/base/JSObjectHolder.h b/xpcom/base/JSObjectHolder.h
new file mode 100644
index 000000000..7b83b813d
--- /dev/null
+++ b/xpcom/base/JSObjectHolder.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_JSObjectHolder_h
+#define mozilla_JSObjectHolder_h
+
+#include "js/RootingAPI.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+// This class is useful when something on one thread needs to keep alive
+// a JS Object from another thread. If they are both on the same thread, the
+// owning class should instead be made a cycle collected SCRIPT_HOLDER class.
+// This object should only be AddRefed and Released on the same thread as
+// mJSObject.
+//
+// Note that this keeps alive the JS object until it goes away, so be sure not to
+// create cycles that keep alive the holder.
+//
+// JSObjectHolder is ISupports to make it usable with NS_ReleaseOnMainThread.
+class JSObjectHolder final : public nsISupports
+{
+public:
+ JSObjectHolder(JSContext* aCx, JSObject* aObject) : mJSObject(aCx, aObject) {}
+
+ NS_DECL_ISUPPORTS
+
+ JSObject* GetJSObject() { return mJSObject; }
+
+private:
+ ~JSObjectHolder() {}
+
+ JS::PersistentRooted<JSObject*> mJSObject;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_JSObjectHolder_h
diff --git a/xpcom/base/LinuxUtils.cpp b/xpcom/base/LinuxUtils.cpp
new file mode 100644
index 000000000..331c82be9
--- /dev/null
+++ b/xpcom/base/LinuxUtils.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LinuxUtils.h"
+
+#if defined(XP_LINUX)
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+
+void
+LinuxUtils::GetThreadName(pid_t aTid, nsACString& aName)
+{
+ aName.Truncate();
+ if (aTid <= 0) {
+ return;
+ }
+
+ const size_t kBuffSize = 16; // 15 chars max + '\n'
+ char buf[kBuffSize];
+ nsPrintfCString path("/proc/%d/comm", aTid);
+ FILE* fp = fopen(path.get(), "r");
+ if (!fp) {
+ // The fopen could also fail if the thread exited before we got here.
+ return;
+ }
+
+ size_t len = fread(buf, 1, kBuffSize, fp);
+ fclose(fp);
+
+ // No need to strip the '\n', since isspace() includes it.
+ while (len > 0 &&
+ (isspace(buf[len - 1]) || isdigit(buf[len - 1]) ||
+ buf[len - 1] == '#' || buf[len - 1] == '_')) {
+ --len;
+ }
+
+ aName.Assign(buf, len);
+}
+
+}
+
+#endif // XP_LINUX
diff --git a/xpcom/base/LinuxUtils.h b/xpcom/base/LinuxUtils.h
new file mode 100644
index 000000000..e82c15e08
--- /dev/null
+++ b/xpcom/base/LinuxUtils.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_LinuxUtils_h
+#define mozilla_LinuxUtils_h
+
+#if defined(XP_LINUX)
+
+#include <unistd.h>
+#include "nsString.h"
+
+namespace mozilla {
+
+class LinuxUtils
+{
+public:
+ // Obtain the name of a thread, omitting any numeric suffix added by a
+ // thread pool library (as in, e.g., "Binder_2" or "mozStorage #1").
+ // The empty string is returned on error.
+ //
+ // Note: if this is ever needed on kernels older than 2.6.33 (early 2010),
+ // it will have to parse /proc/<pid>/status instead, because
+ // /proc/<pid>/comm didn't exist before then.
+ static void GetThreadName(pid_t aTid, nsACString& aName);
+};
+
+}
+
+#endif // XP_LINUX
+
+#endif
diff --git a/xpcom/base/LogModulePrefWatcher.cpp b/xpcom/base/LogModulePrefWatcher.cpp
new file mode 100644
index 000000000..bd04eda98
--- /dev/null
+++ b/xpcom/base/LogModulePrefWatcher.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LogModulePrefWatcher.h"
+
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+#include "base/process_util.h"
+
+static const char kLoggingPrefPrefix[] = "logging.";
+static const char kLoggingConfigPrefPrefix[] = "logging.config";
+static const int kLoggingConfigPrefixLen = sizeof(kLoggingConfigPrefPrefix) - 1;
+static const char kLoggingPrefClearOnStartup[] = "logging.config.clear_on_startup";
+static const char kLoggingPrefLogFile[] = "logging.config.LOG_FILE";
+static const char kLoggingPrefAddTimestamp[] = "logging.config.add_timestamp";
+static const char kLoggingPrefSync[] = "logging.config.sync";
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(LogModulePrefWatcher, nsIObserver)
+
+/**
+ * Resets all the preferences in the logging. branch
+ * This is needed because we may crash while logging, and this would cause us
+ * to log after restarting as well.
+ *
+ * If logging after restart is desired, set the logging.config.clear_on_startup
+ * pref to false, or use the MOZ_LOG_FILE and MOZ_LOG_MODULES env vars.
+ */
+void ResetExistingPrefs()
+{
+ uint32_t count;
+ char** names;
+ nsresult rv = Preferences::GetRootBranch()->
+ GetChildList(kLoggingPrefPrefix, &count, &names);
+ if (NS_SUCCEEDED(rv) && count) {
+ for (size_t i = 0; i < count; i++) {
+ // Clearing the pref will cause it to reload, thus resetting the log level
+ Preferences::ClearUser(names[i]);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
+ }
+}
+
+/**
+ * Loads the log level from the given pref and updates the corresponding
+ * LogModule.
+ */
+static void
+LoadPrefValue(const char* aName)
+{
+ LogLevel logLevel = LogLevel::Disabled;
+
+ nsresult rv;
+ int32_t prefLevel = 0;
+ nsAutoCString prefValue;
+
+ if (strncmp(aName, kLoggingConfigPrefPrefix, kLoggingConfigPrefixLen) == 0) {
+ nsAutoCString prefName(aName);
+
+ if (prefName.EqualsLiteral(kLoggingPrefLogFile)) {
+ rv = Preferences::GetCString(aName, &prefValue);
+ // The pref was reset. Clear the user file.
+ if (NS_FAILED(rv) || prefValue.IsEmpty()) {
+ LogModule::SetLogFile(nullptr);
+ return;
+ }
+
+ // If the pref value doesn't have a PID placeholder, append it to the end.
+ if (!strstr(prefValue.get(), "%PID")) {
+ prefValue.Append("%PID");
+ }
+
+ LogModule::SetLogFile(prefValue.BeginReading());
+ } else if (prefName.EqualsLiteral(kLoggingPrefAddTimestamp)) {
+ bool addTimestamp = Preferences::GetBool(aName, false);
+ LogModule::SetAddTimestamp(addTimestamp);
+ } else if (prefName.EqualsLiteral(kLoggingPrefSync)) {
+ bool sync = Preferences::GetBool(aName, false);
+ LogModule::SetIsSync(sync);
+ }
+ return;
+ }
+
+ if (Preferences::GetInt(aName, &prefLevel) == NS_OK) {
+ logLevel = ToLogLevel(prefLevel);
+ } else if (Preferences::GetCString(aName, &prefValue) == NS_OK) {
+ if (prefValue.LowerCaseEqualsLiteral("error")) {
+ logLevel = LogLevel::Error;
+ } else if (prefValue.LowerCaseEqualsLiteral("warning")) {
+ logLevel = LogLevel::Warning;
+ } else if (prefValue.LowerCaseEqualsLiteral("info")) {
+ logLevel = LogLevel::Info;
+ } else if (prefValue.LowerCaseEqualsLiteral("debug")) {
+ logLevel = LogLevel::Debug;
+ } else if (prefValue.LowerCaseEqualsLiteral("verbose")) {
+ logLevel = LogLevel::Verbose;
+ }
+ }
+
+ const char* moduleName = aName + strlen(kLoggingPrefPrefix);
+ LogModule::Get(moduleName)->SetLevel(logLevel);
+}
+
+void
+LoadExistingPrefs()
+{
+ nsIPrefBranch* root = Preferences::GetRootBranch();
+ if (!root) {
+ return;
+ }
+
+ uint32_t count;
+ char** names;
+ nsresult rv = root->GetChildList(kLoggingPrefPrefix, &count, &names);
+ if (NS_SUCCEEDED(rv) && count) {
+ for (size_t i = 0; i < count; i++) {
+ LoadPrefValue(names[i]);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
+ }
+}
+
+LogModulePrefWatcher::LogModulePrefWatcher()
+{
+}
+
+void
+LogModulePrefWatcher::RegisterPrefWatcher()
+{
+ RefPtr<LogModulePrefWatcher> prefWatcher = new LogModulePrefWatcher();
+ Preferences::AddStrongObserver(prefWatcher, kLoggingPrefPrefix);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService && XRE_IsParentProcess()) {
+ observerService->AddObserver(prefWatcher, "browser-delayed-startup-finished", false);
+ }
+
+ LoadExistingPrefs();
+}
+
+NS_IMETHODIMP
+LogModulePrefWatcher::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic) == 0) {
+ NS_LossyConvertUTF16toASCII prefName(aData);
+ LoadPrefValue(prefName.get());
+ } else if (strcmp("browser-delayed-startup-finished", aTopic) == 0) {
+ bool clear = Preferences::GetBool(kLoggingPrefClearOnStartup, true);
+ if (clear) {
+ ResetExistingPrefs();
+ }
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(this, "browser-delayed-startup-finished");
+ }
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/LogModulePrefWatcher.h b/xpcom/base/LogModulePrefWatcher.h
new file mode 100644
index 000000000..657e54f01
--- /dev/null
+++ b/xpcom/base/LogModulePrefWatcher.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef LogModulePrefWatcher_h
+#define LogModulePrefWatcher_h
+
+#include "nsIObserver.h"
+
+namespace mozilla {
+
+/**
+ * Watches for changes to "logging.*" prefs and then updates the appropriate
+ * LogModule's log level. Both the integer and string versions of the LogLevel
+ * enum are supported.
+ *
+ * For example setting the pref "logging.Foo" to "Verbose" will set the
+ * LogModule for "Foo" to the LogLevel::Verbose level. Setting "logging.Bar" to
+ * 4 would set the LogModule for "Bar" to the LogLevel::Debug level.
+ */
+class LogModulePrefWatcher : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ /**
+ * Starts observing logging pref changes.
+ */
+ static void RegisterPrefWatcher();
+
+private:
+ LogModulePrefWatcher();
+ virtual ~LogModulePrefWatcher()
+ {
+ }
+};
+} // namespace mozilla
+
+#endif // LogModulePrefWatcher_h
diff --git a/xpcom/base/Logging.cpp b/xpcom/base/Logging.cpp
new file mode 100644
index 000000000..e87df91e4
--- /dev/null
+++ b/xpcom/base/Logging.cpp
@@ -0,0 +1,568 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+
+#include <algorithm>
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsClassHashtable.h"
+#include "nsDebug.h"
+#include "NSPRLogModulesParser.h"
+
+#include "prenv.h"
+#include "prprf.h"
+#ifdef XP_WIN
+#include <process.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+// NB: Initial amount determined by auditing the codebase for the total amount
+// of unique module names and padding up to the next power of 2.
+const uint32_t kInitialModuleCount = 256;
+// When rotate option is added to the modules list, this is the hardcoded
+// number of files we create and rotate. When there is rotate:40,
+// we will keep four files per process, each limited to 10MB. Sum is 40MB,
+// the given limit.
+const uint32_t kRotateFilesNumber = 4;
+
+namespace mozilla {
+
+namespace detail {
+
+void log_print(const PRLogModuleInfo* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...)
+{
+ va_list ap;
+ va_start(ap, aFmt);
+ char* buff = PR_vsmprintf(aFmt, ap);
+ PR_LogPrint("%s", buff);
+ PR_smprintf_free(buff);
+ va_end(ap);
+}
+
+void log_print(const LogModule* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...)
+{
+ va_list ap;
+ va_start(ap, aFmt);
+ aModule->Printv(aLevel, aFmt, ap);
+ va_end(ap);
+}
+
+} // detail
+
+LogLevel
+ToLogLevel(int32_t aLevel)
+{
+ aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
+ aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
+ return static_cast<LogLevel>(aLevel);
+}
+
+const char*
+ToLogStr(LogLevel aLevel) {
+ switch (aLevel) {
+ case LogLevel::Error:
+ return "E";
+ case LogLevel::Warning:
+ return "W";
+ case LogLevel::Info:
+ return "I";
+ case LogLevel::Debug:
+ return "D";
+ case LogLevel::Verbose:
+ return "V";
+ case LogLevel::Disabled:
+ default:
+ MOZ_CRASH("Invalid log level.");
+ return "";
+ }
+}
+
+namespace detail {
+
+/**
+ * A helper class providing reference counting for FILE*.
+ * It encapsulates the following:
+ * - the FILE handle
+ * - the order number it was created for when rotating (actual path)
+ * - number of active references
+ */
+class LogFile
+{
+ FILE* mFile;
+ uint32_t mFileNum;
+
+public:
+ LogFile(FILE* aFile, uint32_t aFileNum)
+ : mFile(aFile)
+ , mFileNum(aFileNum)
+ , mNextToRelease(nullptr)
+ {
+ }
+
+ ~LogFile()
+ {
+ fclose(mFile);
+ delete mNextToRelease;
+ }
+
+ FILE* File() const { return mFile; }
+ uint32_t Num() const { return mFileNum; }
+
+ LogFile* mNextToRelease;
+};
+
+const char*
+ExpandPIDMarker(const char* aFilename, char (&buffer)[2048])
+{
+ MOZ_ASSERT(aFilename);
+ static const char kPIDToken[] = "%PID";
+ const char* pidTokenPtr = strstr(aFilename, kPIDToken);
+ if (pidTokenPtr &&
+ SprintfLiteral(buffer, "%.*s%s%d%s",
+ static_cast<int>(pidTokenPtr - aFilename), aFilename,
+ XRE_IsParentProcess() ? "-main." : "-child.",
+ base::GetCurrentProcId(),
+ pidTokenPtr + strlen(kPIDToken)) > 0)
+ {
+ return buffer;
+ }
+
+ return aFilename;
+}
+
+} // detail
+
+namespace {
+ // Helper method that initializes an empty va_list to be empty.
+ void empty_va(va_list *va, ...)
+ {
+ va_start(*va, va);
+ va_end(*va);
+ }
+}
+
+class LogModuleManager
+{
+public:
+ LogModuleManager()
+ : mModulesLock("logmodules")
+ , mModules(kInitialModuleCount)
+ , mPrintEntryCount(0)
+ , mOutFile(nullptr)
+ , mToReleaseFile(nullptr)
+ , mOutFileNum(0)
+ , mOutFilePath(strdup(""))
+ , mMainThread(PR_GetCurrentThread())
+ , mSetFromEnv(false)
+ , mAddTimestamp(false)
+ , mIsSync(false)
+ , mRotate(0)
+ {
+ }
+
+ ~LogModuleManager()
+ {
+ detail::LogFile* logFile = mOutFile.exchange(nullptr);
+ delete logFile;
+ }
+
+ /**
+ * Loads config from env vars if present.
+ */
+ void Init()
+ {
+ bool shouldAppend = false;
+ bool addTimestamp = false;
+ bool isSync = false;
+ int32_t rotate = 0;
+ const char* modules = PR_GetEnv("MOZ_LOG");
+ if (!modules || !modules[0]) {
+ modules = PR_GetEnv("MOZ_LOG_MODULES");
+ if (modules) {
+ NS_WARNING("MOZ_LOG_MODULES is deprecated."
+ "\nPlease use MOZ_LOG instead.");
+ }
+ }
+ if (!modules || !modules[0]) {
+ modules = PR_GetEnv("NSPR_LOG_MODULES");
+ if (modules) {
+ NS_WARNING("NSPR_LOG_MODULES is deprecated."
+ "\nPlease use MOZ_LOG instead.");
+ }
+ }
+
+ NSPRLogModulesParser(modules,
+ [&shouldAppend, &addTimestamp, &isSync, &rotate]
+ (const char* aName, LogLevel aLevel, int32_t aValue) mutable {
+ if (strcmp(aName, "append") == 0) {
+ shouldAppend = true;
+ } else if (strcmp(aName, "timestamp") == 0) {
+ addTimestamp = true;
+ } else if (strcmp(aName, "sync") == 0) {
+ isSync = true;
+ } else if (strcmp(aName, "rotate") == 0) {
+ rotate = (aValue << 20) / kRotateFilesNumber;
+ } else {
+ LogModule::Get(aName)->SetLevel(aLevel);
+ }
+ });
+
+ // Rotate implies timestamp to make the files readable
+ mAddTimestamp = addTimestamp || rotate > 0;
+ mIsSync = isSync;
+ mRotate = rotate;
+
+ if (rotate > 0 && shouldAppend) {
+ NS_WARNING("MOZ_LOG: when you rotate the log, you cannot use append!");
+ }
+
+ const char* logFile = PR_GetEnv("MOZ_LOG_FILE");
+ if (!logFile || !logFile[0]) {
+ logFile = PR_GetEnv("NSPR_LOG_FILE");
+ }
+
+ if (logFile && logFile[0]) {
+ char buf[2048];
+ logFile = detail::ExpandPIDMarker(logFile, buf);
+ mOutFilePath.reset(strdup(logFile));
+
+ if (mRotate > 0) {
+ // Delete all the previously captured files, including non-rotated
+ // log files, so that users don't complain our logs eat space even
+ // after the rotate option has been added and don't happen to send
+ // us old large logs along with the rotated files.
+ remove(mOutFilePath.get());
+ for (uint32_t i = 0; i < kRotateFilesNumber; ++i) {
+ RemoveFile(i);
+ }
+ }
+
+ mOutFile = OpenFile(shouldAppend, mOutFileNum);
+ mSetFromEnv = true;
+ }
+ }
+
+ void SetLogFile(const char* aFilename)
+ {
+ // For now we don't allow you to change the file at runtime.
+ if (mSetFromEnv) {
+ NS_WARNING("LogModuleManager::SetLogFile - Log file was set from the "
+ "MOZ_LOG_FILE environment variable.");
+ return;
+ }
+
+ const char * filename = aFilename ? aFilename : "";
+ char buf[2048];
+ filename = detail::ExpandPIDMarker(filename, buf);
+
+ // Can't use rotate at runtime yet.
+ MOZ_ASSERT(mRotate == 0, "We don't allow rotate for runtime logfile changes");
+ mOutFilePath.reset(strdup(filename));
+
+ // Exchange mOutFile and set it to be released once all the writes are done.
+ detail::LogFile* newFile = OpenFile(false, 0);
+ detail::LogFile* oldFile = mOutFile.exchange(newFile);
+
+ // Since we don't allow changing the logfile if MOZ_LOG_FILE is already set,
+ // and we don't allow log rotation when setting it at runtime, mToReleaseFile
+ // will be null, so we're not leaking.
+ DebugOnly<detail::LogFile*> prevFile = mToReleaseFile.exchange(oldFile);
+ MOZ_ASSERT(!prevFile, "Should be null because rotation is not allowed");
+
+ // If we just need to release a file, we must force print, in order to
+ // trigger the closing and release of mToReleaseFile.
+ if (oldFile) {
+ va_list va;
+ empty_va(&va);
+ Print("Logger", LogLevel::Info, "Flushing old log files\n", va);
+ }
+ }
+
+ uint32_t GetLogFile(char *aBuffer, size_t aLength)
+ {
+ uint32_t len = strlen(mOutFilePath.get());
+ if (len + 1 > aLength) {
+ return 0;
+ }
+ snprintf(aBuffer, aLength, "%s", mOutFilePath.get());
+ return len;
+ }
+
+ void SetIsSync(bool aIsSync)
+ {
+ mIsSync = aIsSync;
+ }
+
+ void SetAddTimestamp(bool aAddTimestamp)
+ {
+ mAddTimestamp = aAddTimestamp;
+ }
+
+ detail::LogFile* OpenFile(bool aShouldAppend, uint32_t aFileNum)
+ {
+ FILE* file;
+
+ if (mRotate > 0) {
+ char buf[2048];
+ SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
+
+ // rotate doesn't support append.
+ file = fopen(buf, "w");
+ } else {
+ file = fopen(mOutFilePath.get(), aShouldAppend ? "a" : "w");
+ }
+
+ if (!file) {
+ return nullptr;
+ }
+
+ return new detail::LogFile(file, aFileNum);
+ }
+
+ void RemoveFile(uint32_t aFileNum)
+ {
+ char buf[2048];
+ SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
+ remove(buf);
+ }
+
+ LogModule* CreateOrGetModule(const char* aName)
+ {
+ OffTheBooksMutexAutoLock guard(mModulesLock);
+ LogModule* module = nullptr;
+ if (!mModules.Get(aName, &module)) {
+ module = new LogModule(aName, LogLevel::Disabled);
+ mModules.Put(aName, module);
+ }
+
+ return module;
+ }
+
+ void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs)
+ {
+ const size_t kBuffSize = 1024;
+ char buff[kBuffSize];
+
+ char* buffToWrite = buff;
+
+ // For backwards compat we need to use the NSPR format string versions
+ // of sprintf and friends and then hand off to printf.
+ va_list argsCopy;
+ va_copy(argsCopy, aArgs);
+ size_t charsWritten = PR_vsnprintf(buff, kBuffSize, aFmt, argsCopy);
+ va_end(argsCopy);
+
+ if (charsWritten == kBuffSize - 1) {
+ // We may have maxed out, allocate a buffer instead.
+ buffToWrite = PR_vsmprintf(aFmt, aArgs);
+ charsWritten = strlen(buffToWrite);
+ }
+
+ // Determine if a newline needs to be appended to the message.
+ const char* newline = "";
+ if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') {
+ newline = "\n";
+ }
+
+ FILE* out = stderr;
+
+ // In case we use rotate, this ensures the FILE is kept alive during
+ // its use. Increased before we load mOutFile.
+ ++mPrintEntryCount;
+
+ detail::LogFile* outFile = mOutFile;
+ if (outFile) {
+ out = outFile->File();
+ }
+
+ // This differs from the NSPR format in that we do not output the
+ // opaque system specific thread pointer (ie pthread_t) cast
+ // to a long. The address of the current PR_Thread continues to be
+ // prefixed.
+ //
+ // Additionally we prefix the output with the abbreviated log level
+ // and the module name.
+ PRThread *currentThread = PR_GetCurrentThread();
+ const char *currentThreadName = (mMainThread == currentThread)
+ ? "Main Thread"
+ : PR_GetThreadName(currentThread);
+
+ char noNameThread[40];
+ if (!currentThreadName) {
+ SprintfLiteral(noNameThread, "Unnamed thread %p", currentThread);
+ currentThreadName = noNameThread;
+ }
+
+ if (!mAddTimestamp) {
+ fprintf_stderr(out,
+ "[%s]: %s/%s %s%s",
+ currentThreadName, ToLogStr(aLevel),
+ aName, buffToWrite, newline);
+ } else {
+ PRExplodedTime now;
+ PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
+ fprintf_stderr(
+ out,
+ "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%s]: %s/%s %s%s",
+ now.tm_year, now.tm_month + 1, now.tm_mday,
+ now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec,
+ currentThreadName, ToLogStr(aLevel),
+ aName, buffToWrite, newline);
+ }
+
+ if (mIsSync) {
+ fflush(out);
+ }
+
+ if (buffToWrite != buff) {
+ PR_smprintf_free(buffToWrite);
+ }
+
+ if (mRotate > 0 && outFile) {
+ int32_t fileSize = ftell(out);
+ if (fileSize > mRotate) {
+ uint32_t fileNum = outFile->Num();
+
+ uint32_t nextFileNum = fileNum + 1;
+ if (nextFileNum >= kRotateFilesNumber) {
+ nextFileNum = 0;
+ }
+
+ // And here is the trick. The current out-file remembers its order
+ // number. When no other thread shifted the global file number yet,
+ // we are the thread to open the next file.
+ if (mOutFileNum.compareExchange(fileNum, nextFileNum)) {
+ // We can work with mToReleaseFile because we are sure the
+ // mPrintEntryCount can't drop to zero now - the condition
+ // to actually delete what's stored in that member.
+ // And also, no other thread can enter this piece of code
+ // because mOutFile is still holding the current file with
+ // the non-shifted number. The compareExchange() above is
+ // a no-op for other threads.
+ outFile->mNextToRelease = mToReleaseFile;
+ mToReleaseFile = outFile;
+
+ mOutFile = OpenFile(false, nextFileNum);
+ }
+ }
+ }
+
+ if (--mPrintEntryCount == 0 && mToReleaseFile) {
+ // We were the last Print() entered, if there is a file to release
+ // do it now. exchange() is atomic and makes sure we release the file
+ // only once on one thread.
+ detail::LogFile* release = mToReleaseFile.exchange(nullptr);
+ delete release;
+ }
+ }
+
+private:
+ OffTheBooksMutex mModulesLock;
+ nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
+
+ // Print() entry counter, actually reflects concurrent use of the current
+ // output file. ReleaseAcquire ensures that manipulation with mOutFile
+ // and mToReleaseFile is synchronized by manipulation with this value.
+ Atomic<uint32_t, ReleaseAcquire> mPrintEntryCount;
+ // File to write to. ReleaseAcquire because we need to sync mToReleaseFile
+ // with this.
+ Atomic<detail::LogFile*, ReleaseAcquire> mOutFile;
+ // File to be released when reference counter drops to zero. This member
+ // is assigned mOutFile when the current file has reached the limit.
+ // It can be Relaxed, since it's synchronized with mPrintEntryCount
+ // manipulation and we do atomic exchange() on it.
+ Atomic<detail::LogFile*, Relaxed> mToReleaseFile;
+ // The next file number. This is mostly only for synchronization sake.
+ // Can have relaxed ordering, since we only do compareExchange on it which
+ // is atomic regardless ordering.
+ Atomic<uint32_t, Relaxed> mOutFileNum;
+ // Just keeps the actual file path for further use.
+ UniqueFreePtr<char[]> mOutFilePath;
+
+ PRThread *mMainThread;
+ bool mSetFromEnv;
+ Atomic<bool, Relaxed> mAddTimestamp;
+ Atomic<bool, Relaxed> mIsSync;
+ int32_t mRotate;
+};
+
+StaticAutoPtr<LogModuleManager> sLogModuleManager;
+
+LogModule*
+LogModule::Get(const char* aName)
+{
+ // This is just a pass through to the LogModuleManager so
+ // that the LogModuleManager implementation can be kept internal.
+ MOZ_ASSERT(sLogModuleManager != nullptr);
+ return sLogModuleManager->CreateOrGetModule(aName);
+}
+
+void
+LogModule::SetLogFile(const char* aFilename)
+{
+ MOZ_ASSERT(sLogModuleManager);
+ sLogModuleManager->SetLogFile(aFilename);
+}
+
+uint32_t
+LogModule::GetLogFile(char *aBuffer, size_t aLength)
+{
+ MOZ_ASSERT(sLogModuleManager);
+ return sLogModuleManager->GetLogFile(aBuffer, aLength);
+}
+
+void
+LogModule::SetAddTimestamp(bool aAddTimestamp)
+{
+ sLogModuleManager->SetAddTimestamp(aAddTimestamp);
+}
+
+void
+LogModule::SetIsSync(bool aIsSync)
+{
+ sLogModuleManager->SetIsSync(aIsSync);
+}
+
+void
+LogModule::Init()
+{
+ // NB: This method is not threadsafe; it is expected to be called very early
+ // in startup prior to any other threads being run.
+ if (sLogModuleManager) {
+ // Already initialized.
+ return;
+ }
+
+ // NB: We intentionally do not register for ClearOnShutdown as that happens
+ // before all logging is complete. And, yes, that means we leak, but
+ // we're doing that intentionally.
+ sLogModuleManager = new LogModuleManager();
+ sLogModuleManager->Init();
+}
+
+void
+LogModule::Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const
+{
+ MOZ_ASSERT(sLogModuleManager != nullptr);
+
+ // Forward to LogModule manager w/ level and name
+ sLogModuleManager->Print(Name(), aLevel, aFmt, aArgs);
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/Logging.h b/xpcom/base/Logging.h
new file mode 100644
index 000000000..040fb9c49
--- /dev/null
+++ b/xpcom/base/Logging.h
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_logging_h
+#define mozilla_logging_h
+
+#include <string.h>
+#include <stdarg.h>
+
+#include "prlog.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MacroForEach.h"
+
+// This file is a placeholder for a replacement to the NSPR logging framework
+// that is defined in prlog.h. Currently it is just a pass through, but as
+// work progresses more functionality will be swapped out in favor of
+// mozilla logging implementations.
+
+// We normally have logging enabled everywhere, but measurements showed that
+// having logging enabled on Android is quite expensive (hundreds of kilobytes
+// for both the format strings for logging and the code to perform all the
+// logging calls). Because retrieving logs from a mobile device is
+// comparatively more difficult for Android than it is for desktop and because
+// desktop machines tend to be less space/bandwidth-constrained than Android
+// devices, we've chosen to leave logging enabled on desktop, but disabled on
+// Android. Given that logging can still be useful for development purposes,
+// however, we leave logging enabled on Android developer builds.
+#if !defined(ANDROID) || !defined(RELEASE_OR_BETA)
+#define MOZ_LOGGING_ENABLED 1
+#else
+#define MOZ_LOGGING_ENABLED 0
+#endif
+
+namespace mozilla {
+
+// While not a 100% mapping to PR_LOG's numeric values, mozilla::LogLevel does
+// maintain a direct mapping for the Disabled, Debug and Verbose levels.
+//
+// Mappings of LogLevel to PR_LOG's numeric values:
+//
+// +---------+------------------+-----------------+
+// | Numeric | NSPR Logging | Mozilla Logging |
+// +---------+------------------+-----------------+
+// | 0 | PR_LOG_NONE | Disabled |
+// | 1 | PR_LOG_ALWAYS | Error |
+// | 2 | PR_LOG_ERROR | Warning |
+// | 3 | PR_LOG_WARNING | Info |
+// | 4 | PR_LOG_DEBUG | Debug |
+// | 5 | PR_LOG_DEBUG + 1 | Verbose |
+// +---------+------------------+-----------------+
+//
+enum class LogLevel {
+ Disabled = 0,
+ Error,
+ Warning,
+ Info,
+ Debug,
+ Verbose,
+};
+
+/**
+ * Safely converts an integer into a valid LogLevel.
+ */
+LogLevel ToLogLevel(int32_t aLevel);
+
+class LogModule
+{
+public:
+ ~LogModule() { ::free(mName); }
+
+ /**
+ * Retrieves the module with the given name. If it does not already exist
+ * it will be created.
+ *
+ * @param aName The name of the module.
+ * @return A log module for the given name. This may be shared.
+ */
+ static LogModule* Get(const char* aName);
+
+ static void Init();
+
+ /**
+ * Sets the log file to the given filename.
+ */
+ static void SetLogFile(const char* aFilename);
+
+ /**
+ * @param aBuffer - pointer to a buffer
+ * @param aLength - the length of the buffer
+ *
+ * @return the actual length of the filepath.
+ */
+ static uint32_t GetLogFile(char *aBuffer, size_t aLength);
+
+ /**
+ * @param aAddTimestamp If we should log a time stamp with every message.
+ */
+ static void SetAddTimestamp(bool aAddTimestamp);
+
+ /**
+ * @param aIsSync If we should flush the file after every logged message.
+ */
+ static void SetIsSync(bool aIsSync);
+
+ /**
+ * Indicates whether or not the given log level is enabled.
+ */
+ bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
+
+ /**
+ * Retrieves the log module's current level.
+ */
+ LogLevel Level() const { return mLevel; }
+
+ /**
+ * Sets the log module's level.
+ */
+ void SetLevel(LogLevel level) { mLevel = level; }
+
+ /**
+ * Print a log message for this module.
+ */
+ void Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const;
+
+ /**
+ * Retrieves the module name.
+ */
+ const char* Name() const { return mName; }
+
+private:
+ friend class LogModuleManager;
+
+ explicit LogModule(const char* aName, LogLevel aLevel)
+ : mName(strdup(aName)), mLevel(aLevel)
+ {
+ }
+
+ LogModule(LogModule&) = delete;
+ LogModule& operator=(const LogModule&) = delete;
+
+ char* mName;
+ Atomic<LogLevel, Relaxed> mLevel;
+};
+
+/**
+ * Helper class that lazy loads the given log module. This is safe to use for
+ * declaring static references to log modules and can be used as a replacement
+ * for accessing a LogModule directly.
+ *
+ * Example usage:
+ * static LazyLogModule sLayoutLog("layout");
+ *
+ * void Foo() {
+ * MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
+ * }
+ */
+class LazyLogModule final
+{
+public:
+ explicit constexpr LazyLogModule(const char* aLogName)
+ : mLogName(aLogName)
+ , mLog(nullptr)
+ {
+ }
+
+ operator LogModule*()
+ {
+ // NB: The use of an atomic makes the reading and assignment of mLog
+ // thread-safe. There is a small chance that mLog will be set more
+ // than once, but that's okay as it will be set to the same LogModule
+ // instance each time. Also note LogModule::Get is thread-safe.
+ LogModule* tmp = mLog;
+ if (MOZ_UNLIKELY(!tmp)) {
+ tmp = LogModule::Get(mLogName);
+ mLog = tmp;
+ }
+
+ return tmp;
+ }
+
+private:
+ const char* const mLogName;
+ Atomic<LogModule*, ReleaseAcquire> mLog;
+};
+
+namespace detail {
+
+inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
+ MOZ_ASSERT(level != LogLevel::Disabled);
+ return module && module->level >= static_cast<int>(level);
+}
+
+/**
+ * A rather inefficient wrapper for PR_LogPrint that always allocates.
+ * PR_LogModuleInfo is deprecated so it's not worth the effort to do
+ * any better.
+ */
+void log_print(const PRLogModuleInfo* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...);
+
+inline bool log_test(const LogModule* module, LogLevel level) {
+ MOZ_ASSERT(level != LogLevel::Disabled);
+ return module && module->ShouldLog(level);
+}
+
+void log_print(const LogModule* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...);
+} // namespace detail
+
+} // namespace mozilla
+
+
+// Helper macro used convert MOZ_LOG's third parameter, |_args|, from a
+// parenthesized form to a varargs form. For example:
+// ("%s", "a message") => "%s", "a message"
+#define MOZ_LOG_EXPAND_ARGS(...) __VA_ARGS__
+
+#if MOZ_LOGGING_ENABLED
+#define MOZ_LOG_TEST(_module,_level) mozilla::detail::log_test(_module, _level)
+#else
+// Define away MOZ_LOG_TEST here so the compiler will fold away entire
+// logging blocks via dead code elimination, e.g.:
+//
+// if (MOZ_LOG_TEST(...)) {
+// ...compute things to log and log them...
+// }
+//
+// This also has the nice property that no special definition of MOZ_LOG is
+// required when logging is disabled.
+#define MOZ_LOG_TEST(_module,_level) false
+#endif
+
+#define MOZ_LOG(_module,_level,_args) \
+ PR_BEGIN_MACRO \
+ if (MOZ_LOG_TEST(_module,_level)) { \
+ mozilla::detail::log_print(_module, _level, MOZ_LOG_EXPAND_ARGS _args); \
+ } \
+ PR_END_MACRO
+
+#undef PR_LOG
+#undef PR_LOG_TEST
+
+// This #define is a Logging.h-only knob! Don't encourage people to get fancy
+// with their log definitions by exporting it outside of Logging.h.
+#undef MOZ_LOGGING_ENABLED
+
+#endif // mozilla_logging_h
diff --git a/xpcom/base/MacHelpers.h b/xpcom/base/MacHelpers.h
new file mode 100644
index 000000000..9716ae3f2
--- /dev/null
+++ b/xpcom/base/MacHelpers.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_MacHelpers_h
+#define mozilla_MacHelpers_h
+
+#include "nsString.h"
+
+namespace mozilla {
+
+nsresult GetSelectedCityInfo(nsAString& aCountryCode);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/MacHelpers.mm b/xpcom/base/MacHelpers.mm
new file mode 100644
index 000000000..19d0d8900
--- /dev/null
+++ b/xpcom/base/MacHelpers.mm
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+#include "MacHelpers.h"
+#include "nsObjCExceptions.h"
+
+#import <Foundation/Foundation.h>
+
+namespace mozilla {
+
+nsresult
+GetSelectedCityInfo(nsAString& aCountryCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Can be replaced with [[NSLocale currentLocale] countryCode] once we build
+ // with the 10.12 SDK.
+ id countryCode = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
+
+ if (![countryCode isKindOfClass:[NSString class]]) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char* countryCodeUTF8 = [(NSString*)countryCode UTF8String];
+
+ if (!countryCodeUTF8) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AppendUTF8toUTF16(countryCodeUTF8, aCountryCode);
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+} // namespace Mozilla
+
diff --git a/xpcom/base/NSPRLogModulesParser.cpp b/xpcom/base/NSPRLogModulesParser.cpp
new file mode 100644
index 000000000..21090925c
--- /dev/null
+++ b/xpcom/base/NSPRLogModulesParser.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NSPRLogModulesParser.h"
+
+#include "mozilla/Tokenizer.h"
+
+const char kDelimiters[] = ", ";
+const char kAdditionalWordChars[] = "_-";
+
+namespace mozilla {
+
+void
+NSPRLogModulesParser(const char* aLogModules,
+ function<void(const char*, LogLevel, int32_t)> aCallback)
+{
+ if (!aLogModules) {
+ return;
+ }
+
+ Tokenizer parser(aLogModules, kDelimiters, kAdditionalWordChars);
+ nsAutoCString moduleName;
+
+ // Format: LOG_MODULES="Foo:2,Bar, Baz:5"
+ while (parser.ReadWord(moduleName)) {
+ // Next should be :<level>, default to Error if not provided.
+ LogLevel logLevel = LogLevel::Error;
+ int32_t levelValue = 0;
+ if (parser.CheckChar(':')) {
+ // Check if a negative value is provided.
+ int32_t multiplier = 1;
+ if (parser.CheckChar([](const char aChar) { return aChar == '-'; })) {
+ multiplier = -1;
+ }
+
+ // NB: If a level isn't provided after the ':' we assume the default
+ // Error level is desired. This differs from NSPR which will stop
+ // processing the log module string in this case.
+ if (parser.ReadInteger(&levelValue)) {
+ logLevel = ToLogLevel(levelValue * multiplier);
+ }
+ }
+
+ aCallback(moduleName.get(), logLevel, levelValue);
+
+ // Skip ahead to the next token.
+ parser.SkipWhites();
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/NSPRLogModulesParser.h b/xpcom/base/NSPRLogModulesParser.h
new file mode 100644
index 000000000..38aab14a3
--- /dev/null
+++ b/xpcom/base/NSPRLogModulesParser.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+#include "mozilla/Function.h"
+
+namespace mozilla {
+
+/**
+ * Helper function that parses the legacy NSPR_LOG_MODULES env var format
+ * for specifying log levels and logging options.
+ *
+ * @param aLogModules The log modules configuration string.
+ * @param aCallback The callback to invoke for each log module config entry.
+ */
+void NSPRLogModulesParser(const char* aLogModules,
+ function<void(const char*, LogLevel, int32_t)> aCallback);
+
+} // namespace mozilla
diff --git a/xpcom/base/OwningNonNull.h b/xpcom/base/OwningNonNull.h
new file mode 100644
index 000000000..b72a250c4
--- /dev/null
+++ b/xpcom/base/OwningNonNull.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A class for non-null strong pointers to reference-counted objects. */
+
+#ifndef mozilla_OwningNonNull_h
+#define mozilla_OwningNonNull_h
+
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionNoteChild.h"
+
+namespace mozilla {
+
+template<class T>
+class OwningNonNull
+{
+public:
+ OwningNonNull() {}
+
+ MOZ_IMPLICIT OwningNonNull(T& aValue)
+ {
+ init(&aValue);
+ }
+
+ template<class U>
+ MOZ_IMPLICIT OwningNonNull(already_AddRefed<U>&& aValue)
+ {
+ init(aValue);
+ }
+
+ template<class U>
+ MOZ_IMPLICIT OwningNonNull(const OwningNonNull<U>& aValue)
+ {
+ init(aValue);
+ }
+
+ // This is no worse than get() in terms of const handling.
+ operator T&() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
+ return *mPtr;
+ }
+
+ operator T*() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
+ return mPtr;
+ }
+
+ // Conversion to bool is always true, so delete to catch errors
+ explicit operator bool() const = delete;
+
+ T*
+ operator->() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
+ return mPtr;
+ }
+
+ OwningNonNull<T>&
+ operator=(T* aValue)
+ {
+ init(aValue);
+ return *this;
+ }
+
+ OwningNonNull<T>&
+ operator=(T& aValue)
+ {
+ init(&aValue);
+ return *this;
+ }
+
+ template<class U>
+ OwningNonNull<T>&
+ operator=(already_AddRefed<U>&& aValue)
+ {
+ init(aValue);
+ return *this;
+ }
+
+ template<class U>
+ OwningNonNull<T>&
+ operator=(const OwningNonNull<U>& aValue)
+ {
+ init(aValue);
+ return *this;
+ }
+
+ // Don't allow assigning nullptr, it makes no sense
+ void operator=(decltype(nullptr)) = delete;
+
+ already_AddRefed<T> forget()
+ {
+#ifdef DEBUG
+ mInited = false;
+#endif
+ return mPtr.forget();
+ }
+
+ template<class U>
+ void
+ forget(U** aOther)
+ {
+#ifdef DEBUG
+ mInited = false;
+#endif
+ mPtr.forget(aOther);
+ }
+
+ // Make us work with smart pointer helpers that expect a get().
+ T* get() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr);
+ return mPtr;
+ }
+
+ template<typename U>
+ void swap(U& aOther)
+ {
+ mPtr.swap(aOther);
+#ifdef DEBUG
+ mInited = mPtr;
+#endif
+ }
+
+ // We have some consumers who want to check whether we're inited in non-debug
+ // builds as well. Luckily, we have the invariant that we're inited precisely
+ // when mPtr is non-null.
+ bool isInitialized() const
+ {
+ MOZ_ASSERT(!!mPtr == mInited, "mInited out of sync with mPtr?");
+ return mPtr;
+ }
+
+protected:
+ template<typename U>
+ void init(U&& aValue)
+ {
+ mPtr = aValue;
+ MOZ_ASSERT(mPtr);
+#ifdef DEBUG
+ mInited = true;
+#endif
+ }
+
+ RefPtr<T> mPtr;
+#ifdef DEBUG
+ bool mInited = false;
+#endif
+};
+
+template <typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ OwningNonNull<T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
+}
+
+} // namespace mozilla
+
+// Declared in nsCOMPtr.h
+template<class T> template<class U>
+nsCOMPtr<T>::nsCOMPtr(const mozilla::OwningNonNull<U>& aOther)
+ : nsCOMPtr(aOther.get())
+{}
+
+template<class T> template<class U>
+nsCOMPtr<T>&
+nsCOMPtr<T>::operator=(const mozilla::OwningNonNull<U>& aOther)
+{
+ return operator=(aOther.get());
+}
+
+// Declared in mozilla/RefPtr.h
+template<class T> template<class U>
+RefPtr<T>::RefPtr(const mozilla::OwningNonNull<U>& aOther)
+ : RefPtr(aOther.get())
+{}
+
+template<class T> template<class U>
+RefPtr<T>&
+RefPtr<T>::operator=(const mozilla::OwningNonNull<U>& aOther)
+{
+ return operator=(aOther.get());
+}
+
+#endif // mozilla_OwningNonNull_h
diff --git a/xpcom/base/StaticMutex.h b/xpcom/base/StaticMutex.h
new file mode 100644
index 000000000..731e69405
--- /dev/null
+++ b/xpcom/base/StaticMutex.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_StaticMutex_h
+#define mozilla_StaticMutex_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+
+/**
+ * StaticMutex is a Mutex that can (and in fact, must) be used as a
+ * global/static variable.
+ *
+ * The main reason to use StaticMutex as opposed to
+ * StaticAutoPtr<OffTheBooksMutex> is that we instantiate the StaticMutex in a
+ * thread-safe manner the first time it's used.
+ *
+ * The same caveats that apply to StaticAutoPtr apply to StaticMutex. In
+ * particular, do not use StaticMutex as a stack variable or a class instance
+ * variable, because this class relies on the fact that global variablies are
+ * initialized to 0 in order to initialize mMutex. It is only safe to use
+ * StaticMutex as a global or static variable.
+ */
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex
+{
+public:
+ // In debug builds, check that mMutex is initialized for us as we expect by
+ // the compiler. In non-debug builds, don't declare a constructor so that
+ // the compiler can see that the constructor is trivial.
+#ifdef DEBUG
+ StaticMutex()
+ {
+ MOZ_ASSERT(!mMutex);
+ }
+#endif
+
+ void Lock()
+ {
+ Mutex()->Lock();
+ }
+
+ void Unlock()
+ {
+ Mutex()->Unlock();
+ }
+
+ void AssertCurrentThreadOwns()
+ {
+#ifdef DEBUG
+ Mutex()->AssertCurrentThreadOwns();
+#endif
+ }
+
+private:
+ OffTheBooksMutex* Mutex()
+ {
+ if (mMutex) {
+ return mMutex;
+ }
+
+ OffTheBooksMutex* mutex = new OffTheBooksMutex("StaticMutex");
+ if (!mMutex.compareExchange(nullptr, mutex)) {
+ delete mutex;
+ }
+
+ return mMutex;
+ }
+
+ Atomic<OffTheBooksMutex*> mMutex;
+
+
+ // Disallow copy constructor, but only in debug mode. We only define
+ // a default constructor in debug mode (see above); if we declared
+ // this constructor always, the compiler wouldn't generate a trivial
+ // default constructor for us in non-debug mode.
+#ifdef DEBUG
+ StaticMutex(StaticMutex& aOther);
+#endif
+
+ // Disallow these operators.
+ StaticMutex& operator=(StaticMutex* aRhs);
+ static void* operator new(size_t) CPP_THROW_NEW;
+ static void operator delete(void*);
+};
+
+typedef BaseAutoLock<StaticMutex> StaticMutexAutoLock;
+typedef BaseAutoUnlock<StaticMutex> StaticMutexAutoUnlock;
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/StaticPtr.h b/xpcom/base/StaticPtr.h
new file mode 100644
index 000000000..f2c820a93
--- /dev/null
+++ b/xpcom/base/StaticPtr.h
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_StaticPtr_h
+#define mozilla_StaticPtr_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+/**
+ * StaticAutoPtr and StaticRefPtr are like nsAutoPtr and nsRefPtr, except they
+ * are suitable for use as global variables.
+ *
+ * In particular, a global instance of Static{Auto,Ref}Ptr doesn't cause the
+ * compiler to emit a static initializer (in release builds, anyway).
+ *
+ * In order to accomplish this, Static{Auto,Ref}Ptr must have a trivial
+ * constructor and destructor. As a consequence, it cannot initialize its raw
+ * pointer to 0 on construction, and it cannot delete/release its raw pointer
+ * upon destruction.
+ *
+ * Since the compiler guarantees that all global variables are initialized to
+ * 0, these trivial constructors are safe. Since we rely on this, the clang
+ * plugin, run as part of our "static analysis" builds, makes it a compile-time
+ * error to use Static{Auto,Ref}Ptr as anything except a global variable.
+ *
+ * Static{Auto,Ref}Ptr have a limited interface as compared to ns{Auto,Ref}Ptr;
+ * this is intentional, since their range of acceptable uses is smaller.
+ */
+
+template<class T>
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticAutoPtr
+{
+public:
+ // In debug builds, check that mRawPtr is initialized for us as we expect
+ // by the compiler. In non-debug builds, don't declare a constructor
+ // so that the compiler can see that the constructor is trivial.
+#ifdef DEBUG
+ StaticAutoPtr()
+ {
+ MOZ_ASSERT(!mRawPtr);
+ }
+#endif
+
+ StaticAutoPtr<T>& operator=(T* aRhs)
+ {
+ Assign(aRhs);
+ return *this;
+ }
+
+ T* get() const { return mRawPtr; }
+
+ operator T*() const { return get(); }
+
+ T* operator->() const
+ {
+ MOZ_ASSERT(mRawPtr);
+ return get();
+ }
+
+ T& operator*() const { return *get(); }
+
+private:
+ // Disallow copy constructor, but only in debug mode. We only define
+ // a default constructor in debug mode (see above); if we declared
+ // this constructor always, the compiler wouldn't generate a trivial
+ // default constructor for us in non-debug mode.
+#ifdef DEBUG
+ StaticAutoPtr(StaticAutoPtr<T>& aOther);
+#endif
+
+ void Assign(T* aNewPtr)
+ {
+ MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr);
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ delete oldPtr;
+ }
+
+ T* mRawPtr;
+};
+
+template<class T>
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticRefPtr
+{
+public:
+ // In debug builds, check that mRawPtr is initialized for us as we expect
+ // by the compiler. In non-debug builds, don't declare a constructor
+ // so that the compiler can see that the constructor is trivial.
+#ifdef DEBUG
+ StaticRefPtr()
+ {
+ MOZ_ASSERT(!mRawPtr);
+ }
+#endif
+
+ StaticRefPtr<T>& operator=(T* aRhs)
+ {
+ AssignWithAddref(aRhs);
+ return *this;
+ }
+
+ StaticRefPtr<T>& operator=(const StaticRefPtr<T>& aRhs)
+ {
+ return (this = aRhs.mRawPtr);
+ }
+
+ StaticRefPtr<T>& operator=(already_AddRefed<T>& aRhs)
+ {
+ AssignAssumingAddRef(aRhs.take());
+ return *this;
+ }
+
+ StaticRefPtr<T>& operator=(already_AddRefed<T>&& aRhs)
+ {
+ AssignAssumingAddRef(aRhs.take());
+ return *this;
+ }
+
+ already_AddRefed<T>
+ forget()
+ {
+ T* temp = mRawPtr;
+ mRawPtr = nullptr;
+ return already_AddRefed<T>(temp);
+ }
+
+ T* get() const { return mRawPtr; }
+
+ operator T*() const { return get(); }
+
+ T* operator->() const
+ {
+ MOZ_ASSERT(mRawPtr);
+ return get();
+ }
+
+ T& operator*() const { return *get(); }
+
+private:
+ void AssignWithAddref(T* aNewPtr)
+ {
+ if (aNewPtr) {
+ aNewPtr->AddRef();
+ }
+ AssignAssumingAddRef(aNewPtr);
+ }
+
+ void AssignAssumingAddRef(T* aNewPtr)
+ {
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ if (oldPtr) {
+ oldPtr->Release();
+ }
+ }
+
+ T* MOZ_OWNING_REF mRawPtr;
+};
+
+namespace StaticPtr_internal {
+class Zero;
+} // namespace StaticPtr_internal
+
+#define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator==(type1 lhs, type2 rhs) \
+ { \
+ return eq_fn; \
+ } \
+ \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator==(type2 lhs, type1 rhs) \
+ { \
+ return rhs == lhs; \
+ } \
+ \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator!=(type1 lhs, type2 rhs) \
+ { \
+ return !(lhs == rhs); \
+ } \
+ \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator!=(type2 lhs, type1 rhs) \
+ { \
+ return !(lhs == rhs); \
+ }
+
+// StaticAutoPtr (in)equality operators
+
+template<class T, class U>
+inline bool
+operator==(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
+{
+ return aLhs.get() == aRhs.get();
+}
+
+template<class T, class U>
+inline bool
+operator!=(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
+{
+ return !(aLhs == aRhs);
+}
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, const U*,
+ lhs.get() == rhs, class T, class U)
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, U*,
+ lhs.get() == rhs, class T, class U)
+
+// Let us compare StaticAutoPtr to 0.
+REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, StaticPtr_internal::Zero*,
+ lhs.get() == nullptr, class T)
+
+// StaticRefPtr (in)equality operators
+
+template<class T, class U>
+inline bool
+operator==(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
+{
+ return aLhs.get() == aRhs.get();
+}
+
+template<class T, class U>
+inline bool
+operator!=(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
+{
+ return !(aLhs == aRhs);
+}
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, const U*,
+ lhs.get() == rhs, class T, class U)
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, U*,
+ lhs.get() == rhs, class T, class U)
+
+// Let us compare StaticRefPtr to 0.
+REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, StaticPtr_internal::Zero*,
+ lhs.get() == nullptr, class T)
+
+#undef REFLEXIVE_EQUALITY_OPERATORS
+
+} // namespace mozilla
+
+// Declared in mozilla/RefPtr.h
+template<class T> template<class U>
+RefPtr<T>::RefPtr(const mozilla::StaticRefPtr<U>& aOther)
+ : RefPtr(aOther.get())
+{}
+
+template<class T> template<class U>
+RefPtr<T>&
+RefPtr<T>::operator=(const mozilla::StaticRefPtr<U>& aOther)
+{
+ return operator=(aOther.get());
+}
+
+#endif
diff --git a/xpcom/base/SystemMemoryReporter.cpp b/xpcom/base/SystemMemoryReporter.cpp
new file mode 100644
index 000000000..105b9c8cf
--- /dev/null
+++ b/xpcom/base/SystemMemoryReporter.cpp
@@ -0,0 +1,989 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/SystemMemoryReporter.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/LinuxUtils.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TaggedAnonymousMemory.h"
+#include "mozilla/Unused.h"
+
+#include "nsDataHashtable.h"
+#include "nsIMemoryReporter.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+// This file implements a Linux-specific, system-wide memory reporter. It
+// gathers all the useful memory measurements obtainable from the OS in a
+// single place, giving a high-level view of memory consumption for the entire
+// machine/device.
+//
+// Other memory reporters measure part of a single process's memory consumption.
+// This reporter is different in that it measures memory consumption of many
+// processes, and they end up in a single reports tree. This is a slight abuse
+// of the memory reporting infrastructure, and therefore the results are given
+// their own "process" called "System", which means they show up in about:memory
+// in their own section, distinct from the per-process sections.
+
+namespace mozilla {
+namespace SystemMemoryReporter {
+
+#if !defined(XP_LINUX)
+#error "This won't work if we're not on Linux."
+#endif
+
+/**
+ * RAII helper that will close an open DIR handle.
+ */
+struct MOZ_STACK_CLASS AutoDir
+{
+ explicit AutoDir(DIR* aDir) : mDir(aDir) {}
+ ~AutoDir() { if (mDir) closedir(mDir); };
+ DIR* mDir;
+};
+
+/**
+ * RAII helper that will close an open FILE handle.
+ */
+struct MOZ_STACK_CLASS AutoFile
+{
+ explicit AutoFile(FILE* aFile) : mFile(aFile) {}
+ ~AutoFile() { if (mFile) fclose(mFile); }
+ FILE* mFile;
+};
+
+static bool
+EndsWithLiteral(const nsCString& aHaystack, const char* aNeedle)
+{
+ int32_t idx = aHaystack.RFind(aNeedle);
+ return idx != -1 && idx + strlen(aNeedle) == aHaystack.Length();
+}
+
+static void
+GetDirname(const nsCString& aPath, nsACString& aOut)
+{
+ int32_t idx = aPath.RFind("/");
+ if (idx == -1) {
+ aOut.Truncate();
+ } else {
+ aOut.Assign(Substring(aPath, 0, idx));
+ }
+}
+
+static void
+GetBasename(const nsCString& aPath, nsACString& aOut)
+{
+ nsCString out;
+ int32_t idx = aPath.RFind("/");
+ if (idx == -1) {
+ out.Assign(aPath);
+ } else {
+ out.Assign(Substring(aPath, idx + 1));
+ }
+
+ // On Android, some entries in /dev/ashmem end with "(deleted)" (e.g.
+ // "/dev/ashmem/libxul.so(deleted)"). We don't care about this modifier, so
+ // cut it off when getting the entry's basename.
+ if (EndsWithLiteral(out, "(deleted)")) {
+ out.Assign(Substring(out, 0, out.RFind("(deleted)")));
+ }
+ out.StripChars(" ");
+
+ aOut.Assign(out);
+}
+
+static bool
+IsNumeric(const char* aStr)
+{
+ MOZ_ASSERT(*aStr); // shouldn't see empty strings
+ while (*aStr) {
+ if (!isdigit(*aStr)) {
+ return false;
+ }
+ ++aStr;
+ }
+ return true;
+}
+
+static bool
+IsAnonymous(const nsACString& aName)
+{
+ // Recent kernels have multiple [stack:nnnn] entries, where |nnnn| is a
+ // thread ID. However, the entire virtual memory area containing a thread's
+ // stack pointer is considered the stack for that thread, even if it was
+ // merged with an adjacent area containing non-stack data. So we treat them
+ // as regular anonymous memory. However, see below about tagged anonymous
+ // memory.
+ return aName.IsEmpty() ||
+ StringBeginsWith(aName, NS_LITERAL_CSTRING("[stack:"));
+}
+
+class SystemReporter final : public nsIMemoryReporter
+{
+ ~SystemReporter() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+#define REPORT(_path, _units, _amount, _desc) \
+ do { \
+ size_t __amount = _amount; /* evaluate _amount only once */ \
+ if (__amount > 0) { \
+ aHandleReport->Callback(NS_LITERAL_CSTRING("System"), _path, \
+ KIND_OTHER, _units, __amount, _desc, aData); \
+ } \
+ } while (0)
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ // There is lots of privacy-sensitive data in /proc. Just skip this
+ // reporter entirely when anonymization is required.
+ if (aAnonymize) {
+ return NS_OK;
+ }
+
+ if (!Preferences::GetBool("memory.system_memory_reporter")) {
+ return NS_OK;
+ }
+
+ // Read relevant fields from /proc/meminfo.
+ int64_t memTotal = 0, memFree = 0;
+ nsresult rv1 = ReadMemInfo(&memTotal, &memFree);
+
+ // Collect per-process reports from /proc/<pid>/smaps.
+ int64_t totalPss = 0;
+ nsresult rv2 = CollectProcessReports(aHandleReport, aData, &totalPss);
+
+ // Report the non-process numbers.
+ if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
+ int64_t other = memTotal - memFree - totalPss;
+ REPORT(NS_LITERAL_CSTRING("mem/other"), UNITS_BYTES, other,
+ NS_LITERAL_CSTRING(
+"Memory which is neither owned by any user-space process nor free. Note that "
+"this includes memory holding cached files from the disk which can be "
+"reclaimed by the OS at any time."));
+
+ REPORT(NS_LITERAL_CSTRING("mem/free"), UNITS_BYTES, memFree,
+ NS_LITERAL_CSTRING(
+"Memory which is free and not being used for any purpose."));
+ }
+
+ // Report reserved memory not included in memTotal.
+ CollectPmemReports(aHandleReport, aData);
+
+ // Report zram usage statistics.
+ CollectZramReports(aHandleReport, aData);
+
+ // Report kgsl graphics memory usage.
+ CollectKgslReports(aHandleReport, aData);
+
+ // Report ION memory usage.
+ CollectIonReports(aHandleReport, aData);
+
+ return NS_OK;
+ }
+
+private:
+ // These are the cross-cutting measurements across all processes.
+ class ProcessSizes
+ {
+ public:
+ void Add(const nsACString& aKey, size_t aSize)
+ {
+ mTagged.Put(aKey, mTagged.Get(aKey) + aSize);
+ }
+
+ void Report(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+ {
+ for (auto iter = mTagged.Iter(); !iter.Done(); iter.Next()) {
+ nsCStringHashKey::KeyType key = iter.Key();
+ size_t amount = iter.UserData();
+
+ nsAutoCString path("processes/");
+ path.Append(key);
+
+ nsAutoCString desc("This is the sum of all processes' '");
+ desc.Append(key);
+ desc.AppendLiteral("' numbers.");
+
+ REPORT(path, UNITS_BYTES, amount, desc);
+ }
+ }
+
+ private:
+ nsDataHashtable<nsCStringHashKey, size_t> mTagged;
+ };
+
+ nsresult ReadMemInfo(int64_t* aMemTotal, int64_t* aMemFree)
+ {
+ FILE* f = fopen("/proc/meminfo", "r");
+ if (!f) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int n1 = fscanf(f, "MemTotal: %" SCNd64 " kB\n", aMemTotal);
+ int n2 = fscanf(f, "MemFree: %" SCNd64 " kB\n", aMemFree);
+
+ fclose(f);
+
+ if (n1 != 1 || n2 != 1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Convert from KB to B.
+ *aMemTotal *= 1024;
+ *aMemFree *= 1024;
+
+ return NS_OK;
+ }
+
+ nsresult CollectProcessReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData,
+ int64_t* aTotalPss)
+ {
+ *aTotalPss = 0;
+ ProcessSizes processSizes;
+
+ DIR* d = opendir("/proc");
+ if (NS_WARN_IF(!d)) {
+ return NS_ERROR_FAILURE;
+ }
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ struct stat statbuf;
+ const char* pidStr = ent->d_name;
+ // Don't check the return value of stat() -- it can return -1 for these
+ // directories even when it has succeeded, apparently.
+ stat(pidStr, &statbuf);
+ if (S_ISDIR(statbuf.st_mode) && IsNumeric(pidStr)) {
+ nsCString processName("process(");
+
+ // Get the command name from cmdline. If that fails, the pid is still
+ // shown.
+ nsPrintfCString cmdlinePath("/proc/%s/cmdline", pidStr);
+ FILE* f = fopen(cmdlinePath.get(), "r");
+ if (f) {
+ static const size_t len = 256;
+ char buf[len];
+ if (fgets(buf, len, f)) {
+ processName.Append(buf);
+ // A hack: replace forward slashes with '\\' so they aren't treated
+ // as path separators. Consumers of this reporter (such as
+ // about:memory) have to undo this change.
+ processName.ReplaceChar('/', '\\');
+ processName.AppendLiteral(", ");
+ }
+ fclose(f);
+ }
+ processName.AppendLiteral("pid=");
+ processName.Append(pidStr);
+ processName.Append(')');
+
+ // Read the PSS values from the smaps file.
+ nsPrintfCString smapsPath("/proc/%s/smaps", pidStr);
+ f = fopen(smapsPath.get(), "r");
+ if (!f) {
+ // Processes can terminate between the readdir() call above and now,
+ // so just skip if we can't open the file.
+ continue;
+ }
+ ParseMappings(f, processName, aHandleReport, aData, &processSizes,
+ aTotalPss);
+ fclose(f);
+
+ // Report the open file descriptors for this process.
+ nsPrintfCString procFdPath("/proc/%s/fd", pidStr);
+ CollectOpenFileReports(aHandleReport, aData, procFdPath, processName);
+ }
+ }
+ closedir(d);
+
+ // Report the "processes/" tree.
+ processSizes.Report(aHandleReport, aData);
+
+ return NS_OK;
+ }
+
+ void ParseMappings(FILE* aFile,
+ const nsACString& aProcessName,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData,
+ ProcessSizes* aProcessSizes,
+ int64_t* aTotalPss)
+ {
+ // The first line of an entry in /proc/<pid>/smaps looks just like an entry
+ // in /proc/<pid>/maps:
+ //
+ // address perms offset dev inode pathname
+ // 02366000-025d8000 rw-p 00000000 00:00 0 [heap]
+ //
+ // Each of the following lines contains a key and a value, separated
+ // by ": ", where the key does not contain either of those characters.
+ // Assuming more than this about the structure of those lines has
+ // failed to be future-proof in the past, so we avoid doing so.
+ //
+ // This makes it difficult to detect the start of a new entry
+ // until it's been removed from the stdio buffer, so we just loop
+ // over all lines in the file in this routine.
+
+ const int argCount = 8;
+
+ unsigned long long addrStart, addrEnd;
+ char perms[5];
+ unsigned long long offset;
+ // The 2.6 and 3.0 kernels allocate 12 bits for the major device number and
+ // 20 bits for the minor device number. Future kernels might allocate more.
+ // 64 bits ought to be enough for anybody.
+ char devMajor[17];
+ char devMinor[17];
+ unsigned int inode;
+ char line[1025];
+
+ // This variable holds the path of the current entry, or is void
+ // if we're scanning for the start of a new entry.
+ nsAutoCString currentPath;
+ int pathOffset;
+
+ currentPath.SetIsVoid(true);
+ while (fgets(line, sizeof(line), aFile)) {
+ if (currentPath.IsVoid()) {
+ int n = sscanf(line,
+ "%llx-%llx %4s %llx "
+ "%16[0-9a-fA-F]:%16[0-9a-fA-F] %u %n",
+ &addrStart, &addrEnd, perms, &offset, devMajor,
+ devMinor, &inode, &pathOffset);
+
+ if (n >= argCount - 1) {
+ currentPath.Assign(line + pathOffset);
+ currentPath.StripChars("\n");
+ }
+ continue;
+ }
+
+ // Now that we have a name and other metadata, scan for the PSS.
+ size_t pss_kb;
+ int n = sscanf(line, "Pss: %zu", &pss_kb);
+ if (n < 1) {
+ continue;
+ }
+
+ size_t pss = pss_kb * 1024;
+ if (pss > 0) {
+ nsAutoCString name, description, tag;
+ GetReporterNameAndDescription(currentPath.get(), perms, name, description, tag);
+
+ nsAutoCString processMemPath("mem/processes/");
+ processMemPath.Append(aProcessName);
+ processMemPath.Append('/');
+ processMemPath.Append(name);
+
+ REPORT(processMemPath, UNITS_BYTES, pss, description);
+
+ // Increment the appropriate aProcessSizes values, and the total.
+ aProcessSizes->Add(tag, pss);
+ *aTotalPss += pss;
+ }
+
+ // Now that we've seen the PSS, we're done with this entry.
+ currentPath.SetIsVoid(true);
+ }
+ }
+
+ void GetReporterNameAndDescription(const char* aPath,
+ const char* aPerms,
+ nsACString& aName,
+ nsACString& aDesc,
+ nsACString& aTag)
+ {
+ aName.Truncate();
+ aDesc.Truncate();
+ aTag.Truncate();
+
+ // If aPath points to a file, we have its absolute path; it might
+ // also be a bracketed pseudo-name (see below). In either case
+ // there is also some whitespace to trim.
+ nsAutoCString absPath;
+ absPath.Append(aPath);
+ absPath.StripChars(" ");
+
+ if (absPath.EqualsLiteral("[heap]")) {
+ aName.AppendLiteral("anonymous/brk-heap");
+ aDesc.AppendLiteral(
+ "Memory in anonymous mappings within the boundaries defined by "
+ "brk() / sbrk(). This is likely to be just a portion of the "
+ "application's heap; the remainder lives in other anonymous mappings. "
+ "This corresponds to '[heap]' in /proc/<pid>/smaps.");
+ aTag = aName;
+ } else if (absPath.EqualsLiteral("[stack]")) {
+ aName.AppendLiteral("stack/main-thread");
+ aDesc.AppendPrintf(
+ "The stack size of the process's main thread. This corresponds to "
+ "'[stack]' in /proc/<pid>/smaps.");
+ aTag = aName;
+ } else if (MozTaggedMemoryIsSupported() &&
+ StringBeginsWith(absPath, NS_LITERAL_CSTRING("[stack:"))) {
+ // If tagged memory is supported, we can be reasonably sure that
+ // the virtual memory area containing the stack hasn't been
+ // merged with unrelated heap memory. (This prevents the
+ // "[stack:" entries from reaching the IsAnonymous case below.)
+ pid_t tid = atoi(absPath.get() + 7);
+ nsAutoCString threadName, escapedThreadName;
+ LinuxUtils::GetThreadName(tid, threadName);
+ if (threadName.IsEmpty()) {
+ threadName.AssignLiteral("<unknown>");
+ }
+ escapedThreadName.Assign(threadName);
+ escapedThreadName.StripChars("()");
+ escapedThreadName.ReplaceChar('/', '\\');
+
+ aName.AppendLiteral("stack/non-main-thread");
+ aName.AppendLiteral("/name(");
+ aName.Append(escapedThreadName);
+ aName.Append(')');
+ aTag = aName;
+ aName.AppendPrintf("/thread(%d)", tid);
+
+ aDesc.AppendPrintf("The stack size of a non-main thread named '%s' with "
+ "thread ID %d. This corresponds to '[stack:%d]' "
+ "in /proc/%d/smaps.", threadName.get(), tid, tid);
+ } else if (absPath.EqualsLiteral("[vdso]")) {
+ aName.AppendLiteral("vdso");
+ aDesc.AppendLiteral(
+ "The virtual dynamically-linked shared object, also known as the "
+ "'vsyscall page'. This is a memory region mapped by the operating "
+ "system for the purpose of allowing processes to perform some "
+ "privileged actions without the overhead of a syscall.");
+ aTag = aName;
+ } else if (StringBeginsWith(absPath, NS_LITERAL_CSTRING("[anon:")) &&
+ EndsWithLiteral(absPath, "]")) {
+ // It's tagged memory; see also "mfbt/TaggedAnonymousMemory.h".
+ nsAutoCString tag(Substring(absPath, 6, absPath.Length() - 7));
+
+ aName.AppendLiteral("anonymous/");
+ aName.Append(tag);
+ aTag = aName;
+ aDesc.AppendLiteral("Memory in anonymous mappings tagged with '");
+ aDesc.Append(tag);
+ aDesc.Append('\'');
+ } else if (!IsAnonymous(absPath)) {
+ // We now know it's an actual file. Truncate this to its
+ // basename, and put the absolute path in the description.
+ nsAutoCString basename, dirname;
+ GetBasename(absPath, basename);
+ GetDirname(absPath, dirname);
+
+ // Hack: A file is a shared library if the basename contains ".so" and
+ // its dirname contains "/lib", or if the basename ends with ".so".
+ if (EndsWithLiteral(basename, ".so") ||
+ (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
+ aName.AppendLiteral("shared-libraries/");
+ aTag = aName;
+
+ if (strncmp(aPerms, "r-x", 3) == 0) {
+ aTag.AppendLiteral("read-executable");
+ } else if (strncmp(aPerms, "rw-", 3) == 0) {
+ aTag.AppendLiteral("read-write");
+ } else if (strncmp(aPerms, "r--", 3) == 0) {
+ aTag.AppendLiteral("read-only");
+ } else {
+ aTag.AppendLiteral("other");
+ }
+
+ } else {
+ aName.AppendLiteral("other-files");
+ if (EndsWithLiteral(basename, ".xpi")) {
+ aName.AppendLiteral("/extensions");
+ } else if (dirname.Find("/fontconfig") != -1) {
+ aName.AppendLiteral("/fontconfig");
+ } else {
+ aName.AppendLiteral("/misc");
+ }
+ aTag = aName;
+ aName.Append('/');
+ }
+
+ aName.Append(basename);
+ aDesc.Append(absPath);
+ } else {
+ if (MozTaggedMemoryIsSupported()) {
+ aName.AppendLiteral("anonymous/untagged");
+ aDesc.AppendLiteral("Memory in untagged anonymous mappings.");
+ aTag = aName;
+ } else {
+ aName.AppendLiteral("anonymous/outside-brk");
+ aDesc.AppendLiteral("Memory in anonymous mappings outside the "
+ "boundaries defined by brk() / sbrk().");
+ aTag = aName;
+ }
+ }
+
+ aName.AppendLiteral("/[");
+ aName.Append(aPerms);
+ aName.Append(']');
+
+ // Append the permissions. This is useful for non-verbose mode in
+ // about:memory when the filename is long and goes of the right side of the
+ // window.
+ aDesc.AppendLiteral(" [");
+ aDesc.Append(aPerms);
+ aDesc.Append(']');
+ }
+
+ void CollectPmemReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // The pmem subsystem allocates physically contiguous memory for
+ // interfacing with hardware. In order to ensure availability,
+ // this memory is reserved during boot, and allocations are made
+ // within these regions at runtime.
+ //
+ // There are typically several of these pools allocated at boot.
+ // The /sys/kernel/pmem_regions directory contains a subdirectory
+ // for each one. Within each subdirectory, the files we care
+ // about are "size" (the total amount of physical memory) and
+ // "mapped_regions" (a list of the current allocations within that
+ // area).
+ DIR* d = opendir("/sys/kernel/pmem_regions");
+ if (!d) {
+ return;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* name = ent->d_name;
+ uint64_t size;
+ int scanned;
+
+ // Skip "." and ".." (and any other dotfiles).
+ if (name[0] == '.') {
+ continue;
+ }
+
+ // Read the total size. The file gives the size in decimal and
+ // hex, in the form "13631488(0xd00000)"; we parse the former.
+ nsPrintfCString sizePath("/sys/kernel/pmem_regions/%s/size", name);
+ FILE* sizeFile = fopen(sizePath.get(), "r");
+ if (NS_WARN_IF(!sizeFile)) {
+ continue;
+ }
+ scanned = fscanf(sizeFile, "%" SCNu64, &size);
+ fclose(sizeFile);
+ if (NS_WARN_IF(scanned != 1)) {
+ continue;
+ }
+
+ // Read mapped regions; format described below.
+ uint64_t freeSize = size;
+ nsPrintfCString regionsPath("/sys/kernel/pmem_regions/%s/mapped_regions",
+ name);
+ FILE* regionsFile = fopen(regionsPath.get(), "r");
+ if (regionsFile) {
+ static const size_t bufLen = 4096;
+ char buf[bufLen];
+ while (fgets(buf, bufLen, regionsFile)) {
+ int pid;
+
+ // Skip header line.
+ if (strncmp(buf, "pid #", 5) == 0) {
+ continue;
+ }
+ // Line format: "pid N:" + zero or more "(Start,Len) ".
+ // N is decimal; Start and Len are in hex.
+ scanned = sscanf(buf, "pid %d", &pid);
+ if (NS_WARN_IF(scanned != 1)) {
+ continue;
+ }
+ for (const char* nextParen = strchr(buf, '(');
+ nextParen != nullptr;
+ nextParen = strchr(nextParen + 1, '(')) {
+ uint64_t mapStart, mapLen;
+
+ scanned = sscanf(nextParen + 1, "%" SCNx64 ",%" SCNx64,
+ &mapStart, &mapLen);
+ if (NS_WARN_IF(scanned != 2)) {
+ break;
+ }
+
+ nsPrintfCString path("mem/pmem/used/%s/segment(pid=%d, "
+ "offset=0x%" PRIx64 ")", name, pid, mapStart);
+ nsPrintfCString desc("Physical memory reserved for the \"%s\" pool "
+ "and allocated to a buffer.", name);
+ REPORT(path, UNITS_BYTES, mapLen, desc);
+ freeSize -= mapLen;
+ }
+ }
+ fclose(regionsFile);
+ }
+
+ nsPrintfCString path("mem/pmem/free/%s", name);
+ nsPrintfCString desc("Physical memory reserved for the \"%s\" pool and "
+ "unavailable to the rest of the system, but not "
+ "currently allocated.", name);
+ REPORT(path, UNITS_BYTES, freeSize, desc);
+ }
+ closedir(d);
+ }
+
+ void
+ CollectIonReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // ION is a replacement for PMEM (and other similar allocators).
+ //
+ // More details from http://lwn.net/Articles/480055/
+ // "Like its PMEM-like predecessors, ION manages one or more memory pools,
+ // some of which are set aside at boot time to combat fragmentation or to
+ // serve special hardware needs. GPUs, display controllers, and cameras
+ // are some of the hardware blocks that may have special memory
+ // requirements."
+ //
+ // The file format starts as follows:
+ // client pid size
+ // ----------------------------------------------------
+ // adsprpc-smd 1 4096
+ // fd900000.qcom,mdss_mdp 1 1658880
+ // ----------------------------------------------------
+ // orphaned allocations (info is from last known client):
+ // Homescreen 24100 294912 0 1
+ // b2g 23987 1658880 0 1
+ // mdss_fb0 401 1658880 0 1
+ // b2g 23987 4096 0 1
+ // Built-in Keyboa 24205 61440 0 1
+ // ----------------------------------------------------
+ // <other stuff>
+ //
+ // For our purposes we only care about the first portion of the file noted
+ // above which contains memory alloations (both sections). The term
+ // "orphaned" is misleading, it appears that every allocation not by the
+ // first process is considered orphaned on FxOS devices.
+
+ // The first three fields of each entry interest us:
+ // 1) client - Essentially the process name. We limit client names to 63
+ // characters, in theory they should never be greater than 15
+ // due to thread name length limitations.
+ // 2) pid - The ID of the allocating process, read as a uint32_t.
+ // 3) size - The size of the allocation in bytes, read as as a uint64_t.
+ const char* const kFormatString = "%63s %" SCNu32 " %" SCNu64;
+ const int kNumFields = 3;
+ const size_t kStringSize = 64;
+ const char* const kIonIommuPath = "/sys/kernel/debug/ion/iommu";
+
+ FILE* iommu = fopen(kIonIommuPath, "r");
+ if (!iommu) {
+ return;
+ }
+
+ AutoFile iommuGuard(iommu);
+
+ const size_t kBufferLen = 256;
+ char buffer[kBufferLen];
+ char client[kStringSize];
+ uint32_t pid;
+ uint64_t size;
+
+ // Ignore the header line.
+ Unused << fgets(buffer, kBufferLen, iommu);
+
+ // Ignore the separator line.
+ Unused << fgets(buffer, kBufferLen, iommu);
+
+ const char* const kSep = "----";
+ const size_t kSepLen = 4;
+
+ // Read non-orphaned entries.
+ while (fgets(buffer, kBufferLen, iommu) &&
+ strncmp(kSep, buffer, kSepLen) != 0) {
+ if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
+ nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
+ REPORT(entryPath, UNITS_BYTES, size,
+ NS_LITERAL_CSTRING("An ION kernel memory allocation."));
+ }
+ }
+
+ // Ignore the orphaned header.
+ Unused << fgets(buffer, kBufferLen, iommu);
+
+ // Read orphaned entries.
+ while (fgets(buffer, kBufferLen, iommu) &&
+ strncmp(kSep, buffer, kSepLen) != 0) {
+ if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
+ nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
+ REPORT(entryPath, UNITS_BYTES, size,
+ NS_LITERAL_CSTRING("An ION kernel memory allocation."));
+ }
+ }
+
+ // Ignore the rest of the file.
+ }
+
+ uint64_t
+ ReadSizeFromFile(const char* aFilename)
+ {
+ FILE* sizeFile = fopen(aFilename, "r");
+ if (NS_WARN_IF(!sizeFile)) {
+ return 0;
+ }
+
+ uint64_t size = 0;
+ Unused << fscanf(sizeFile, "%" SCNu64, &size);
+ fclose(sizeFile);
+
+ return size;
+ }
+
+ void
+ CollectZramReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // zram usage stats files can be found under:
+ // /sys/block/zram<id>
+ // |--> disksize - Maximum amount of uncompressed data that can be
+ // stored on the disk (bytes)
+ // |--> orig_data_size - Uncompressed size of data in the disk (bytes)
+ // |--> compr_data_size - Compressed size of the data in the disk (bytes)
+ // |--> num_reads - Number of attempted reads to the disk (count)
+ // |--> num_writes - Number of attempted writes to the disk (count)
+ //
+ // Each file contains a single integer value in decimal form.
+
+ DIR* d = opendir("/sys/block");
+ if (!d) {
+ return;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* name = ent->d_name;
+
+ // Skip non-zram entries.
+ if (strncmp("zram", name, 4) != 0) {
+ continue;
+ }
+
+ // Report disk size statistics.
+ nsPrintfCString diskSizeFile("/sys/block/%s/disksize", name);
+ nsPrintfCString origSizeFile("/sys/block/%s/orig_data_size", name);
+
+ uint64_t diskSize = ReadSizeFromFile(diskSizeFile.get());
+ uint64_t origSize = ReadSizeFromFile(origSizeFile.get());
+ uint64_t unusedSize = diskSize - origSize;
+
+ nsPrintfCString diskUsedPath("zram-disksize/%s/used", name);
+ nsPrintfCString diskUsedDesc(
+ "The uncompressed size of data stored in \"%s.\" "
+ "This excludes zero-filled pages since "
+ "no memory is allocated for them.", name);
+ REPORT(diskUsedPath, UNITS_BYTES, origSize, diskUsedDesc);
+
+ nsPrintfCString diskUnusedPath("zram-disksize/%s/unused", name);
+ nsPrintfCString diskUnusedDesc(
+ "The amount of uncompressed data that can still be "
+ "be stored in \"%s\"", name);
+ REPORT(diskUnusedPath, UNITS_BYTES, unusedSize, diskUnusedDesc);
+
+ // Report disk accesses.
+ nsPrintfCString readsFile("/sys/block/%s/num_reads", name);
+ nsPrintfCString writesFile("/sys/block/%s/num_writes", name);
+
+ uint64_t reads = ReadSizeFromFile(readsFile.get());
+ uint64_t writes = ReadSizeFromFile(writesFile.get());
+
+ nsPrintfCString readsDesc(
+ "The number of reads (failed or successful) done on "
+ "\"%s\"", name);
+ nsPrintfCString readsPath("zram-accesses/%s/reads", name);
+ REPORT(readsPath, UNITS_COUNT_CUMULATIVE, reads, readsDesc);
+
+ nsPrintfCString writesDesc(
+ "The number of writes (failed or successful) done "
+ "on \"%s\"", name);
+ nsPrintfCString writesPath("zram-accesses/%s/writes", name);
+ REPORT(writesPath, UNITS_COUNT_CUMULATIVE, writes, writesDesc);
+
+ // Report compressed data size.
+ nsPrintfCString comprSizeFile("/sys/block/%s/compr_data_size", name);
+ uint64_t comprSize = ReadSizeFromFile(comprSizeFile.get());
+
+ nsPrintfCString comprSizeDesc(
+ "The compressed size of data stored in \"%s\"",
+ name);
+ nsPrintfCString comprSizePath("zram-compr-data-size/%s", name);
+ REPORT(comprSizePath, UNITS_BYTES, comprSize, comprSizeDesc);
+ }
+
+ closedir(d);
+ }
+
+ void
+ CollectOpenFileReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData,
+ const nsACString& aProcPath,
+ const nsACString& aProcessName)
+ {
+ // All file descriptors opened by a process are listed under
+ // /proc/<pid>/fd/<numerical_fd>. Each entry is a symlink that points to the
+ // path that was opened. This can be an actual file, a socket, a pipe, an
+ // anon_inode, or possibly an uncategorized device.
+ const char kFilePrefix[] = "/";
+ const char kSocketPrefix[] = "socket:";
+ const char kPipePrefix[] = "pipe:";
+ const char kAnonInodePrefix[] = "anon_inode:";
+
+ const nsCString procPath(aProcPath);
+ DIR* d = opendir(procPath.get());
+ if (!d) {
+ return;
+ }
+
+ char linkPath[PATH_MAX + 1];
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* fd = ent->d_name;
+
+ // Skip "." and ".." (and any other dotfiles).
+ if (fd[0] == '.') {
+ continue;
+ }
+
+ nsPrintfCString fullPath("%s/%s", procPath.get(), fd);
+ ssize_t linkPathSize = readlink(fullPath.get(), linkPath, PATH_MAX);
+ if (linkPathSize > 0) {
+ linkPath[linkPathSize] = '\0';
+
+#define CHECK_PREFIX(prefix) \
+ (strncmp(linkPath, prefix, sizeof(prefix) - 1) == 0)
+
+ const char* category = nullptr;
+ const char* descriptionPrefix = nullptr;
+
+ if (CHECK_PREFIX(kFilePrefix)) {
+ category = "files"; // No trailing slash, the file path will have one
+ descriptionPrefix = "An open";
+ } else if (CHECK_PREFIX(kSocketPrefix)) {
+ category = "sockets/";
+ descriptionPrefix = "A socket";
+ } else if (CHECK_PREFIX(kPipePrefix)) {
+ category = "pipes/";
+ descriptionPrefix = "A pipe";
+ } else if (CHECK_PREFIX(kAnonInodePrefix)) {
+ category = "anon_inodes/";
+ descriptionPrefix = "An anon_inode";
+ } else {
+ category = "";
+ descriptionPrefix = "An uncategorized";
+ }
+
+#undef CHECK_PREFIX
+
+ const nsCString processName(aProcessName);
+ nsPrintfCString entryPath("open-fds/%s/%s%s/%s",
+ processName.get(), category, linkPath, fd);
+ nsPrintfCString entryDescription("%s file descriptor opened by the process",
+ descriptionPrefix);
+ REPORT(entryPath, UNITS_COUNT, 1, entryDescription);
+ }
+ }
+
+ closedir(d);
+ }
+
+ void
+ CollectKgslReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // Each process that uses kgsl memory will have an entry under
+ // /sys/kernel/debug/kgsl/proc/<pid>/mem. This file format includes a
+ // header and then entries with types as follows:
+ // gpuaddr useraddr size id flags type usage sglen
+ // hexaddr hexaddr int int str str str int
+ // We care primarily about the usage and size.
+
+ // For simplicity numbers will be uint64_t, strings 63 chars max.
+ const char* const kScanFormat =
+ "%" SCNx64 " %" SCNx64 " %" SCNu64 " %" SCNu64
+ " %63s %63s %63s %" SCNu64;
+ const int kNumFields = 8;
+ const size_t kStringSize = 64;
+
+ DIR* d = opendir("/sys/kernel/debug/kgsl/proc/");
+ if (!d) {
+ return;
+ }
+
+ AutoDir dirGuard(d);
+
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* pid = ent->d_name;
+
+ // Skip "." and ".." (and any other dotfiles).
+ if (pid[0] == '.') {
+ continue;
+ }
+
+ nsPrintfCString memPath("/sys/kernel/debug/kgsl/proc/%s/mem", pid);
+ FILE* memFile = fopen(memPath.get(), "r");
+ if (NS_WARN_IF(!memFile)) {
+ continue;
+ }
+
+ AutoFile fileGuard(memFile);
+
+ // Attempt to map the pid to a more useful name.
+ nsAutoCString procName;
+ LinuxUtils::GetThreadName(atoi(pid), procName);
+
+ if (procName.IsEmpty()) {
+ procName.Append("pid=");
+ procName.Append(pid);
+ } else {
+ procName.Append(" (pid=");
+ procName.Append(pid);
+ procName.Append(")");
+ }
+
+ uint64_t gpuaddr, useraddr, size, id, sglen;
+ char flags[kStringSize];
+ char type[kStringSize];
+ char usage[kStringSize];
+
+ // Bypass the header line.
+ char buff[1024];
+ Unused << fgets(buff, 1024, memFile);
+
+ while (fscanf(memFile, kScanFormat, &gpuaddr, &useraddr, &size, &id,
+ flags, type, usage, &sglen) == kNumFields) {
+ nsPrintfCString entryPath("kgsl-memory/%s/%s", procName.get(), usage);
+ REPORT(entryPath, UNITS_BYTES, size,
+ NS_LITERAL_CSTRING("A kgsl graphics memory allocation."));
+ }
+ }
+ }
+};
+
+NS_IMPL_ISUPPORTS(SystemReporter, nsIMemoryReporter)
+
+void
+Init()
+{
+ RegisterStrongMemoryReporter(new SystemReporter());
+}
+
+} // namespace SystemMemoryReporter
+} // namespace mozilla
diff --git a/xpcom/base/SystemMemoryReporter.h b/xpcom/base/SystemMemoryReporter.h
new file mode 100644
index 000000000..e52487613
--- /dev/null
+++ b/xpcom/base/SystemMemoryReporter.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SystemMemoryReporter_h_
+#define mozilla_SystemMemoryReporter_h_
+
+namespace mozilla {
+namespace SystemMemoryReporter {
+
+// This only works on Linux, but to make callers' lives easier, we stub out
+// empty functions on other platforms.
+
+#if defined(XP_LINUX)
+void
+Init();
+#else
+void
+Init()
+{
+}
+#endif
+
+} // namespace SystemMemoryReporter
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build
new file mode 100644
index 000000000..d6a336b40
--- /dev/null
+++ b/xpcom/base/moz.build
@@ -0,0 +1,159 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIConsoleListener.idl',
+ 'nsIConsoleMessage.idl',
+ 'nsIConsoleService.idl',
+ 'nsICycleCollectorListener.idl',
+ 'nsIDebug2.idl',
+ 'nsIErrorService.idl',
+ 'nsIException.idl',
+ 'nsIGZFileWriter.idl',
+ 'nsIInterfaceRequestor.idl',
+ 'nsIMemory.idl',
+ 'nsIMemoryInfoDumper.idl',
+ 'nsIMemoryReporter.idl',
+ 'nsIMessageLoop.idl',
+ 'nsIMutable.idl',
+ 'nsIProgrammingLanguage.idl',
+ 'nsISecurityConsoleMessage.idl',
+ 'nsIStatusReporter.idl',
+ 'nsISupports.idl',
+ 'nsIUUIDGenerator.idl',
+ 'nsIVersionComparator.idl',
+ 'nsIWeakReference.idl',
+ 'nsrootidl.idl',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ XPIDL_SOURCES += [
+ 'nsIMacUtils.idl',
+ ]
+ EXPORTS.mozilla += [
+ 'MacHelpers.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'MacHelpers.mm',
+ ]
+
+XPIDL_MODULE = 'xpcom_base'
+
+EXPORTS += [
+ 'CodeAddressService.h',
+ 'ErrorList.h',
+ 'nsAgg.h',
+ 'nsAlgorithm.h',
+ 'nsAutoPtr.h',
+ 'nsAutoRef.h',
+ 'nsCom.h',
+ 'nscore.h',
+ 'nsCycleCollector.h',
+ 'nsDebugImpl.h',
+ 'nsDumpUtils.h',
+ 'nsError.h',
+ 'nsGZFileWriter.h',
+ 'nsIID.h',
+ 'nsInterfaceRequestorAgg.h',
+ 'nsISizeOf.h',
+ 'nsISupportsBase.h',
+ 'nsObjCExceptions.h',
+ 'nsQueryObject.h',
+ 'nsTraceRefcnt.h',
+ 'nsWeakPtr.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS += [
+ 'nsWindowsHelpers.h',
+ ]
+
+EXPORTS.mozilla += [
+ 'AvailableMemoryTracker.h',
+ 'ClearOnShutdown.h',
+ 'CountingAllocatorBase.h',
+ 'CycleCollectedJSContext.h',
+ 'Debug.h',
+ 'DebuggerOnGCRunnable.h',
+ 'DeferredFinalize.h',
+ 'ErrorNames.h',
+ 'HoldDropJSObjects.h',
+ 'JSObjectHolder.h',
+ 'LinuxUtils.h',
+ 'Logging.h',
+ 'nsMemoryInfoDumper.h',
+ 'OwningNonNull.h',
+ 'StaticMutex.h',
+ 'StaticPtr.h',
+ 'SystemMemoryReporter.h',
+]
+
+# nsDebugImpl isn't unified because we disable PGO so that NS_ABORT_OOM isn't
+# optimized away oddly.
+SOURCES += [
+ 'nsDebugImpl.cpp',
+]
+SOURCES['nsDebugImpl.cpp'].no_pgo = True
+
+UNIFIED_SOURCES += [
+ 'AvailableMemoryTracker.cpp',
+ 'ClearOnShutdown.cpp',
+ 'CycleCollectedJSContext.cpp',
+ 'Debug.cpp',
+ 'DebuggerOnGCRunnable.cpp',
+ 'DeferredFinalize.cpp',
+ 'ErrorNames.cpp',
+ 'HoldDropJSObjects.cpp',
+ 'JSObjectHolder.cpp',
+ 'Logging.cpp',
+ 'LogModulePrefWatcher.cpp',
+ 'nsConsoleMessage.cpp',
+ 'nsConsoleService.cpp',
+ 'nsCycleCollector.cpp',
+ 'nsCycleCollectorTraceJSHelpers.cpp',
+ 'nsDumpUtils.cpp',
+ 'nsErrorService.cpp',
+ 'nsGZFileWriter.cpp',
+ 'nsInterfaceRequestorAgg.cpp',
+ 'nsMemoryImpl.cpp',
+ 'nsMemoryInfoDumper.cpp',
+ 'nsMemoryReporterManager.cpp',
+ 'nsMessageLoop.cpp',
+ 'NSPRLogModulesParser.cpp',
+ 'nsSecurityConsoleMessage.cpp',
+ 'nsStatusReporterManager.cpp',
+ 'nsSystemInfo.cpp',
+ 'nsTraceRefcnt.cpp',
+ 'nsUUIDGenerator.cpp',
+ 'nsVersionComparatorImpl.cpp',
+]
+
+if CONFIG['OS_ARCH'] == 'Linux':
+ SOURCES += [
+ 'LinuxUtils.cpp',
+ 'SystemMemoryReporter.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'nsMacUtilsImpl.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'nsCrashOnException.cpp',
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../build',
+ '/xpcom/ds',
+]
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/xpcom/base/nsAgg.h b/xpcom/base/nsAgg.h
new file mode 100644
index 000000000..8dcf8067e
--- /dev/null
+++ b/xpcom/base/nsAgg.h
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAgg_h___
+#define nsAgg_h___
+
+#include "nsISupports.h"
+#include "nsCycleCollectionParticipant.h"
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Put NS_DECL_AGGREGATED or NS_DECL_CYCLE_COLLECTING_AGGREGATED in your class's
+// declaration.
+#define NS_DECL_AGGREGATED \
+ NS_DECL_ISUPPORTS \
+ NS_DECL_AGGREGATED_HELPER
+
+#define NS_DECL_CYCLE_COLLECTING_AGGREGATED \
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
+ NS_DECL_AGGREGATED_HELPER
+
+#define NS_DECL_AGGREGATED_HELPER \
+public: \
+ \
+ /** \
+ * Returns the nsISupports pointer of the inner object (aka the \
+ * aggregatee). This pointer is really only useful to the outer object \
+ * (aka the aggregator), which can use it to hold on to the inner \
+ * object. Anything else wants the nsISupports pointer of the outer \
+ * object (gotten by QI'ing inner or outer to nsISupports). This method \
+ * returns a non-addrefed pointer. \
+ * @return the nsISupports pointer of the inner object \
+ */ \
+ nsISupports* InnerObject(void) { return &fAggregated; } \
+ \
+ /** \
+ * Returns true if this object is part of an aggregated object. \
+ */ \
+ bool IsPartOfAggregated(void) { return fOuter != InnerObject(); } \
+ \
+private: \
+ \
+ /* You must implement this operation instead of the nsISupports */ \
+ /* methods. */ \
+ nsresult \
+ AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr); \
+ \
+ class Internal : public nsISupports { \
+ public: \
+ \
+ Internal() {} \
+ \
+ NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void); \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void); \
+ \
+ NS_DECL_OWNINGTHREAD \
+ }; \
+ \
+ friend class Internal; \
+ \
+ nsISupports* MOZ_UNSAFE_REF("fOuter can either point to fAggregated " \
+ "or to an outer object, and the safety " \
+ "of this reference depends on the exact " \
+ "lifetime semantics of the AddRef/Release " \
+ "functions created by these macros.") \
+ fOuter; \
+ Internal fAggregated; \
+ \
+public: \
+
+#define NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+public: \
+ NS_IMETHOD_(void) Unlink(void *p) override; \
+ NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void* p) override \
+ { \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class):: \
+ Downcast(static_cast<nsISupports*>(p))->DeleteCycleCollectable(); \
+ } \
+ static _class* Downcast(nsISupports* s) \
+ { \
+ return (_class*)((char*)(s) - offsetof(_class, fAggregated)); \
+ } \
+ static nsISupports* Upcast(_class *p) \
+ { \
+ return p->InnerObject(); \
+ } \
+ static nsXPCOMCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class); \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+// Put this in your class's constructor:
+#define NS_INIT_AGGREGATED(outer) \
+ PR_BEGIN_MACRO \
+ fOuter = outer ? outer : &fAggregated; \
+ PR_END_MACRO
+
+
+// Put this in your class's implementation file:
+#define NS_IMPL_AGGREGATED(_class) \
+ \
+NS_IMPL_AGGREGATED_HELPER(_class) \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::AddRef(void) \
+{ \
+ _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ ++agg->mRefCnt; \
+ NS_LOG_ADDREF(this, agg->mRefCnt, #_class, sizeof(*this)); \
+ return agg->mRefCnt; \
+} \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::Release(void) \
+{ \
+ _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ --agg->mRefCnt; \
+ NS_LOG_RELEASE(this, agg->mRefCnt, #_class); \
+ if (agg->mRefCnt == 0) { \
+ agg->mRefCnt = 1; /* stabilize */ \
+ delete agg; \
+ return 0; \
+ } \
+ return agg->mRefCnt; \
+} \
+
+#define NS_IMPL_CYCLE_COLLECTING_AGGREGATED(_class) \
+ \
+NS_IMPL_AGGREGATED_HELPER(_class) \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::AddRef(void) \
+{ \
+ _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
+ nsrefcnt count = agg->mRefCnt.incr(this); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \
+ return count; \
+} \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::Release(void) \
+{ \
+ _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
+ nsrefcnt count = agg->mRefCnt.decr(this); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count; \
+} \
+NS_IMETHODIMP_(void) \
+_class::DeleteCycleCollectable(void) \
+{ \
+ delete this; \
+}
+
+#define NS_IMPL_AGGREGATED_HELPER(_class) \
+NS_IMETHODIMP \
+_class::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
+{ \
+ return fOuter->QueryInterface(aIID, aInstancePtr); \
+} \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::AddRef(void) \
+{ \
+ return fOuter->AddRef(); \
+} \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Release(void) \
+{ \
+ return fOuter->Release(); \
+} \
+ \
+NS_IMETHODIMP \
+_class::Internal::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
+{ \
+ _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
+ return agg->AggregatedQueryInterface(aIID, aInstancePtr); \
+} \
+
+/**
+ * To make aggregated objects participate in cycle collection we need to enable
+ * the outer object (aggregator) to traverse/unlink the objects held by the
+ * inner object (the aggregatee). We can't just make the inner object QI'able to
+ * NS_CYCLECOLLECTIONPARTICIPANT_IID, we don't want to return the inner object's
+ * nsCycleCollectionParticipant for the outer object (which will happen if the
+ * outer object doesn't participate in cycle collection itself).
+ * NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID enables the outer object to get
+ * the inner objects nsCycleCollectionParticipant.
+ *
+ * There are three cases:
+ * - No aggregation
+ * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the inner
+ * object's nsCycleCollectionParticipant.
+ *
+ * - Aggregation and outer object does not participate in cycle collection
+ * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will not return anything.
+ *
+ * - Aggregation and outer object does participate in cycle collection
+ * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the outer
+ * object's nsCycleCollectionParticipant. The outer object's
+ * nsCycleCollectionParticipant can then QI the inner object to
+ * NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID to get the inner object's
+ * nsCycleCollectionParticipant, which it can use to traverse/unlink the
+ * objects reachable from the inner object.
+ */
+#define NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID \
+{ \
+ 0x32889b7e, \
+ 0xe4fe, \
+ 0x43f4, \
+ { 0x85, 0x31, 0xb5, 0x28, 0x23, 0xa2, 0xe9, 0xfc } \
+}
+
+/**
+ * Just holds the IID so NS_GET_IID works.
+ */
+class nsAggregatedCycleCollectionParticipant
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsAggregatedCycleCollectionParticipant,
+ NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID)
+
+// for use with QI macros in nsISupportsUtils.h:
+
+#define NS_INTERFACE_MAP_BEGIN_AGGREGATED(_class) \
+ NS_IMPL_AGGREGATED_QUERY_HEAD(_class)
+
+#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \
+ NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
+
+#define NS_IMPL_AGGREGATED_QUERY_HEAD(_class) \
+nsresult \
+_class::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "AggregatedQueryInterface requires a non-NULL result ptr!"); \
+ if ( !aInstancePtr ) \
+ return NS_ERROR_NULL_POINTER; \
+ nsISupports* foundInterface; \
+ if ( aIID.Equals(NS_GET_IID(nsISupports)) ) \
+ foundInterface = InnerObject(); \
+ else
+
+#define NS_IMPL_AGGREGATED_QUERY_CYCLE_COLLECTION(_class) \
+ if (aIID.Equals(IsPartOfAggregated() ? \
+ NS_GET_IID(nsCycleCollectionParticipant) : \
+ NS_GET_IID(nsAggregatedCycleCollectionParticipant))) \
+ foundInterface = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
+ else
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(_class) \
+ NS_IMETHODIMP \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \
+ (void *p, nsCycleCollectionTraversalCallback &cb) \
+ { \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ MOZ_ASSERT(CheckForRightISupports(s), \
+ "not the nsISupports pointer we expect"); \
+ _class *tmp = static_cast<_class*>(Downcast(s)); \
+ if (!tmp->IsPartOfAggregated()) \
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, tmp->mRefCnt.get())
+
+#define NS_GENERIC_AGGREGATED_CONSTRUCTOR(_InstanceClass) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ *aResult = nullptr; \
+ if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \
+ return NS_ERROR_INVALID_ARG; \
+ \
+ RefPtr<_InstanceClass> inst = new _InstanceClass(aOuter); \
+ if (!inst) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ \
+ nsISupports* inner = inst->InnerObject(); \
+ nsresult rv = inner->QueryInterface(aIID, aResult); \
+ \
+ return rv; \
+} \
+
+#define NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ *aResult = nullptr; \
+ if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \
+ return NS_ERROR_INVALID_ARG; \
+ \
+ RefPtr<_InstanceClass> inst = new _InstanceClass(aOuter); \
+ if (!inst) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ \
+ nsISupports* inner = inst->InnerObject(); \
+ NS_ADDREF(inner); \
+ nsresult rv = inst->_InitMethod(); \
+ if (NS_SUCCEEDED(rv)) { \
+ rv = inner->QueryInterface(aIID, aResult); \
+ } \
+ NS_RELEASE(inner); \
+ \
+ return rv; \
+} \
+
+#endif /* nsAgg_h___ */
diff --git a/xpcom/base/nsAlgorithm.h b/xpcom/base/nsAlgorithm.h
new file mode 100644
index 000000000..ceaa3ace3
--- /dev/null
+++ b/xpcom/base/nsAlgorithm.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAlgorithm_h___
+#define nsAlgorithm_h___
+
+#include "nsCharTraits.h" // for |nsCharSourceTraits|, |nsCharSinkTraits|
+
+template <class T>
+inline T
+NS_ROUNDUP(const T& aA, const T& aB)
+{
+ return ((aA + (aB - 1)) / aB) * aB;
+}
+
+// We use these instead of std::min/max because we can't include the algorithm
+// header in all of XPCOM because the stl wrappers will error out when included
+// in parts of XPCOM. These functions should never be used outside of XPCOM.
+template <class T>
+inline const T&
+XPCOM_MIN(const T& aA, const T& aB)
+{
+ return aB < aA ? aB : aA;
+}
+
+// Must return b when a == b in case a is -0
+template <class T>
+inline const T&
+XPCOM_MAX(const T& aA, const T& aB)
+{
+ return aA > aB ? aA : aB;
+}
+
+namespace mozilla {
+
+template <class T>
+inline const T&
+clamped(const T& aA, const T& aMin, const T& aMax)
+{
+ MOZ_ASSERT(aMax >= aMin,
+ "clamped(): aMax must be greater than or equal to aMin");
+ return XPCOM_MIN(XPCOM_MAX(aA, aMin), aMax);
+}
+
+} // namespace mozilla
+
+template <class InputIterator, class T>
+inline uint32_t
+NS_COUNT(InputIterator& aFirst, const InputIterator& aLast, const T& aValue)
+{
+ uint32_t result = 0;
+ for (; aFirst != aLast; ++aFirst)
+ if (*aFirst == aValue) {
+ ++result;
+ }
+ return result;
+}
+
+template <class InputIterator, class OutputIterator>
+inline OutputIterator&
+copy_string(const InputIterator& aFirst, const InputIterator& aLast,
+ OutputIterator& aResult)
+{
+ typedef nsCharSourceTraits<InputIterator> source_traits;
+ typedef nsCharSinkTraits<OutputIterator> sink_traits;
+
+ sink_traits::write(aResult, source_traits::read(aFirst),
+ source_traits::readable_distance(aFirst, aLast));
+ return aResult;
+}
+
+#endif // !defined(nsAlgorithm_h___)
diff --git a/xpcom/base/nsAllocator.h b/xpcom/base/nsAllocator.h
new file mode 100644
index 000000000..a9ad1c70a
--- /dev/null
+++ b/xpcom/base/nsAllocator.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+////////////////////////////////////////////////////////////////////////////////
+// obsolete
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef nsAllocator_h__
+#define nsAllocator_h__
+
+#include "nsAgg.h"
+#include "nsIFactory.h"
+
+#endif // nsAllocator_h__
diff --git a/xpcom/base/nsAutoPtr.h b/xpcom/base/nsAutoPtr.h
new file mode 100644
index 000000000..b5a15000c
--- /dev/null
+++ b/xpcom/base/nsAutoPtr.h
@@ -0,0 +1,454 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAutoPtr_h
+#define nsAutoPtr_h
+
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TypeTraits.h"
+
+#include "nsCycleCollectionNoteChild.h"
+#include "mozilla/MemoryReporting.h"
+
+/*****************************************************************************/
+
+// template <class T> class nsAutoPtrGetterTransfers;
+
+template <class T>
+class nsAutoPtr
+{
+private:
+ static_assert(!mozilla::IsScalar<T>::value, "If you are using "
+ "nsAutoPtr to hold an array, use UniquePtr<T[]> instead");
+
+ void**
+ begin_assignment()
+ {
+ assign(0);
+ return reinterpret_cast<void**>(&mRawPtr);
+ }
+
+ void
+ assign(T* aNewPtr)
+ {
+ T* oldPtr = mRawPtr;
+
+ if (aNewPtr && aNewPtr == oldPtr) {
+ NS_RUNTIMEABORT("Logic flaw in the caller");
+ }
+
+ mRawPtr = aNewPtr;
+ delete oldPtr;
+ }
+
+ // |class Ptr| helps us prevent implicit "copy construction"
+ // through |operator T*() const| from a |const nsAutoPtr<T>|
+ // because two implicit conversions in a row aren't allowed.
+ // It still allows assignment from T* through implicit conversion
+ // from |T*| to |nsAutoPtr<T>::Ptr|
+ class Ptr
+ {
+ public:
+ MOZ_IMPLICIT Ptr(T* aPtr)
+ : mPtr(aPtr)
+ {
+ }
+
+ operator T*() const
+ {
+ return mPtr;
+ }
+
+ private:
+ T* MOZ_NON_OWNING_REF mPtr;
+ };
+
+private:
+ T* MOZ_OWNING_REF mRawPtr;
+
+public:
+ typedef T element_type;
+
+ ~nsAutoPtr()
+ {
+ delete mRawPtr;
+ }
+
+ // Constructors
+
+ nsAutoPtr()
+ : mRawPtr(0)
+ // default constructor
+ {
+ }
+
+ MOZ_IMPLICIT nsAutoPtr(Ptr aRawPtr)
+ : mRawPtr(aRawPtr)
+ // construct from a raw pointer (of the right type)
+ {
+ }
+
+ // This constructor shouldn't exist; we should just use the &&
+ // constructor.
+ nsAutoPtr(nsAutoPtr<T>& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ template <typename I>
+ MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ nsAutoPtr(nsAutoPtr<T>&& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ template <typename I>
+ MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>&& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ // Assignment operators
+
+ nsAutoPtr<T>&
+ operator=(T* aRhs)
+ // assign from a raw pointer (of the right type)
+ {
+ assign(aRhs);
+ return *this;
+ }
+
+ nsAutoPtr<T>& operator=(nsAutoPtr<T>& aRhs)
+ // assign by transferring ownership from another smart pointer.
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ template <typename I>
+ nsAutoPtr<T>& operator=(nsAutoPtr<I>& aRhs)
+ // assign by transferring ownership from another smart pointer.
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ nsAutoPtr<T>& operator=(nsAutoPtr<T>&& aRhs)
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ template <typename I>
+ nsAutoPtr<T>& operator=(nsAutoPtr<I>&& aRhs)
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ // Other pointer operators
+
+ T*
+ get() const
+ /*
+ Prefer the implicit conversion provided automatically by
+ |operator T*() const|. Use |get()| _only_ to resolve
+ ambiguity.
+ */
+ {
+ return mRawPtr;
+ }
+
+ operator T*() const
+ /*
+ ...makes an |nsAutoPtr| act like its underlying raw pointer
+ type whenever it is used in a context where a raw pointer
+ is expected. It is this operator that makes an |nsAutoPtr|
+ substitutable for a raw pointer.
+
+ Prefer the implicit use of this operator to calling |get()|,
+ except where necessary to resolve ambiguity.
+ */
+ {
+ return get();
+ }
+
+ T*
+ forget()
+ {
+ T* temp = mRawPtr;
+ mRawPtr = 0;
+ return temp;
+ }
+
+ T*
+ operator->() const
+ {
+ NS_PRECONDITION(mRawPtr != 0,
+ "You can't dereference a NULL nsAutoPtr with operator->().");
+ return get();
+ }
+
+ template <typename R, typename... Args>
+ class Proxy
+ {
+ typedef R (T::*member_function)(Args...);
+ T* mRawPtr;
+ member_function mFunction;
+ public:
+ Proxy(T* aRawPtr, member_function aFunction)
+ : mRawPtr(aRawPtr),
+ mFunction(aFunction)
+ {
+ }
+ template<typename... ActualArgs>
+ R operator()(ActualArgs&&... aArgs)
+ {
+ return ((*mRawPtr).*mFunction)(mozilla::Forward<ActualArgs>(aArgs)...);
+ }
+ };
+
+ template <typename R, typename C, typename... Args>
+ Proxy<R, Args...> operator->*(R (C::*aFptr)(Args...)) const
+ {
+ NS_PRECONDITION(mRawPtr != 0,
+ "You can't dereference a NULL nsAutoPtr with operator->*().");
+ return Proxy<R, Args...>(get(), aFptr);
+ }
+
+ nsAutoPtr<T>*
+ get_address()
+ // This is not intended to be used by clients. See |address_of|
+ // below.
+ {
+ return this;
+ }
+
+ const nsAutoPtr<T>*
+ get_address() const
+ // This is not intended to be used by clients. See |address_of|
+ // below.
+ {
+ return this;
+ }
+
+public:
+ T&
+ operator*() const
+ {
+ NS_PRECONDITION(mRawPtr != 0,
+ "You can't dereference a NULL nsAutoPtr with operator*().");
+ return *get();
+ }
+
+ T**
+ StartAssignment()
+ {
+#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+ return reinterpret_cast<T**>(begin_assignment());
+#else
+ assign(0);
+ return reinterpret_cast<T**>(&mRawPtr);
+#endif
+ }
+};
+
+template <class T>
+inline nsAutoPtr<T>*
+address_of(nsAutoPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+template <class T>
+inline const nsAutoPtr<T>*
+address_of(const nsAutoPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+template <class T>
+class nsAutoPtrGetterTransfers
+/*
+ ...
+
+ This class is designed to be used for anonymous temporary objects in the
+ argument list of calls that return COM interface pointers, e.g.,
+
+ nsAutoPtr<IFoo> fooP;
+ ...->GetTransferedPointer(getter_Transfers(fooP))
+
+ DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_Transfers()| instead.
+
+ When initialized with a |nsAutoPtr|, as in the example above, it returns
+ a |void**|, a |T**|, or an |nsISupports**| as needed, that the
+ outer call (|GetTransferedPointer| in this case) can fill in.
+
+ This type should be a nested class inside |nsAutoPtr<T>|.
+*/
+{
+public:
+ explicit
+ nsAutoPtrGetterTransfers(nsAutoPtr<T>& aSmartPtr)
+ : mTargetSmartPtr(aSmartPtr)
+ {
+ // nothing else to do
+ }
+
+ operator void**()
+ {
+ return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
+ }
+
+ operator T**()
+ {
+ return mTargetSmartPtr.StartAssignment();
+ }
+
+ T*&
+ operator*()
+ {
+ return *(mTargetSmartPtr.StartAssignment());
+ }
+
+private:
+ nsAutoPtr<T>& mTargetSmartPtr;
+};
+
+template <class T>
+inline nsAutoPtrGetterTransfers<T>
+getter_Transfers(nsAutoPtr<T>& aSmartPtr)
+/*
+ Used around a |nsAutoPtr| when
+ ...makes the class |nsAutoPtrGetterTransfers<T>| invisible.
+*/
+{
+ return nsAutoPtrGetterTransfers<T>(aSmartPtr);
+}
+
+
+
+// Comparing two |nsAutoPtr|s
+
+template <class T, class U>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, const nsAutoPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs.get());
+}
+
+
+template <class T, class U>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, const nsAutoPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs.get());
+}
+
+
+// Comparing an |nsAutoPtr| to a raw pointer
+
+template <class T, class U>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs);
+}
+
+template <class T, class U>
+inline bool
+operator==(const U* aLhs, const nsAutoPtr<T>& aRhs)
+{
+ return static_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
+}
+
+template <class T, class U>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs);
+}
+
+template <class T, class U>
+inline bool
+operator!=(const U* aLhs, const nsAutoPtr<T>& aRhs)
+{
+ return static_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
+}
+
+template <class T, class U>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == const_cast<const U*>(aRhs);
+}
+
+template <class T, class U>
+inline bool
+operator==(U* aLhs, const nsAutoPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
+}
+
+template <class T, class U>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != const_cast<const U*>(aRhs);
+}
+
+template <class T, class U>
+inline bool
+operator!=(U* aLhs, const nsAutoPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
+}
+
+
+
+// Comparing an |nsAutoPtr| to |nullptr|
+
+template <class T>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() == nullptr;
+}
+
+template <class T>
+inline bool
+operator==(decltype(nullptr), const nsAutoPtr<T>& aRhs)
+{
+ return nullptr == aRhs.get();
+}
+
+template <class T>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() != nullptr;
+}
+
+template <class T>
+inline bool
+operator!=(decltype(nullptr), const nsAutoPtr<T>& aRhs)
+{
+ return nullptr != aRhs.get();
+}
+
+
+/*****************************************************************************/
+
+#endif // !defined(nsAutoPtr_h)
diff --git a/xpcom/base/nsAutoRef.h b/xpcom/base/nsAutoRef.h
new file mode 100644
index 000000000..9986c8a56
--- /dev/null
+++ b/xpcom/base/nsAutoRef.h
@@ -0,0 +1,672 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAutoRef_h_
+#define nsAutoRef_h_
+
+#include "mozilla/Attributes.h"
+
+#include "nscore.h" // for nullptr, bool
+
+template <class T> class nsSimpleRef;
+template <class T> class nsAutoRefBase;
+template <class T> class nsReturnRef;
+template <class T> class nsReturningRef;
+
+/**
+ * template <class T> class nsAutoRef
+ *
+ * A class that holds a handle to a resource that must be released.
+ * No reference is added on construction.
+ *
+ * No copy constructor nor copy assignment operators are available, so the
+ * resource will be held until released on destruction or explicitly
+ * |reset()| or transferred through provided methods.
+ *
+ * The publicly available methods are the public methods on this class and its
+ * public base classes |nsAutoRefBase<T>| and |nsSimpleRef<T>|.
+ *
+ * For ref-counted resources see also |nsCountedRef<T>|.
+ * For function return values see |nsReturnRef<T>|.
+ *
+ * For each class |T|, |nsAutoRefTraits<T>| or |nsSimpleRef<T>| must be
+ * specialized to use |nsAutoRef<T>| and |nsCountedRef<T>|.
+ *
+ * @param T A class identifying the type of reference held by the
+ * |nsAutoRef<T>| and the unique set methods for managing references
+ * to the resource (defined by |nsAutoRefTraits<T>| or
+ * |nsSimpleRef<T>|).
+ *
+ * Often this is the class representing the resource. Sometimes a
+ * new possibly-incomplete class may need to be declared.
+ *
+ *
+ * Example: An Automatically closing file descriptor
+ *
+ * // References that are simple integral types (as file-descriptors are)
+ * // usually need a new class to represent the resource and how to handle its
+ * // references.
+ * class nsRawFD;
+ *
+ * // Specializing nsAutoRefTraits<nsRawFD> describes how to manage file
+ * // descriptors, so that nsAutoRef<nsRawFD> provides automatic closing of
+ * // its file descriptor on destruction.
+ * template <>
+ * class nsAutoRefTraits<nsRawFD> {
+ * public:
+ * // The file descriptor is held in an int.
+ * typedef int RawRef;
+ * // -1 means that there is no file associated with the handle.
+ * static int Void() { return -1; }
+ * // The file associated with a file descriptor is released with close().
+ * static void Release(RawRef aFD) { close(aFD); }
+ * };
+ *
+ * // A function returning a file descriptor that must be closed.
+ * nsReturnRef<nsRawFD> get_file(const char *filename) {
+ * // Constructing from a raw file descriptor assumes ownership.
+ * nsAutoRef<nsRawFD> fd(open(filename, O_RDONLY));
+ * fcntl(fd, F_SETFD, FD_CLOEXEC);
+ * return fd.out();
+ * }
+ *
+ * void f() {
+ * unsigned char buf[1024];
+ *
+ * // Hold a file descriptor for /etc/hosts in fd1.
+ * nsAutoRef<nsRawFD> fd1(get_file("/etc/hosts"));
+ *
+ * nsAutoRef<nsRawFD> fd2;
+ * fd2.steal(fd1); // fd2 takes the file descriptor from fd1
+ * ssize_t count = read(fd1, buf, 1024); // error fd1 has no file
+ * count = read(fd2, buf, 1024); // reads from /etc/hosts
+ *
+ * // If the file descriptor is not stored then it is closed.
+ * get_file("/etc/login.defs"); // login.defs is closed
+ *
+ * // Now use fd1 to hold a file descriptor for /etc/passwd.
+ * fd1 = get_file("/etc/passwd");
+ *
+ * // The nsAutoRef<nsRawFD> can give up the file descriptor if explicitly
+ * // instructed, but the caller must then ensure that the file is closed.
+ * int rawfd = fd1.disown();
+ *
+ * // Assume ownership of another file descriptor.
+ * fd1.own(open("/proc/1/maps");
+ *
+ * // On destruction, fd1 closes /proc/1/maps and fd2 closes /etc/hosts,
+ * // but /etc/passwd is not closed.
+ * }
+ *
+ */
+
+
+template <class T>
+class nsAutoRef : public nsAutoRefBase<T>
+{
+protected:
+ typedef nsAutoRef<T> ThisClass;
+ typedef nsAutoRefBase<T> BaseClass;
+ typedef nsSimpleRef<T> SimpleRef;
+ typedef typename BaseClass::RawRefOnly RawRefOnly;
+ typedef typename BaseClass::LocalSimpleRef LocalSimpleRef;
+
+public:
+ nsAutoRef()
+ {
+ }
+
+ // Explicit construction is required so as not to risk unintentionally
+ // releasing the resource associated with a raw ref.
+ explicit nsAutoRef(RawRefOnly aRefToRelease)
+ : BaseClass(aRefToRelease)
+ {
+ }
+
+ // Construction from a nsReturnRef<T> function return value, which expects
+ // to give up ownership, transfers ownership.
+ // (nsReturnRef<T> is converted to const nsReturningRef<T>.)
+ explicit nsAutoRef(const nsReturningRef<T>& aReturning)
+ : BaseClass(aReturning)
+ {
+ }
+
+ // The only assignment operator provided is for transferring from an
+ // nsReturnRef smart reference, which expects to pass its ownership to
+ // another object.
+ //
+ // With raw references and other smart references, the type of the lhs and
+ // its taking and releasing nature is often not obvious from an assignment
+ // statement. Assignment from a raw ptr especially is not normally
+ // expected to release the reference.
+ //
+ // Use |steal| for taking ownership from other smart refs.
+ //
+ // For raw references, use |own| to indicate intention to have the
+ // resource released.
+ //
+ // Or, to create another owner of the same reference, use an nsCountedRef.
+
+ ThisClass& operator=(const nsReturningRef<T>& aReturning)
+ {
+ BaseClass::steal(aReturning.mReturnRef);
+ return *this;
+ }
+
+ // Conversion to a raw reference allow the nsAutoRef<T> to often be used
+ // like a raw reference.
+ operator typename SimpleRef::RawRef() const
+ {
+ return this->get();
+ }
+
+ // Transfer ownership from another smart reference.
+ void steal(ThisClass& aOtherRef)
+ {
+ BaseClass::steal(aOtherRef);
+ }
+
+ // Assume ownership of a raw ref.
+ //
+ // |own| has similar function to |steal|, and is useful for receiving
+ // ownership from a return value of a function. It is named differently
+ // because |own| requires more care to ensure that the function intends to
+ // give away ownership, and so that |steal| can be safely used, knowing
+ // that it won't steal ownership from any methods returning raw ptrs to
+ // data owned by a foreign object.
+ void own(RawRefOnly aRefToRelease)
+ {
+ BaseClass::own(aRefToRelease);
+ }
+
+ // Exchange ownership with |aOther|
+ void swap(ThisClass& aOther)
+ {
+ LocalSimpleRef temp;
+ temp.SimpleRef::operator=(*this);
+ SimpleRef::operator=(aOther);
+ aOther.SimpleRef::operator=(temp);
+ }
+
+ // Release the reference now.
+ void reset()
+ {
+ this->SafeRelease();
+ LocalSimpleRef empty;
+ SimpleRef::operator=(empty);
+ }
+
+ // Pass out the reference for a function return values.
+ nsReturnRef<T> out()
+ {
+ return nsReturnRef<T>(this->disown());
+ }
+
+ // operator->() and disown() are provided by nsAutoRefBase<T>.
+ // The default nsSimpleRef<T> provides get().
+
+private:
+ // No copy constructor
+ explicit nsAutoRef(ThisClass& aRefToSteal);
+};
+
+/**
+ * template <class T> class nsCountedRef
+ *
+ * A class that creates (adds) a new reference to a resource on construction
+ * or assignment and releases on destruction.
+ *
+ * This class is similar to nsAutoRef<T> and inherits its methods, but also
+ * provides copy construction and assignment operators that enable more than
+ * one concurrent reference to the same resource.
+ *
+ * Specialize |nsAutoRefTraits<T>| or |nsSimpleRef<T>| to use this. This
+ * class assumes that the resource itself counts references and so can only be
+ * used when |T| represents a reference-counting resource.
+ */
+
+template <class T>
+class nsCountedRef : public nsAutoRef<T>
+{
+protected:
+ typedef nsCountedRef<T> ThisClass;
+ typedef nsAutoRef<T> BaseClass;
+ typedef nsSimpleRef<T> SimpleRef;
+ typedef typename BaseClass::RawRef RawRef;
+
+public:
+ nsCountedRef()
+ {
+ }
+
+ // Construction and assignment from a another nsCountedRef
+ // or a raw ref copies and increments the ref count.
+ nsCountedRef(const ThisClass& aRefToCopy)
+ {
+ SimpleRef::operator=(aRefToCopy);
+ SafeAddRef();
+ }
+ ThisClass& operator=(const ThisClass& aRefToCopy)
+ {
+ if (this == &aRefToCopy) {
+ return *this;
+ }
+
+ this->SafeRelease();
+ SimpleRef::operator=(aRefToCopy);
+ SafeAddRef();
+ return *this;
+ }
+
+ // Implicit conversion from another smart ref argument (to a raw ref) is
+ // accepted here because construction and assignment safely creates a new
+ // reference without interfering with the reference to copy.
+ explicit nsCountedRef(RawRef aRefToCopy)
+ : BaseClass(aRefToCopy)
+ {
+ SafeAddRef();
+ }
+ ThisClass& operator=(RawRef aRefToCopy)
+ {
+ this->own(aRefToCopy);
+ SafeAddRef();
+ return *this;
+ }
+
+ // Construction and assignment from an nsReturnRef function return value,
+ // which expects to give up ownership, transfers ownership.
+ explicit nsCountedRef(const nsReturningRef<T>& aReturning)
+ : BaseClass(aReturning)
+ {
+ }
+ ThisClass& operator=(const nsReturningRef<T>& aReturning)
+ {
+ BaseClass::operator=(aReturning);
+ return *this;
+ }
+
+protected:
+ // Increase the reference count if there is a resource.
+ void SafeAddRef()
+ {
+ if (this->HaveResource()) {
+ this->AddRef(this->get());
+ }
+ }
+};
+
+/**
+ * template <class T> class nsReturnRef
+ *
+ * A type for function return values that hold a reference to a resource that
+ * must be released. See also |nsAutoRef<T>::out()|.
+ */
+
+template <class T>
+class nsReturnRef : public nsAutoRefBase<T>
+{
+protected:
+ typedef nsAutoRefBase<T> BaseClass;
+ typedef typename BaseClass::RawRefOnly RawRefOnly;
+
+public:
+ // For constructing a return value with no resource
+ nsReturnRef()
+ {
+ }
+
+ // For returning a smart reference from a raw reference that must be
+ // released. Explicit construction is required so as not to risk
+ // unintentionally releasing the resource associated with a raw ref.
+ MOZ_IMPLICIT nsReturnRef(RawRefOnly aRefToRelease)
+ : BaseClass(aRefToRelease)
+ {
+ }
+
+ // Copy construction transfers ownership
+ nsReturnRef(nsReturnRef<T>& aRefToSteal)
+ : BaseClass(aRefToSteal)
+ {
+ }
+
+ MOZ_IMPLICIT nsReturnRef(const nsReturningRef<T>& aReturning)
+ : BaseClass(aReturning)
+ {
+ }
+
+ // Conversion to a temporary (const) object referring to this object so
+ // that the reference may be passed from a function return value
+ // (temporary) to another smart reference. There is no need to use this
+ // explicitly. Simply assign a nsReturnRef<T> function return value to a
+ // smart reference.
+ operator nsReturningRef<T>()
+ {
+ return nsReturningRef<T>(*this);
+ }
+
+ // No conversion to RawRef operator is provided on nsReturnRef, to ensure
+ // that the return value is not carelessly assigned to a raw ptr (and the
+ // resource then released). If passing to a function that takes a raw
+ // ptr, use get or disown as appropriate.
+};
+
+/**
+ * template <class T> class nsReturningRef
+ *
+ * A class to allow ownership to be transferred from nsReturnRef function
+ * return values.
+ *
+ * It should not be necessary for clients to reference this
+ * class directly. Simply pass an nsReturnRef<T> to a parameter taking an
+ * |nsReturningRef<T>|.
+ *
+ * The conversion operator on nsReturnRef constructs a temporary wrapper of
+ * class nsReturningRef<T> around a non-const reference to the nsReturnRef.
+ * The wrapper can then be passed as an rvalue parameter.
+ */
+
+template <class T>
+class nsReturningRef
+{
+private:
+ friend class nsReturnRef<T>;
+
+ explicit nsReturningRef(nsReturnRef<T>& aReturnRef)
+ : mReturnRef(aReturnRef)
+ {
+ }
+public:
+ nsReturnRef<T>& mReturnRef;
+};
+
+/**
+ * template <class T> class nsAutoRefTraits
+ *
+ * A class describing traits of references managed by the default
+ * |nsSimpleRef<T>| implementation and thus |nsAutoRef<T>| and |nsCountedRef|.
+ * The default |nsSimpleRef<T> is suitable for resources with handles that
+ * have a void value. (If there is no such void value for a handle,
+ * specialize |nsSimpleRef<T>|.)
+ *
+ * Specializations must be provided for each class |T| according to the
+ * following pattern:
+ *
+ * // The template parameter |T| should be a class such that the set of fields
+ * // in class nsAutoRefTraits<T> is unique for class |T|. Usually the
+ * // resource object class is sufficient. For handles that are simple
+ * // integral typedefs, a new unique possibly-incomplete class may need to be
+ * // declared.
+ *
+ * template <>
+ * class nsAutoRefTraits<T>
+ * {
+ * // Specializations must provide a typedef for RawRef, describing the
+ * // type of the handle to the resource.
+ * typedef <handle-type> RawRef;
+ *
+ * // Specializations should define Void(), a function returning a value
+ * // suitable for a handle that does not have an associated resource.
+ * //
+ * // The return type must be a suitable as the parameter to a RawRef
+ * // constructor and operator==.
+ * //
+ * // If this method is not accessible then some limited nsAutoRef
+ * // functionality will still be available, but the default constructor,
+ * // |reset|, and most transfer of ownership methods will not be available.
+ * static <return-type> Void();
+ *
+ * // Specializations must define Release() to properly finalize the
+ * // handle to a non-void custom-deleted or reference-counted resource.
+ * static void Release(RawRef aRawRef);
+ *
+ * // For reference-counted resources, if |nsCountedRef<T>| is to be used,
+ * // specializations must define AddRef to increment the reference count
+ * // held by a non-void handle.
+ * // (AddRef() is not necessary for |nsAutoRef<T>|.)
+ * static void AddRef(RawRef aRawRef);
+ * };
+ *
+ * See nsPointerRefTraits for example specializations for simple pointer
+ * references. See nsAutoRef for an example specialization for a non-pointer
+ * reference.
+ */
+
+template <class T> class nsAutoRefTraits;
+
+/**
+ * template <class T> class nsPointerRefTraits
+ *
+ * A convenience class useful as a base class for specializations of
+ * |nsAutoRefTraits<T>| where the handle to the resource is a pointer to |T|.
+ * By inheriting from this class, definitions of only Release(RawRef) and
+ * possibly AddRef(RawRef) need to be added.
+ *
+ * Examples of use:
+ *
+ * template <>
+ * class nsAutoRefTraits<PRFileDesc> : public nsPointerRefTraits<PRFileDesc>
+ * {
+ * public:
+ * static void Release(PRFileDesc *ptr) { PR_Close(ptr); }
+ * };
+ *
+ * template <>
+ * class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
+ * {
+ * public:
+ * static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
+ * static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
+ * };
+ */
+
+template <class T>
+class nsPointerRefTraits
+{
+public:
+ // The handle is a pointer to T.
+ typedef T* RawRef;
+ // A nullptr does not have a resource.
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+};
+
+/**
+ * template <class T> class nsSimpleRef
+ *
+ * Constructs a non-smart reference, and provides methods to test whether
+ * there is an associated resource and (if so) get its raw handle.
+ *
+ * A default implementation is suitable for resources with handles that have a
+ * void value. This is not intended for direct use but used by |nsAutoRef<T>|
+ * and thus |nsCountedRef<T>|.
+ *
+ * Specialize this class if there is no particular void value for the resource
+ * handle. A specialized implementation must also provide Release(RawRef),
+ * and, if |nsCountedRef<T>| is required, AddRef(RawRef), as described in
+ * nsAutoRefTraits<T>.
+ */
+
+template <class T>
+class nsSimpleRef : protected nsAutoRefTraits<T>
+{
+protected:
+ // The default implementation uses nsAutoRefTrait<T>.
+ // Specializations need not define this typedef.
+ typedef nsAutoRefTraits<T> Traits;
+ // The type of the handle to the resource.
+ // A specialization must provide a typedef for RawRef.
+ typedef typename Traits::RawRef RawRef;
+
+ // Construct with no resource.
+ //
+ // If this constructor is not accessible then some limited nsAutoRef
+ // functionality will still be available, but the default constructor,
+ // |reset|, and most transfer of ownership methods will not be available.
+ nsSimpleRef()
+ : mRawRef(Traits::Void())
+ {
+ }
+ // Construct with a handle to a resource.
+ // A specialization must provide this.
+ explicit nsSimpleRef(RawRef aRawRef)
+ : mRawRef(aRawRef)
+ {
+ }
+
+ // Test whether there is an associated resource. A specialization must
+ // provide this. The function is permitted to always return true if the
+ // default constructor is not accessible, or if Release (and AddRef) can
+ // deal with void handles.
+ bool HaveResource() const
+ {
+ return mRawRef != Traits::Void();
+ }
+
+public:
+ // A specialization must provide get() or loose some functionality. This
+ // is inherited by derived classes and the specialization may choose
+ // whether it is public or protected.
+ RawRef get() const
+ {
+ return mRawRef;
+ }
+
+private:
+ RawRef mRawRef;
+};
+
+
+/**
+ * template <class T> class nsAutoRefBase
+ *
+ * Internal base class for |nsAutoRef<T>| and |nsReturnRef<T>|.
+ * Adds release on destruction to a |nsSimpleRef<T>|.
+ */
+
+template <class T>
+class nsAutoRefBase : public nsSimpleRef<T>
+{
+protected:
+ typedef nsAutoRefBase<T> ThisClass;
+ typedef nsSimpleRef<T> SimpleRef;
+ typedef typename SimpleRef::RawRef RawRef;
+
+ nsAutoRefBase()
+ {
+ }
+
+ // A type for parameters that should be passed a raw ref but should not
+ // accept implicit conversions (from another smart ref). (The only
+ // conversion to this type is from a raw ref so only raw refs will be
+ // accepted.)
+ class RawRefOnly
+ {
+ public:
+ MOZ_IMPLICIT RawRefOnly(RawRef aRawRef)
+ : mRawRef(aRawRef)
+ {
+ }
+ operator RawRef() const
+ {
+ return mRawRef;
+ }
+ private:
+ RawRef mRawRef;
+ };
+
+ // Construction from a raw ref assumes ownership
+ explicit nsAutoRefBase(RawRefOnly aRefToRelease)
+ : SimpleRef(aRefToRelease)
+ {
+ }
+
+ // Constructors that steal ownership
+ explicit nsAutoRefBase(ThisClass& aRefToSteal)
+ : SimpleRef(aRefToSteal.disown())
+ {
+ }
+ explicit nsAutoRefBase(const nsReturningRef<T>& aReturning)
+ : SimpleRef(aReturning.mReturnRef.disown())
+ {
+ }
+
+ ~nsAutoRefBase()
+ {
+ SafeRelease();
+ }
+
+ // An internal class providing access to protected nsSimpleRef<T>
+ // constructors for construction of temporary simple references (that are
+ // not ThisClass).
+ class LocalSimpleRef : public SimpleRef
+ {
+ public:
+ LocalSimpleRef()
+ {
+ }
+ explicit LocalSimpleRef(RawRef aRawRef)
+ : SimpleRef(aRawRef)
+ {
+ }
+ };
+
+private:
+ ThisClass& operator=(const ThisClass& aSmartRef) = delete;
+
+public:
+ RawRef operator->() const
+ {
+ return this->get();
+ }
+
+ // Transfer ownership to a raw reference.
+ //
+ // THE CALLER MUST ENSURE THAT THE REFERENCE IS EXPLICITLY RELEASED.
+ //
+ // Is this really what you want to use? Using this removes any guarantee
+ // of release. Use nsAutoRef<T>::out() for return values, or an
+ // nsAutoRef<T> modifiable lvalue for an out parameter. Use disown() when
+ // the reference must be stored in a POD type object, such as may be
+ // preferred for a namespace-scope object with static storage duration,
+ // for example.
+ RawRef disown()
+ {
+ RawRef temp = this->get();
+ LocalSimpleRef empty;
+ SimpleRef::operator=(empty);
+ return temp;
+ }
+
+protected:
+ // steal and own are protected because they make no sense on nsReturnRef,
+ // but steal is implemented on this class for access to aOtherRef.disown()
+ // when aOtherRef is an nsReturnRef;
+
+ // Transfer ownership from another smart reference.
+ void steal(ThisClass& aOtherRef)
+ {
+ own(aOtherRef.disown());
+ }
+ // Assume ownership of a raw ref.
+ void own(RawRefOnly aRefToRelease)
+ {
+ SafeRelease();
+ LocalSimpleRef ref(aRefToRelease);
+ SimpleRef::operator=(ref);
+ }
+
+ // Release a resource if there is one.
+ void SafeRelease()
+ {
+ if (this->HaveResource()) {
+ this->Release(this->get());
+ }
+ }
+};
+
+#endif // !defined(nsAutoRef_h_)
diff --git a/xpcom/base/nsCom.h b/xpcom/base/nsCom.h
new file mode 100644
index 000000000..20353a266
--- /dev/null
+++ b/xpcom/base/nsCom.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCom_h__
+#define nsCom_h__
+#include "nscore.h"
+#endif
+
+
diff --git a/xpcom/base/nsConsoleMessage.cpp b/xpcom/base/nsConsoleMessage.cpp
new file mode 100644
index 000000000..e3530745b
--- /dev/null
+++ b/xpcom/base/nsConsoleMessage.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Base implementation for console messages.
+ */
+
+#include "nsConsoleMessage.h"
+#include "jsapi.h"
+
+NS_IMPL_ISUPPORTS(nsConsoleMessage, nsIConsoleMessage)
+
+nsConsoleMessage::nsConsoleMessage()
+ : mTimeStamp(0)
+ , mMessage()
+{
+}
+
+nsConsoleMessage::nsConsoleMessage(const char16_t* aMessage)
+{
+ mTimeStamp = JS_Now() / 1000;
+ mMessage.Assign(aMessage);
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::GetMessageMoz(char16_t** aResult)
+{
+ *aResult = ToNewUnicode(mMessage);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::GetLogLevel(uint32_t* aLogLevel)
+{
+ *aLogLevel = nsConsoleMessage::info;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::GetTimeStamp(int64_t* aTimeStamp)
+{
+ *aTimeStamp = mTimeStamp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::ToString(nsACString& /*UTF8*/ aResult)
+{
+ CopyUTF16toUTF8(mMessage, aResult);
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsConsoleMessage.h b/xpcom/base/nsConsoleMessage.h
new file mode 100644
index 000000000..64ad921a5
--- /dev/null
+++ b/xpcom/base/nsConsoleMessage.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsconsolemessage_h__
+#define __nsconsolemessage_h__
+
+#include "mozilla/Attributes.h"
+
+#include "nsIConsoleMessage.h"
+#include "nsString.h"
+
+class nsConsoleMessage final : public nsIConsoleMessage
+{
+public:
+ nsConsoleMessage();
+ explicit nsConsoleMessage(const char16_t* aMessage);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSICONSOLEMESSAGE
+
+private:
+ ~nsConsoleMessage()
+ {
+ }
+
+ int64_t mTimeStamp;
+ nsString mMessage;
+};
+
+#endif /* __nsconsolemessage_h__ */
diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp
new file mode 100644
index 000000000..3862a02c4
--- /dev/null
+++ b/xpcom/base/nsConsoleService.cpp
@@ -0,0 +1,473 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Maintains a circular buffer of recent messages, and notifies
+ * listeners when new messages are logged.
+ */
+
+/* Threadsafe. */
+
+#include "nsMemory.h"
+#include "nsCOMArray.h"
+#include "nsThreadUtils.h"
+
+#include "nsConsoleService.h"
+#include "nsConsoleMessage.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIConsoleListener.h"
+#include "nsPrintfCString.h"
+#include "nsProxyRelease.h"
+#include "nsIScriptError.h"
+#include "nsISupportsPrimitives.h"
+
+#include "mozilla/Preferences.h"
+
+#if defined(ANDROID)
+#include <android/log.h>
+#include "mozilla/dom/ContentChild.h"
+#endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+using namespace mozilla::tasktracer;
+#endif
+
+using namespace mozilla;
+
+NS_IMPL_ADDREF(nsConsoleService)
+NS_IMPL_RELEASE(nsConsoleService)
+NS_IMPL_CLASSINFO(nsConsoleService, nullptr,
+ nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
+ NS_CONSOLESERVICE_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService, nsIObserver)
+NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService, nsIObserver)
+
+static bool sLoggingEnabled = true;
+static bool sLoggingBuffered = true;
+#if defined(ANDROID)
+static bool sLoggingLogcat = true;
+#endif // defined(ANDROID)
+
+nsConsoleService::MessageElement::~MessageElement()
+{
+}
+
+nsConsoleService::nsConsoleService()
+ : mCurrentSize(0)
+ , mDeliveringMessage(false)
+ , mLock("nsConsoleService.mLock")
+{
+ // XXX grab this from a pref!
+ // hm, but worry about circularity, bc we want to be able to report
+ // prefs errs...
+ mMaximumSize = 250;
+}
+
+
+void
+nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mLock);
+
+ for (MessageElement* e = mMessages.getFirst(); e != nullptr; ) {
+ // Only messages implementing nsIScriptError interface expose the
+ // inner window ID.
+ nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(e->Get());
+ if (!scriptError) {
+ e = e->getNext();
+ continue;
+ }
+ uint64_t innerWindowID;
+ nsresult rv = scriptError->GetInnerWindowID(&innerWindowID);
+ if (NS_FAILED(rv) || innerWindowID != innerID) {
+ e = e->getNext();
+ continue;
+ }
+
+ MessageElement* next = e->getNext();
+ e->remove();
+ delete e;
+ mCurrentSize--;
+ MOZ_ASSERT(mCurrentSize < mMaximumSize);
+
+ e = next;
+ }
+}
+
+void
+nsConsoleService::ClearMessages()
+{
+ // NB: A lock is not required here as it's only called from |Reset| which
+ // locks for us and from the dtor.
+ while (!mMessages.isEmpty()) {
+ MessageElement* e = mMessages.popFirst();
+ delete e;
+ }
+ mCurrentSize = 0;
+}
+
+nsConsoleService::~nsConsoleService()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ ClearMessages();
+}
+
+class AddConsolePrefWatchers : public Runnable
+{
+public:
+ explicit AddConsolePrefWatchers(nsConsoleService* aConsole) : mConsole(aConsole)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ Preferences::AddBoolVarCache(&sLoggingEnabled, "consoleservice.enabled", true);
+ Preferences::AddBoolVarCache(&sLoggingBuffered, "consoleservice.buffered", true);
+#if defined(ANDROID)
+ Preferences::AddBoolVarCache(&sLoggingLogcat, "consoleservice.logcat", true);
+#endif // defined(ANDROID)
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obs);
+ obs->AddObserver(mConsole, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ obs->AddObserver(mConsole, "inner-window-destroyed", false);
+
+ if (!sLoggingBuffered) {
+ mConsole->Reset();
+ }
+ return NS_OK;
+ }
+
+private:
+ RefPtr<nsConsoleService> mConsole;
+};
+
+nsresult
+nsConsoleService::Init()
+{
+ NS_DispatchToMainThread(new AddConsolePrefWatchers(this));
+
+ return NS_OK;
+}
+
+namespace {
+
+class LogMessageRunnable : public Runnable
+{
+public:
+ LogMessageRunnable(nsIConsoleMessage* aMessage, nsConsoleService* aService)
+ : mMessage(aMessage)
+ , mService(aService)
+ { }
+
+ NS_DECL_NSIRUNNABLE
+
+private:
+ nsCOMPtr<nsIConsoleMessage> mMessage;
+ RefPtr<nsConsoleService> mService;
+};
+
+NS_IMETHODIMP
+LogMessageRunnable::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Snapshot of listeners so that we don't reenter this hash during
+ // enumeration.
+ nsCOMArray<nsIConsoleListener> listeners;
+ mService->CollectCurrentListeners(listeners);
+
+ mService->SetIsDelivering();
+
+ for (int32_t i = 0; i < listeners.Count(); ++i) {
+ listeners[i]->Observe(mMessage);
+ }
+
+ mService->SetDoneDelivering();
+
+ return NS_OK;
+}
+
+} // namespace
+
+// nsIConsoleService methods
+NS_IMETHODIMP
+nsConsoleService::LogMessage(nsIConsoleMessage* aMessage)
+{
+ return LogMessageWithMode(aMessage, OutputToLog);
+}
+
+// This can be called off the main thread.
+nsresult
+nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage,
+ nsConsoleService::OutputMode aOutputMode)
+{
+ if (!aMessage) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (!sLoggingEnabled) {
+ return NS_OK;
+ }
+
+ if (NS_IsMainThread() && mDeliveringMessage) {
+ nsCString msg;
+ aMessage->ToString(msg);
+ NS_WARNING(nsPrintfCString("Reentrancy error: some client attempted "
+ "to display a message to the console while in a console listener. "
+ "The following message was discarded: \"%s\"", msg.get()).get());
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<LogMessageRunnable> r;
+ nsCOMPtr<nsIConsoleMessage> retiredMessage;
+
+ /*
+ * Lock while updating buffer, and while taking snapshot of
+ * listeners array.
+ */
+ {
+ MutexAutoLock lock(mLock);
+
+#if defined(ANDROID)
+ if (sLoggingLogcat && aOutputMode == OutputToLog) {
+ nsCString msg;
+ aMessage->ToString(msg);
+
+ /** Attempt to use the process name as the log tag. */
+ mozilla::dom::ContentChild* child =
+ mozilla::dom::ContentChild::GetSingleton();
+ nsCString appName;
+ if (child) {
+ child->GetProcessName(appName);
+ } else {
+ appName = "GeckoConsole";
+ }
+
+ uint32_t logLevel = 0;
+ aMessage->GetLogLevel(&logLevel);
+
+ android_LogPriority logPriority = ANDROID_LOG_INFO;
+ switch (logLevel) {
+ case nsIConsoleMessage::debug:
+ logPriority = ANDROID_LOG_DEBUG;
+ break;
+ case nsIConsoleMessage::info:
+ logPriority = ANDROID_LOG_INFO;
+ break;
+ case nsIConsoleMessage::warn:
+ logPriority = ANDROID_LOG_WARN;
+ break;
+ case nsIConsoleMessage::error:
+ logPriority = ANDROID_LOG_ERROR;
+ break;
+ }
+
+ __android_log_print(logPriority, appName.get(), "%s", msg.get());
+ }
+#endif
+#ifdef XP_WIN
+ if (IsDebuggerPresent()) {
+ nsString msg;
+ aMessage->GetMessageMoz(getter_Copies(msg));
+ msg.Append('\n');
+ OutputDebugStringW(msg.get());
+ }
+#endif
+#ifdef MOZ_TASK_TRACER
+ {
+ nsCString msg;
+ aMessage->ToString(msg);
+ int prefixPos = msg.Find(GetJSLabelPrefix());
+ if (prefixPos >= 0) {
+ nsDependentCSubstring submsg(msg, prefixPos);
+ AddLabel("%s", submsg.BeginReading());
+ }
+ }
+#endif
+
+ if (sLoggingBuffered) {
+ MessageElement* e = new MessageElement(aMessage);
+ mMessages.insertBack(e);
+ if (mCurrentSize != mMaximumSize) {
+ mCurrentSize++;
+ } else {
+ MessageElement* p = mMessages.popFirst();
+ MOZ_ASSERT(p);
+ p->swapMessage(retiredMessage);
+ delete p;
+ }
+ }
+
+ if (mListeners.Count() > 0) {
+ r = new LogMessageRunnable(aMessage, this);
+ }
+ }
+
+ if (retiredMessage) {
+ // Release |retiredMessage| on the main thread in case it is an instance of
+ // a mainthread-only class like nsScriptErrorWithStack and we're off the
+ // main thread.
+ NS_ReleaseOnMainThread(retiredMessage.forget());
+ }
+
+ if (r) {
+ // avoid failing in XPCShell tests
+ nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+ if (mainThread) {
+ NS_DispatchToMainThread(r.forget());
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsConsoleService::CollectCurrentListeners(
+ nsCOMArray<nsIConsoleListener>& aListeners)
+{
+ MutexAutoLock lock(mLock);
+ for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
+ nsIConsoleListener* value = iter.UserData();
+ aListeners.AppendObject(value);
+ }
+}
+
+NS_IMETHODIMP
+nsConsoleService::LogStringMessage(const char16_t* aMessage)
+{
+ if (!sLoggingEnabled) {
+ return NS_OK;
+ }
+
+ RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
+ return this->LogMessage(msg);
+}
+
+NS_IMETHODIMP
+nsConsoleService::GetMessageArray(uint32_t* aCount,
+ nsIConsoleMessage*** aMessages)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MutexAutoLock lock(mLock);
+
+ if (mMessages.isEmpty()) {
+ /*
+ * Make a 1-length output array so that nobody gets confused,
+ * and return a count of 0. This should result in a 0-length
+ * array object when called from script.
+ */
+ nsIConsoleMessage** messageArray = (nsIConsoleMessage**)
+ moz_xmalloc(sizeof(nsIConsoleMessage*));
+ *messageArray = nullptr;
+ *aMessages = messageArray;
+ *aCount = 0;
+
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mCurrentSize <= mMaximumSize);
+ nsIConsoleMessage** messageArray =
+ static_cast<nsIConsoleMessage**>(moz_xmalloc(sizeof(nsIConsoleMessage*)
+ * mCurrentSize));
+
+ uint32_t i = 0;
+ for (MessageElement* e = mMessages.getFirst(); e != nullptr; e = e->getNext()) {
+ nsCOMPtr<nsIConsoleMessage> m = e->Get();
+ m.forget(&messageArray[i]);
+ i++;
+ }
+
+ MOZ_ASSERT(i == mCurrentSize);
+
+ *aCount = i;
+ *aMessages = messageArray;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::RegisterListener(nsIConsoleListener* aListener)
+{
+ if (!NS_IsMainThread()) {
+ NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
+
+ MutexAutoLock lock(mLock);
+ if (mListeners.GetWeak(canonical)) {
+ // Reregistering a listener isn't good
+ return NS_ERROR_FAILURE;
+ }
+ mListeners.Put(canonical, aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::UnregisterListener(nsIConsoleListener* aListener)
+{
+ if (!NS_IsMainThread()) {
+ NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
+
+ MutexAutoLock lock(mLock);
+
+ if (!mListeners.GetWeak(canonical)) {
+ // Unregistering a listener that was never registered?
+ return NS_ERROR_FAILURE;
+ }
+ mListeners.Remove(canonical);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::Reset()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ /*
+ * Make sure nobody trips into the buffer while it's being reset
+ */
+ MutexAutoLock lock(mLock);
+
+ ClearMessages();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ // Dump all our messages, in case any are cycle collected.
+ Reset();
+ // We could remove ourselves from the observer service, but it is about to
+ // drop all observers anyways, so why bother.
+ } else if (!strcmp(aTopic, "inner-window-destroyed")) {
+ nsCOMPtr<nsISupportsPRUint64> supportsInt = do_QueryInterface(aSubject);
+ MOZ_ASSERT(supportsInt);
+
+ uint64_t windowId;
+ MOZ_ALWAYS_SUCCEEDS(supportsInt->GetData(&windowId));
+
+ ClearMessagesForWindowID(windowId);
+ } else {
+ MOZ_CRASH();
+ }
+ return NS_OK;
+}
diff --git a/xpcom/base/nsConsoleService.h b/xpcom/base/nsConsoleService.h
new file mode 100644
index 000000000..089de8106
--- /dev/null
+++ b/xpcom/base/nsConsoleService.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * nsConsoleService class declaration.
+ */
+
+#ifndef __nsconsoleservice_h__
+#define __nsconsoleservice_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+
+#include "nsInterfaceHashtable.h"
+#include "nsHashKeys.h"
+
+#include "nsIConsoleService.h"
+
+class nsConsoleService final : public nsIConsoleService,
+ public nsIObserver
+{
+public:
+ nsConsoleService();
+ nsresult Init();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSICONSOLESERVICE
+ NS_DECL_NSIOBSERVER
+
+ void SetIsDelivering()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mDeliveringMessage);
+ mDeliveringMessage = true;
+ }
+
+ void SetDoneDelivering()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mDeliveringMessage);
+ mDeliveringMessage = false;
+ }
+
+ // This is a variant of LogMessage which allows the caller to determine
+ // if the message should be output to an OS-specific log. This is used on
+ // B2G to control whether the message is logged to the android log or not.
+
+ enum OutputMode {
+ SuppressLog,
+ OutputToLog
+ };
+ virtual nsresult LogMessageWithMode(nsIConsoleMessage* aMessage,
+ OutputMode aOutputMode);
+
+ typedef nsInterfaceHashtable<nsISupportsHashKey,
+ nsIConsoleListener> ListenerHash;
+ void CollectCurrentListeners(nsCOMArray<nsIConsoleListener>& aListeners);
+
+private:
+ class MessageElement : public mozilla::LinkedListElement<MessageElement>
+ {
+ public:
+ explicit MessageElement(nsIConsoleMessage* aMessage) : mMessage(aMessage)
+ {}
+
+ nsIConsoleMessage* Get()
+ {
+ return mMessage.get();
+ }
+
+ // Swap directly into an nsCOMPtr to avoid spurious refcount
+ // traffic off the main thread in debug builds from
+ // NSCAP_ASSERT_NO_QUERY_NEEDED().
+ void swapMessage(nsCOMPtr<nsIConsoleMessage>& aRetVal)
+ {
+ mMessage.swap(aRetVal);
+ }
+
+ ~MessageElement();
+
+ private:
+ nsCOMPtr<nsIConsoleMessage> mMessage;
+
+ MessageElement(const MessageElement&) = delete;
+ MessageElement& operator=(const MessageElement&) = delete;
+ MessageElement(MessageElement&&) = delete;
+ MessageElement& operator=(MessageElement&&) = delete;
+ };
+
+ ~nsConsoleService();
+
+ void ClearMessagesForWindowID(const uint64_t innerID);
+ void ClearMessages();
+
+ mozilla::LinkedList<MessageElement> mMessages;
+
+ // The current size of mMessages.
+ uint32_t mCurrentSize;
+
+ // The maximum size of mMessages.
+ uint32_t mMaximumSize;
+
+ // Are we currently delivering a console message on the main thread? If
+ // so, we suppress incoming messages on the main thread only, to avoid
+ // infinite repitition.
+ bool mDeliveringMessage;
+
+ // Listeners to notify whenever a new message is logged.
+ ListenerHash mListeners;
+
+ // To serialize interesting methods.
+ mozilla::Mutex mLock;
+};
+
+#endif /* __nsconsoleservice_h__ */
diff --git a/xpcom/base/nsCrashOnException.cpp b/xpcom/base/nsCrashOnException.cpp
new file mode 100644
index 000000000..0f8042531
--- /dev/null
+++ b/xpcom/base/nsCrashOnException.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCrashOnException.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsICrashReporter.h"
+#endif
+
+namespace mozilla {
+
+static int
+ReportException(EXCEPTION_POINTERS* aExceptionInfo)
+{
+#ifdef MOZ_CRASHREPORTER
+ nsCOMPtr<nsICrashReporter> cr =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (cr) {
+ cr->WriteMinidumpForException(aExceptionInfo);
+ }
+#endif
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+XPCOM_API(LRESULT)
+CallWindowProcCrashProtected(WNDPROC aWndProc, HWND aHWnd, UINT aMsg,
+ WPARAM aWParam, LPARAM aLParam)
+{
+ MOZ_SEH_TRY {
+ return aWndProc(aHWnd, aMsg, aWParam, aLParam);
+ }
+ MOZ_SEH_EXCEPT(ReportException(GetExceptionInformation())) {
+ ::TerminateProcess(::GetCurrentProcess(), 253);
+ }
+ return 0; // not reached
+}
+
+}
+
diff --git a/xpcom/base/nsCrashOnException.h b/xpcom/base/nsCrashOnException.h
new file mode 100644
index 000000000..cdf3fdf09
--- /dev/null
+++ b/xpcom/base/nsCrashOnException.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCrashOnException_h
+#define nsCrashOnException_h
+
+#include <nscore.h>
+#include <windows.h>
+
+namespace mozilla {
+
+// Call a given window procedure, and catch any Win32 exceptions raised from it,
+// and report them as crashes.
+XPCOM_API(LRESULT) CallWindowProcCrashProtected(WNDPROC aWndProc, HWND aHWnd,
+ UINT aMsg, WPARAM aWParam,
+ LPARAM aLParam);
+
+}
+
+#endif
diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp
new file mode 100644
index 000000000..ca7057628
--- /dev/null
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -0,0 +1,4213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// This file implements a garbage-cycle collector based on the paper
+//
+// Concurrent Cycle Collection in Reference Counted Systems
+// Bacon & Rajan (2001), ECOOP 2001 / Springer LNCS vol 2072
+//
+// We are not using the concurrent or acyclic cases of that paper; so
+// the green, red and orange colors are not used.
+//
+// The collector is based on tracking pointers of four colors:
+//
+// Black nodes are definitely live. If we ever determine a node is
+// black, it's ok to forget about, drop from our records.
+//
+// White nodes are definitely garbage cycles. Once we finish with our
+// scanning, we unlink all the white nodes and expect that by
+// unlinking them they will self-destruct (since a garbage cycle is
+// only keeping itself alive with internal links, by definition).
+//
+// Snow-white is an addition to the original algorithm. Snow-white object
+// has reference count zero and is just waiting for deletion.
+//
+// Grey nodes are being scanned. Nodes that turn grey will turn
+// either black if we determine that they're live, or white if we
+// determine that they're a garbage cycle. After the main collection
+// algorithm there should be no grey nodes.
+//
+// Purple nodes are *candidates* for being scanned. They are nodes we
+// haven't begun scanning yet because they're not old enough, or we're
+// still partway through the algorithm.
+//
+// XPCOM objects participating in garbage-cycle collection are obliged
+// to inform us when they ought to turn purple; that is, when their
+// refcount transitions from N+1 -> N, for nonzero N. Furthermore we
+// require that *after* an XPCOM object has informed us of turning
+// purple, they will tell us when they either transition back to being
+// black (incremented refcount) or are ultimately deleted.
+
+// Incremental cycle collection
+//
+// Beyond the simple state machine required to implement incremental
+// collection, the CC needs to be able to compensate for things the browser
+// is doing during the collection. There are two kinds of problems. For each
+// of these, there are two cases to deal with: purple-buffered C++ objects
+// and JS objects.
+
+// The first problem is that an object in the CC's graph can become garbage.
+// This is bad because the CC touches the objects in its graph at every
+// stage of its operation.
+//
+// All cycle collected C++ objects that die during a cycle collection
+// will end up actually getting deleted by the SnowWhiteKiller. Before
+// the SWK deletes an object, it checks if an ICC is running, and if so,
+// if the object is in the graph. If it is, the CC clears mPointer and
+// mParticipant so it does not point to the raw object any more. Because
+// objects could die any time the CC returns to the mutator, any time the CC
+// accesses a PtrInfo it must perform a null check on mParticipant to
+// ensure the object has not gone away.
+//
+// JS objects don't always run finalizers, so the CC can't remove them from
+// the graph when they die. Fortunately, JS objects can only die during a GC,
+// so if a GC is begun during an ICC, the browser synchronously finishes off
+// the ICC, which clears the entire CC graph. If the GC and CC are scheduled
+// properly, this should be rare.
+//
+// The second problem is that objects in the graph can be changed, say by
+// being addrefed or released, or by having a field updated, after the object
+// has been added to the graph. The problem is that ICC can miss a newly
+// created reference to an object, and end up unlinking an object that is
+// actually alive.
+//
+// The basic idea of the solution, from "An on-the-fly Reference Counting
+// Garbage Collector for Java" by Levanoni and Petrank, is to notice if an
+// object has had an additional reference to it created during the collection,
+// and if so, don't collect it during the current collection. This avoids having
+// to rerun the scan as in Bacon & Rajan 2001.
+//
+// For cycle collected C++ objects, we modify AddRef to place the object in
+// the purple buffer, in addition to Release. Then, in the CC, we treat any
+// objects in the purple buffer as being alive, after graph building has
+// completed. Because they are in the purple buffer, they will be suspected
+// in the next CC, so there's no danger of leaks. This is imprecise, because
+// we will treat as live an object that has been Released but not AddRefed
+// during graph building, but that's probably rare enough that the additional
+// bookkeeping overhead is not worthwhile.
+//
+// For JS objects, the cycle collector is only looking at gray objects. If a
+// gray object is touched during ICC, it will be made black by UnmarkGray.
+// Thus, if a JS object has become black during the ICC, we treat it as live.
+// Merged JS zones have to be handled specially: we scan all zone globals.
+// If any are black, we treat the zone as being black.
+
+
+// Safety
+//
+// An XPCOM object is either scan-safe or scan-unsafe, purple-safe or
+// purple-unsafe.
+//
+// An nsISupports object is scan-safe if:
+//
+// - It can be QI'ed to |nsXPCOMCycleCollectionParticipant|, though
+// this operation loses ISupports identity (like nsIClassInfo).
+// - Additionally, the operation |traverse| on the resulting
+// nsXPCOMCycleCollectionParticipant does not cause *any* refcount
+// adjustment to occur (no AddRef / Release calls).
+//
+// A non-nsISupports ("native") object is scan-safe by explicitly
+// providing its nsCycleCollectionParticipant.
+//
+// An object is purple-safe if it satisfies the following properties:
+//
+// - The object is scan-safe.
+//
+// When we receive a pointer |ptr| via
+// |nsCycleCollector::suspect(ptr)|, we assume it is purple-safe. We
+// can check the scan-safety, but have no way to ensure the
+// purple-safety; objects must obey, or else the entire system falls
+// apart. Don't involve an object in this scheme if you can't
+// guarantee its purple-safety. The easiest way to ensure that an
+// object is purple-safe is to use nsCycleCollectingAutoRefCnt.
+//
+// When we have a scannable set of purple nodes ready, we begin
+// our walks. During the walks, the nodes we |traverse| should only
+// feed us more scan-safe nodes, and should not adjust the refcounts
+// of those nodes.
+//
+// We do not |AddRef| or |Release| any objects during scanning. We
+// rely on the purple-safety of the roots that call |suspect| to
+// hold, such that we will clear the pointer from the purple buffer
+// entry to the object before it is destroyed. The pointers that are
+// merely scan-safe we hold only for the duration of scanning, and
+// there should be no objects released from the scan-safe set during
+// the scan.
+//
+// We *do* call |Root| and |Unroot| on every white object, on
+// either side of the calls to |Unlink|. This keeps the set of white
+// objects alive during the unlinking.
+//
+
+#if !defined(__MINGW32__)
+#ifdef WIN32
+#include <crtdbg.h>
+#include <errno.h>
+#endif
+#endif
+
+#include "base/process_util.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/HoldDropJSObjects.h"
+/* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
+#include "mozilla/LinkedList.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/SegmentedVector.h"
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsCycleCollectionNoteRootCallback.h"
+#include "nsDeque.h"
+#include "nsCycleCollector.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "prenv.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
+#include "nsIConsoleService.h"
+#include "mozilla/Attributes.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIMemoryReporter.h"
+#include "nsIFile.h"
+#include "nsDumpUtils.h"
+#include "xpcpublic.h"
+#include "GeckoProfiler.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mozilla/AutoGlobalTimelineMarker.h"
+#include "mozilla/Likely.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/ThreadLocal.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+using namespace mozilla;
+
+//#define COLLECT_TIME_DEBUG
+
+// Enable assertions that are useful for diagnosing errors in graph construction.
+//#define DEBUG_CC_GRAPH
+
+#define DEFAULT_SHUTDOWN_COLLECTIONS 5
+
+// One to do the freeing, then another to detect there is no more work to do.
+#define NORMAL_SHUTDOWN_COLLECTIONS 2
+
+// Cycle collector environment variables
+//
+// MOZ_CC_LOG_ALL: If defined, always log cycle collector heaps.
+//
+// MOZ_CC_LOG_SHUTDOWN: If defined, log cycle collector heaps at shutdown.
+//
+// MOZ_CC_LOG_THREAD: If set to "main", only automatically log main thread
+// CCs. If set to "worker", only automatically log worker CCs. If set to "all",
+// log either. The default value is "all". This must be used with either
+// MOZ_CC_LOG_ALL or MOZ_CC_LOG_SHUTDOWN for it to do anything.
+//
+// MOZ_CC_LOG_PROCESS: If set to "main", only automatically log main process
+// CCs. If set to "content", only automatically log tab CCs. If set to
+// "plugins", only automatically log plugin CCs. If set to "all", log
+// everything. The default value is "all". This must be used with either
+// MOZ_CC_LOG_ALL or MOZ_CC_LOG_SHUTDOWN for it to do anything.
+//
+// MOZ_CC_ALL_TRACES: If set to "all", any cycle collector
+// logging done will be WantAllTraces, which disables
+// various cycle collector optimizations to give a fuller picture of
+// the heap. If set to "shutdown", only shutdown logging will be WantAllTraces.
+// The default is none.
+//
+// MOZ_CC_RUN_DURING_SHUTDOWN: In non-DEBUG or builds, if this is set,
+// run cycle collections at shutdown.
+//
+// MOZ_CC_LOG_DIRECTORY: The directory in which logs are placed (such as
+// logs from MOZ_CC_LOG_ALL and MOZ_CC_LOG_SHUTDOWN, or other uses
+// of nsICycleCollectorListener)
+
+// Various parameters of this collector can be tuned using environment
+// variables.
+
+struct nsCycleCollectorParams
+{
+ bool mLogAll;
+ bool mLogShutdown;
+ bool mAllTracesAll;
+ bool mAllTracesShutdown;
+ bool mLogThisThread;
+
+ nsCycleCollectorParams() :
+ mLogAll(PR_GetEnv("MOZ_CC_LOG_ALL") != nullptr),
+ mLogShutdown(PR_GetEnv("MOZ_CC_LOG_SHUTDOWN") != nullptr),
+ mAllTracesAll(false),
+ mAllTracesShutdown(false)
+ {
+ const char* logThreadEnv = PR_GetEnv("MOZ_CC_LOG_THREAD");
+ bool threadLogging = true;
+ if (logThreadEnv && !!strcmp(logThreadEnv, "all")) {
+ if (NS_IsMainThread()) {
+ threadLogging = !strcmp(logThreadEnv, "main");
+ } else {
+ threadLogging = !strcmp(logThreadEnv, "worker");
+ }
+ }
+
+ const char* logProcessEnv = PR_GetEnv("MOZ_CC_LOG_PROCESS");
+ bool processLogging = true;
+ if (logProcessEnv && !!strcmp(logProcessEnv, "all")) {
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Default:
+ processLogging = !strcmp(logProcessEnv, "main");
+ break;
+ case GeckoProcessType_Plugin:
+ processLogging = !strcmp(logProcessEnv, "plugins");
+ break;
+ case GeckoProcessType_Content:
+ processLogging = !strcmp(logProcessEnv, "content");
+ break;
+ default:
+ processLogging = false;
+ break;
+ }
+ }
+ mLogThisThread = threadLogging && processLogging;
+
+ const char* allTracesEnv = PR_GetEnv("MOZ_CC_ALL_TRACES");
+ if (allTracesEnv) {
+ if (!strcmp(allTracesEnv, "all")) {
+ mAllTracesAll = true;
+ } else if (!strcmp(allTracesEnv, "shutdown")) {
+ mAllTracesShutdown = true;
+ }
+ }
+ }
+
+ bool LogThisCC(bool aIsShutdown)
+ {
+ return (mLogAll || (aIsShutdown && mLogShutdown)) && mLogThisThread;
+ }
+
+ bool AllTracesThisCC(bool aIsShutdown)
+ {
+ return mAllTracesAll || (aIsShutdown && mAllTracesShutdown);
+ }
+};
+
+#ifdef COLLECT_TIME_DEBUG
+class TimeLog
+{
+public:
+ TimeLog() : mLastCheckpoint(TimeStamp::Now())
+ {
+ }
+
+ void
+ Checkpoint(const char* aEvent)
+ {
+ TimeStamp now = TimeStamp::Now();
+ double dur = (now - mLastCheckpoint).ToMilliseconds();
+ if (dur >= 0.5) {
+ printf("cc: %s took %.1fms\n", aEvent, dur);
+ }
+ mLastCheckpoint = now;
+ }
+
+private:
+ TimeStamp mLastCheckpoint;
+};
+#else
+class TimeLog
+{
+public:
+ TimeLog()
+ {
+ }
+ void Checkpoint(const char* aEvent)
+ {
+ }
+};
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// Base types
+////////////////////////////////////////////////////////////////////////
+
+struct PtrInfo;
+
+class EdgePool
+{
+public:
+ // EdgePool allocates arrays of void*, primarily to hold PtrInfo*.
+ // However, at the end of a block, the last two pointers are a null
+ // and then a void** pointing to the next block. This allows
+ // EdgePool::Iterators to be a single word but still capable of crossing
+ // block boundaries.
+
+ EdgePool()
+ {
+ mSentinelAndBlocks[0].block = nullptr;
+ mSentinelAndBlocks[1].block = nullptr;
+ }
+
+ ~EdgePool()
+ {
+ MOZ_ASSERT(!mSentinelAndBlocks[0].block &&
+ !mSentinelAndBlocks[1].block,
+ "Didn't call Clear()?");
+ }
+
+ void Clear()
+ {
+ EdgeBlock* b = EdgeBlocks();
+ while (b) {
+ EdgeBlock* next = b->Next();
+ delete b;
+ b = next;
+ }
+
+ mSentinelAndBlocks[0].block = nullptr;
+ mSentinelAndBlocks[1].block = nullptr;
+ }
+
+#ifdef DEBUG
+ bool IsEmpty()
+ {
+ return !mSentinelAndBlocks[0].block &&
+ !mSentinelAndBlocks[1].block;
+ }
+#endif
+
+private:
+ struct EdgeBlock;
+ union PtrInfoOrBlock
+ {
+ // Use a union to avoid reinterpret_cast and the ensuing
+ // potential aliasing bugs.
+ PtrInfo* ptrInfo;
+ EdgeBlock* block;
+ };
+ struct EdgeBlock
+ {
+ enum { EdgeBlockSize = 16 * 1024 };
+
+ PtrInfoOrBlock mPointers[EdgeBlockSize];
+ EdgeBlock()
+ {
+ mPointers[EdgeBlockSize - 2].block = nullptr; // sentinel
+ mPointers[EdgeBlockSize - 1].block = nullptr; // next block pointer
+ }
+ EdgeBlock*& Next()
+ {
+ return mPointers[EdgeBlockSize - 1].block;
+ }
+ PtrInfoOrBlock* Start()
+ {
+ return &mPointers[0];
+ }
+ PtrInfoOrBlock* End()
+ {
+ return &mPointers[EdgeBlockSize - 2];
+ }
+ };
+
+ // Store the null sentinel so that we can have valid iterators
+ // before adding any edges and without adding any blocks.
+ PtrInfoOrBlock mSentinelAndBlocks[2];
+
+ EdgeBlock*& EdgeBlocks()
+ {
+ return mSentinelAndBlocks[1].block;
+ }
+ EdgeBlock* EdgeBlocks() const
+ {
+ return mSentinelAndBlocks[1].block;
+ }
+
+public:
+ class Iterator
+ {
+ public:
+ Iterator() : mPointer(nullptr) {}
+ explicit Iterator(PtrInfoOrBlock* aPointer) : mPointer(aPointer) {}
+ Iterator(const Iterator& aOther) : mPointer(aOther.mPointer) {}
+
+ Iterator& operator++()
+ {
+ if (!mPointer->ptrInfo) {
+ // Null pointer is a sentinel for link to the next block.
+ mPointer = (mPointer + 1)->block->mPointers;
+ }
+ ++mPointer;
+ return *this;
+ }
+
+ PtrInfo* operator*() const
+ {
+ if (!mPointer->ptrInfo) {
+ // Null pointer is a sentinel for link to the next block.
+ return (mPointer + 1)->block->mPointers->ptrInfo;
+ }
+ return mPointer->ptrInfo;
+ }
+ bool operator==(const Iterator& aOther) const
+ {
+ return mPointer == aOther.mPointer;
+ }
+ bool operator!=(const Iterator& aOther) const
+ {
+ return mPointer != aOther.mPointer;
+ }
+
+#ifdef DEBUG_CC_GRAPH
+ bool Initialized() const
+ {
+ return mPointer != nullptr;
+ }
+#endif
+
+ private:
+ PtrInfoOrBlock* mPointer;
+ };
+
+ class Builder;
+ friend class Builder;
+ class Builder
+ {
+ public:
+ explicit Builder(EdgePool& aPool)
+ : mCurrent(&aPool.mSentinelAndBlocks[0])
+ , mBlockEnd(&aPool.mSentinelAndBlocks[0])
+ , mNextBlockPtr(&aPool.EdgeBlocks())
+ {
+ }
+
+ Iterator Mark()
+ {
+ return Iterator(mCurrent);
+ }
+
+ void Add(PtrInfo* aEdge)
+ {
+ if (mCurrent == mBlockEnd) {
+ EdgeBlock* b = new EdgeBlock();
+ *mNextBlockPtr = b;
+ mCurrent = b->Start();
+ mBlockEnd = b->End();
+ mNextBlockPtr = &b->Next();
+ }
+ (mCurrent++)->ptrInfo = aEdge;
+ }
+ private:
+ // mBlockEnd points to space for null sentinel
+ PtrInfoOrBlock* mCurrent;
+ PtrInfoOrBlock* mBlockEnd;
+ EdgeBlock** mNextBlockPtr;
+ };
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = 0;
+ EdgeBlock* b = EdgeBlocks();
+ while (b) {
+ n += aMallocSizeOf(b);
+ b = b->Next();
+ }
+ return n;
+ }
+};
+
+#ifdef DEBUG_CC_GRAPH
+#define CC_GRAPH_ASSERT(b) MOZ_ASSERT(b)
+#else
+#define CC_GRAPH_ASSERT(b)
+#endif
+
+#define CC_TELEMETRY(_name, _value) \
+ PR_BEGIN_MACRO \
+ if (NS_IsMainThread()) { \
+ Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR##_name, _value); \
+ } else { \
+ Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_WORKER##_name, _value); \
+ } \
+ PR_END_MACRO
+
+enum NodeColor { black, white, grey };
+
+// This structure should be kept as small as possible; we may expect
+// hundreds of thousands of them to be allocated and touched
+// repeatedly during each cycle collection.
+
+struct PtrInfo
+{
+ void* mPointer;
+ nsCycleCollectionParticipant* mParticipant;
+ uint32_t mColor : 2;
+ uint32_t mInternalRefs : 30;
+ uint32_t mRefCount;
+private:
+ EdgePool::Iterator mFirstChild;
+
+ static const uint32_t kInitialRefCount = UINT32_MAX - 1;
+
+public:
+
+ PtrInfo(void* aPointer, nsCycleCollectionParticipant* aParticipant)
+ : mPointer(aPointer),
+ mParticipant(aParticipant),
+ mColor(grey),
+ mInternalRefs(0),
+ mRefCount(kInitialRefCount),
+ mFirstChild()
+ {
+ MOZ_ASSERT(aParticipant);
+
+ // We initialize mRefCount to a large non-zero value so
+ // that it doesn't look like a JS object to the cycle collector
+ // in the case where the object dies before being traversed.
+ MOZ_ASSERT(!IsGrayJS() && !IsBlackJS());
+ }
+
+ // Allow NodePool::NodeBlock's constructor to compile.
+ PtrInfo()
+ {
+ NS_NOTREACHED("should never be called");
+ }
+
+ bool IsGrayJS() const
+ {
+ return mRefCount == 0;
+ }
+
+ bool IsBlackJS() const
+ {
+ return mRefCount == UINT32_MAX;
+ }
+
+ bool WasTraversed() const
+ {
+ return mRefCount != kInitialRefCount;
+ }
+
+ EdgePool::Iterator FirstChild() const
+ {
+ CC_GRAPH_ASSERT(mFirstChild.Initialized());
+ return mFirstChild;
+ }
+
+ // this PtrInfo must be part of a NodePool
+ EdgePool::Iterator LastChild() const
+ {
+ CC_GRAPH_ASSERT((this + 1)->mFirstChild.Initialized());
+ return (this + 1)->mFirstChild;
+ }
+
+ void SetFirstChild(EdgePool::Iterator aFirstChild)
+ {
+ CC_GRAPH_ASSERT(aFirstChild.Initialized());
+ mFirstChild = aFirstChild;
+ }
+
+ // this PtrInfo must be part of a NodePool
+ void SetLastChild(EdgePool::Iterator aLastChild)
+ {
+ CC_GRAPH_ASSERT(aLastChild.Initialized());
+ (this + 1)->mFirstChild = aLastChild;
+ }
+};
+
+/**
+ * A structure designed to be used like a linked list of PtrInfo, except
+ * it allocates many PtrInfos at a time.
+ */
+class NodePool
+{
+private:
+ // The -2 allows us to use |NodeBlockSize + 1| for |mEntries|, and fit
+ // |mNext|, all without causing slop.
+ enum { NodeBlockSize = 4 * 1024 - 2 };
+
+ struct NodeBlock
+ {
+ // We create and destroy NodeBlock using moz_xmalloc/free rather than new
+ // and delete to avoid calling its constructor and destructor.
+ NodeBlock()
+ {
+ NS_NOTREACHED("should never be called");
+
+ // Ensure NodeBlock is the right size (see the comment on NodeBlockSize
+ // above).
+ static_assert(
+ sizeof(NodeBlock) == 81904 || // 32-bit; equals 19.996 x 4 KiB pages
+ sizeof(NodeBlock) == 131048, // 64-bit; equals 31.994 x 4 KiB pages
+ "ill-sized NodeBlock"
+ );
+ }
+ ~NodeBlock()
+ {
+ NS_NOTREACHED("should never be called");
+ }
+
+ NodeBlock* mNext;
+ PtrInfo mEntries[NodeBlockSize + 1]; // +1 to store last child of last node
+ };
+
+public:
+ NodePool()
+ : mBlocks(nullptr)
+ , mLast(nullptr)
+ {
+ }
+
+ ~NodePool()
+ {
+ MOZ_ASSERT(!mBlocks, "Didn't call Clear()?");
+ }
+
+ void Clear()
+ {
+ NodeBlock* b = mBlocks;
+ while (b) {
+ NodeBlock* n = b->mNext;
+ free(b);
+ b = n;
+ }
+
+ mBlocks = nullptr;
+ mLast = nullptr;
+ }
+
+#ifdef DEBUG
+ bool IsEmpty()
+ {
+ return !mBlocks && !mLast;
+ }
+#endif
+
+ class Builder;
+ friend class Builder;
+ class Builder
+ {
+ public:
+ explicit Builder(NodePool& aPool)
+ : mNextBlock(&aPool.mBlocks)
+ , mNext(aPool.mLast)
+ , mBlockEnd(nullptr)
+ {
+ MOZ_ASSERT(!aPool.mBlocks && !aPool.mLast, "pool not empty");
+ }
+ PtrInfo* Add(void* aPointer, nsCycleCollectionParticipant* aParticipant)
+ {
+ if (mNext == mBlockEnd) {
+ NodeBlock* block = static_cast<NodeBlock*>(malloc(sizeof(NodeBlock)));
+ if (!block) {
+ return nullptr;
+ }
+
+ *mNextBlock = block;
+ mNext = block->mEntries;
+ mBlockEnd = block->mEntries + NodeBlockSize;
+ block->mNext = nullptr;
+ mNextBlock = &block->mNext;
+ }
+ return new (mozilla::KnownNotNull, mNext++) PtrInfo(aPointer, aParticipant);
+ }
+ private:
+ NodeBlock** mNextBlock;
+ PtrInfo*& mNext;
+ PtrInfo* mBlockEnd;
+ };
+
+ class Enumerator;
+ friend class Enumerator;
+ class Enumerator
+ {
+ public:
+ explicit Enumerator(NodePool& aPool)
+ : mFirstBlock(aPool.mBlocks)
+ , mCurBlock(nullptr)
+ , mNext(nullptr)
+ , mBlockEnd(nullptr)
+ , mLast(aPool.mLast)
+ {
+ }
+
+ bool IsDone() const
+ {
+ return mNext == mLast;
+ }
+
+ bool AtBlockEnd() const
+ {
+ return mNext == mBlockEnd;
+ }
+
+ PtrInfo* GetNext()
+ {
+ MOZ_ASSERT(!IsDone(), "calling GetNext when done");
+ if (mNext == mBlockEnd) {
+ NodeBlock* nextBlock = mCurBlock ? mCurBlock->mNext : mFirstBlock;
+ mNext = nextBlock->mEntries;
+ mBlockEnd = mNext + NodeBlockSize;
+ mCurBlock = nextBlock;
+ }
+ return mNext++;
+ }
+ private:
+ // mFirstBlock is a reference to allow an Enumerator to be constructed
+ // for an empty graph.
+ NodeBlock*& mFirstBlock;
+ NodeBlock* mCurBlock;
+ // mNext is the next value we want to return, unless mNext == mBlockEnd
+ // NB: mLast is a reference to allow enumerating while building!
+ PtrInfo* mNext;
+ PtrInfo* mBlockEnd;
+ PtrInfo*& mLast;
+ };
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ // We don't measure the things pointed to by mEntries[] because those
+ // pointers are non-owning.
+ size_t n = 0;
+ NodeBlock* b = mBlocks;
+ while (b) {
+ n += aMallocSizeOf(b);
+ b = b->mNext;
+ }
+ return n;
+ }
+
+private:
+ NodeBlock* mBlocks;
+ PtrInfo* mLast;
+};
+
+
+// Declarations for mPtrToNodeMap.
+
+struct PtrToNodeEntry : public PLDHashEntryHdr
+{
+ // The key is mNode->mPointer
+ PtrInfo* mNode;
+};
+
+static bool
+PtrToNodeMatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const PtrToNodeEntry* n = static_cast<const PtrToNodeEntry*>(aEntry);
+ return n->mNode->mPointer == aKey;
+}
+
+static PLDHashTableOps PtrNodeOps = {
+ PLDHashTable::HashVoidPtrKeyStub,
+ PtrToNodeMatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+
+struct WeakMapping
+{
+ // map and key will be null if the corresponding objects are GC marked
+ PtrInfo* mMap;
+ PtrInfo* mKey;
+ PtrInfo* mKeyDelegate;
+ PtrInfo* mVal;
+};
+
+class CCGraphBuilder;
+
+struct CCGraph
+{
+ NodePool mNodes;
+ EdgePool mEdges;
+ nsTArray<WeakMapping> mWeakMaps;
+ uint32_t mRootCount;
+
+private:
+ PLDHashTable mPtrToNodeMap;
+ bool mOutOfMemory;
+
+ static const uint32_t kInitialMapLength = 16384;
+
+public:
+ CCGraph()
+ : mRootCount(0)
+ , mPtrToNodeMap(&PtrNodeOps, sizeof(PtrToNodeEntry), kInitialMapLength)
+ , mOutOfMemory(false)
+ {}
+
+ ~CCGraph() {}
+
+ void Init()
+ {
+ MOZ_ASSERT(IsEmpty(), "Failed to call CCGraph::Clear");
+ }
+
+ void Clear()
+ {
+ mNodes.Clear();
+ mEdges.Clear();
+ mWeakMaps.Clear();
+ mRootCount = 0;
+ mPtrToNodeMap.ClearAndPrepareForLength(kInitialMapLength);
+ mOutOfMemory = false;
+ }
+
+#ifdef DEBUG
+ bool IsEmpty()
+ {
+ return mNodes.IsEmpty() && mEdges.IsEmpty() &&
+ mWeakMaps.IsEmpty() && mRootCount == 0 &&
+ mPtrToNodeMap.EntryCount() == 0;
+ }
+#endif
+
+ PtrInfo* FindNode(void* aPtr);
+ PtrToNodeEntry* AddNodeToMap(void* aPtr);
+ void RemoveObjectFromMap(void* aObject);
+
+ uint32_t MapCount() const
+ {
+ return mPtrToNodeMap.EntryCount();
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = 0;
+
+ n += mNodes.SizeOfExcludingThis(aMallocSizeOf);
+ n += mEdges.SizeOfExcludingThis(aMallocSizeOf);
+
+ // We don't measure what the WeakMappings point to, because the
+ // pointers are non-owning.
+ n += mWeakMaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ n += mPtrToNodeMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ return n;
+ }
+
+private:
+ PtrToNodeEntry* FindNodeEntry(void* aPtr)
+ {
+ return static_cast<PtrToNodeEntry*>(mPtrToNodeMap.Search(aPtr));
+ }
+};
+
+PtrInfo*
+CCGraph::FindNode(void* aPtr)
+{
+ PtrToNodeEntry* e = FindNodeEntry(aPtr);
+ return e ? e->mNode : nullptr;
+}
+
+PtrToNodeEntry*
+CCGraph::AddNodeToMap(void* aPtr)
+{
+ JS::AutoSuppressGCAnalysis suppress;
+ if (mOutOfMemory) {
+ return nullptr;
+ }
+
+ auto e = static_cast<PtrToNodeEntry*>(mPtrToNodeMap.Add(aPtr, fallible));
+ if (!e) {
+ mOutOfMemory = true;
+ MOZ_ASSERT(false, "Ran out of memory while building cycle collector graph");
+ return nullptr;
+ }
+ return e;
+}
+
+void
+CCGraph::RemoveObjectFromMap(void* aObj)
+{
+ PtrToNodeEntry* e = FindNodeEntry(aObj);
+ PtrInfo* pinfo = e ? e->mNode : nullptr;
+ if (pinfo) {
+ mPtrToNodeMap.RemoveEntry(e);
+
+ pinfo->mPointer = nullptr;
+ pinfo->mParticipant = nullptr;
+ }
+}
+
+
+static nsISupports*
+CanonicalizeXPCOMParticipant(nsISupports* aIn)
+{
+ nsISupports* out = nullptr;
+ aIn->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+ reinterpret_cast<void**>(&out));
+ return out;
+}
+
+static inline void
+ToParticipant(nsISupports* aPtr, nsXPCOMCycleCollectionParticipant** aCp);
+
+static void
+CanonicalizeParticipant(void** aParti, nsCycleCollectionParticipant** aCp)
+{
+ // If the participant is null, this is an nsISupports participant,
+ // so we must QI to get the real participant.
+
+ if (!*aCp) {
+ nsISupports* nsparti = static_cast<nsISupports*>(*aParti);
+ nsparti = CanonicalizeXPCOMParticipant(nsparti);
+ NS_ASSERTION(nsparti,
+ "Don't add objects that don't participate in collection!");
+ nsXPCOMCycleCollectionParticipant* xcp;
+ ToParticipant(nsparti, &xcp);
+ *aParti = nsparti;
+ *aCp = xcp;
+ }
+}
+
+struct nsPurpleBufferEntry
+{
+ union
+ {
+ void* mObject; // when low bit unset
+ nsPurpleBufferEntry* mNextInFreeList; // when low bit set
+ };
+
+ nsCycleCollectingAutoRefCnt* mRefCnt;
+
+ nsCycleCollectionParticipant* mParticipant; // nullptr for nsISupports
+};
+
+class nsCycleCollector;
+
+struct nsPurpleBuffer
+{
+private:
+ struct PurpleBlock
+ {
+ PurpleBlock* mNext;
+ // Try to match the size of a jemalloc bucket, to minimize slop bytes.
+ // - On 32-bit platforms sizeof(nsPurpleBufferEntry) is 12, so mEntries
+ // is 16,380 bytes, which leaves 4 bytes for mNext.
+ // - On 64-bit platforms sizeof(nsPurpleBufferEntry) is 24, so mEntries
+ // is 32,544 bytes, which leaves 8 bytes for mNext.
+ nsPurpleBufferEntry mEntries[1365];
+
+ PurpleBlock() : mNext(nullptr)
+ {
+ // Ensure PurpleBlock is the right size (see above).
+ static_assert(
+ sizeof(PurpleBlock) == 16384 || // 32-bit
+ sizeof(PurpleBlock) == 32768, // 64-bit
+ "ill-sized nsPurpleBuffer::PurpleBlock"
+ );
+
+ InitNextPointers();
+ }
+
+ // Put all the entries in the block on the free list.
+ void InitNextPointers()
+ {
+ for (uint32_t i = 1; i < ArrayLength(mEntries); ++i) {
+ mEntries[i - 1].mNextInFreeList =
+ (nsPurpleBufferEntry*)(uintptr_t(mEntries + i) | 1);
+ }
+ mEntries[ArrayLength(mEntries) - 1].mNextInFreeList =
+ (nsPurpleBufferEntry*)1;
+ }
+
+ template<class PurpleVisitor>
+ void VisitEntries(nsPurpleBuffer& aBuffer, PurpleVisitor& aVisitor)
+ {
+ nsPurpleBufferEntry* eEnd = ArrayEnd(mEntries);
+ for (nsPurpleBufferEntry* e = mEntries; e != eEnd; ++e) {
+ MOZ_ASSERT(e->mObject, "There should be no null mObject when we iterate over the purple buffer");
+ if (!(uintptr_t(e->mObject) & uintptr_t(1)) && e->mObject) {
+ aVisitor.Visit(aBuffer, e);
+ }
+ }
+ }
+ };
+ // This class wraps a linked list of the elements in the purple
+ // buffer.
+
+ uint32_t mCount;
+ PurpleBlock mFirstBlock;
+ nsPurpleBufferEntry* mFreeList;
+
+public:
+ nsPurpleBuffer()
+ {
+ InitBlocks();
+ }
+
+ ~nsPurpleBuffer()
+ {
+ FreeBlocks();
+ }
+
+ template<class PurpleVisitor>
+ void VisitEntries(PurpleVisitor& aVisitor)
+ {
+ for (PurpleBlock* b = &mFirstBlock; b; b = b->mNext) {
+ b->VisitEntries(*this, aVisitor);
+ }
+ }
+
+ void InitBlocks()
+ {
+ mCount = 0;
+ mFreeList = mFirstBlock.mEntries;
+ }
+
+ void FreeBlocks()
+ {
+ if (mCount > 0) {
+ UnmarkRemainingPurple(&mFirstBlock);
+ }
+ PurpleBlock* b = mFirstBlock.mNext;
+ while (b) {
+ if (mCount > 0) {
+ UnmarkRemainingPurple(b);
+ }
+ PurpleBlock* next = b->mNext;
+ delete b;
+ b = next;
+ }
+ mFirstBlock.mNext = nullptr;
+ }
+
+ struct UnmarkRemainingPurpleVisitor
+ {
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ if (aEntry->mRefCnt) {
+ aEntry->mRefCnt->RemoveFromPurpleBuffer();
+ aEntry->mRefCnt = nullptr;
+ }
+ aEntry->mObject = nullptr;
+ --aBuffer.mCount;
+ }
+ };
+
+ void UnmarkRemainingPurple(PurpleBlock* aBlock)
+ {
+ UnmarkRemainingPurpleVisitor visitor;
+ aBlock->VisitEntries(*this, visitor);
+ }
+
+ void SelectPointers(CCGraphBuilder& aBuilder);
+
+ // RemoveSkippable removes entries from the purple buffer synchronously
+ // (1) if aAsyncSnowWhiteFreeing is false and nsPurpleBufferEntry::mRefCnt is 0 or
+ // (2) if the object's nsXPCOMCycleCollectionParticipant::CanSkip() returns true or
+ // (3) if nsPurpleBufferEntry::mRefCnt->IsPurple() is false.
+ // (4) If removeChildlessNodes is true, then any nodes in the purple buffer
+ // that will have no children in the cycle collector graph will also be
+ // removed. CanSkip() may be run on these children.
+ void RemoveSkippable(nsCycleCollector* aCollector,
+ bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing,
+ CC_ForgetSkippableCallback aCb);
+
+ MOZ_ALWAYS_INLINE nsPurpleBufferEntry* NewEntry()
+ {
+ if (MOZ_UNLIKELY(!mFreeList)) {
+ PurpleBlock* b = new PurpleBlock;
+ mFreeList = b->mEntries;
+
+ // Add the new block as the second block in the list.
+ b->mNext = mFirstBlock.mNext;
+ mFirstBlock.mNext = b;
+ }
+
+ nsPurpleBufferEntry* e = mFreeList;
+ mFreeList = (nsPurpleBufferEntry*)
+ (uintptr_t(mFreeList->mNextInFreeList) & ~uintptr_t(1));
+ return e;
+ }
+
+ MOZ_ALWAYS_INLINE void Put(void* aObject, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt)
+ {
+ nsPurpleBufferEntry* e = NewEntry();
+
+ ++mCount;
+
+ e->mObject = aObject;
+ e->mRefCnt = aRefCnt;
+ e->mParticipant = aCp;
+ }
+
+ void Remove(nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(mCount != 0, "must have entries");
+
+ if (aEntry->mRefCnt) {
+ aEntry->mRefCnt->RemoveFromPurpleBuffer();
+ aEntry->mRefCnt = nullptr;
+ }
+ aEntry->mNextInFreeList =
+ (nsPurpleBufferEntry*)(uintptr_t(mFreeList) | uintptr_t(1));
+ mFreeList = aEntry;
+
+ --mCount;
+ }
+
+ uint32_t Count() const
+ {
+ return mCount;
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = 0;
+
+ // Don't measure mFirstBlock because it's within |this|.
+ const PurpleBlock* block = mFirstBlock.mNext;
+ while (block) {
+ n += aMallocSizeOf(block);
+ block = block->mNext;
+ }
+
+ // mFreeList is deliberately not measured because it points into
+ // the purple buffer, which is within mFirstBlock and thus within |this|.
+ //
+ // We also don't measure the things pointed to by mEntries[] because
+ // those pointers are non-owning.
+
+ return n;
+ }
+};
+
+static bool
+AddPurpleRoot(CCGraphBuilder& aBuilder, void* aRoot,
+ nsCycleCollectionParticipant* aParti);
+
+struct SelectPointersVisitor
+{
+ explicit SelectPointersVisitor(CCGraphBuilder& aBuilder)
+ : mBuilder(aBuilder)
+ {
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject, "Null object in purple buffer");
+ MOZ_ASSERT(aEntry->mRefCnt->get() != 0,
+ "SelectPointersVisitor: snow-white object in the purple buffer");
+ if (!aEntry->mRefCnt->IsPurple() ||
+ AddPurpleRoot(mBuilder, aEntry->mObject, aEntry->mParticipant)) {
+ aBuffer.Remove(aEntry);
+ }
+ }
+
+private:
+ CCGraphBuilder& mBuilder;
+};
+
+void
+nsPurpleBuffer::SelectPointers(CCGraphBuilder& aBuilder)
+{
+ SelectPointersVisitor visitor(aBuilder);
+ VisitEntries(visitor);
+
+ NS_ASSERTION(mCount == 0, "AddPurpleRoot failed");
+ if (mCount == 0) {
+ FreeBlocks();
+ InitBlocks();
+ mFirstBlock.InitNextPointers();
+ }
+}
+
+enum ccPhase
+{
+ IdlePhase,
+ GraphBuildingPhase,
+ ScanAndCollectWhitePhase,
+ CleanupPhase
+};
+
+enum ccType
+{
+ SliceCC, /* If a CC is in progress, continue it. Otherwise, start a new one. */
+ ManualCC, /* Explicitly triggered. */
+ ShutdownCC /* Shutdown CC, used for finding leaks. */
+};
+
+////////////////////////////////////////////////////////////////////////
+// Top level structure for the cycle collector.
+////////////////////////////////////////////////////////////////////////
+
+using js::SliceBudget;
+
+class JSPurpleBuffer;
+
+class nsCycleCollector : public nsIMemoryReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+private:
+ bool mActivelyCollecting;
+ bool mFreeingSnowWhite;
+ // mScanInProgress should be false when we're collecting white objects.
+ bool mScanInProgress;
+ CycleCollectorResults mResults;
+ TimeStamp mCollectionStart;
+
+ CycleCollectedJSContext* mJSContext;
+
+ ccPhase mIncrementalPhase;
+ CCGraph mGraph;
+ nsAutoPtr<CCGraphBuilder> mBuilder;
+ RefPtr<nsCycleCollectorLogger> mLogger;
+
+#ifdef DEBUG
+ void* mThread;
+#endif
+
+ nsCycleCollectorParams mParams;
+
+ uint32_t mWhiteNodeCount;
+
+ CC_BeforeUnlinkCallback mBeforeUnlinkCB;
+ CC_ForgetSkippableCallback mForgetSkippableCB;
+
+ nsPurpleBuffer mPurpleBuf;
+
+ uint32_t mUnmergedNeeded;
+ uint32_t mMergedInARow;
+
+ RefPtr<JSPurpleBuffer> mJSPurpleBuffer;
+
+private:
+ virtual ~nsCycleCollector();
+
+public:
+ nsCycleCollector();
+
+ void RegisterJSContext(CycleCollectedJSContext* aJSContext);
+ void ForgetJSContext();
+
+ void SetBeforeUnlinkCallback(CC_BeforeUnlinkCallback aBeforeUnlinkCB)
+ {
+ CheckThreadSafety();
+ mBeforeUnlinkCB = aBeforeUnlinkCB;
+ }
+
+ void SetForgetSkippableCallback(CC_ForgetSkippableCallback aForgetSkippableCB)
+ {
+ CheckThreadSafety();
+ mForgetSkippableCB = aForgetSkippableCB;
+ }
+
+ void Suspect(void* aPtr, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt);
+ uint32_t SuspectedCount();
+ void ForgetSkippable(bool aRemoveChildlessNodes, bool aAsyncSnowWhiteFreeing);
+ bool FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
+
+ // This method assumes its argument is already canonicalized.
+ void RemoveObjectFromGraph(void* aPtr);
+
+ void PrepareForGarbageCollection();
+ void FinishAnyCurrentCollection();
+
+ bool Collect(ccType aCCType,
+ SliceBudget& aBudget,
+ nsICycleCollectorListener* aManualListener,
+ bool aPreferShorterSlices = false);
+ void Shutdown(bool aDoCollect);
+
+ bool IsIdle() const { return mIncrementalPhase == IdlePhase; }
+
+ void SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ size_t* aObjectSize,
+ size_t* aGraphSize,
+ size_t* aPurpleBufferSize) const;
+
+ JSPurpleBuffer* GetJSPurpleBuffer();
+
+ CycleCollectedJSContext* Context() { return mJSContext; }
+
+private:
+ void CheckThreadSafety();
+ void ShutdownCollect();
+
+ void FixGrayBits(bool aForceGC, TimeLog& aTimeLog);
+ bool IsIncrementalGCInProgress();
+ void FinishAnyIncrementalGCInProgress();
+ bool ShouldMergeZones(ccType aCCType);
+
+ void BeginCollection(ccType aCCType, nsICycleCollectorListener* aManualListener);
+ void MarkRoots(SliceBudget& aBudget);
+ void ScanRoots(bool aFullySynchGraphBuild);
+ void ScanIncrementalRoots();
+ void ScanWhiteNodes(bool aFullySynchGraphBuild);
+ void ScanBlackNodes();
+ void ScanWeakMaps();
+
+ // returns whether anything was collected
+ bool CollectWhite();
+
+ void CleanupAfterCollection();
+};
+
+NS_IMPL_ISUPPORTS(nsCycleCollector, nsIMemoryReporter)
+
+/**
+ * GraphWalker is templatized over a Visitor class that must provide
+ * the following two methods:
+ *
+ * bool ShouldVisitNode(PtrInfo const *pi);
+ * void VisitNode(PtrInfo *pi);
+ */
+template<class Visitor>
+class GraphWalker
+{
+private:
+ Visitor mVisitor;
+
+ void DoWalk(nsDeque& aQueue);
+
+ void CheckedPush(nsDeque& aQueue, PtrInfo* aPi)
+ {
+ if (!aPi) {
+ MOZ_CRASH();
+ }
+ if (!aQueue.Push(aPi, fallible)) {
+ mVisitor.Failed();
+ }
+ }
+
+public:
+ void Walk(PtrInfo* aPi);
+ void WalkFromRoots(CCGraph& aGraph);
+ // copy-constructing the visitor should be cheap, and less
+ // indirection than using a reference
+ explicit GraphWalker(const Visitor aVisitor) : mVisitor(aVisitor)
+ {
+ }
+};
+
+
+////////////////////////////////////////////////////////////////////////
+// The static collector struct
+////////////////////////////////////////////////////////////////////////
+
+struct CollectorData
+{
+ RefPtr<nsCycleCollector> mCollector;
+ CycleCollectedJSContext* mContext;
+};
+
+static MOZ_THREAD_LOCAL(CollectorData*) sCollectorData;
+
+////////////////////////////////////////////////////////////////////////
+// Utility functions
+////////////////////////////////////////////////////////////////////////
+
+static inline void
+ToParticipant(nsISupports* aPtr, nsXPCOMCycleCollectionParticipant** aCp)
+{
+ // We use QI to move from an nsISupports to an
+ // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
+ // object that implements traversal and unlinking logic for the nsISupports
+ // in question.
+ *aCp = nullptr;
+ CallQueryInterface(aPtr, aCp);
+}
+
+template<class Visitor>
+MOZ_NEVER_INLINE void
+GraphWalker<Visitor>::Walk(PtrInfo* aPi)
+{
+ nsDeque queue;
+ CheckedPush(queue, aPi);
+ DoWalk(queue);
+}
+
+template<class Visitor>
+MOZ_NEVER_INLINE void
+GraphWalker<Visitor>::WalkFromRoots(CCGraph& aGraph)
+{
+ nsDeque queue;
+ NodePool::Enumerator etor(aGraph.mNodes);
+ for (uint32_t i = 0; i < aGraph.mRootCount; ++i) {
+ CheckedPush(queue, etor.GetNext());
+ }
+ DoWalk(queue);
+}
+
+template<class Visitor>
+MOZ_NEVER_INLINE void
+GraphWalker<Visitor>::DoWalk(nsDeque& aQueue)
+{
+ // Use a aQueue to match the breadth-first traversal used when we
+ // built the graph, for hopefully-better locality.
+ while (aQueue.GetSize() > 0) {
+ PtrInfo* pi = static_cast<PtrInfo*>(aQueue.PopFront());
+
+ if (pi->WasTraversed() && mVisitor.ShouldVisitNode(pi)) {
+ mVisitor.VisitNode(pi);
+ for (EdgePool::Iterator child = pi->FirstChild(),
+ child_end = pi->LastChild();
+ child != child_end; ++child) {
+ CheckedPush(aQueue, *child);
+ }
+ }
+ }
+}
+
+struct CCGraphDescriber : public LinkedListElement<CCGraphDescriber>
+{
+ CCGraphDescriber()
+ : mAddress("0x"), mCnt(0), mType(eUnknown)
+ {
+ }
+
+ enum Type
+ {
+ eRefCountedObject,
+ eGCedObject,
+ eGCMarkedObject,
+ eEdge,
+ eRoot,
+ eGarbage,
+ eUnknown
+ };
+
+ nsCString mAddress;
+ nsCString mName;
+ nsCString mCompartmentOrToAddress;
+ uint32_t mCnt;
+ Type mType;
+};
+
+class LogStringMessageAsync : public CancelableRunnable
+{
+public:
+ explicit LogStringMessageAsync(const nsAString& aMsg) : mMsg(aMsg)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIConsoleService> cs =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (cs) {
+ cs->LogStringMessage(mMsg.get());
+ }
+ return NS_OK;
+ }
+
+private:
+ nsString mMsg;
+};
+
+class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsCycleCollectorLogSinkToFile() :
+ mProcessIdentifier(base::GetCurrentProcId()),
+ mGCLog("gc-edges"), mCCLog("cc-edges")
+ {
+ }
+
+ NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override
+ {
+ aIdentifier = mFilenameIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override
+ {
+ mFilenameIdentifier = aIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetProcessIdentifier(int32_t* aIdentifier) override
+ {
+ *aIdentifier = mProcessIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override
+ {
+ mProcessIdentifier = aIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetGcLog(nsIFile** aPath) override
+ {
+ NS_IF_ADDREF(*aPath = mGCLog.mFile);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetCcLog(nsIFile** aPath) override
+ {
+ NS_IF_ADDREF(*aPath = mCCLog.mFile);
+ return NS_OK;
+ }
+
+ NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override
+ {
+ nsresult rv;
+
+ if (mGCLog.mStream || mCCLog.mStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = OpenLog(&mGCLog);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aGCLog = mGCLog.mStream;
+
+ rv = OpenLog(&mCCLog);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aCCLog = mCCLog.mStream;
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseGCLog() override
+ {
+ if (!mGCLog.mStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CloseLog(&mGCLog, NS_LITERAL_STRING("Garbage"));
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseCCLog() override
+ {
+ if (!mCCLog.mStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CloseLog(&mCCLog, NS_LITERAL_STRING("Cycle"));
+ return NS_OK;
+ }
+
+private:
+ ~nsCycleCollectorLogSinkToFile()
+ {
+ if (mGCLog.mStream) {
+ MozillaUnRegisterDebugFILE(mGCLog.mStream);
+ fclose(mGCLog.mStream);
+ }
+ if (mCCLog.mStream) {
+ MozillaUnRegisterDebugFILE(mCCLog.mStream);
+ fclose(mCCLog.mStream);
+ }
+ }
+
+ struct FileInfo
+ {
+ const char* const mPrefix;
+ nsCOMPtr<nsIFile> mFile;
+ FILE* mStream;
+
+ explicit FileInfo(const char* aPrefix) : mPrefix(aPrefix), mStream(nullptr) { }
+ };
+
+ /**
+ * Create a new file named something like aPrefix.$PID.$IDENTIFIER.log in
+ * $MOZ_CC_LOG_DIRECTORY or in the system's temp directory. No existing
+ * file will be overwritten; if aPrefix.$PID.$IDENTIFIER.log exists, we'll
+ * try a file named something like aPrefix.$PID.$IDENTIFIER-1.log, and so
+ * on.
+ */
+ already_AddRefed<nsIFile> CreateTempFile(const char* aPrefix)
+ {
+ nsPrintfCString filename("%s.%d%s%s.log",
+ aPrefix,
+ mProcessIdentifier,
+ mFilenameIdentifier.IsEmpty() ? "" : ".",
+ NS_ConvertUTF16toUTF8(mFilenameIdentifier).get());
+
+ // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from
+ // the fallback directories in OpenTempFile. We don't use an nsCOMPtr
+ // here because OpenTempFile uses an in/out param and getter_AddRefs
+ // wouldn't work.
+ nsIFile* logFile = nullptr;
+ if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) {
+ NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true,
+ &logFile);
+ }
+
+ // On Android or B2G, this function will open a file named
+ // aFilename under a memory-reporting-specific folder
+ // (/data/local/tmp/memory-reports). Otherwise, it will open a
+ // file named aFilename under "NS_OS_TEMP_DIR".
+ nsresult rv = nsDumpUtils::OpenTempFile(filename, &logFile,
+ NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_FAILED(rv)) {
+ NS_IF_RELEASE(logFile);
+ return nullptr;
+ }
+
+ return dont_AddRef(logFile);
+ }
+
+ nsresult OpenLog(FileInfo* aLog)
+ {
+ // Initially create the log in a file starting with "incomplete-".
+ // We'll move the file and strip off the "incomplete-" once the dump
+ // completes. (We do this because we don't want scripts which poll
+ // the filesystem looking for GC/CC dumps to grab a file before we're
+ // finished writing to it.)
+ nsAutoCString incomplete;
+ incomplete += "incomplete-";
+ incomplete += aLog->mPrefix;
+ MOZ_ASSERT(!aLog->mFile);
+ aLog->mFile = CreateTempFile(incomplete.get());
+ if (NS_WARN_IF(!aLog->mFile)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ MOZ_ASSERT(!aLog->mStream);
+ nsresult rv = aLog->mFile->OpenANSIFileDesc("w", &aLog->mStream);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ MozillaRegisterDebugFILE(aLog->mStream);
+ return NS_OK;
+ }
+
+ nsresult CloseLog(FileInfo* aLog, const nsAString& aCollectorKind)
+ {
+ MOZ_ASSERT(aLog->mStream);
+ MOZ_ASSERT(aLog->mFile);
+
+ MozillaUnRegisterDebugFILE(aLog->mStream);
+ fclose(aLog->mStream);
+ aLog->mStream = nullptr;
+
+ // Strip off "incomplete-".
+ nsCOMPtr<nsIFile> logFileFinalDestination =
+ CreateTempFile(aLog->mPrefix);
+ if (NS_WARN_IF(!logFileFinalDestination)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoString logFileFinalDestinationName;
+ logFileFinalDestination->GetLeafName(logFileFinalDestinationName);
+ if (NS_WARN_IF(logFileFinalDestinationName.IsEmpty())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aLog->mFile->MoveTo(/* directory */ nullptr, logFileFinalDestinationName);
+
+ // Save the file path.
+ aLog->mFile = logFileFinalDestination;
+
+ // Log to the error console.
+ nsAutoString logPath;
+ logFileFinalDestination->GetPath(logPath);
+ nsAutoString msg = aCollectorKind +
+ NS_LITERAL_STRING(" Collector log dumped to ") + logPath;
+
+ // We don't want any JS to run between ScanRoots and CollectWhite calls,
+ // and since ScanRoots calls this method, better to log the message
+ // asynchronously.
+ RefPtr<LogStringMessageAsync> log = new LogStringMessageAsync(msg);
+ NS_DispatchToCurrentThread(log);
+ return NS_OK;
+ }
+
+ int32_t mProcessIdentifier;
+ nsString mFilenameIdentifier;
+ FileInfo mGCLog;
+ FileInfo mCCLog;
+};
+
+NS_IMPL_ISUPPORTS(nsCycleCollectorLogSinkToFile, nsICycleCollectorLogSink)
+
+
+class nsCycleCollectorLogger final : public nsICycleCollectorListener
+{
+ ~nsCycleCollectorLogger()
+ {
+ ClearDescribers();
+ }
+
+public:
+ nsCycleCollectorLogger()
+ : mLogSink(nsCycleCollector_createLogSink())
+ , mWantAllTraces(false)
+ , mDisableLog(false)
+ , mWantAfterProcessing(false)
+ , mCCLog(nullptr)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+
+ void SetAllTraces()
+ {
+ mWantAllTraces = true;
+ }
+
+ bool IsAllTraces()
+ {
+ return mWantAllTraces;
+ }
+
+ NS_IMETHOD AllTraces(nsICycleCollectorListener** aListener) override
+ {
+ SetAllTraces();
+ NS_ADDREF(*aListener = this);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetWantAllTraces(bool* aAllTraces) override
+ {
+ *aAllTraces = mWantAllTraces;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetDisableLog(bool* aDisableLog) override
+ {
+ *aDisableLog = mDisableLog;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetDisableLog(bool aDisableLog) override
+ {
+ mDisableLog = aDisableLog;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetWantAfterProcessing(bool* aWantAfterProcessing) override
+ {
+ *aWantAfterProcessing = mWantAfterProcessing;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetWantAfterProcessing(bool aWantAfterProcessing) override
+ {
+ mWantAfterProcessing = aWantAfterProcessing;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetLogSink(nsICycleCollectorLogSink** aLogSink) override
+ {
+ NS_ADDREF(*aLogSink = mLogSink);
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetLogSink(nsICycleCollectorLogSink* aLogSink) override
+ {
+ if (!aLogSink) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ mLogSink = aLogSink;
+ return NS_OK;
+ }
+
+ nsresult Begin()
+ {
+ nsresult rv;
+
+ mCurrentAddress.AssignLiteral("0x");
+ ClearDescribers();
+ if (mDisableLog) {
+ return NS_OK;
+ }
+
+ FILE* gcLog;
+ rv = mLogSink->Open(&gcLog, &mCCLog);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Dump the JS heap.
+ CollectorData* data = sCollectorData.get();
+ if (data && data->mContext) {
+ data->mContext->DumpJSHeap(gcLog);
+ }
+ rv = mLogSink->CloseGCLog();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ fprintf(mCCLog, "# WantAllTraces=%s\n", mWantAllTraces ? "true" : "false");
+ return NS_OK;
+ }
+ void NoteRefCountedObject(uint64_t aAddress, uint32_t aRefCount,
+ const char* aObjectDescription)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [rc=%u] %s\n", (void*)aAddress, aRefCount,
+ aObjectDescription);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ mCurrentAddress.AssignLiteral("0x");
+ mCurrentAddress.AppendInt(aAddress, 16);
+ d->mType = CCGraphDescriber::eRefCountedObject;
+ d->mAddress = mCurrentAddress;
+ d->mCnt = aRefCount;
+ d->mName.Append(aObjectDescription);
+ }
+ }
+ void NoteGCedObject(uint64_t aAddress, bool aMarked,
+ const char* aObjectDescription,
+ uint64_t aCompartmentAddress)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [gc%s] %s\n", (void*)aAddress,
+ aMarked ? ".marked" : "", aObjectDescription);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ mCurrentAddress.AssignLiteral("0x");
+ mCurrentAddress.AppendInt(aAddress, 16);
+ d->mType = aMarked ? CCGraphDescriber::eGCMarkedObject :
+ CCGraphDescriber::eGCedObject;
+ d->mAddress = mCurrentAddress;
+ d->mName.Append(aObjectDescription);
+ if (aCompartmentAddress) {
+ d->mCompartmentOrToAddress.AssignLiteral("0x");
+ d->mCompartmentOrToAddress.AppendInt(aCompartmentAddress, 16);
+ } else {
+ d->mCompartmentOrToAddress.SetIsVoid(true);
+ }
+ }
+ }
+ void NoteEdge(uint64_t aToAddress, const char* aEdgeName)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "> %p %s\n", (void*)aToAddress, aEdgeName);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ d->mType = CCGraphDescriber::eEdge;
+ d->mAddress = mCurrentAddress;
+ d->mCompartmentOrToAddress.AssignLiteral("0x");
+ d->mCompartmentOrToAddress.AppendInt(aToAddress, 16);
+ d->mName.Append(aEdgeName);
+ }
+ }
+ void NoteWeakMapEntry(uint64_t aMap, uint64_t aKey,
+ uint64_t aKeyDelegate, uint64_t aValue)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n",
+ (void*)aMap, (void*)aKey, (void*)aKeyDelegate, (void*)aValue);
+ }
+ // We don't support after-processing for weak map entries.
+ }
+ void NoteIncrementalRoot(uint64_t aAddress)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "IncrementalRoot %p\n", (void*)aAddress);
+ }
+ // We don't support after-processing for incremental roots.
+ }
+ void BeginResults()
+ {
+ if (!mDisableLog) {
+ fputs("==========\n", mCCLog);
+ }
+ }
+ void DescribeRoot(uint64_t aAddress, uint32_t aKnownEdges)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [known=%u]\n", (void*)aAddress, aKnownEdges);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ d->mType = CCGraphDescriber::eRoot;
+ d->mAddress.AppendInt(aAddress, 16);
+ d->mCnt = aKnownEdges;
+ }
+ }
+ void DescribeGarbage(uint64_t aAddress)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [garbage]\n", (void*)aAddress);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ d->mType = CCGraphDescriber::eGarbage;
+ d->mAddress.AppendInt(aAddress, 16);
+ }
+ }
+ void End()
+ {
+ if (!mDisableLog) {
+ mCCLog = nullptr;
+ Unused << NS_WARN_IF(NS_FAILED(mLogSink->CloseCCLog()));
+ }
+ }
+ NS_IMETHOD ProcessNext(nsICycleCollectorHandler* aHandler,
+ bool* aCanContinue) override
+ {
+ if (NS_WARN_IF(!aHandler) || NS_WARN_IF(!mWantAfterProcessing)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CCGraphDescriber* d = mDescribers.popFirst();
+ if (d) {
+ switch (d->mType) {
+ case CCGraphDescriber::eRefCountedObject:
+ aHandler->NoteRefCountedObject(d->mAddress,
+ d->mCnt,
+ d->mName);
+ break;
+ case CCGraphDescriber::eGCedObject:
+ case CCGraphDescriber::eGCMarkedObject:
+ aHandler->NoteGCedObject(d->mAddress,
+ d->mType ==
+ CCGraphDescriber::eGCMarkedObject,
+ d->mName,
+ d->mCompartmentOrToAddress);
+ break;
+ case CCGraphDescriber::eEdge:
+ aHandler->NoteEdge(d->mAddress,
+ d->mCompartmentOrToAddress,
+ d->mName);
+ break;
+ case CCGraphDescriber::eRoot:
+ aHandler->DescribeRoot(d->mAddress,
+ d->mCnt);
+ break;
+ case CCGraphDescriber::eGarbage:
+ aHandler->DescribeGarbage(d->mAddress);
+ break;
+ case CCGraphDescriber::eUnknown:
+ NS_NOTREACHED("CCGraphDescriber::eUnknown");
+ break;
+ }
+ delete d;
+ }
+ if (!(*aCanContinue = !mDescribers.isEmpty())) {
+ mCurrentAddress.AssignLiteral("0x");
+ }
+ return NS_OK;
+ }
+ NS_IMETHOD AsLogger(nsCycleCollectorLogger** aRetVal) override
+ {
+ RefPtr<nsCycleCollectorLogger> rval = this;
+ rval.forget(aRetVal);
+ return NS_OK;
+ }
+private:
+ void ClearDescribers()
+ {
+ CCGraphDescriber* d;
+ while ((d = mDescribers.popFirst())) {
+ delete d;
+ }
+ }
+
+ nsCOMPtr<nsICycleCollectorLogSink> mLogSink;
+ bool mWantAllTraces;
+ bool mDisableLog;
+ bool mWantAfterProcessing;
+ nsCString mCurrentAddress;
+ mozilla::LinkedList<CCGraphDescriber> mDescribers;
+ FILE* mCCLog;
+};
+
+NS_IMPL_ISUPPORTS(nsCycleCollectorLogger, nsICycleCollectorListener)
+
+nsresult
+nsCycleCollectorLoggerConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsISupports* logger = new nsCycleCollectorLogger();
+
+ return logger->QueryInterface(aIID, aInstancePtr);
+}
+
+static bool
+GCThingIsGrayCCThing(JS::GCCellPtr thing)
+{
+ return AddToCCKind(thing.kind()) &&
+ JS::GCThingIsMarkedGray(thing);
+}
+
+static bool
+ValueIsGrayCCThing(const JS::Value& value)
+{
+ return AddToCCKind(value.traceKind()) &&
+ JS::GCThingIsMarkedGray(value.toGCCellPtr());
+}
+
+////////////////////////////////////////////////////////////////////////
+// Bacon & Rajan's |MarkRoots| routine.
+////////////////////////////////////////////////////////////////////////
+
+class CCGraphBuilder final : public nsCycleCollectionTraversalCallback,
+ public nsCycleCollectionNoteRootCallback
+{
+private:
+ CCGraph& mGraph;
+ CycleCollectorResults& mResults;
+ NodePool::Builder mNodeBuilder;
+ EdgePool::Builder mEdgeBuilder;
+ MOZ_INIT_OUTSIDE_CTOR PtrInfo* mCurrPi;
+ nsCycleCollectionParticipant* mJSParticipant;
+ nsCycleCollectionParticipant* mJSZoneParticipant;
+ nsCString mNextEdgeName;
+ RefPtr<nsCycleCollectorLogger> mLogger;
+ bool mMergeZones;
+ nsAutoPtr<NodePool::Enumerator> mCurrNode;
+
+public:
+ CCGraphBuilder(CCGraph& aGraph,
+ CycleCollectorResults& aResults,
+ CycleCollectedJSContext* aJSContext,
+ nsCycleCollectorLogger* aLogger,
+ bool aMergeZones);
+ virtual ~CCGraphBuilder();
+
+ bool WantAllTraces() const
+ {
+ return nsCycleCollectionNoteRootCallback::WantAllTraces();
+ }
+
+ bool AddPurpleRoot(void* aRoot, nsCycleCollectionParticipant* aParti);
+
+ // This is called when all roots have been added to the graph, to prepare for BuildGraph().
+ void DoneAddingRoots();
+
+ // Do some work traversing nodes in the graph. Returns true if this graph building is finished.
+ bool BuildGraph(SliceBudget& aBudget);
+
+private:
+ PtrInfo* AddNode(void* aPtr, nsCycleCollectionParticipant* aParticipant);
+ PtrInfo* AddWeakMapNode(JS::GCCellPtr aThing);
+ PtrInfo* AddWeakMapNode(JSObject* aObject);
+
+ void SetFirstChild()
+ {
+ mCurrPi->SetFirstChild(mEdgeBuilder.Mark());
+ }
+
+ void SetLastChild()
+ {
+ mCurrPi->SetLastChild(mEdgeBuilder.Mark());
+ }
+
+public:
+ // nsCycleCollectionNoteRootCallback methods.
+ NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports* aRoot);
+ NS_IMETHOD_(void) NoteJSRoot(JSObject* aRoot);
+ NS_IMETHOD_(void) NoteNativeRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant);
+ NS_IMETHOD_(void) NoteWeakMapping(JSObject* aMap, JS::GCCellPtr aKey,
+ JSObject* aKdelegate, JS::GCCellPtr aVal);
+
+ // nsCycleCollectionTraversalCallback methods.
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefCount,
+ const char* aObjName);
+ NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked, const char* aObjName,
+ uint64_t aCompartmentAddress);
+
+ NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild);
+ NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aThing);
+ NS_IMETHOD_(void) NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aParticipant);
+ NS_IMETHOD_(void) NoteNextEdgeName(const char* aName);
+
+private:
+ void NoteJSChild(JS::GCCellPtr aChild);
+
+ NS_IMETHOD_(void) NoteRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant)
+ {
+ MOZ_ASSERT(aRoot);
+ MOZ_ASSERT(aParticipant);
+
+ if (!aParticipant->CanSkipInCC(aRoot) || MOZ_UNLIKELY(WantAllTraces())) {
+ AddNode(aRoot, aParticipant);
+ }
+ }
+
+ NS_IMETHOD_(void) NoteChild(void* aChild, nsCycleCollectionParticipant* aCp,
+ nsCString& aEdgeName)
+ {
+ PtrInfo* childPi = AddNode(aChild, aCp);
+ if (!childPi) {
+ return;
+ }
+ mEdgeBuilder.Add(childPi);
+ if (mLogger) {
+ mLogger->NoteEdge((uint64_t)aChild, aEdgeName.get());
+ }
+ ++childPi->mInternalRefs;
+ }
+
+ JS::Zone* MergeZone(JS::GCCellPtr aGcthing)
+ {
+ if (!mMergeZones) {
+ return nullptr;
+ }
+ JS::Zone* zone = JS::GetTenuredGCThingZone(aGcthing);
+ if (js::IsSystemZone(zone)) {
+ return nullptr;
+ }
+ return zone;
+ }
+};
+
+CCGraphBuilder::CCGraphBuilder(CCGraph& aGraph,
+ CycleCollectorResults& aResults,
+ CycleCollectedJSContext* aJSContext,
+ nsCycleCollectorLogger* aLogger,
+ bool aMergeZones)
+ : mGraph(aGraph)
+ , mResults(aResults)
+ , mNodeBuilder(aGraph.mNodes)
+ , mEdgeBuilder(aGraph.mEdges)
+ , mJSParticipant(nullptr)
+ , mJSZoneParticipant(nullptr)
+ , mLogger(aLogger)
+ , mMergeZones(aMergeZones)
+{
+ if (aJSContext) {
+ mJSParticipant = aJSContext->GCThingParticipant();
+ mJSZoneParticipant = aJSContext->ZoneParticipant();
+ }
+
+ if (mLogger) {
+ mFlags |= nsCycleCollectionTraversalCallback::WANT_DEBUG_INFO;
+ if (mLogger->IsAllTraces()) {
+ mFlags |= nsCycleCollectionTraversalCallback::WANT_ALL_TRACES;
+ mWantAllTraces = true; // for nsCycleCollectionNoteRootCallback
+ }
+ }
+
+ mMergeZones = mMergeZones && MOZ_LIKELY(!WantAllTraces());
+
+ MOZ_ASSERT(nsCycleCollectionNoteRootCallback::WantAllTraces() ==
+ nsCycleCollectionTraversalCallback::WantAllTraces());
+}
+
+CCGraphBuilder::~CCGraphBuilder()
+{
+}
+
+PtrInfo*
+CCGraphBuilder::AddNode(void* aPtr, nsCycleCollectionParticipant* aParticipant)
+{
+ PtrToNodeEntry* e = mGraph.AddNodeToMap(aPtr);
+ if (!e) {
+ return nullptr;
+ }
+
+ PtrInfo* result;
+ if (!e->mNode) {
+ // New entry.
+ result = mNodeBuilder.Add(aPtr, aParticipant);
+ if (!result) {
+ return nullptr;
+ }
+
+ e->mNode = result;
+ NS_ASSERTION(result, "mNodeBuilder.Add returned null");
+ } else {
+ result = e->mNode;
+ MOZ_ASSERT(result->mParticipant == aParticipant,
+ "nsCycleCollectionParticipant shouldn't change!");
+ }
+ return result;
+}
+
+bool
+CCGraphBuilder::AddPurpleRoot(void* aRoot, nsCycleCollectionParticipant* aParti)
+{
+ CanonicalizeParticipant(&aRoot, &aParti);
+
+ if (WantAllTraces() || !aParti->CanSkipInCC(aRoot)) {
+ PtrInfo* pinfo = AddNode(aRoot, aParti);
+ if (!pinfo) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+CCGraphBuilder::DoneAddingRoots()
+{
+ // We've finished adding roots, and everything in the graph is a root.
+ mGraph.mRootCount = mGraph.MapCount();
+
+ mCurrNode = new NodePool::Enumerator(mGraph.mNodes);
+}
+
+MOZ_NEVER_INLINE bool
+CCGraphBuilder::BuildGraph(SliceBudget& aBudget)
+{
+ const intptr_t kNumNodesBetweenTimeChecks = 1000;
+ const intptr_t kStep = SliceBudget::CounterReset / kNumNodesBetweenTimeChecks;
+
+ MOZ_ASSERT(mCurrNode);
+
+ while (!aBudget.isOverBudget() && !mCurrNode->IsDone()) {
+ PtrInfo* pi = mCurrNode->GetNext();
+ if (!pi) {
+ MOZ_CRASH();
+ }
+
+ mCurrPi = pi;
+
+ // We need to call SetFirstChild() even on deleted nodes, to set their
+ // firstChild() that may be read by a prior non-deleted neighbor.
+ SetFirstChild();
+
+ if (pi->mParticipant) {
+ nsresult rv = pi->mParticipant->Traverse(pi->mPointer, *this);
+ MOZ_RELEASE_ASSERT(!NS_FAILED(rv), "Cycle collector Traverse method failed");
+ }
+
+ if (mCurrNode->AtBlockEnd()) {
+ SetLastChild();
+ }
+
+ aBudget.step(kStep);
+ }
+
+ if (!mCurrNode->IsDone()) {
+ return false;
+ }
+
+ if (mGraph.mRootCount > 0) {
+ SetLastChild();
+ }
+
+ mCurrNode = nullptr;
+
+ return true;
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteXPCOMRoot(nsISupports* aRoot)
+{
+ aRoot = CanonicalizeXPCOMParticipant(aRoot);
+ NS_ASSERTION(aRoot,
+ "Don't add objects that don't participate in collection!");
+
+ nsXPCOMCycleCollectionParticipant* cp;
+ ToParticipant(aRoot, &cp);
+
+ NoteRoot(aRoot, cp);
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteJSRoot(JSObject* aRoot)
+{
+ if (JS::Zone* zone = MergeZone(JS::GCCellPtr(aRoot))) {
+ NoteRoot(zone, mJSZoneParticipant);
+ } else {
+ NoteRoot(aRoot, mJSParticipant);
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteNativeRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant)
+{
+ NoteRoot(aRoot, aParticipant);
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::DescribeRefCountedNode(nsrefcnt aRefCount, const char* aObjName)
+{
+ MOZ_RELEASE_ASSERT(aRefCount != 0, "CCed refcounted object has zero refcount");
+ MOZ_RELEASE_ASSERT(aRefCount != UINT32_MAX, "CCed refcounted object has overflowing refcount");
+
+ mResults.mVisitedRefCounted++;
+
+ if (mLogger) {
+ mLogger->NoteRefCountedObject((uint64_t)mCurrPi->mPointer, aRefCount,
+ aObjName);
+ }
+
+ mCurrPi->mRefCount = aRefCount;
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::DescribeGCedNode(bool aIsMarked, const char* aObjName,
+ uint64_t aCompartmentAddress)
+{
+ uint32_t refCount = aIsMarked ? UINT32_MAX : 0;
+ mResults.mVisitedGCed++;
+
+ if (mLogger) {
+ mLogger->NoteGCedObject((uint64_t)mCurrPi->mPointer, aIsMarked,
+ aObjName, aCompartmentAddress);
+ }
+
+ mCurrPi->mRefCount = refCount;
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteXPCOMChild(nsISupports* aChild)
+{
+ nsCString edgeName;
+ if (WantDebugInfo()) {
+ edgeName.Assign(mNextEdgeName);
+ mNextEdgeName.Truncate();
+ }
+ if (!aChild || !(aChild = CanonicalizeXPCOMParticipant(aChild))) {
+ return;
+ }
+
+ nsXPCOMCycleCollectionParticipant* cp;
+ ToParticipant(aChild, &cp);
+ if (cp && (!cp->CanSkipThis(aChild) || WantAllTraces())) {
+ NoteChild(aChild, cp, edgeName);
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aParticipant)
+{
+ nsCString edgeName;
+ if (WantDebugInfo()) {
+ edgeName.Assign(mNextEdgeName);
+ mNextEdgeName.Truncate();
+ }
+ if (!aChild) {
+ return;
+ }
+
+ MOZ_ASSERT(aParticipant, "Need a nsCycleCollectionParticipant!");
+ if (!aParticipant->CanSkipThis(aChild) || WantAllTraces()) {
+ NoteChild(aChild, aParticipant, edgeName);
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteJSChild(const JS::GCCellPtr& aChild)
+{
+ if (!aChild) {
+ return;
+ }
+
+ nsCString edgeName;
+ if (MOZ_UNLIKELY(WantDebugInfo())) {
+ edgeName.Assign(mNextEdgeName);
+ mNextEdgeName.Truncate();
+ }
+
+ if (GCThingIsGrayCCThing(aChild) || MOZ_UNLIKELY(WantAllTraces())) {
+ if (JS::Zone* zone = MergeZone(aChild)) {
+ NoteChild(zone, mJSZoneParticipant, edgeName);
+ } else {
+ NoteChild(aChild.asCell(), mJSParticipant, edgeName);
+ }
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteNextEdgeName(const char* aName)
+{
+ if (WantDebugInfo()) {
+ mNextEdgeName = aName;
+ }
+}
+
+PtrInfo*
+CCGraphBuilder::AddWeakMapNode(JS::GCCellPtr aNode)
+{
+ MOZ_ASSERT(aNode, "Weak map node should be non-null.");
+
+ if (!GCThingIsGrayCCThing(aNode) && !WantAllTraces()) {
+ return nullptr;
+ }
+
+ if (JS::Zone* zone = MergeZone(aNode)) {
+ return AddNode(zone, mJSZoneParticipant);
+ }
+ return AddNode(aNode.asCell(), mJSParticipant);
+}
+
+PtrInfo*
+CCGraphBuilder::AddWeakMapNode(JSObject* aObject)
+{
+ return AddWeakMapNode(JS::GCCellPtr(aObject));
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteWeakMapping(JSObject* aMap, JS::GCCellPtr aKey,
+ JSObject* aKdelegate, JS::GCCellPtr aVal)
+{
+ // Don't try to optimize away the entry here, as we've already attempted to
+ // do that in TraceWeakMapping in nsXPConnect.
+ WeakMapping* mapping = mGraph.mWeakMaps.AppendElement();
+ mapping->mMap = aMap ? AddWeakMapNode(aMap) : nullptr;
+ mapping->mKey = aKey ? AddWeakMapNode(aKey) : nullptr;
+ mapping->mKeyDelegate = aKdelegate ? AddWeakMapNode(aKdelegate) : mapping->mKey;
+ mapping->mVal = aVal ? AddWeakMapNode(aVal) : nullptr;
+
+ if (mLogger) {
+ mLogger->NoteWeakMapEntry((uint64_t)aMap, aKey ? aKey.unsafeAsInteger() : 0,
+ (uint64_t)aKdelegate,
+ aVal ? aVal.unsafeAsInteger() : 0);
+ }
+}
+
+static bool
+AddPurpleRoot(CCGraphBuilder& aBuilder, void* aRoot,
+ nsCycleCollectionParticipant* aParti)
+{
+ return aBuilder.AddPurpleRoot(aRoot, aParti);
+}
+
+// MayHaveChild() will be false after a Traverse if the object does
+// not have any children the CC will visit.
+class ChildFinder : public nsCycleCollectionTraversalCallback
+{
+public:
+ ChildFinder() : mMayHaveChild(false)
+ {
+ }
+
+ // The logic of the Note*Child functions must mirror that of their
+ // respective functions in CCGraphBuilder.
+ NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild);
+ NS_IMETHOD_(void) NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aHelper);
+ NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aThing);
+
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefcount,
+ const char* aObjname)
+ {
+ }
+ NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked,
+ const char* aObjname,
+ uint64_t aCompartmentAddress)
+ {
+ }
+ NS_IMETHOD_(void) NoteNextEdgeName(const char* aName)
+ {
+ }
+ bool MayHaveChild()
+ {
+ return mMayHaveChild;
+ }
+private:
+ bool mMayHaveChild;
+};
+
+NS_IMETHODIMP_(void)
+ChildFinder::NoteXPCOMChild(nsISupports* aChild)
+{
+ if (!aChild || !(aChild = CanonicalizeXPCOMParticipant(aChild))) {
+ return;
+ }
+ nsXPCOMCycleCollectionParticipant* cp;
+ ToParticipant(aChild, &cp);
+ if (cp && !cp->CanSkip(aChild, true)) {
+ mMayHaveChild = true;
+ }
+}
+
+NS_IMETHODIMP_(void)
+ChildFinder::NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aHelper)
+{
+ if (!aChild) {
+ return;
+ }
+ MOZ_ASSERT(aHelper, "Native child must have a participant");
+ if (!aHelper->CanSkip(aChild, true)) {
+ mMayHaveChild = true;
+ }
+}
+
+NS_IMETHODIMP_(void)
+ChildFinder::NoteJSChild(const JS::GCCellPtr& aChild)
+{
+ if (aChild && JS::GCThingIsMarkedGray(aChild)) {
+ mMayHaveChild = true;
+ }
+}
+
+static bool
+MayHaveChild(void* aObj, nsCycleCollectionParticipant* aCp)
+{
+ ChildFinder cf;
+ aCp->Traverse(aObj, cf);
+ return cf.MayHaveChild();
+}
+
+// JSPurpleBuffer keeps references to GCThings which might affect the
+// next cycle collection. It is owned only by itself and during unlink its
+// self reference is broken down and the object ends up killing itself.
+// If GC happens before CC, references to GCthings and the self reference are
+// removed.
+class JSPurpleBuffer
+{
+ ~JSPurpleBuffer()
+ {
+ MOZ_ASSERT(mValues.IsEmpty());
+ MOZ_ASSERT(mObjects.IsEmpty());
+ }
+
+public:
+ explicit JSPurpleBuffer(RefPtr<JSPurpleBuffer>& aReferenceToThis)
+ : mReferenceToThis(aReferenceToThis)
+ , mValues(kSegmentSize)
+ , mObjects(kSegmentSize)
+ {
+ mReferenceToThis = this;
+ mozilla::HoldJSObjects(this);
+ }
+
+ void Destroy()
+ {
+ mReferenceToThis = nullptr;
+ mValues.Clear();
+ mObjects.Clear();
+ mozilla::DropJSObjects(this);
+ }
+
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(JSPurpleBuffer)
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(JSPurpleBuffer)
+
+ RefPtr<JSPurpleBuffer>& mReferenceToThis;
+
+ // These are raw pointers instead of Heap<T> because we only need Heap<T> for
+ // pointers which may point into the nursery. The purple buffer never contains
+ // pointers to the nursery because nursery gcthings can never be gray and only
+ // gray things can be inserted into the purple buffer.
+ static const size_t kSegmentSize = 512;
+ SegmentedVector<JS::Value, kSegmentSize, InfallibleAllocPolicy> mValues;
+ SegmentedVector<JSObject*, kSegmentSize, InfallibleAllocPolicy> mObjects;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(JSPurpleBuffer)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSPurpleBuffer)
+ tmp->Destroy();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSPurpleBuffer)
+ CycleCollectionNoteChild(cb, tmp, "self");
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_TRACE_SEGMENTED_ARRAY(_field, _type) \
+ { \
+ for (auto iter = tmp->_field.Iter(); !iter.Done(); iter.Next()) { \
+ js::gc::CallTraceCallbackOnNonHeap<_type, TraceCallbacks>( \
+ &iter.Get(), aCallbacks, #_field, aClosure); \
+ } \
+ }
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(JSPurpleBuffer)
+ NS_TRACE_SEGMENTED_ARRAY(mValues, JS::Value)
+ NS_TRACE_SEGMENTED_ARRAY(mObjects, JSObject*)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSPurpleBuffer, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(JSPurpleBuffer, Release)
+
+class SnowWhiteKiller : public TraceCallbacks
+{
+ struct SnowWhiteObject
+ {
+ void* mPointer;
+ nsCycleCollectionParticipant* mParticipant;
+ nsCycleCollectingAutoRefCnt* mRefCnt;
+ };
+
+ // Segments are 4 KiB on 32-bit and 8 KiB on 64-bit.
+ static const size_t kSegmentSize = sizeof(void*) * 1024;
+ typedef SegmentedVector<SnowWhiteObject, kSegmentSize, InfallibleAllocPolicy>
+ ObjectsVector;
+
+public:
+ explicit SnowWhiteKiller(nsCycleCollector* aCollector)
+ : mCollector(aCollector)
+ , mObjects(kSegmentSize)
+ {
+ MOZ_ASSERT(mCollector, "Calling SnowWhiteKiller after nsCC went away");
+ }
+
+ ~SnowWhiteKiller()
+ {
+ for (auto iter = mObjects.Iter(); !iter.Done(); iter.Next()) {
+ SnowWhiteObject& o = iter.Get();
+ if (!o.mRefCnt->get() && !o.mRefCnt->IsInPurpleBuffer()) {
+ mCollector->RemoveObjectFromGraph(o.mPointer);
+ o.mRefCnt->stabilizeForDeletion();
+ {
+ JS::AutoEnterCycleCollection autocc(mCollector->Context()->Context());
+ o.mParticipant->Trace(o.mPointer, *this, nullptr);
+ }
+ o.mParticipant->DeleteCycleCollectable(o.mPointer);
+ }
+ }
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject, "Null object in purple buffer");
+ if (!aEntry->mRefCnt->get()) {
+ void* o = aEntry->mObject;
+ nsCycleCollectionParticipant* cp = aEntry->mParticipant;
+ CanonicalizeParticipant(&o, &cp);
+ SnowWhiteObject swo = { o, cp, aEntry->mRefCnt };
+ mObjects.InfallibleAppend(swo);
+ aBuffer.Remove(aEntry);
+ }
+ }
+
+ bool HasSnowWhiteObjects() const
+ {
+ return !mObjects.IsEmpty();
+ }
+
+ virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName,
+ void* aClosure) const override
+ {
+ const JS::Value& val = aValue->unbarrieredGet();
+ if (val.isMarkable() && ValueIsGrayCCThing(val)) {
+ MOZ_ASSERT(!js::gc::IsInsideNursery(val.toGCThing()));
+ mCollector->GetJSPurpleBuffer()->mValues.InfallibleAppend(val);
+ }
+ }
+
+ virtual void Trace(JS::Heap<jsid>* aId, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+ void AppendJSObjectToPurpleBuffer(JSObject* obj) const
+ {
+ if (obj && JS::ObjectIsMarkedGray(obj)) {
+ MOZ_ASSERT(JS::ObjectIsTenured(obj));
+ mCollector->GetJSPurpleBuffer()->mObjects.InfallibleAppend(obj);
+ }
+ }
+
+ virtual void Trace(JS::Heap<JSObject*>* aObject, const char* aName,
+ void* aClosure) const override
+ {
+ AppendJSObjectToPurpleBuffer(aObject->unbarrieredGet());
+ }
+
+ virtual void Trace(JSObject** aObject, const char* aName,
+ void* aClosure) const override
+ {
+ AppendJSObjectToPurpleBuffer(*aObject);
+ }
+
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aObject, const char* aName,
+ void* aClosure) const override
+ {
+ AppendJSObjectToPurpleBuffer(aObject->unbarrieredGetPtr());
+ }
+
+ virtual void Trace(JS::Heap<JSString*>* aString, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+ virtual void Trace(JS::Heap<JSScript*>* aScript, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+ virtual void Trace(JS::Heap<JSFunction*>* aFunction, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+private:
+ RefPtr<nsCycleCollector> mCollector;
+ ObjectsVector mObjects;
+};
+
+class RemoveSkippableVisitor : public SnowWhiteKiller
+{
+public:
+ RemoveSkippableVisitor(nsCycleCollector* aCollector,
+ bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing,
+ CC_ForgetSkippableCallback aCb)
+ : SnowWhiteKiller(aCollector)
+ , mRemoveChildlessNodes(aRemoveChildlessNodes)
+ , mAsyncSnowWhiteFreeing(aAsyncSnowWhiteFreeing)
+ , mDispatchedDeferredDeletion(false)
+ , mCallback(aCb)
+ {
+ }
+
+ ~RemoveSkippableVisitor()
+ {
+ // Note, we must call the callback before SnowWhiteKiller calls
+ // DeleteCycleCollectable!
+ if (mCallback) {
+ mCallback();
+ }
+ if (HasSnowWhiteObjects()) {
+ // Effectively a continuation.
+ nsCycleCollector_dispatchDeferredDeletion(true);
+ }
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject, "null mObject in purple buffer");
+ if (!aEntry->mRefCnt->get()) {
+ if (!mAsyncSnowWhiteFreeing) {
+ SnowWhiteKiller::Visit(aBuffer, aEntry);
+ } else if (!mDispatchedDeferredDeletion) {
+ mDispatchedDeferredDeletion = true;
+ nsCycleCollector_dispatchDeferredDeletion(false);
+ }
+ return;
+ }
+ void* o = aEntry->mObject;
+ nsCycleCollectionParticipant* cp = aEntry->mParticipant;
+ CanonicalizeParticipant(&o, &cp);
+ if (aEntry->mRefCnt->IsPurple() && !cp->CanSkip(o, false) &&
+ (!mRemoveChildlessNodes || MayHaveChild(o, cp))) {
+ return;
+ }
+ aBuffer.Remove(aEntry);
+ }
+
+private:
+ bool mRemoveChildlessNodes;
+ bool mAsyncSnowWhiteFreeing;
+ bool mDispatchedDeferredDeletion;
+ CC_ForgetSkippableCallback mCallback;
+};
+
+void
+nsPurpleBuffer::RemoveSkippable(nsCycleCollector* aCollector,
+ bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing,
+ CC_ForgetSkippableCallback aCb)
+{
+ RemoveSkippableVisitor visitor(aCollector, aRemoveChildlessNodes,
+ aAsyncSnowWhiteFreeing, aCb);
+ VisitEntries(visitor);
+}
+
+bool
+nsCycleCollector::FreeSnowWhite(bool aUntilNoSWInPurpleBuffer)
+{
+ CheckThreadSafety();
+
+ if (mFreeingSnowWhite) {
+ return false;
+ }
+
+ AutoRestore<bool> ar(mFreeingSnowWhite);
+ mFreeingSnowWhite = true;
+
+ bool hadSnowWhiteObjects = false;
+ do {
+ SnowWhiteKiller visitor(this);
+ mPurpleBuf.VisitEntries(visitor);
+ hadSnowWhiteObjects = hadSnowWhiteObjects ||
+ visitor.HasSnowWhiteObjects();
+ if (!visitor.HasSnowWhiteObjects()) {
+ break;
+ }
+ } while (aUntilNoSWInPurpleBuffer);
+ return hadSnowWhiteObjects;
+}
+
+void
+nsCycleCollector::ForgetSkippable(bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing)
+{
+ CheckThreadSafety();
+
+ mozilla::Maybe<mozilla::AutoGlobalTimelineMarker> marker;
+ if (NS_IsMainThread()) {
+ marker.emplace("nsCycleCollector::ForgetSkippable", MarkerStackRequest::NO_STACK);
+ }
+
+ // If we remove things from the purple buffer during graph building, we may
+ // lose track of an object that was mutated during graph building.
+ MOZ_ASSERT(IsIdle());
+
+ if (mJSContext) {
+ mJSContext->PrepareForForgetSkippable();
+ }
+ MOZ_ASSERT(!mScanInProgress,
+ "Don't forget skippable or free snow-white while scan is in progress.");
+ mPurpleBuf.RemoveSkippable(this, aRemoveChildlessNodes,
+ aAsyncSnowWhiteFreeing, mForgetSkippableCB);
+}
+
+MOZ_NEVER_INLINE void
+nsCycleCollector::MarkRoots(SliceBudget& aBudget)
+{
+ JS::AutoAssertNoGC nogc;
+ TimeLog timeLog;
+ AutoRestore<bool> ar(mScanInProgress);
+ MOZ_RELEASE_ASSERT(!mScanInProgress);
+ mScanInProgress = true;
+ MOZ_ASSERT(mIncrementalPhase == GraphBuildingPhase);
+
+ JS::AutoEnterCycleCollection autocc(Context()->Context());
+ bool doneBuilding = mBuilder->BuildGraph(aBudget);
+
+ if (!doneBuilding) {
+ timeLog.Checkpoint("MarkRoots()");
+ return;
+ }
+
+ mBuilder = nullptr;
+ mIncrementalPhase = ScanAndCollectWhitePhase;
+ timeLog.Checkpoint("MarkRoots()");
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Bacon & Rajan's |ScanRoots| routine.
+////////////////////////////////////////////////////////////////////////
+
+
+struct ScanBlackVisitor
+{
+ ScanBlackVisitor(uint32_t& aWhiteNodeCount, bool& aFailed)
+ : mWhiteNodeCount(aWhiteNodeCount), mFailed(aFailed)
+ {
+ }
+
+ bool ShouldVisitNode(PtrInfo const* aPi)
+ {
+ return aPi->mColor != black;
+ }
+
+ MOZ_NEVER_INLINE void VisitNode(PtrInfo* aPi)
+ {
+ if (aPi->mColor == white) {
+ --mWhiteNodeCount;
+ }
+ aPi->mColor = black;
+ }
+
+ void Failed()
+ {
+ mFailed = true;
+ }
+
+private:
+ uint32_t& mWhiteNodeCount;
+ bool& mFailed;
+};
+
+static void
+FloodBlackNode(uint32_t& aWhiteNodeCount, bool& aFailed, PtrInfo* aPi)
+{
+ GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(aWhiteNodeCount,
+ aFailed)).Walk(aPi);
+ MOZ_ASSERT(aPi->mColor == black || !aPi->WasTraversed(),
+ "FloodBlackNode should make aPi black");
+}
+
+// Iterate over the WeakMaps. If we mark anything while iterating
+// over the WeakMaps, we must iterate over all of the WeakMaps again.
+void
+nsCycleCollector::ScanWeakMaps()
+{
+ bool anyChanged;
+ bool failed = false;
+ do {
+ anyChanged = false;
+ for (uint32_t i = 0; i < mGraph.mWeakMaps.Length(); i++) {
+ WeakMapping* wm = &mGraph.mWeakMaps[i];
+
+ // If any of these are null, the original object was marked black.
+ uint32_t mColor = wm->mMap ? wm->mMap->mColor : black;
+ uint32_t kColor = wm->mKey ? wm->mKey->mColor : black;
+ uint32_t kdColor = wm->mKeyDelegate ? wm->mKeyDelegate->mColor : black;
+ uint32_t vColor = wm->mVal ? wm->mVal->mColor : black;
+
+ MOZ_ASSERT(mColor != grey, "Uncolored weak map");
+ MOZ_ASSERT(kColor != grey, "Uncolored weak map key");
+ MOZ_ASSERT(kdColor != grey, "Uncolored weak map key delegate");
+ MOZ_ASSERT(vColor != grey, "Uncolored weak map value");
+
+ if (mColor == black && kColor != black && kdColor == black) {
+ FloodBlackNode(mWhiteNodeCount, failed, wm->mKey);
+ anyChanged = true;
+ }
+
+ if (mColor == black && kColor == black && vColor != black) {
+ FloodBlackNode(mWhiteNodeCount, failed, wm->mVal);
+ anyChanged = true;
+ }
+ }
+ } while (anyChanged);
+
+ if (failed) {
+ MOZ_ASSERT(false, "Ran out of memory in ScanWeakMaps");
+ CC_TELEMETRY(_OOM, true);
+ }
+}
+
+// Flood black from any objects in the purple buffer that are in the CC graph.
+class PurpleScanBlackVisitor
+{
+public:
+ PurpleScanBlackVisitor(CCGraph& aGraph, nsCycleCollectorLogger* aLogger,
+ uint32_t& aCount, bool& aFailed)
+ : mGraph(aGraph), mLogger(aLogger), mCount(aCount), mFailed(aFailed)
+ {
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject,
+ "Entries with null mObject shouldn't be in the purple buffer.");
+ MOZ_ASSERT(aEntry->mRefCnt->get() != 0,
+ "Snow-white objects shouldn't be in the purple buffer.");
+
+ void* obj = aEntry->mObject;
+ if (!aEntry->mParticipant) {
+ obj = CanonicalizeXPCOMParticipant(static_cast<nsISupports*>(obj));
+ MOZ_ASSERT(obj, "Don't add objects that don't participate in collection!");
+ }
+
+ PtrInfo* pi = mGraph.FindNode(obj);
+ if (!pi) {
+ return;
+ }
+ MOZ_ASSERT(pi->mParticipant, "No dead objects should be in the purple buffer.");
+ if (MOZ_UNLIKELY(mLogger)) {
+ mLogger->NoteIncrementalRoot((uint64_t)pi->mPointer);
+ }
+ if (pi->mColor == black) {
+ return;
+ }
+ FloodBlackNode(mCount, mFailed, pi);
+ }
+
+private:
+ CCGraph& mGraph;
+ RefPtr<nsCycleCollectorLogger> mLogger;
+ uint32_t& mCount;
+ bool& mFailed;
+};
+
+// Objects that have been stored somewhere since the start of incremental graph building must
+// be treated as live for this cycle collection, because we may not have accurate information
+// about who holds references to them.
+void
+nsCycleCollector::ScanIncrementalRoots()
+{
+ TimeLog timeLog;
+
+ // Reference counted objects:
+ // We cleared the purple buffer at the start of the current ICC, so if a
+ // refcounted object is purple, it may have been AddRef'd during the current
+ // ICC. (It may also have only been released.) If that is the case, we cannot
+ // be sure that the set of things pointing to the object in the CC graph
+ // is accurate. Therefore, for safety, we treat any purple objects as being
+ // live during the current CC. We don't remove anything from the purple
+ // buffer here, so these objects will be suspected and freed in the next CC
+ // if they are garbage.
+ bool failed = false;
+ PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mLogger,
+ mWhiteNodeCount, failed);
+ mPurpleBuf.VisitEntries(purpleScanBlackVisitor);
+ timeLog.Checkpoint("ScanIncrementalRoots::fix purple");
+
+ bool hasJSContext = !!mJSContext;
+ nsCycleCollectionParticipant* jsParticipant =
+ hasJSContext ? mJSContext->GCThingParticipant() : nullptr;
+ nsCycleCollectionParticipant* zoneParticipant =
+ hasJSContext ? mJSContext->ZoneParticipant() : nullptr;
+ bool hasLogger = !!mLogger;
+
+ NodePool::Enumerator etor(mGraph.mNodes);
+ while (!etor.IsDone()) {
+ PtrInfo* pi = etor.GetNext();
+
+ // As an optimization, if an object has already been determined to be live,
+ // don't consider it further. We can't do this if there is a listener,
+ // because the listener wants to know the complete set of incremental roots.
+ if (pi->mColor == black && MOZ_LIKELY(!hasLogger)) {
+ continue;
+ }
+
+ // Garbage collected objects:
+ // If a GCed object was added to the graph with a refcount of zero, and is
+ // now marked black by the GC, it was probably gray before and was exposed
+ // to active JS, so it may have been stored somewhere, so it needs to be
+ // treated as live.
+ if (pi->IsGrayJS() && MOZ_LIKELY(hasJSContext)) {
+ // If the object is still marked gray by the GC, nothing could have gotten
+ // hold of it, so it isn't an incremental root.
+ if (pi->mParticipant == jsParticipant) {
+ JS::GCCellPtr ptr(pi->mPointer, JS::GCThingTraceKind(pi->mPointer));
+ if (GCThingIsGrayCCThing(ptr)) {
+ continue;
+ }
+ } else if (pi->mParticipant == zoneParticipant) {
+ JS::Zone* zone = static_cast<JS::Zone*>(pi->mPointer);
+ if (js::ZoneGlobalsAreAllGray(zone)) {
+ continue;
+ }
+ } else {
+ MOZ_ASSERT(false, "Non-JS thing with 0 refcount? Treating as live.");
+ }
+ } else if (!pi->mParticipant && pi->WasTraversed()) {
+ // Dead traversed refcounted objects:
+ // If the object was traversed, it must have been alive at the start of
+ // the CC, and thus had a positive refcount. It is dead now, so its
+ // refcount must have decreased at some point during the CC. Therefore,
+ // it would be in the purple buffer if it wasn't dead, so treat it as an
+ // incremental root.
+ //
+ // This should not cause leaks because as the object died it should have
+ // released anything it held onto, which will add them to the purple
+ // buffer, which will cause them to be considered in the next CC.
+ } else {
+ continue;
+ }
+
+ // At this point, pi must be an incremental root.
+
+ // If there's a listener, tell it about this root. We don't bother with the
+ // optimization of skipping the Walk() if pi is black: it will just return
+ // without doing anything and there's no need to make this case faster.
+ if (MOZ_UNLIKELY(hasLogger) && pi->mPointer) {
+ // Dead objects aren't logged. See bug 1031370.
+ mLogger->NoteIncrementalRoot((uint64_t)pi->mPointer);
+ }
+
+ FloodBlackNode(mWhiteNodeCount, failed, pi);
+ }
+
+ timeLog.Checkpoint("ScanIncrementalRoots::fix nodes");
+
+ if (failed) {
+ NS_ASSERTION(false, "Ran out of memory in ScanIncrementalRoots");
+ CC_TELEMETRY(_OOM, true);
+ }
+}
+
+// Mark nodes white and make sure their refcounts are ok.
+// No nodes are marked black during this pass to ensure that refcount
+// checking is run on all nodes not marked black by ScanIncrementalRoots.
+void
+nsCycleCollector::ScanWhiteNodes(bool aFullySynchGraphBuild)
+{
+ NodePool::Enumerator nodeEnum(mGraph.mNodes);
+ while (!nodeEnum.IsDone()) {
+ PtrInfo* pi = nodeEnum.GetNext();
+ if (pi->mColor == black) {
+ // Incremental roots can be in a nonsensical state, so don't
+ // check them. This will miss checking nodes that are merely
+ // reachable from incremental roots.
+ MOZ_ASSERT(!aFullySynchGraphBuild,
+ "In a synch CC, no nodes should be marked black early on.");
+ continue;
+ }
+ MOZ_ASSERT(pi->mColor == grey);
+
+ if (!pi->WasTraversed()) {
+ // This node was deleted before it was traversed, so there's no reason
+ // to look at it.
+ MOZ_ASSERT(!pi->mParticipant, "Live nodes should all have been traversed");
+ continue;
+ }
+
+ if (pi->mInternalRefs == pi->mRefCount || pi->IsGrayJS()) {
+ pi->mColor = white;
+ ++mWhiteNodeCount;
+ continue;
+ }
+
+ if (pi->mInternalRefs > pi->mRefCount) {
+#ifdef MOZ_CRASHREPORTER
+ const char* piName = "Unknown";
+ if (pi->mParticipant) {
+ piName = pi->mParticipant->ClassName();
+ }
+ nsPrintfCString msg("More references to an object than its refcount, for class %s", piName);
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CycleCollector"), msg);
+#endif
+ MOZ_CRASH();
+ }
+
+ // This node will get marked black in the next pass.
+ }
+}
+
+// Any remaining grey nodes that haven't already been deleted must be alive,
+// so mark them and their children black. Any nodes that are black must have
+// already had their children marked black, so there's no need to look at them
+// again. This pass may turn some white nodes to black.
+void
+nsCycleCollector::ScanBlackNodes()
+{
+ bool failed = false;
+ NodePool::Enumerator nodeEnum(mGraph.mNodes);
+ while (!nodeEnum.IsDone()) {
+ PtrInfo* pi = nodeEnum.GetNext();
+ if (pi->mColor == grey && pi->WasTraversed()) {
+ FloodBlackNode(mWhiteNodeCount, failed, pi);
+ }
+ }
+
+ if (failed) {
+ NS_ASSERTION(false, "Ran out of memory in ScanBlackNodes");
+ CC_TELEMETRY(_OOM, true);
+ }
+}
+
+void
+nsCycleCollector::ScanRoots(bool aFullySynchGraphBuild)
+{
+ JS::AutoAssertNoGC nogc;
+ AutoRestore<bool> ar(mScanInProgress);
+ MOZ_RELEASE_ASSERT(!mScanInProgress);
+ mScanInProgress = true;
+ mWhiteNodeCount = 0;
+ MOZ_ASSERT(mIncrementalPhase == ScanAndCollectWhitePhase);
+
+ JS::AutoEnterCycleCollection autocc(Context()->Context());
+
+ if (!aFullySynchGraphBuild) {
+ ScanIncrementalRoots();
+ }
+
+ TimeLog timeLog;
+ ScanWhiteNodes(aFullySynchGraphBuild);
+ timeLog.Checkpoint("ScanRoots::ScanWhiteNodes");
+
+ ScanBlackNodes();
+ timeLog.Checkpoint("ScanRoots::ScanBlackNodes");
+
+ // Scanning weak maps must be done last.
+ ScanWeakMaps();
+ timeLog.Checkpoint("ScanRoots::ScanWeakMaps");
+
+ if (mLogger) {
+ mLogger->BeginResults();
+
+ NodePool::Enumerator etor(mGraph.mNodes);
+ while (!etor.IsDone()) {
+ PtrInfo* pi = etor.GetNext();
+ if (!pi->WasTraversed()) {
+ continue;
+ }
+ switch (pi->mColor) {
+ case black:
+ if (!pi->IsGrayJS() && !pi->IsBlackJS() &&
+ pi->mInternalRefs != pi->mRefCount) {
+ mLogger->DescribeRoot((uint64_t)pi->mPointer,
+ pi->mInternalRefs);
+ }
+ break;
+ case white:
+ mLogger->DescribeGarbage((uint64_t)pi->mPointer);
+ break;
+ case grey:
+ MOZ_ASSERT(false, "All traversed objects should be black or white");
+ break;
+ }
+ }
+
+ mLogger->End();
+ mLogger = nullptr;
+ timeLog.Checkpoint("ScanRoots::listener");
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Bacon & Rajan's |CollectWhite| routine, somewhat modified.
+////////////////////////////////////////////////////////////////////////
+
+bool
+nsCycleCollector::CollectWhite()
+{
+ // Explanation of "somewhat modified": we have no way to collect the
+ // set of whites "all at once", we have to ask each of them to drop
+ // their outgoing links and assume this will cause the garbage cycle
+ // to *mostly* self-destruct (except for the reference we continue
+ // to hold).
+ //
+ // To do this "safely" we must make sure that the white nodes we're
+ // operating on are stable for the duration of our operation. So we
+ // make 3 sets of calls to language runtimes:
+ //
+ // - Root(whites), which should pin the whites in memory.
+ // - Unlink(whites), which drops outgoing links on each white.
+ // - Unroot(whites), which returns the whites to normal GC.
+
+ // Segments are 4 KiB on 32-bit and 8 KiB on 64-bit.
+ static const size_t kSegmentSize = sizeof(void*) * 1024;
+ SegmentedVector<PtrInfo*, kSegmentSize, InfallibleAllocPolicy>
+ whiteNodes(kSegmentSize);
+ TimeLog timeLog;
+
+ MOZ_ASSERT(mIncrementalPhase == ScanAndCollectWhitePhase);
+
+ uint32_t numWhiteNodes = 0;
+ uint32_t numWhiteGCed = 0;
+ uint32_t numWhiteJSZones = 0;
+
+ {
+ JS::AutoAssertNoGC nogc;
+ bool hasJSContext = !!mJSContext;
+ nsCycleCollectionParticipant* zoneParticipant =
+ hasJSContext ? mJSContext->ZoneParticipant() : nullptr;
+
+ NodePool::Enumerator etor(mGraph.mNodes);
+ while (!etor.IsDone()) {
+ PtrInfo* pinfo = etor.GetNext();
+ if (pinfo->mColor == white && pinfo->mParticipant) {
+ if (pinfo->IsGrayJS()) {
+ MOZ_ASSERT(mJSContext);
+ ++numWhiteGCed;
+ JS::Zone* zone;
+ if (MOZ_UNLIKELY(pinfo->mParticipant == zoneParticipant)) {
+ ++numWhiteJSZones;
+ zone = static_cast<JS::Zone*>(pinfo->mPointer);
+ } else {
+ JS::GCCellPtr ptr(pinfo->mPointer, JS::GCThingTraceKind(pinfo->mPointer));
+ zone = JS::GetTenuredGCThingZone(ptr);
+ }
+ mJSContext->AddZoneWaitingForGC(zone);
+ } else {
+ whiteNodes.InfallibleAppend(pinfo);
+ pinfo->mParticipant->Root(pinfo->mPointer);
+ ++numWhiteNodes;
+ }
+ }
+ }
+ }
+
+ mResults.mFreedRefCounted += numWhiteNodes;
+ mResults.mFreedGCed += numWhiteGCed;
+ mResults.mFreedJSZones += numWhiteJSZones;
+
+ timeLog.Checkpoint("CollectWhite::Root");
+
+ if (mBeforeUnlinkCB) {
+ mBeforeUnlinkCB();
+ timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB");
+ }
+
+ // Unlink() can trigger a GC, so do not touch any JS or anything
+ // else not in whiteNodes after here.
+
+ for (auto iter = whiteNodes.Iter(); !iter.Done(); iter.Next()) {
+ PtrInfo* pinfo = iter.Get();
+ MOZ_ASSERT(pinfo->mParticipant,
+ "Unlink shouldn't see objects removed from graph.");
+ pinfo->mParticipant->Unlink(pinfo->mPointer);
+#ifdef DEBUG
+ if (mJSContext) {
+ mJSContext->AssertNoObjectsToTrace(pinfo->mPointer);
+ }
+#endif
+ }
+ timeLog.Checkpoint("CollectWhite::Unlink");
+
+ JS::AutoAssertNoGC nogc;
+ for (auto iter = whiteNodes.Iter(); !iter.Done(); iter.Next()) {
+ PtrInfo* pinfo = iter.Get();
+ MOZ_ASSERT(pinfo->mParticipant,
+ "Unroot shouldn't see objects removed from graph.");
+ pinfo->mParticipant->Unroot(pinfo->mPointer);
+ }
+ timeLog.Checkpoint("CollectWhite::Unroot");
+
+ nsCycleCollector_dispatchDeferredDeletion(false, true);
+ timeLog.Checkpoint("CollectWhite::dispatchDeferredDeletion");
+
+ mIncrementalPhase = CleanupPhase;
+
+ return numWhiteNodes > 0 || numWhiteGCed > 0 || numWhiteJSZones > 0;
+}
+
+
+////////////////////////
+// Memory reporting
+////////////////////////
+
+MOZ_DEFINE_MALLOC_SIZE_OF(CycleCollectorMallocSizeOf)
+
+NS_IMETHODIMP
+nsCycleCollector::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ size_t objectSize, graphSize, purpleBufferSize;
+ SizeOfIncludingThis(CycleCollectorMallocSizeOf,
+ &objectSize, &graphSize,
+ &purpleBufferSize);
+
+ if (objectSize > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/cycle-collector/collector-object", KIND_HEAP, UNITS_BYTES,
+ objectSize,
+ "Memory used for the cycle collector object itself.");
+ }
+
+ if (graphSize > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/cycle-collector/graph", KIND_HEAP, UNITS_BYTES,
+ graphSize,
+ "Memory used for the cycle collector's graph. This should be zero when "
+ "the collector is idle.");
+ }
+
+ if (purpleBufferSize > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/cycle-collector/purple-buffer", KIND_HEAP, UNITS_BYTES,
+ purpleBufferSize,
+ "Memory used for the cycle collector's purple buffer.");
+ }
+
+ return NS_OK;
+};
+
+
+////////////////////////////////////////////////////////////////////////
+// Collector implementation
+////////////////////////////////////////////////////////////////////////
+
+nsCycleCollector::nsCycleCollector() :
+ mActivelyCollecting(false),
+ mFreeingSnowWhite(false),
+ mScanInProgress(false),
+ mJSContext(nullptr),
+ mIncrementalPhase(IdlePhase),
+#ifdef DEBUG
+ mThread(NS_GetCurrentThread()),
+#endif
+ mWhiteNodeCount(0),
+ mBeforeUnlinkCB(nullptr),
+ mForgetSkippableCB(nullptr),
+ mUnmergedNeeded(0),
+ mMergedInARow(0)
+{
+}
+
+nsCycleCollector::~nsCycleCollector()
+{
+ UnregisterWeakMemoryReporter(this);
+}
+
+void
+nsCycleCollector::RegisterJSContext(CycleCollectedJSContext* aJSContext)
+{
+ MOZ_RELEASE_ASSERT(!mJSContext, "Multiple registrations of JS context in cycle collector");
+ mJSContext = aJSContext;
+
+ if (!NS_IsMainThread()) {
+ return;
+ }
+
+ // We can't register as a reporter in nsCycleCollector() because that runs
+ // before the memory reporter manager is initialized. So we do it here
+ // instead.
+ RegisterWeakMemoryReporter(this);
+}
+
+void
+nsCycleCollector::ForgetJSContext()
+{
+ MOZ_RELEASE_ASSERT(mJSContext, "Forgetting JS context in cycle collector before a JS context was registered");
+ mJSContext = nullptr;
+}
+
+#ifdef DEBUG
+static bool
+HasParticipant(void* aPtr, nsCycleCollectionParticipant* aParti)
+{
+ if (aParti) {
+ return true;
+ }
+
+ nsXPCOMCycleCollectionParticipant* xcp;
+ ToParticipant(static_cast<nsISupports*>(aPtr), &xcp);
+ return xcp != nullptr;
+}
+#endif
+
+MOZ_ALWAYS_INLINE void
+nsCycleCollector::Suspect(void* aPtr, nsCycleCollectionParticipant* aParti,
+ nsCycleCollectingAutoRefCnt* aRefCnt)
+{
+ CheckThreadSafety();
+
+ // Don't call AddRef or Release of a CCed object in a Traverse() method.
+ MOZ_ASSERT(!mScanInProgress, "Attempted to call Suspect() while a scan was in progress");
+
+ if (MOZ_UNLIKELY(mScanInProgress)) {
+ return;
+ }
+
+ MOZ_ASSERT(aPtr, "Don't suspect null pointers");
+
+ MOZ_ASSERT(HasParticipant(aPtr, aParti),
+ "Suspected nsISupports pointer must QI to nsXPCOMCycleCollectionParticipant");
+
+ mPurpleBuf.Put(aPtr, aParti, aRefCnt);
+}
+
+void
+nsCycleCollector::CheckThreadSafety()
+{
+#ifdef DEBUG
+ nsIThread* currentThread = NS_GetCurrentThread();
+ // XXXkhuey we can be called so late in shutdown that NS_GetCurrentThread
+ // returns null (after the thread manager has shut down)
+ MOZ_ASSERT(mThread == currentThread || !currentThread);
+#endif
+}
+
+// The cycle collector uses the mark bitmap to discover what JS objects
+// were reachable only from XPConnect roots that might participate in
+// cycles. We ask the JS context whether we need to force a GC before
+// this CC. It returns true on startup (before the mark bits have been set),
+// and also when UnmarkGray has run out of stack. We also force GCs on shut
+// down to collect cycles involving both DOM and JS.
+void
+nsCycleCollector::FixGrayBits(bool aForceGC, TimeLog& aTimeLog)
+{
+ CheckThreadSafety();
+
+ if (!mJSContext) {
+ return;
+ }
+
+ if (!aForceGC) {
+ mJSContext->FixWeakMappingGrayBits();
+ aTimeLog.Checkpoint("FixWeakMappingGrayBits");
+
+ bool needGC = !mJSContext->AreGCGrayBitsValid();
+ // Only do a telemetry ping for non-shutdown CCs.
+ CC_TELEMETRY(_NEED_GC, needGC);
+ if (!needGC) {
+ return;
+ }
+ mResults.mForcedGC = true;
+ }
+
+ mJSContext->GarbageCollect(aForceGC ? JS::gcreason::SHUTDOWN_CC :
+ JS::gcreason::CC_FORCED);
+ aTimeLog.Checkpoint("FixGrayBits GC");
+}
+
+bool
+nsCycleCollector::IsIncrementalGCInProgress()
+{
+ return mJSContext && JS::IsIncrementalGCInProgress(mJSContext->Context());
+}
+
+void
+nsCycleCollector::FinishAnyIncrementalGCInProgress()
+{
+ if (IsIncrementalGCInProgress()) {
+ NS_WARNING("Finishing incremental GC in progress during CC");
+ JS::PrepareForIncrementalGC(mJSContext->Context());
+ JS::FinishIncrementalGC(mJSContext->Context(), JS::gcreason::CC_FORCED);
+ }
+}
+
+void
+nsCycleCollector::CleanupAfterCollection()
+{
+ TimeLog timeLog;
+ MOZ_ASSERT(mIncrementalPhase == CleanupPhase);
+ mGraph.Clear();
+ timeLog.Checkpoint("CleanupAfterCollection::mGraph.Clear()");
+
+ uint32_t interval =
+ (uint32_t)((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
+#ifdef COLLECT_TIME_DEBUG
+ printf("cc: total cycle collector time was %ums in %u slices\n", interval,
+ mResults.mNumSlices);
+ printf("cc: visited %u ref counted and %u GCed objects, freed %d ref counted and %d GCed objects",
+ mResults.mVisitedRefCounted, mResults.mVisitedGCed,
+ mResults.mFreedRefCounted, mResults.mFreedGCed);
+ uint32_t numVisited = mResults.mVisitedRefCounted + mResults.mVisitedGCed;
+ if (numVisited > 1000) {
+ uint32_t numFreed = mResults.mFreedRefCounted + mResults.mFreedGCed;
+ printf(" (%d%%)", 100 * numFreed / numVisited);
+ }
+ printf(".\ncc: \n");
+#endif
+
+ CC_TELEMETRY( , interval);
+ CC_TELEMETRY(_VISITED_REF_COUNTED, mResults.mVisitedRefCounted);
+ CC_TELEMETRY(_VISITED_GCED, mResults.mVisitedGCed);
+ CC_TELEMETRY(_COLLECTED, mWhiteNodeCount);
+ timeLog.Checkpoint("CleanupAfterCollection::telemetry");
+
+ if (mJSContext) {
+ mJSContext->FinalizeDeferredThings(mResults.mAnyManual
+ ? CycleCollectedJSContext::FinalizeNow
+ : CycleCollectedJSContext::FinalizeIncrementally);
+ mJSContext->EndCycleCollectionCallback(mResults);
+ timeLog.Checkpoint("CleanupAfterCollection::EndCycleCollectionCallback()");
+ }
+ mIncrementalPhase = IdlePhase;
+}
+
+void
+nsCycleCollector::ShutdownCollect()
+{
+ FinishAnyIncrementalGCInProgress();
+
+ SliceBudget unlimitedBudget = SliceBudget::unlimited();
+ uint32_t i;
+ for (i = 0; i < DEFAULT_SHUTDOWN_COLLECTIONS; ++i) {
+ if (!Collect(ShutdownCC, unlimitedBudget, nullptr)) {
+ break;
+ }
+ }
+ NS_WARNING_ASSERTION(i < NORMAL_SHUTDOWN_COLLECTIONS, "Extra shutdown CC");
+}
+
+static void
+PrintPhase(const char* aPhase)
+{
+#ifdef DEBUG_PHASES
+ printf("cc: begin %s on %s\n", aPhase,
+ NS_IsMainThread() ? "mainthread" : "worker");
+#endif
+}
+
+bool
+nsCycleCollector::Collect(ccType aCCType,
+ SliceBudget& aBudget,
+ nsICycleCollectorListener* aManualListener,
+ bool aPreferShorterSlices)
+{
+ CheckThreadSafety();
+
+ // This can legitimately happen in a few cases. See bug 383651.
+ if (mActivelyCollecting || mFreeingSnowWhite) {
+ return false;
+ }
+ mActivelyCollecting = true;
+
+ MOZ_ASSERT(!IsIncrementalGCInProgress());
+
+ mozilla::Maybe<mozilla::AutoGlobalTimelineMarker> marker;
+ if (NS_IsMainThread()) {
+ marker.emplace("nsCycleCollector::Collect", MarkerStackRequest::NO_STACK);
+ }
+
+ bool startedIdle = IsIdle();
+ bool collectedAny = false;
+
+ // If the CC started idle, it will call BeginCollection, which
+ // will do FreeSnowWhite, so it doesn't need to be done here.
+ if (!startedIdle) {
+ TimeLog timeLog;
+ FreeSnowWhite(true);
+ timeLog.Checkpoint("Collect::FreeSnowWhite");
+ }
+
+ if (aCCType != SliceCC) {
+ mResults.mAnyManual = true;
+ }
+
+ ++mResults.mNumSlices;
+
+ bool continueSlice = aBudget.isUnlimited() || !aPreferShorterSlices;
+ do {
+ switch (mIncrementalPhase) {
+ case IdlePhase:
+ PrintPhase("BeginCollection");
+ BeginCollection(aCCType, aManualListener);
+ break;
+ case GraphBuildingPhase:
+ PrintPhase("MarkRoots");
+ MarkRoots(aBudget);
+
+ // Only continue this slice if we're running synchronously or the
+ // next phase will probably be short, to reduce the max pause for this
+ // collection.
+ // (There's no need to check if we've finished graph building, because
+ // if we haven't, we've already exceeded our budget, and will finish
+ // this slice anyways.)
+ continueSlice = aBudget.isUnlimited() ||
+ (mResults.mNumSlices < 3 && !aPreferShorterSlices);
+ break;
+ case ScanAndCollectWhitePhase:
+ // We do ScanRoots and CollectWhite in a single slice to ensure
+ // that we won't unlink a live object if a weak reference is
+ // promoted to a strong reference after ScanRoots has finished.
+ // See bug 926533.
+ PrintPhase("ScanRoots");
+ ScanRoots(startedIdle);
+ PrintPhase("CollectWhite");
+ collectedAny = CollectWhite();
+ break;
+ case CleanupPhase:
+ PrintPhase("CleanupAfterCollection");
+ CleanupAfterCollection();
+ continueSlice = false;
+ break;
+ }
+ if (continueSlice) {
+ // Force SliceBudget::isOverBudget to check the time.
+ aBudget.step(SliceBudget::CounterReset);
+ continueSlice = !aBudget.isOverBudget();
+ }
+ } while (continueSlice);
+
+ // Clear mActivelyCollecting here to ensure that a recursive call to
+ // Collect() does something.
+ mActivelyCollecting = false;
+
+ if (aCCType != SliceCC && !startedIdle) {
+ // We were in the middle of an incremental CC (using its own listener).
+ // Somebody has forced a CC, so after having finished out the current CC,
+ // run the CC again using the new listener.
+ MOZ_ASSERT(IsIdle());
+ if (Collect(aCCType, aBudget, aManualListener)) {
+ collectedAny = true;
+ }
+ }
+
+ MOZ_ASSERT_IF(aCCType != SliceCC, IsIdle());
+
+ return collectedAny;
+}
+
+// Any JS objects we have in the graph could die when we GC, but we
+// don't want to abandon the current CC, because the graph contains
+// information about purple roots. So we synchronously finish off
+// the current CC.
+void
+nsCycleCollector::PrepareForGarbageCollection()
+{
+ if (IsIdle()) {
+ MOZ_ASSERT(mGraph.IsEmpty(), "Non-empty graph when idle");
+ MOZ_ASSERT(!mBuilder, "Non-null builder when idle");
+ if (mJSPurpleBuffer) {
+ mJSPurpleBuffer->Destroy();
+ }
+ return;
+ }
+
+ FinishAnyCurrentCollection();
+}
+
+void
+nsCycleCollector::FinishAnyCurrentCollection()
+{
+ if (IsIdle()) {
+ return;
+ }
+
+ SliceBudget unlimitedBudget = SliceBudget::unlimited();
+ PrintPhase("FinishAnyCurrentCollection");
+ // Use SliceCC because we only want to finish the CC in progress.
+ Collect(SliceCC, unlimitedBudget, nullptr);
+
+ // It is only okay for Collect() to have failed to finish the
+ // current CC if we're reentering the CC at some point past
+ // graph building. We need to be past the point where the CC will
+ // look at JS objects so that it is safe to GC.
+ MOZ_ASSERT(IsIdle() ||
+ (mActivelyCollecting && mIncrementalPhase != GraphBuildingPhase),
+ "Reentered CC during graph building");
+}
+
+// Don't merge too many times in a row, and do at least a minimum
+// number of unmerged CCs in a row.
+static const uint32_t kMinConsecutiveUnmerged = 3;
+static const uint32_t kMaxConsecutiveMerged = 3;
+
+bool
+nsCycleCollector::ShouldMergeZones(ccType aCCType)
+{
+ if (!mJSContext) {
+ return false;
+ }
+
+ MOZ_ASSERT(mUnmergedNeeded <= kMinConsecutiveUnmerged);
+ MOZ_ASSERT(mMergedInARow <= kMaxConsecutiveMerged);
+
+ if (mMergedInARow == kMaxConsecutiveMerged) {
+ MOZ_ASSERT(mUnmergedNeeded == 0);
+ mUnmergedNeeded = kMinConsecutiveUnmerged;
+ }
+
+ if (mUnmergedNeeded > 0) {
+ mUnmergedNeeded--;
+ mMergedInARow = 0;
+ return false;
+ }
+
+ if (aCCType == SliceCC && mJSContext->UsefulToMergeZones()) {
+ mMergedInARow++;
+ return true;
+ } else {
+ mMergedInARow = 0;
+ return false;
+ }
+}
+
+void
+nsCycleCollector::BeginCollection(ccType aCCType,
+ nsICycleCollectorListener* aManualListener)
+{
+ TimeLog timeLog;
+ MOZ_ASSERT(IsIdle());
+
+ mCollectionStart = TimeStamp::Now();
+
+ if (mJSContext) {
+ mJSContext->BeginCycleCollectionCallback();
+ timeLog.Checkpoint("BeginCycleCollectionCallback()");
+ }
+
+ bool isShutdown = (aCCType == ShutdownCC);
+
+ // Set up the listener for this CC.
+ MOZ_ASSERT_IF(isShutdown, !aManualListener);
+ MOZ_ASSERT(!mLogger, "Forgot to clear a previous listener?");
+
+ if (aManualListener) {
+ aManualListener->AsLogger(getter_AddRefs(mLogger));
+ }
+
+ aManualListener = nullptr;
+ if (!mLogger && mParams.LogThisCC(isShutdown)) {
+ mLogger = new nsCycleCollectorLogger();
+ if (mParams.AllTracesThisCC(isShutdown)) {
+ mLogger->SetAllTraces();
+ }
+ }
+
+ // On a WantAllTraces CC, force a synchronous global GC to prevent
+ // hijinks from ForgetSkippable and compartmental GCs.
+ bool forceGC = isShutdown || (mLogger && mLogger->IsAllTraces());
+
+ // BeginCycleCollectionCallback() might have started an IGC, and we need
+ // to finish it before we run FixGrayBits.
+ FinishAnyIncrementalGCInProgress();
+ timeLog.Checkpoint("Pre-FixGrayBits finish IGC");
+
+ FixGrayBits(forceGC, timeLog);
+
+ FreeSnowWhite(true);
+ timeLog.Checkpoint("BeginCollection FreeSnowWhite");
+
+ if (mLogger && NS_FAILED(mLogger->Begin())) {
+ mLogger = nullptr;
+ }
+
+ // FreeSnowWhite could potentially have started an IGC, which we need
+ // to finish before we look at any JS roots.
+ FinishAnyIncrementalGCInProgress();
+ timeLog.Checkpoint("Post-FreeSnowWhite finish IGC");
+
+ // Set up the data structures for building the graph.
+ JS::AutoAssertNoGC nogc;
+ JS::AutoEnterCycleCollection autocc(mJSContext->Context());
+ mGraph.Init();
+ mResults.Init();
+ mResults.mAnyManual = (aCCType != SliceCC);
+ bool mergeZones = ShouldMergeZones(aCCType);
+ mResults.mMergedZones = mergeZones;
+
+ MOZ_ASSERT(!mBuilder, "Forgot to clear mBuilder");
+ mBuilder = new CCGraphBuilder(mGraph, mResults, mJSContext, mLogger,
+ mergeZones);
+ timeLog.Checkpoint("BeginCollection prepare graph builder");
+
+ if (mJSContext) {
+ mJSContext->TraverseRoots(*mBuilder);
+ timeLog.Checkpoint("mJSContext->TraverseRoots()");
+ }
+
+ AutoRestore<bool> ar(mScanInProgress);
+ MOZ_RELEASE_ASSERT(!mScanInProgress);
+ mScanInProgress = true;
+ mPurpleBuf.SelectPointers(*mBuilder);
+ timeLog.Checkpoint("SelectPointers()");
+
+ mBuilder->DoneAddingRoots();
+ mIncrementalPhase = GraphBuildingPhase;
+}
+
+uint32_t
+nsCycleCollector::SuspectedCount()
+{
+ CheckThreadSafety();
+ return mPurpleBuf.Count();
+}
+
+void
+nsCycleCollector::Shutdown(bool aDoCollect)
+{
+ CheckThreadSafety();
+
+ // Always delete snow white objects.
+ FreeSnowWhite(true);
+
+ if (aDoCollect) {
+ ShutdownCollect();
+ }
+}
+
+void
+nsCycleCollector::RemoveObjectFromGraph(void* aObj)
+{
+ if (IsIdle()) {
+ return;
+ }
+
+ mGraph.RemoveObjectFromMap(aObj);
+}
+
+void
+nsCycleCollector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ size_t* aObjectSize,
+ size_t* aGraphSize,
+ size_t* aPurpleBufferSize) const
+{
+ *aObjectSize = aMallocSizeOf(this);
+
+ *aGraphSize = mGraph.SizeOfExcludingThis(aMallocSizeOf);
+
+ *aPurpleBufferSize = mPurpleBuf.SizeOfExcludingThis(aMallocSizeOf);
+
+ // These fields are deliberately not measured:
+ // - mJSContext: because it's non-owning and measured by JS reporters.
+ // - mParams: because it only contains scalars.
+}
+
+JSPurpleBuffer*
+nsCycleCollector::GetJSPurpleBuffer()
+{
+ if (!mJSPurpleBuffer) {
+ // The Release call here confuses the GC analysis.
+ JS::AutoSuppressGCAnalysis nogc;
+ // JSPurpleBuffer keeps itself alive, but we need to create it in such way
+ // that it ends up in the normal purple buffer. That happens when
+ // nsRefPtr goes out of the scope and calls Release.
+ RefPtr<JSPurpleBuffer> pb = new JSPurpleBuffer(mJSPurpleBuffer);
+ }
+ return mJSPurpleBuffer;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Module public API (exported in nsCycleCollector.h)
+// Just functions that redirect into the singleton, once it's built.
+////////////////////////////////////////////////////////////////////////
+
+void
+nsCycleCollector_registerJSContext(CycleCollectedJSContext* aCx)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+ // But we shouldn't already have a context.
+ MOZ_ASSERT(!data->mContext);
+
+ data->mContext = aCx;
+ data->mCollector->RegisterJSContext(aCx);
+}
+
+void
+nsCycleCollector_forgetJSContext()
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ // And we shouldn't have already forgotten our context.
+ MOZ_ASSERT(data->mContext);
+
+ // But it may have shutdown already.
+ if (data->mCollector) {
+ data->mCollector->ForgetJSContext();
+ data->mContext = nullptr;
+ } else {
+ data->mContext = nullptr;
+ delete data;
+ sCollectorData.set(nullptr);
+ }
+}
+
+/* static */ CycleCollectedJSContext*
+CycleCollectedJSContext::Get()
+{
+ CollectorData* data = sCollectorData.get();
+ if (data) {
+ return data->mContext;
+ }
+ return nullptr;
+}
+
+MOZ_NEVER_INLINE static void
+SuspectAfterShutdown(void* aPtr, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete)
+{
+ if (aRefCnt->get() == 0) {
+ if (!aShouldDelete) {
+ // The CC is shut down, so we can't be in the middle of an ICC.
+ CanonicalizeParticipant(&aPtr, &aCp);
+ aRefCnt->stabilizeForDeletion();
+ aCp->DeleteCycleCollectable(aPtr);
+ } else {
+ *aShouldDelete = true;
+ }
+ } else {
+ // Make sure we'll get called again.
+ aRefCnt->RemoveFromPurpleBuffer();
+ }
+}
+
+void
+NS_CycleCollectorSuspect3(void* aPtr, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+
+ if (MOZ_LIKELY(data->mCollector)) {
+ data->mCollector->Suspect(aPtr, aCp, aRefCnt);
+ return;
+ }
+ SuspectAfterShutdown(aPtr, aCp, aRefCnt, aShouldDelete);
+}
+
+uint32_t
+nsCycleCollector_suspectedCount()
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+
+ if (!data->mCollector) {
+ return 0;
+ }
+
+ return data->mCollector->SuspectedCount();
+}
+
+bool
+nsCycleCollector_init()
+{
+#ifdef DEBUG
+ static bool sInitialized;
+
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+ MOZ_ASSERT(!sInitialized, "Called twice!?");
+ sInitialized = true;
+#endif
+
+ return sCollectorData.init();
+}
+
+void
+nsCycleCollector_startup()
+{
+ if (sCollectorData.get()) {
+ MOZ_CRASH();
+ }
+
+ CollectorData* data = new CollectorData;
+ data->mCollector = new nsCycleCollector();
+ data->mContext = nullptr;
+
+ sCollectorData.set(data);
+}
+
+void
+nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ data->mCollector->SetBeforeUnlinkCallback(aCB);
+}
+
+void
+nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ data->mCollector->SetForgetSkippableCallback(aCB);
+}
+
+void
+nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ PROFILER_LABEL("nsCycleCollector", "forgetSkippable",
+ js::ProfileEntry::Category::CC);
+
+ TimeLog timeLog;
+ data->mCollector->ForgetSkippable(aRemoveChildlessNodes,
+ aAsyncSnowWhiteFreeing);
+ timeLog.Checkpoint("ForgetSkippable()");
+}
+
+void
+nsCycleCollector_dispatchDeferredDeletion(bool aContinuation, bool aPurge)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ if (cx) {
+ cx->DispatchDeferredDeletion(aContinuation, aPurge);
+ }
+}
+
+bool
+nsCycleCollector_doDeferredDeletion()
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+ MOZ_ASSERT(data->mContext);
+
+ return data->mCollector->FreeSnowWhite(false);
+}
+
+already_AddRefed<nsICycleCollectorLogSink>
+nsCycleCollector_createLogSink()
+{
+ nsCOMPtr<nsICycleCollectorLogSink> sink = new nsCycleCollectorLogSinkToFile();
+ return sink.forget();
+}
+
+void
+nsCycleCollector_collect(nsICycleCollectorListener* aManualListener)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ PROFILER_LABEL("nsCycleCollector", "collect",
+ js::ProfileEntry::Category::CC);
+
+ SliceBudget unlimitedBudget = SliceBudget::unlimited();
+ data->mCollector->Collect(ManualCC, unlimitedBudget, aManualListener);
+}
+
+void
+nsCycleCollector_collectSlice(SliceBudget& budget,
+ bool aPreferShorterSlices)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ PROFILER_LABEL("nsCycleCollector", "collectSlice",
+ js::ProfileEntry::Category::CC);
+
+ data->mCollector->Collect(SliceCC, budget, nullptr, aPreferShorterSlices);
+}
+
+void
+nsCycleCollector_prepareForGarbageCollection()
+{
+ CollectorData* data = sCollectorData.get();
+
+ MOZ_ASSERT(data);
+
+ if (!data->mCollector) {
+ return;
+ }
+
+ data->mCollector->PrepareForGarbageCollection();
+}
+
+void
+nsCycleCollector_finishAnyCurrentCollection()
+{
+ CollectorData* data = sCollectorData.get();
+
+ MOZ_ASSERT(data);
+
+ if (!data->mCollector) {
+ return;
+ }
+
+ data->mCollector->FinishAnyCurrentCollection();
+}
+
+void
+nsCycleCollector_shutdown(bool aDoCollect)
+{
+ CollectorData* data = sCollectorData.get();
+
+ if (data) {
+ MOZ_ASSERT(data->mCollector);
+ PROFILER_LABEL("nsCycleCollector", "shutdown",
+ js::ProfileEntry::Category::CC);
+
+ data->mCollector->Shutdown(aDoCollect);
+ data->mCollector = nullptr;
+ if (data->mContext) {
+ // Run any remaining tasks that may have been enqueued via
+ // RunInStableState during the final cycle collection.
+ data->mContext->ProcessStableStateQueue();
+ }
+ if (!data->mContext) {
+ delete data;
+ sCollectorData.set(nullptr);
+ }
+ }
+}
diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h
new file mode 100644
index 000000000..cd3fff406
--- /dev/null
+++ b/xpcom/base/nsCycleCollector.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCycleCollector_h__
+#define nsCycleCollector_h__
+
+class nsICycleCollectorListener;
+class nsICycleCollectorLogSink;
+class nsISupports;
+template<class T> struct already_AddRefed;
+
+#include "nsError.h"
+#include "nsID.h"
+
+#include "js/SliceBudget.h"
+
+namespace mozilla {
+class CycleCollectedJSContext;
+} // namespace mozilla
+
+bool nsCycleCollector_init();
+
+void nsCycleCollector_startup();
+
+typedef void (*CC_BeforeUnlinkCallback)(void);
+void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
+
+typedef void (*CC_ForgetSkippableCallback)(void);
+void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
+
+void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
+ bool aAsyncSnowWhiteFreeing = false);
+
+void nsCycleCollector_prepareForGarbageCollection();
+
+// If an incremental cycle collection is in progress, finish it.
+void nsCycleCollector_finishAnyCurrentCollection();
+
+void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false,
+ bool aPurge = false);
+bool nsCycleCollector_doDeferredDeletion();
+
+already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink();
+
+void nsCycleCollector_collect(nsICycleCollectorListener* aManualListener);
+
+void nsCycleCollector_collectSlice(js::SliceBudget& budget,
+ bool aPreferShorterSlices = false);
+
+uint32_t nsCycleCollector_suspectedCount();
+
+// If aDoCollect is true, then run the GC and CC a few times before
+// shutting down the CC completely.
+void nsCycleCollector_shutdown(bool aDoCollect = true);
+
+// Helpers for interacting with JS
+void nsCycleCollector_registerJSContext(mozilla::CycleCollectedJSContext* aCx);
+void nsCycleCollector_forgetJSContext();
+
+#define NS_CYCLE_COLLECTOR_LOGGER_CID \
+{ 0x58be81b4, 0x39d2, 0x437c, \
+{ 0x94, 0xea, 0xae, 0xde, 0x2c, 0x62, 0x08, 0xd3 } }
+
+extern nsresult
+nsCycleCollectorLoggerConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr);
+
+#endif // nsCycleCollector_h__
diff --git a/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp b/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
new file mode 100644
index 000000000..eb06a389c
--- /dev/null
+++ b/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCycleCollectionParticipant.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+void
+CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
+ const char* aName,
+ uint32_t aFlags)
+{
+ nsAutoCString arrayEdgeName(aName);
+ if (aFlags & CycleCollectionEdgeNameArrayFlag) {
+ arrayEdgeName.AppendLiteral("[i]");
+ }
+ aCallback.NoteNextEdgeName(arrayEdgeName.get());
+}
+
+void
+nsScriptObjectTracer::NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
+ void* aClosure)
+{
+ nsCycleCollectionTraversalCallback* cb =
+ static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, aName);
+ if (mozilla::AddToCCKind(aGCThing.kind())) {
+ cb->NoteJSChild(aGCThing);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (aPtr->unbarrieredGet().isMarkable()) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (JSID_IS_GCTHING(aPtr->unbarrieredGet())) {
+ mCallback(JSID_TO_GCTHING(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGetPtr()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp
new file mode 100644
index 000000000..36288d203
--- /dev/null
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Chromium headers must come before Mozilla headers.
+#include "base/process_util.h"
+
+#include "mozilla/Atomics.h"
+
+#include "nsDebugImpl.h"
+#include "nsDebug.h"
+#ifdef MOZ_CRASHREPORTER
+# include "nsExceptionHandler.h"
+#endif
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+#include "prprf.h"
+#include "nsError.h"
+#include "prerror.h"
+#include "prerr.h"
+#include "prenv.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+#ifdef _WIN32
+/* for getenv() */
+#include <stdlib.h>
+#endif
+
+#include "nsTraceRefcnt.h"
+
+#if defined(XP_UNIX)
+#include <signal.h>
+#endif
+
+#if defined(XP_WIN)
+#include <tchar.h>
+#include "nsString.h"
+#endif
+
+#if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__OpenBSD__)
+#include <sys/proc.h>
+#endif
+
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#include <sys/user.h>
+#endif
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(XP_MACOSX)
+#define KP_FLAGS kp_proc.p_flag
+#elif defined(__DragonFly__)
+#define KP_FLAGS kp_flags
+#elif defined(__FreeBSD__)
+#define KP_FLAGS ki_flag
+#elif defined(__OpenBSD__) && !defined(_P_TRACED)
+#define KP_FLAGS p_psflags
+#define P_TRACED PS_TRACED
+#else
+#define KP_FLAGS p_flag
+#endif
+
+#include "mozilla/mozalloc_abort.h"
+
+static void
+Abort(const char* aMsg);
+
+static void
+RealBreak();
+
+static void
+Break(const char* aMsg);
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <signal.h>
+#include <malloc.h> // for _alloca
+#elif defined(XP_UNIX)
+#include <stdlib.h>
+#endif
+
+using namespace mozilla;
+
+static const char* sMultiprocessDescription = nullptr;
+
+static Atomic<int32_t> gAssertionCount;
+
+NS_IMPL_QUERY_INTERFACE(nsDebugImpl, nsIDebug2)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsDebugImpl::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsDebugImpl::Release()
+{
+ return 1;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Assertion(const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Warning(const char* aStr, const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_WARNING, aStr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Break(const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_BREAK, nullptr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Abort(const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetIsDebugBuild(bool* aResult)
+{
+#ifdef DEBUG
+ *aResult = true;
+#else
+ *aResult = false;
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetAssertionCount(int32_t* aResult)
+{
+ *aResult = gAssertionCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetIsDebuggerAttached(bool* aResult)
+{
+ *aResult = false;
+
+#if defined(XP_WIN)
+ *aResult = ::IsDebuggerPresent();
+#elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+ // Specify the info we're looking for
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int mibSize = sizeof(mib) / sizeof(int);
+
+ KINFO_PROC info;
+ size_t infoSize = sizeof(info);
+ memset(&info, 0, infoSize);
+
+ if (sysctl(mib, mibSize, &info, &infoSize, nullptr, 0)) {
+ // if the call fails, default to false
+ *aResult = false;
+ return NS_OK;
+ }
+
+ if (info.KP_FLAGS & P_TRACED) {
+ *aResult = true;
+ }
+#endif
+
+ return NS_OK;
+}
+
+/* static */ void
+nsDebugImpl::SetMultiprocessMode(const char* aDesc)
+{
+ sMultiprocessDescription = aDesc;
+}
+
+/**
+ * Implementation of the nsDebug methods. Note that this code is
+ * always compiled in, in case some other module that uses it is
+ * compiled with debugging even if this library is not.
+ */
+enum nsAssertBehavior
+{
+ NS_ASSERT_UNINITIALIZED,
+ NS_ASSERT_WARN,
+ NS_ASSERT_SUSPEND,
+ NS_ASSERT_STACK,
+ NS_ASSERT_TRAP,
+ NS_ASSERT_ABORT,
+ NS_ASSERT_STACK_AND_ABORT
+};
+
+static nsAssertBehavior
+GetAssertBehavior()
+{
+ static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
+ if (gAssertBehavior != NS_ASSERT_UNINITIALIZED) {
+ return gAssertBehavior;
+ }
+
+ gAssertBehavior = NS_ASSERT_WARN;
+
+ const char* assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
+ if (!assertString || !*assertString) {
+ return gAssertBehavior;
+ }
+ if (!strcmp(assertString, "warn")) {
+ return gAssertBehavior = NS_ASSERT_WARN;
+ }
+ if (!strcmp(assertString, "suspend")) {
+ return gAssertBehavior = NS_ASSERT_SUSPEND;
+ }
+ if (!strcmp(assertString, "stack")) {
+ return gAssertBehavior = NS_ASSERT_STACK;
+ }
+ if (!strcmp(assertString, "abort")) {
+ return gAssertBehavior = NS_ASSERT_ABORT;
+ }
+ if (!strcmp(assertString, "trap") || !strcmp(assertString, "break")) {
+ return gAssertBehavior = NS_ASSERT_TRAP;
+ }
+ if (!strcmp(assertString, "stack-and-abort")) {
+ return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
+ }
+
+ fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
+ return gAssertBehavior;
+}
+
+struct FixedBuffer
+{
+ FixedBuffer() : curlen(0)
+ {
+ buffer[0] = '\0';
+ }
+
+ char buffer[500];
+ uint32_t curlen;
+};
+
+static int
+StuffFixedBuffer(void* aClosure, const char* aBuf, uint32_t aLen)
+{
+ if (!aLen) {
+ return 0;
+ }
+
+ FixedBuffer* fb = (FixedBuffer*)aClosure;
+
+ // strip the trailing null, we add it again later
+ if (aBuf[aLen - 1] == '\0') {
+ --aLen;
+ }
+
+ if (fb->curlen + aLen >= sizeof(fb->buffer)) {
+ aLen = sizeof(fb->buffer) - fb->curlen - 1;
+ }
+
+ if (aLen) {
+ memcpy(fb->buffer + fb->curlen, aBuf, aLen);
+ fb->curlen += aLen;
+ fb->buffer[fb->curlen] = '\0';
+ }
+
+ return aLen;
+}
+
+EXPORT_XPCOM_API(void)
+NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ FixedBuffer nonPIDBuf;
+ FixedBuffer buf;
+ const char* sevString = "WARNING";
+
+ switch (aSeverity) {
+ case NS_DEBUG_ASSERTION:
+ sevString = "###!!! ASSERTION";
+ break;
+
+ case NS_DEBUG_BREAK:
+ sevString = "###!!! BREAK";
+ break;
+
+ case NS_DEBUG_ABORT:
+ sevString = "###!!! ABORT";
+ break;
+
+ default:
+ aSeverity = NS_DEBUG_WARNING;
+ }
+
+#define PRINT_TO_NONPID_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &nonPIDBuf, __VA_ARGS__)
+ PRINT_TO_NONPID_BUFFER("%s: ", sevString);
+ if (aStr) {
+ PRINT_TO_NONPID_BUFFER("%s: ", aStr);
+ }
+ if (aExpr) {
+ PRINT_TO_NONPID_BUFFER("'%s', ", aExpr);
+ }
+ if (aFile) {
+ PRINT_TO_NONPID_BUFFER("file %s, ", aFile);
+ }
+ if (aLine != -1) {
+ PRINT_TO_NONPID_BUFFER("line %d", aLine);
+ }
+#undef PRINT_TO_NONPID_BUFFER
+
+ // Print "[PID]" or "[Desc PID]" at the beginning of the message.
+#define PRINT_TO_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &buf, __VA_ARGS__)
+ PRINT_TO_BUFFER("[");
+ if (sMultiprocessDescription) {
+ PRINT_TO_BUFFER("%s ", sMultiprocessDescription);
+ }
+ PRINT_TO_BUFFER("%d] %s", base::GetCurrentProcId(), nonPIDBuf.buffer);
+#undef PRINT_TO_BUFFER
+
+
+ // errors on platforms without a debugdlg ring a bell on stderr
+#if !defined(XP_WIN)
+ if (aSeverity != NS_DEBUG_WARNING) {
+ fprintf(stderr, "\07");
+ }
+#endif
+
+#ifdef ANDROID
+ __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
+#endif
+
+ // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
+ // is set.
+ if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity == NS_DEBUG_WARNING)) {
+ fprintf(stderr, "%s\n", buf.buffer);
+ fflush(stderr);
+ }
+
+ switch (aSeverity) {
+ case NS_DEBUG_WARNING:
+ return;
+
+ case NS_DEBUG_BREAK:
+ Break(buf.buffer);
+ return;
+
+ case NS_DEBUG_ABORT: {
+#if defined(MOZ_CRASHREPORTER)
+ // Updating crash annotations in the child causes us to do IPC. This can
+ // really cause trouble if we're asserting from within IPC code. So we
+ // have to do without the annotations in that case.
+ if (XRE_IsParentProcess()) {
+ // Don't include the PID in the crash report annotation to
+ // allow faceting on crash-stats.mozilla.org.
+ nsCString note("xpcom_runtime_abort(");
+ note += nonPIDBuf.buffer;
+ note += ")";
+ CrashReporter::AppendAppNotesToCrashReport(note);
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AbortMessage"),
+ nsDependentCString(nonPIDBuf.buffer));
+ }
+#endif // MOZ_CRASHREPORTER
+
+#if defined(DEBUG) && defined(_WIN32)
+ RealBreak();
+#endif
+#if defined(DEBUG)
+ nsTraceRefcnt::WalkTheStack(stderr);
+#endif
+ Abort(buf.buffer);
+ return;
+ }
+ }
+
+ // Now we deal with assertions
+ gAssertionCount++;
+
+ switch (GetAssertBehavior()) {
+ case NS_ASSERT_WARN:
+ return;
+
+ case NS_ASSERT_SUSPEND:
+#ifdef XP_UNIX
+ fprintf(stderr, "Suspending process; attach with the debugger.\n");
+ kill(0, SIGSTOP);
+#else
+ Break(buf.buffer);
+#endif
+ return;
+
+ case NS_ASSERT_STACK:
+ nsTraceRefcnt::WalkTheStack(stderr);
+ return;
+
+ case NS_ASSERT_STACK_AND_ABORT:
+ nsTraceRefcnt::WalkTheStack(stderr);
+ // Fall through to abort
+ MOZ_FALLTHROUGH;
+
+ case NS_ASSERT_ABORT:
+ Abort(buf.buffer);
+ return;
+
+ case NS_ASSERT_TRAP:
+ case NS_ASSERT_UNINITIALIZED: // Default to "trap" behavior
+ Break(buf.buffer);
+ return;
+ }
+}
+
+static void
+Abort(const char* aMsg)
+{
+ mozalloc_abort(aMsg);
+}
+
+static void
+RealBreak()
+{
+#if defined(_WIN32)
+ ::DebugBreak();
+#elif defined(XP_MACOSX)
+ raise(SIGTRAP);
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
+ asm("int $3");
+#elif defined(__arm__)
+ asm(
+#ifdef __ARM_ARCH_4T__
+ /* ARMv4T doesn't support the BKPT instruction, so if the compiler target
+ * is ARMv4T, we want to ensure the assembler will understand that ARMv5T
+ * instruction, while keeping the resulting object tagged as ARMv4T.
+ */
+ ".arch armv5t\n"
+ ".object_arch armv4t\n"
+#endif
+ "BKPT #0");
+#elif defined(SOLARIS)
+#if defined(__i386__) || defined(__i386) || defined(__x86_64__)
+ asm("int $3");
+#else
+ raise(SIGTRAP);
+#endif
+#else
+#warning do not know how to break on this platform
+#endif
+}
+
+// Abort() calls this function, don't call it!
+static void
+Break(const char* aMsg)
+{
+#if defined(_WIN32)
+ static int ignoreDebugger;
+ if (!ignoreDebugger) {
+ const char* shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
+ ignoreDebugger =
+ 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
+ }
+ if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) {
+ DWORD code = IDRETRY;
+
+ /* Create the debug dialog out of process to avoid the crashes caused by
+ * Windows events leaking into our event loop from an in process dialog.
+ * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
+ * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
+ */
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+ wchar_t executable[MAX_PATH];
+ wchar_t* pName;
+
+ memset(&pi, 0, sizeof(pi));
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.wShowWindow = SW_SHOW;
+
+ // 2nd arg of CreateProcess is in/out
+ wchar_t* msgCopy = (wchar_t*)_alloca((strlen(aMsg) + 1) * sizeof(wchar_t));
+ wcscpy(msgCopy, NS_ConvertUTF8toUTF16(aMsg).get());
+
+ if (GetModuleFileNameW(GetModuleHandleW(L"xpcom.dll"), executable, MAX_PATH) &&
+ (pName = wcsrchr(executable, '\\')) != nullptr &&
+ wcscpy(pName + 1, L"windbgdlg.exe") &&
+ CreateProcessW(executable, msgCopy, nullptr, nullptr,
+ false, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
+ nullptr, nullptr, &si, &pi)) {
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ GetExitCodeProcess(pi.hProcess, &code);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+
+ switch (code) {
+ case IDABORT:
+ //This should exit us
+ raise(SIGABRT);
+ //If we are ignored exit this way..
+ _exit(3);
+
+ case IDIGNORE:
+ return;
+ }
+ }
+
+ RealBreak();
+#elif defined(XP_MACOSX)
+ /* Note that we put this Mac OS X test above the GNUC/x86 test because the
+ * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
+ * impls to be the same.
+ */
+ RealBreak();
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
+ RealBreak();
+#elif defined(__arm__)
+ RealBreak();
+#elif defined(SOLARIS)
+ RealBreak();
+#else
+#warning do not know how to break on this platform
+#endif
+}
+
+nsresult
+nsDebugImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr)
+{
+ static const nsDebugImpl* sImpl;
+
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ if (!sImpl) {
+ sImpl = new nsDebugImpl();
+ }
+
+ return const_cast<nsDebugImpl*>(sImpl)->QueryInterface(aIID, aInstancePtr);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_ErrorAccordingToNSPR()
+{
+ PRErrorCode err = PR_GetError();
+ switch (err) {
+ case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
+ case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
+ case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
+ case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
+ case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
+ case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
+ case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+ case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
+ case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
+ case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
+ case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
+ case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
+ case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
+ default: return NS_ERROR_FAILURE;
+ }
+}
+
+void
+NS_ABORT_OOM(size_t aSize)
+{
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateOOMAllocationSize(aSize);
+#endif
+ MOZ_CRASH("OOM");
+}
diff --git a/xpcom/base/nsDebugImpl.h b/xpcom/base/nsDebugImpl.h
new file mode 100644
index 000000000..23680238a
--- /dev/null
+++ b/xpcom/base/nsDebugImpl.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDebugImpl_h
+#define nsDebugImpl_h
+
+#include "nsIDebug2.h"
+
+class nsDebugImpl : public nsIDebug2
+{
+public:
+ nsDebugImpl() = default;
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDEBUG2
+
+ static nsresult Create(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr);
+
+ /*
+ * Inform nsDebugImpl that we're in multiprocess mode.
+ *
+ * If aDesc is not nullptr, the string it points to must be
+ * statically-allocated (i.e., it must be a string literal).
+ */
+ static void SetMultiprocessMode(const char* aDesc);
+};
+
+
+#define NS_DEBUG_CONTRACTID "@mozilla.org/xpcom/debug;1"
+#define NS_DEBUG_CID \
+{ /* cb6cdb94-e417-4601-b4a5-f991bf41453d */ \
+ 0xcb6cdb94, \
+ 0xe417, \
+ 0x4601, \
+ {0xb4, 0xa5, 0xf9, 0x91, 0xbf, 0x41, 0x45, 0x3d} \
+}
+
+#endif // nsDebugImpl_h
diff --git a/xpcom/base/nsDumpUtils.cpp b/xpcom/base/nsDumpUtils.cpp
new file mode 100644
index 000000000..c68862d08
--- /dev/null
+++ b/xpcom/base/nsDumpUtils.cpp
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDumpUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "prenv.h"
+#include <errno.h>
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Unused.h"
+
+#ifdef XP_UNIX // {
+#include "mozilla/Preferences.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace mozilla;
+
+/*
+ * The following code supports triggering a registered callback upon
+ * receiving a specific signal.
+ *
+ * Take about:memory for example, we register
+ * 1. doGCCCDump for doMemoryReport
+ * 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN)
+ * and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1).
+ *
+ * When we receive one of these signals, we write the signal number to a pipe.
+ * The IO thread then notices that the pipe has been written to, and kicks off
+ * the appropriate task on the main thread.
+ *
+ * This scheme is similar to using signalfd(), except it's portable and it
+ * doesn't require the use of sigprocmask, which is problematic because it
+ * masks signals received by child processes.
+ *
+ * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
+ * But that uses libevent, which does not handle the realtime signals (bug
+ * 794074).
+ */
+
+// This is the write-end of a pipe that we use to notice when a
+// specific signal occurs.
+static Atomic<int> sDumpPipeWriteFd(-1);
+
+const char* const FifoWatcher::kPrefName =
+ "memory_info_dumper.watch_fifo.enabled";
+
+static void
+DumpSignalHandler(int aSignum)
+{
+ // This is a signal handler, so everything in here needs to be
+ // async-signal-safe. Be careful!
+
+ if (sDumpPipeWriteFd != -1) {
+ uint8_t signum = static_cast<int>(aSignum);
+ Unused << write(sDumpPipeWriteFd, &signum, sizeof(signum));
+ }
+}
+
+NS_IMPL_ISUPPORTS(FdWatcher, nsIObserver);
+
+void
+FdWatcher::Init()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false);
+
+ XRE_GetIOMessageLoop()->PostTask(NewRunnableMethod(this, &FdWatcher::StartWatching));
+}
+
+// Implementations may call this function multiple times if they ensure that
+// it's safe to call OpenFd() multiple times and they call StopWatching()
+// first.
+void
+FdWatcher::StartWatching()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+ MOZ_ASSERT(mFd == -1);
+
+ mFd = OpenFd();
+ if (mFd == -1) {
+ LOG("FdWatcher: OpenFd failed.");
+ return;
+ }
+
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ mFd, /* persistent = */ true,
+ MessageLoopForIO::WATCH_READ,
+ &mReadWatcher, this);
+}
+
+// Since implementations can call StartWatching() multiple times, they can of
+// course call StopWatching() multiple times.
+void
+FdWatcher::StopWatching()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ mReadWatcher.StopWatchingFileDescriptor();
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+StaticRefPtr<SignalPipeWatcher> SignalPipeWatcher::sSingleton;
+
+/* static */ SignalPipeWatcher*
+SignalPipeWatcher::GetSingleton()
+{
+ if (!sSingleton) {
+ sSingleton = new SignalPipeWatcher();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+}
+
+void
+SignalPipeWatcher::RegisterCallback(uint8_t aSignal,
+ PipeCallback aCallback)
+{
+ MutexAutoLock lock(mSignalInfoLock);
+
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); ++i) {
+ if (mSignalInfo[i].mSignal == aSignal) {
+ LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal);
+ return;
+ }
+ }
+ SignalInfo signalInfo = { aSignal, aCallback };
+ mSignalInfo.AppendElement(signalInfo);
+ RegisterSignalHandler(signalInfo.mSignal);
+}
+
+void
+SignalPipeWatcher::RegisterSignalHandler(uint8_t aSignal)
+{
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = DumpSignalHandler;
+
+ if (aSignal) {
+ if (sigaction(aSignal, &action, nullptr)) {
+ LOG("SignalPipeWatcher failed to register sig %d.", aSignal);
+ }
+ } else {
+ MutexAutoLock lock(mSignalInfoLock);
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
+ if (sigaction(mSignalInfo[i].mSignal, &action, nullptr)) {
+ LOG("SignalPipeWatcher failed to register signal(%d) "
+ "dump signal handler.", mSignalInfo[i].mSignal);
+ }
+ }
+ }
+}
+
+SignalPipeWatcher::~SignalPipeWatcher()
+{
+ if (sDumpPipeWriteFd != -1) {
+ StopWatching();
+ }
+}
+
+int
+SignalPipeWatcher::OpenFd()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ // Create a pipe. When we receive a signal in our signal handler, we'll
+ // write the signum to the write-end of this pipe.
+ int pipeFds[2];
+ if (pipe(pipeFds)) {
+ LOG("SignalPipeWatcher failed to create pipe.");
+ return -1;
+ }
+
+ // Close this pipe on calls to exec().
+ fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
+ fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
+
+ int readFd = pipeFds[0];
+ sDumpPipeWriteFd = pipeFds[1];
+
+ RegisterSignalHandler();
+ return readFd;
+}
+
+void
+SignalPipeWatcher::StopWatching()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ // Close sDumpPipeWriteFd /after/ setting the fd to -1.
+ // Otherwise we have the (admittedly far-fetched) race where we
+ //
+ // 1) close sDumpPipeWriteFd
+ // 2) open a new fd with the same number as sDumpPipeWriteFd
+ // had.
+ // 3) receive a signal, then write to the fd.
+ int pipeWriteFd = sDumpPipeWriteFd.exchange(-1);
+ close(pipeWriteFd);
+
+ FdWatcher::StopWatching();
+}
+
+void
+SignalPipeWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ uint8_t signum;
+ ssize_t numReceived = read(aFd, &signum, sizeof(signum));
+ if (numReceived != sizeof(signum)) {
+ LOG("Error reading from buffer in "
+ "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
+ return;
+ }
+
+ {
+ MutexAutoLock lock(mSignalInfoLock);
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
+ if (signum == mSignalInfo[i].mSignal) {
+ mSignalInfo[i].mCallback(signum);
+ return;
+ }
+ }
+ }
+ LOG("SignalPipeWatcher got unexpected signum.");
+}
+
+StaticRefPtr<FifoWatcher> FifoWatcher::sSingleton;
+
+/* static */ FifoWatcher*
+FifoWatcher::GetSingleton()
+{
+ if (!sSingleton) {
+ nsAutoCString dirPath;
+ Preferences::GetCString(
+ "memory_info_dumper.watch_fifo.directory", &dirPath);
+ sSingleton = new FifoWatcher(dirPath);
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+}
+
+/* static */ bool
+FifoWatcher::MaybeCreate()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!XRE_IsParentProcess()) {
+ // We want this to be main-process only, since two processes can't listen
+ // to the same fifo.
+ return false;
+ }
+
+ if (!Preferences::GetBool(kPrefName, false)) {
+ LOG("Fifo watcher disabled via pref.");
+ return false;
+ }
+
+ // The FifoWatcher is held alive by the observer service.
+ if (!sSingleton) {
+ GetSingleton();
+ }
+ return true;
+}
+
+void
+FifoWatcher::RegisterCallback(const nsCString& aCommand, FifoCallback aCallback)
+{
+ MutexAutoLock lock(mFifoInfoLock);
+
+ for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); ++i) {
+ if (mFifoInfo[i].mCommand.Equals(aCommand)) {
+ LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get());
+ return;
+ }
+ }
+ FifoInfo aFifoInfo = { aCommand, aCallback };
+ mFifoInfo.AppendElement(aFifoInfo);
+}
+
+FifoWatcher::~FifoWatcher()
+{
+}
+
+int
+FifoWatcher::OpenFd()
+{
+ // If the memory_info_dumper.directory pref is specified, put the fifo
+ // there. Otherwise, put it into the system's tmp directory.
+
+ nsCOMPtr<nsIFile> file;
+
+ nsresult rv;
+ if (mDirPath.Length() > 0) {
+ rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get());
+ return -1;
+ }
+ } else {
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return -1;
+ }
+ }
+
+ rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return -1;
+ }
+
+ nsAutoCString path;
+ rv = file->GetNativePath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return -1;
+ }
+
+ // unlink might fail because the file doesn't exist, or for other reasons.
+ // But we don't care it fails; any problems will be detected later, when we
+ // try to mkfifo or open the file.
+ if (unlink(path.get())) {
+ LOG("FifoWatcher::OpenFifo unlink failed; errno=%d. "
+ "Continuing despite error.", errno);
+ }
+
+ if (mkfifo(path.get(), 0766)) {
+ LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno);
+ return -1;
+ }
+
+#ifdef ANDROID
+ // Android runs with a umask, so we need to chmod our fifo to make it
+ // world-writable.
+ chmod(path.get(), 0666);
+#endif
+
+ int fd;
+ do {
+ // The fifo will block until someone else has written to it. In
+ // particular, open() will block until someone else has opened it for
+ // writing! We want open() to succeed and read() to block, so we open
+ // with NONBLOCK and then fcntl that away.
+ fd = open(path.get(), O_RDONLY | O_NONBLOCK);
+ } while (fd == -1 && errno == EINTR);
+
+ if (fd == -1) {
+ LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno);
+ return -1;
+ }
+
+ // Make fd blocking now that we've opened it.
+ if (fcntl(fd, F_SETFL, 0)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+void
+FifoWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ char buf[1024];
+ int nread;
+ do {
+ // sizeof(buf) - 1 to leave space for the null-terminator.
+ nread = read(aFd, buf, sizeof(buf));
+ } while (nread == -1 && errno == EINTR);
+
+ if (nread == -1) {
+ // We want to avoid getting into a situation where
+ // OnFileCanReadWithoutBlocking is called in an infinite loop, so when
+ // something goes wrong, stop watching the fifo altogether.
+ LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
+ StopWatching();
+ return;
+ }
+
+ if (nread == 0) {
+ // If we get EOF, that means that the other side closed the fifo. We need
+ // to close and re-open the fifo; if we don't,
+ // OnFileCanWriteWithoutBlocking will be called in an infinite loop.
+
+ LOG("FifoWatcher closing and re-opening fifo.");
+ StopWatching();
+ StartWatching();
+ return;
+ }
+
+ nsAutoCString inputStr;
+ inputStr.Append(buf, nread);
+
+ // Trimming whitespace is important because if you do
+ // |echo "foo" >> debug_info_trigger|,
+ // it'll actually write "foo\n" to the fifo.
+ inputStr.Trim("\b\t\r\n");
+
+ {
+ MutexAutoLock lock(mFifoInfoLock);
+
+ for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) {
+ const nsCString commandStr = mFifoInfo[i].mCommand;
+ if (inputStr == commandStr.get()) {
+ mFifoInfo[i].mCallback(inputStr);
+ return;
+ }
+ }
+ }
+ LOG("Got unexpected value from fifo; ignoring it.");
+}
+
+#endif // XP_UNIX }
+
+// In Android case, this function will open a file named aFilename under
+// /data/local/tmp/"aFoldername".
+// Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR".
+/* static */ nsresult
+nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile,
+ const nsACString& aFoldername, Mode aMode)
+{
+#ifdef ANDROID
+ // For Android, first try the downloads directory which is world-readable
+ // rather than the temp directory which is not.
+ if (!*aFile) {
+ char* env = PR_GetEnv("DOWNLOADS_DIRECTORY");
+ if (env) {
+ NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
+ }
+ }
+#endif
+ nsresult rv;
+ if (!*aFile) {
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+#ifdef ANDROID
+ // /data/local/tmp is a true tmp directory; anyone can create a file there,
+ // but only the user which created the file can remove it. We want non-root
+ // users to be able to remove these files, so we write them into a
+ // subdirectory of the temp directory and chmod 777 that directory.
+ if (aFoldername != EmptyCString()) {
+ rv = (*aFile)->AppendNative(aFoldername);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // It's OK if this fails; that probably just means that the directory
+ // already exists.
+ Unused << (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
+
+ nsAutoCString dirPath;
+ rv = (*aFile)->GetNativePath(dirPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {
+ }
+ }
+#endif
+
+ nsCOMPtr<nsIFile> file(*aFile);
+
+ rv = file->AppendNative(aFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aMode == CREATE_UNIQUE) {
+ rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
+ } else {
+ rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifdef ANDROID
+ // Make this file world-read/writable; the permissions passed to the
+ // CreateUnique call above are not sufficient on Android, which runs with a
+ // umask.
+ nsAutoCString path;
+ rv = file->GetNativePath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ while (chmod(path.get(), 0666) == -1 && errno == EINTR) {
+ }
+#endif
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsDumpUtils.h b/xpcom/base/nsDumpUtils.h
new file mode 100644
index 000000000..12a99da18
--- /dev/null
+++ b/xpcom/base/nsDumpUtils.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_nsDumpUtils_h
+#define mozilla_nsDumpUtils_h
+
+#include "nsIObserver.h"
+#include "base/message_loop.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "nsTArray.h"
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef ANDROID
+#include "android/log.h"
+#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "Gecko:DumpUtils", ## __VA_ARGS__)
+#else
+#define LOG(...)
+#endif
+
+#ifdef XP_UNIX // {
+
+/**
+ * Abstract base class for something which watches an fd and takes action when
+ * we can read from it without blocking.
+ */
+class FdWatcher
+ : public MessageLoopForIO::Watcher
+ , public nsIObserver
+{
+protected:
+ MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+ int mFd;
+
+ virtual ~FdWatcher()
+ {
+ // StopWatching should have run.
+ MOZ_ASSERT(mFd == -1);
+ }
+
+public:
+ FdWatcher()
+ : mFd(-1)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ /**
+ * Open the fd to watch. If we encounter an error, return -1.
+ */
+ virtual int OpenFd() = 0;
+
+ /**
+ * Called when you can read() from the fd without blocking. Note that this
+ * function is also called when you're at eof (read() returns 0 in this case).
+ */
+ virtual void OnFileCanReadWithoutBlocking(int aFd) override = 0;
+ virtual void OnFileCanWriteWithoutBlocking(int aFd) override {};
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /**
+ * Initialize this object. This should be called right after the object is
+ * constructed. (This would go in the constructor, except we interact with
+ * XPCOM, which we can't do from a constructor because our refcount is 0 at
+ * that point.)
+ */
+ void Init();
+
+ // Implementations may call this function multiple times if they ensure that
+
+ virtual void StartWatching();
+
+ // Since implementations can call StartWatching() multiple times, they can of
+ // course call StopWatching() multiple times.
+ virtual void StopWatching();
+
+ NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+
+ XRE_GetIOMessageLoop()->PostTask(mozilla::NewRunnableMethod(this, &FdWatcher::StopWatching));
+
+ return NS_OK;
+ }
+};
+
+typedef void (*FifoCallback)(const nsCString& aInputStr);
+struct FifoInfo
+{
+ nsCString mCommand;
+ FifoCallback mCallback;
+};
+typedef nsTArray<FifoInfo> FifoInfoArray;
+
+class FifoWatcher : public FdWatcher
+{
+public:
+ /**
+ * The name of the preference used to enable/disable the FifoWatcher.
+ */
+ static const char* const kPrefName;
+
+ static FifoWatcher* GetSingleton();
+
+ static bool MaybeCreate();
+
+ void RegisterCallback(const nsCString& aCommand, FifoCallback aCallback);
+
+ virtual ~FifoWatcher();
+
+ virtual int OpenFd();
+
+ virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+private:
+ nsAutoCString mDirPath;
+
+ static mozilla::StaticRefPtr<FifoWatcher> sSingleton;
+
+ explicit FifoWatcher(nsCString aPath)
+ : mDirPath(aPath)
+ , mFifoInfoLock("FifoWatcher.mFifoInfoLock")
+ {
+ }
+
+ mozilla::Mutex mFifoInfoLock; // protects mFifoInfo
+ FifoInfoArray mFifoInfo;
+};
+
+typedef void (*PipeCallback)(const uint8_t aRecvSig);
+struct SignalInfo
+{
+ uint8_t mSignal;
+ PipeCallback mCallback;
+};
+typedef nsTArray<SignalInfo> SignalInfoArray;
+
+class SignalPipeWatcher : public FdWatcher
+{
+public:
+ static SignalPipeWatcher* GetSingleton();
+
+ void RegisterCallback(uint8_t aSignal, PipeCallback aCallback);
+
+ void RegisterSignalHandler(uint8_t aSignal = 0);
+
+ virtual ~SignalPipeWatcher();
+
+ virtual int OpenFd();
+
+ virtual void StopWatching();
+
+ virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+private:
+ static mozilla::StaticRefPtr<SignalPipeWatcher> sSingleton;
+
+ SignalPipeWatcher()
+ : mSignalInfoLock("SignalPipeWatcher.mSignalInfoLock")
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ mozilla::Mutex mSignalInfoLock; // protects mSignalInfo
+ SignalInfoArray mSignalInfo;
+};
+
+#endif // XP_UNIX }
+
+class nsDumpUtils
+{
+public:
+
+ enum Mode {
+ CREATE,
+ CREATE_UNIQUE
+ };
+
+ /**
+ * This function creates a new unique file based on |aFilename| in a
+ * world-readable temp directory. This is the system temp directory
+ * or, in the case of Android, the downloads directory. If |aFile| is
+ * non-null, it is assumed to point to a folder, and that folder is used
+ * instead.
+ */
+ static nsresult OpenTempFile(const nsACString& aFilename,
+ nsIFile** aFile,
+ const nsACString& aFoldername = EmptyCString(),
+ Mode aMode = CREATE_UNIQUE);
+};
+
+#endif
diff --git a/xpcom/base/nsError.h b/xpcom/base/nsError.h
new file mode 100644
index 000000000..b9e5d23f6
--- /dev/null
+++ b/xpcom/base/nsError.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsError_h__
+#define nsError_h__
+
+#ifndef __cplusplus
+#error nsError.h no longer supports C sources
+#endif
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#include <stdint.h>
+
+/*
+ * To add error code to your module, you need to do the following:
+ *
+ * 1) Add a module offset code. Add yours to the bottom of the list
+ * right below this comment, adding 1.
+ *
+ * 2) In your module, define a header file which uses one of the
+ * NE_ERROR_GENERATExxxxxx macros. Some examples below:
+ *
+ * #define NS_ERROR_MYMODULE_MYERROR1 NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR,NS_ERROR_MODULE_MYMODULE,1)
+ * #define NS_ERROR_MYMODULE_MYERROR2 NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_MYMODULE,2)
+ * #define NS_ERROR_MYMODULE_MYERROR3 NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_MYMODULE,3)
+ *
+ */
+
+
+/**
+ * @name Standard Module Offset Code. Each Module should identify a unique number
+ * and then all errors associated with that module become offsets from the
+ * base associated with that module id. There are 16 bits of code bits for
+ * each module.
+ */
+
+#define NS_ERROR_MODULE_XPCOM 1
+#define NS_ERROR_MODULE_BASE 2
+#define NS_ERROR_MODULE_GFX 3
+#define NS_ERROR_MODULE_WIDGET 4
+#define NS_ERROR_MODULE_CALENDAR 5
+#define NS_ERROR_MODULE_NETWORK 6
+#define NS_ERROR_MODULE_PLUGINS 7
+#define NS_ERROR_MODULE_LAYOUT 8
+#define NS_ERROR_MODULE_HTMLPARSER 9
+#define NS_ERROR_MODULE_RDF 10
+#define NS_ERROR_MODULE_UCONV 11
+#define NS_ERROR_MODULE_REG 12
+#define NS_ERROR_MODULE_FILES 13
+#define NS_ERROR_MODULE_DOM 14
+#define NS_ERROR_MODULE_IMGLIB 15
+#define NS_ERROR_MODULE_MAILNEWS 16
+#define NS_ERROR_MODULE_EDITOR 17
+#define NS_ERROR_MODULE_XPCONNECT 18
+#define NS_ERROR_MODULE_PROFILE 19
+#define NS_ERROR_MODULE_LDAP 20
+#define NS_ERROR_MODULE_SECURITY 21
+#define NS_ERROR_MODULE_DOM_XPATH 22
+/* 23 used to be NS_ERROR_MODULE_DOM_RANGE (see bug 711047) */
+#define NS_ERROR_MODULE_URILOADER 24
+#define NS_ERROR_MODULE_CONTENT 25
+#define NS_ERROR_MODULE_PYXPCOM 26
+#define NS_ERROR_MODULE_XSLT 27
+#define NS_ERROR_MODULE_IPC 28
+#define NS_ERROR_MODULE_SVG 29
+#define NS_ERROR_MODULE_STORAGE 30
+#define NS_ERROR_MODULE_SCHEMA 31
+#define NS_ERROR_MODULE_DOM_FILE 32
+#define NS_ERROR_MODULE_DOM_INDEXEDDB 33
+#define NS_ERROR_MODULE_DOM_FILEHANDLE 34
+#define NS_ERROR_MODULE_SIGNED_JAR 35
+#define NS_ERROR_MODULE_DOM_FILESYSTEM 36
+#define NS_ERROR_MODULE_DOM_BLUETOOTH 37
+#define NS_ERROR_MODULE_SIGNED_APP 38
+#define NS_ERROR_MODULE_DOM_ANIM 39
+#define NS_ERROR_MODULE_DOM_PUSH 40
+#define NS_ERROR_MODULE_DOM_MEDIA 41
+
+/* NS_ERROR_MODULE_GENERAL should be used by modules that do not
+ * care if return code values overlap. Callers of methods that
+ * return such codes should be aware that they are not
+ * globally unique. Implementors should be careful about blindly
+ * returning codes from other modules that might also use
+ * the generic base.
+ */
+#define NS_ERROR_MODULE_GENERAL 51
+
+/**
+ * @name Severity Code. This flag identifies the level of warning
+ */
+
+#define NS_ERROR_SEVERITY_SUCCESS 0
+#define NS_ERROR_SEVERITY_ERROR 1
+
+/**
+ * @name Mozilla Code. This flag separates consumers of mozilla code
+ * from the native platform
+ */
+
+#define NS_ERROR_MODULE_BASE_OFFSET 0x45
+
+/* Helpers for defining our enum, to be undef'd later */
+#define SUCCESS_OR_FAILURE(sev, module, code) \
+ ((uint32_t)(sev) << 31) | \
+ ((uint32_t)(module + NS_ERROR_MODULE_BASE_OFFSET) << 16) | \
+ (uint32_t)(code)
+#define SUCCESS(code) \
+ SUCCESS_OR_FAILURE(NS_ERROR_SEVERITY_SUCCESS, MODULE, code)
+#define FAILURE(code) \
+ SUCCESS_OR_FAILURE(NS_ERROR_SEVERITY_ERROR, MODULE, code)
+
+/**
+ * @name Standard return values
+ */
+
+/*@{*/
+
+enum class nsresult : uint32_t
+{
+ #undef ERROR
+ #define ERROR(key, val) key = val
+ #include "ErrorList.h"
+ #undef ERROR
+};
+
+/*
+ * enum classes don't place their initializers in the global scope, so we need
+ * constants for compatibility with old code.
+ */
+const nsresult
+ #define ERROR(key, val) key = nsresult::key
+ #include "ErrorList.h"
+ #undef ERROR
+;
+
+#undef SUCCESS_OR_FAILURE
+#undef SUCCESS
+#undef FAILURE
+
+/**
+ * @name Standard Error Handling Macros
+ * @return 0 or 1 (false/true with bool type for C++)
+ */
+
+inline uint32_t
+NS_FAILED_impl(nsresult aErr)
+{
+ return static_cast<uint32_t>(aErr) & 0x80000000;
+}
+#define NS_FAILED(_nsresult) ((bool)MOZ_UNLIKELY(NS_FAILED_impl(_nsresult)))
+#define NS_SUCCEEDED(_nsresult) ((bool)MOZ_LIKELY(!NS_FAILED_impl(_nsresult)))
+
+/* Check that our enum type is actually uint32_t as expected */
+static_assert(((nsresult)0) < ((nsresult)-1),
+ "nsresult must be an unsigned type");
+static_assert(sizeof(nsresult) == sizeof(uint32_t),
+ "nsresult must be 32 bits");
+
+#define MOZ_ALWAYS_SUCCEEDS(expr) MOZ_ALWAYS_TRUE(NS_SUCCEEDED(expr))
+
+/**
+ * @name Standard Error Generating Macros
+ */
+
+#define NS_ERROR_GENERATE(sev, module, code) \
+ (nsresult)(((uint32_t)(sev) << 31) | \
+ ((uint32_t)(module + NS_ERROR_MODULE_BASE_OFFSET) << 16) | \
+ ((uint32_t)(code)))
+
+#define NS_ERROR_GENERATE_SUCCESS(module, code) \
+ NS_ERROR_GENERATE(NS_ERROR_SEVERITY_SUCCESS, module, code)
+
+#define NS_ERROR_GENERATE_FAILURE(module, code) \
+ NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR, module, code)
+
+ /*
+ * This will return the nsresult corresponding to the most recent NSPR failure
+ * returned by PR_GetError.
+ *
+ ***********************************************************************
+ * Do not depend on this function. It will be going away!
+ ***********************************************************************
+ */
+extern nsresult
+NS_ErrorAccordingToNSPR();
+
+
+/**
+ * @name Standard Macros for retrieving error bits
+ */
+
+inline constexpr uint16_t
+NS_ERROR_GET_CODE(nsresult aErr)
+{
+ return uint32_t(aErr) & 0xffff;
+}
+inline constexpr uint16_t
+NS_ERROR_GET_MODULE(nsresult aErr)
+{
+ return ((uint32_t(aErr) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff;
+}
+inline bool
+NS_ERROR_GET_SEVERITY(nsresult aErr)
+{
+ return uint32_t(aErr) >> 31;
+}
+
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4251) /* 'nsCOMPtr<class nsIInputStream>' needs to have dll-interface to be used by clients of class 'nsInputStream' */
+#pragma warning(disable: 4275) /* non dll-interface class 'nsISupports' used as base for dll-interface class 'nsIRDFNode' */
+#endif
+
+#endif
diff --git a/xpcom/base/nsErrorService.cpp b/xpcom/base/nsErrorService.cpp
new file mode 100644
index 000000000..d39b4f31e
--- /dev/null
+++ b/xpcom/base/nsErrorService.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsErrorService.h"
+#include "nsCRTGlue.h"
+#include "nsAutoPtr.h"
+
+NS_IMPL_ISUPPORTS(nsErrorService, nsIErrorService)
+
+nsresult
+nsErrorService::Create(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ RefPtr<nsErrorService> serv = new nsErrorService();
+ return serv->QueryInterface(aIID, aInstancePtr);
+}
+
+NS_IMETHODIMP
+nsErrorService::RegisterErrorStringBundle(int16_t aErrorModule,
+ const char* aStringBundleURL)
+{
+ mErrorStringBundleURLMap.Put(aErrorModule, new nsCString(aStringBundleURL));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsErrorService::UnregisterErrorStringBundle(int16_t aErrorModule)
+{
+ mErrorStringBundleURLMap.Remove(aErrorModule);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsErrorService::GetErrorStringBundle(int16_t aErrorModule, char** aResult)
+{
+ nsCString* bundleURL = mErrorStringBundleURLMap.Get(aErrorModule);
+ if (!bundleURL) {
+ return NS_ERROR_FAILURE;
+ }
+ *aResult = ToNewCString(*bundleURL);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/base/nsErrorService.h b/xpcom/base/nsErrorService.h
new file mode 100644
index 000000000..783c99ef9
--- /dev/null
+++ b/xpcom/base/nsErrorService.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsErrorService_h__
+#define nsErrorService_h__
+
+#include "mozilla/Attributes.h"
+
+#include "nsIErrorService.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+
+class nsErrorService final : public nsIErrorService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIERRORSERVICE
+
+ nsErrorService()
+ {
+ }
+
+ static nsresult
+ Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr);
+
+private:
+ ~nsErrorService()
+ {
+ }
+
+ nsClassHashtable<nsUint32HashKey, nsCString> mErrorStringBundleURLMap;
+};
+
+#endif // nsErrorService_h__
diff --git a/xpcom/base/nsGZFileWriter.cpp b/xpcom/base/nsGZFileWriter.cpp
new file mode 100644
index 000000000..a5bc5be39
--- /dev/null
+++ b/xpcom/base/nsGZFileWriter.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsGZFileWriter.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "zlib.h"
+
+#ifdef XP_WIN
+#include <io.h>
+#define _dup dup
+#else
+#include <unistd.h>
+#endif
+
+NS_IMPL_ISUPPORTS(nsGZFileWriter, nsIGZFileWriter)
+
+nsGZFileWriter::nsGZFileWriter(Operation aMode)
+ : mMode(aMode)
+ , mInitialized(false)
+ , mFinished(false)
+{
+}
+
+nsGZFileWriter::~nsGZFileWriter()
+{
+ if (mInitialized && !mFinished) {
+ Finish();
+ }
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::Init(nsIFile* aFile)
+{
+ if (NS_WARN_IF(mInitialized) ||
+ NS_WARN_IF(mFinished)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get a FILE out of our nsIFile. Convert that into a file descriptor which
+ // gzip can own. Then close our FILE, leaving only gzip's fd open.
+
+ FILE* file;
+ nsresult rv = aFile->OpenANSIFileDesc(mMode == Create ? "wb" : "ab", &file);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return InitANSIFileDesc(file);
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::InitANSIFileDesc(FILE* aFile)
+{
+ mGZFile = gzdopen(dup(fileno(aFile)), mMode == Create ? "wb" : "ab");
+ fclose(aFile);
+
+ // gzdopen returns nullptr on error.
+ if (NS_WARN_IF(!mGZFile)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mInitialized = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::Write(const nsACString& aStr)
+{
+ if (NS_WARN_IF(!mInitialized) ||
+ NS_WARN_IF(mFinished)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // gzwrite uses a return value of 0 to indicate failure. Otherwise, it
+ // returns the number of uncompressed bytes written. To ensure we can
+ // distinguish between success and failure, don't call gzwrite when we have 0
+ // bytes to write.
+ if (aStr.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // gzwrite never does a short write -- that is, the return value should
+ // always be either 0 or aStr.Length(), and we shouldn't have to call it
+ // multiple times in order to get it to read the whole buffer.
+ int rv = gzwrite(mGZFile, aStr.BeginReading(), aStr.Length());
+ if (NS_WARN_IF(rv != static_cast<int>(aStr.Length()))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::Finish()
+{
+ if (NS_WARN_IF(!mInitialized) ||
+ NS_WARN_IF(mFinished)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mFinished = true;
+ gzclose(mGZFile);
+
+ // Ignore errors from gzclose; it's not like there's anything we can do about
+ // it, at this point!
+ return NS_OK;
+}
diff --git a/xpcom/base/nsGZFileWriter.h b/xpcom/base/nsGZFileWriter.h
new file mode 100644
index 000000000..4bb91173c
--- /dev/null
+++ b/xpcom/base/nsGZFileWriter.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsGZFileWriter_h
+#define nsGZFileWriter_h
+
+#include "nsIGZFileWriter.h"
+#include "zlib.h"
+
+/**
+ * A simple class for writing .gz files.
+ */
+class nsGZFileWriter final : public nsIGZFileWriter
+{
+ virtual ~nsGZFileWriter();
+
+public:
+
+ enum Operation {
+ Append,
+ Create
+ };
+
+
+ explicit nsGZFileWriter(Operation aMode = Create);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGZFILEWRITER
+
+ /**
+ * nsIGZFileWriter exposes two non-virtual overloads of Write(). We
+ * duplicate them here so that you can call these overloads on a pointer to
+ * the concrete nsGZFileWriter class.
+ */
+ MOZ_MUST_USE nsresult Write(const char* aStr)
+ {
+ return nsIGZFileWriter::Write(aStr);
+ }
+
+ MOZ_MUST_USE nsresult Write(const char* aStr, uint32_t aLen)
+ {
+ return nsIGZFileWriter::Write(aStr, aLen);
+ }
+
+private:
+ Operation mMode;
+ bool mInitialized;
+ bool mFinished;
+ gzFile mGZFile;
+};
+
+#endif
diff --git a/xpcom/base/nsIConsoleListener.idl b/xpcom/base/nsIConsoleListener.idl
new file mode 100644
index 000000000..45e6fcb1f
--- /dev/null
+++ b/xpcom/base/nsIConsoleListener.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Used by the console service to notify listeners of new console messages.
+ */
+
+#include "nsISupports.idl"
+
+interface nsIConsoleMessage;
+
+[scriptable, function, uuid(35c400a4-5792-438c-b915-65e30d58d557)]
+interface nsIConsoleListener : nsISupports
+{
+ void observe(in nsIConsoleMessage aMessage);
+};
diff --git a/xpcom/base/nsIConsoleMessage.idl b/xpcom/base/nsIConsoleMessage.idl
new file mode 100644
index 000000000..bf233b28b
--- /dev/null
+++ b/xpcom/base/nsIConsoleMessage.idl
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsISupports.idl"
+
+/**
+ * This is intended as a base interface; implementations may want to
+ * provide an object that can be qi'ed to provide more specific
+ * message information.
+ */
+[scriptable, uuid(3aba9617-10e2-4839-83ae-2e6fc4df428b)]
+interface nsIConsoleMessage : nsISupports
+{
+ /** Log level constants. */
+ const uint32_t debug = 0;
+ const uint32_t info = 1;
+ const uint32_t warn = 2;
+ const uint32_t error = 3;
+
+ /**
+ * The log level of this message.
+ */
+ readonly attribute uint32_t logLevel;
+
+ /**
+ * The time (in milliseconds from the Epoch) that the message instance
+ * was initialised.
+ * The timestamp is initialized as JS_now/1000 so that it can be
+ * compared to Date.now in Javascript.
+ */
+ readonly attribute long long timeStamp;
+
+ [binaryname(MessageMoz)] readonly attribute wstring message;
+
+ AUTF8String toString();
+};
+
+%{ C++
+#define NS_CONSOLEMESSAGE_CID \
+{ 0x024efc9e, 0x54dc, 0x4844, { 0x80, 0x4b, 0x41, 0xd3, 0xf3, 0x69, 0x90, 0x73 }}
+%}
diff --git a/xpcom/base/nsIConsoleService.idl b/xpcom/base/nsIConsoleService.idl
new file mode 100644
index 000000000..fb9e906fb
--- /dev/null
+++ b/xpcom/base/nsIConsoleService.idl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIConsoleListener;
+interface nsIConsoleMessage;
+
+[scriptable, uuid(0eb81d20-c37e-42d4-82a8-ca9ae96bdf52)]
+interface nsIConsoleService : nsISupports
+{
+ void logMessage(in nsIConsoleMessage message);
+
+ /**
+ * Convenience method for logging simple messages.
+ */
+ void logStringMessage(in wstring message);
+
+ /**
+ * Get an array of all the messages logged so far. If no messages
+ * are logged, this function will return a count of 0, but still
+ * will allocate one word for messages, so as to show up as a
+ * 0-length array when called from script.
+ */
+ void getMessageArray([optional] out uint32_t count,
+ [retval, array, size_is(count)] out nsIConsoleMessage messages);
+
+ /**
+ * To guard against stack overflows from listeners that could log
+ * messages (it's easy to do this inadvertently from listeners
+ * implemented in JavaScript), we don't call any listeners when
+ * another error is already being logged.
+ */
+ void registerListener(in nsIConsoleListener listener);
+
+ /**
+ * Each registered listener should also be unregistered.
+ */
+ void unregisterListener(in nsIConsoleListener listener);
+
+ /**
+ * Clear the message buffer (e.g. for privacy reasons).
+ */
+ void reset();
+};
+
+
+%{ C++
+#define NS_CONSOLESERVICE_CID \
+{ 0x7e3ff85c, 0x1dd2, 0x11b2, { 0x8d, 0x4b, 0xeb, 0x45, 0x2c, 0xb0, 0xff, 0x40 }}
+
+#define NS_CONSOLESERVICE_CONTRACTID "@mozilla.org/consoleservice;1"
+%}
+
diff --git a/xpcom/base/nsICycleCollectorListener.idl b/xpcom/base/nsICycleCollectorListener.idl
new file mode 100644
index 000000000..cfdf9abe9
--- /dev/null
+++ b/xpcom/base/nsICycleCollectorListener.idl
@@ -0,0 +1,164 @@
+/* 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"
+
+%{C++
+#include <stdio.h>
+
+class nsCycleCollectorLogger;
+%}
+
+[ptr] native FILE(FILE);
+[ptr] native nsCycleCollectorLoggerPtr (nsCycleCollectorLogger);
+interface nsIFile;
+
+/**
+ * A set of interfaces for recording the cycle collector's work. An instance
+ * of @mozilla.org/cycle-collector-logger;1 can be configured to enable various
+ * options, then passed to the cycle collector when it runs.
+ * Note that additional logging options are available by setting environment
+ * variables, as described at the top of nsCycleCollector.cpp.
+ */
+
+/**
+ * nsICycleCollectorHandler is the interface JS code should implement to
+ * receive the results logged by a @mozilla.org/cycle-collector-logger;1
+ * instance. Pass an instance of this to the logger's 'processNext' method
+ * after the collection has run. This will describe the objects the cycle
+ * collector visited, the edges it found, and the conclusions it reached
+ * about the liveness of objects.
+ *
+ * In more detail:
+ * - For each node in the graph:
+ * - a call is made to either |noteRefCountedObject| or |noteGCedObject|, to
+ * describe the node itself; and
+ * - for each edge starting at that node, a call is made to |noteEdge|.
+ *
+ * - Then, a series of calls are made to:
+ * - |describeRoot|, for reference-counted nodes that the CC has identified as
+ * being alive because there are unknown references to those nodes.
+ * - |describeGarbage|, for nodes the cycle collector has identified as garbage.
+ *
+ * Any node not mentioned in a call to |describeRoot| or |describeGarbage| is
+ * neither a root nor garbage. The cycle collector was able to find all of the
+ * edges implied by the node's reference count.
+ */
+[scriptable, uuid(7f093367-1492-4b89-87af-c01dbc831246)]
+interface nsICycleCollectorHandler : nsISupports
+{
+ void noteRefCountedObject(in ACString aAddress,
+ in unsigned long aRefCount,
+ in ACString aObjectDescription);
+ void noteGCedObject(in ACString aAddress,
+ in boolean aMarked,
+ in ACString aObjectDescription,
+ in ACString aCompartmentAddress);
+ void noteEdge(in ACString aFromAddress,
+ in ACString aToAddress,
+ in ACString aEdgeName);
+ void describeRoot(in ACString aAddress,
+ in unsigned long aKnownEdges);
+ void describeGarbage(in ACString aAddress);
+};
+
+
+/**
+ * This interface allows replacing the log-writing backend for an
+ * nsICycleCollectorListener. As this interface is also called while
+ * the cycle collector is running, it cannot be implemented in JS.
+ */
+[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
+interface nsICycleCollectorLogSink : nsISupports
+{
+ [noscript] void open(out FILE aGCLog, out FILE aCCLog);
+ void closeGCLog();
+ void closeCCLog();
+
+ // This string will appear somewhere in the log's filename.
+ attribute AString filenameIdentifier;
+
+ // This is the process ID; it can be changed if logging is on behalf
+ // of another process.
+ attribute int32_t processIdentifier;
+
+ // The GC log file, if logging to files.
+ readonly attribute nsIFile gcLog;
+
+ // The CC log file, if logging to files.
+ readonly attribute nsIFile ccLog;
+};
+
+
+/**
+ * This interface is used to configure some reporting options for the cycle
+ * collector. This interface cannot be implemented by JavaScript code, as it
+ * is called while the cycle collector is running.
+ *
+ * To analyze cycle collection data in JS:
+ *
+ * - Create an instance of @mozilla.org/cycle-collector-logger;1, which
+ * implements this interface.
+ *
+ * - Set its |disableLog| property to true. This prevents the logger from
+ * printing messages about each method call to a temporary log file.
+ *
+ * - Set its |wantAfterProcessing| property to true. This tells the logger
+ * to record calls to its methods in memory. The |processNext| method
+ * returns events from this record.
+ *
+ * - Perform a collection using the logger. For example, call
+ * |nsIDOMWindowUtils|'s |garbageCollect| method, passing the logger as
+ * the |aListener| argument.
+ *
+ * - When the collection is complete, loop calling the logger's
+ * |processNext| method, passing a JavaScript object that implements
+ * nsICycleCollectorHandler. This JS code is free to allocate and operate
+ * on objects however it pleases: the cycle collector has finished its
+ * work, and the JS code is simply consuming recorded data.
+ */
+[scriptable, builtinclass, uuid(703b53b6-24f6-40c6-9ea9-aeb2dc53d170)]
+interface nsICycleCollectorListener : nsISupports
+{
+ // Return a listener that directs the cycle collector to traverse
+ // objects that it knows won't be collectable.
+ //
+ // Note that even this listener will not visit every node in the heap;
+ // the cycle collector can't see the entire heap. But while this
+ // listener is in use, the collector disables some optimizations it
+ // normally uses to avoid certain classes of objects that are certainly
+ // alive. So, if your purpose is to get a view of the portion of the
+ // heap that is of interest to the cycle collector, and not simply find
+ // garbage, then you should use the listener this returns.
+ //
+ // Note that this does not necessarily return a new listener; rather, it may
+ // simply set a flag on this listener (a side effect!) and return it.
+ nsICycleCollectorListener allTraces();
+
+ // True if this listener will behave like one returned by allTraces().
+ readonly attribute boolean wantAllTraces;
+
+ // If true, do not log each method call to a temporary file.
+ // Initially false.
+ attribute boolean disableLog;
+
+ // If |disableLog| is false, this object will be sent the log text.
+ attribute nsICycleCollectorLogSink logSink;
+
+ // If true, record all method calls in memory, to be retrieved later
+ // using |processNext|. Initially false.
+ attribute boolean wantAfterProcessing;
+
+ // Report the next recorded event to |aHandler|, and remove it from the
+ // record. Return false if there isn't anything more to process.
+ //
+ // Note that we only record events to report here if our
+ // |wantAfterProcessing| property is true.
+ boolean processNext(in nsICycleCollectorHandler aHandler);
+
+ // Return the current object as an nsCycleCollectorLogger*, which is the
+ // only class that should be implementing this interface. We need the
+ // concrete implementation type to help the GC rooting analysis.
+ [noscript] nsCycleCollectorLoggerPtr asLogger();
+};
diff --git a/xpcom/base/nsIDebug2.idl b/xpcom/base/nsIDebug2.idl
new file mode 100644
index 000000000..4401f8a91
--- /dev/null
+++ b/xpcom/base/nsIDebug2.idl
@@ -0,0 +1,82 @@
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+/* 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/. */
+
+/* interface to expose information about calls to NS_DebugBreak */
+
+#include "nsISupports.idl"
+
+/**
+ * For use by consumers in scripted languages (JavaScript, Java, Python,
+ * Perl, ...).
+ *
+ * @note C/C++ consumers who are planning to use the nsIDebug2 interface with
+ * the "@mozilla.org/xpcom;1" contract should use NS_DebugBreak from xpcom
+ * glue instead.
+ *
+ */
+
+[scriptable, uuid(9641dc15-10fb-42e3-a285-18be90a5c10b)]
+interface nsIDebug2 : nsISupports
+{
+ /**
+ * Whether XPCOM was compiled with DEBUG defined. This often
+ * correlates to whether other code (e.g., Firefox, XULRunner) was
+ * compiled with DEBUG defined.
+ */
+ readonly attribute boolean isDebugBuild;
+
+ /**
+ * The number of assertions since process start.
+ */
+ readonly attribute long assertionCount;
+
+ /**
+ * Whether a debugger is currently attached.
+ * Supports Windows + Mac
+ */
+ readonly attribute bool isDebuggerAttached;
+
+ /**
+ * Show an assertion and trigger nsIDebug2.break().
+ *
+ * @param aStr assertion message
+ * @param aExpr expression that failed
+ * @param aFile file containing assertion
+ * @param aLine line number of assertion
+ */
+ void assertion(in string aStr,
+ in string aExpr,
+ in string aFile,
+ in long aLine);
+
+ /**
+ * Show a warning.
+ *
+ * @param aStr warning message
+ * @param aFile file containing assertion
+ * @param aLine line number of assertion
+ */
+ void warning(in string aStr,
+ in string aFile,
+ in long aLine);
+
+ /**
+ * Request to break into a debugger.
+ *
+ * @param aFile file containing break request
+ * @param aLine line number of break request
+ */
+ void break(in string aFile,
+ in long aLine);
+
+ /**
+ * Request the process to trigger a fatal abort.
+ *
+ * @param aFile file containing abort request
+ * @param aLine line number of abort request
+ */
+ void abort(in string aFile,
+ in long aLine);
+};
diff --git a/xpcom/base/nsIErrorService.idl b/xpcom/base/nsIErrorService.idl
new file mode 100644
index 000000000..9eeea2382
--- /dev/null
+++ b/xpcom/base/nsIErrorService.idl
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * nsIErrorService: This is an interim service that allows nsresult codes to be mapped to
+ * string bundles that can be used to look up error messages. String bundle keys can also
+ * be mapped.
+ *
+ * This service will eventually get replaced by extending xpidl to allow errors to be defined.
+ * (http://bugzilla.mozilla.org/show_bug.cgi?id=13423).
+ */
+[scriptable, uuid(afe1f190-a3c2-11e3-a5e2-0800200c9a66)]
+interface nsIErrorService : nsISupports
+{
+ /**
+ * Registers a string bundle URL for an error module. Error modules are obtained from
+ * nsresult code with NS_ERROR_GET_MODULE.
+ */
+ void registerErrorStringBundle(in short errorModule, in string stringBundleURL);
+
+ /**
+ * Unregisters a string bundle URL for an error module.
+ */
+ void unregisterErrorStringBundle(in short errorModule);
+
+ /**
+ * Retrieves a string bundle URL for an error module.
+ */
+ string getErrorStringBundle(in short errorModule);
+};
+
+%{C++
+
+// The global nsIErrorService:
+#define NS_ERRORSERVICE_NAME "Error Service"
+#define NS_ERRORSERVICE_CONTRACTID "@mozilla.org/xpcom/error-service;1"
+#define NS_ERRORSERVICE_CID \
+{ /* 744afd5e-5f8c-11d4-9877-00c04fa0cf4a */ \
+ 0x744afd5e, \
+ 0x5f8c, \
+ 0x11d4, \
+ {0x98, 0x77, 0x00, 0xc0, 0x4f, 0xa0, 0xcf, 0x4a} \
+}
+
+%}
diff --git a/xpcom/base/nsIException.idl b/xpcom/base/nsIException.idl
new file mode 100644
index 000000000..ff5a402ed
--- /dev/null
+++ b/xpcom/base/nsIException.idl
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Interfaces for representing cross-language exceptions and stack traces.
+ */
+
+
+#include "nsISupports.idl"
+
+[scriptable, builtinclass, uuid(28bfb2a2-5ea6-4738-918b-049dc4d51f0b)]
+interface nsIStackFrame : nsISupports
+{
+ // see nsIProgrammingLanguage for list of language consts
+ readonly attribute uint32_t language;
+ readonly attribute AUTF8String languageName;
+ [implicit_jscontext]
+ readonly attribute AString filename;
+ [implicit_jscontext]
+ readonly attribute AString name;
+ // Valid line numbers begin at '1'. '0' indicates unknown.
+ [implicit_jscontext]
+ readonly attribute int32_t lineNumber;
+ [implicit_jscontext]
+ readonly attribute int32_t columnNumber;
+ readonly attribute AUTF8String sourceLine;
+ [implicit_jscontext]
+ readonly attribute AString asyncCause;
+ [implicit_jscontext]
+ readonly attribute nsIStackFrame asyncCaller;
+ [implicit_jscontext]
+ readonly attribute nsIStackFrame caller;
+
+ // Returns a formatted stack string that looks like the sort of
+ // string that would be returned by .stack on JS Error objects.
+ // Only works on JS-language stack frames.
+ [implicit_jscontext]
+ readonly attribute AString formattedStack;
+
+ // Returns the underlying SavedFrame object for native JavaScript stacks,
+ // or null if this is not a native JavaScript stack frame.
+ readonly attribute jsval nativeSavedFrame;
+
+ [implicit_jscontext]
+ AUTF8String toString();
+};
+
+[scriptable, builtinclass, uuid(4371b5bf-6845-487f-8d9d-3f1e4a9badd2)]
+interface nsIException : nsISupports
+{
+ // A custom message set by the thrower.
+ [binaryname(MessageMoz)] readonly attribute AUTF8String message;
+ // The nsresult associated with this exception.
+ readonly attribute nsresult result;
+ // The name of the error code (ie, a string repr of |result|)
+ readonly attribute AUTF8String name;
+
+ // Filename location. This is the location that caused the
+ // error, which may or may not be a source file location.
+ // For example, standard language errors would generally have
+ // the same location as their top stack entry. File
+ // parsers may put the location of the file they were parsing,
+ // etc.
+
+ // null indicates "no data"
+ [implicit_jscontext]
+ readonly attribute AString filename;
+ // Valid line numbers begin at '1'. '0' indicates unknown.
+ [implicit_jscontext]
+ readonly attribute uint32_t lineNumber;
+ // Valid column numbers begin at 0.
+ // We don't have an unambiguous indicator for unknown.
+ readonly attribute uint32_t columnNumber;
+
+ // A stack trace, if available.
+ readonly attribute nsIStackFrame location;
+
+ // Arbitary data for the implementation.
+ readonly attribute nsISupports data;
+
+ // A generic formatter - make it suitable to print, etc.
+ [implicit_jscontext]
+ AUTF8String toString();
+};
diff --git a/xpcom/base/nsIGZFileWriter.idl b/xpcom/base/nsIGZFileWriter.idl
new file mode 100644
index 000000000..11e27f5c4
--- /dev/null
+++ b/xpcom/base/nsIGZFileWriter.idl
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include "nsDependentString.h"
+#include <stdio.h>
+%}
+
+interface nsIFile;
+[ptr] native FILE(FILE);
+
+/**
+ * A simple interface for writing to a .gz file.
+ *
+ * Note that the file that this interface produces has a different format than
+ * what you'd get if you compressed your data as a gzip stream and dumped the
+ * result to a file.
+ *
+ * The standard gunzip tool cannot decompress a raw gzip stream, but can handle
+ * the files produced by this interface.
+ */
+[scriptable, uuid(6bd5642c-1b90-4499-ba4b-199f27efaba5)]
+interface nsIGZFileWriter : nsISupports
+{
+ /**
+ * Initialize this object. We'll write our gzip'ed data to the given file,
+ * overwriting its contents if the file exists.
+ *
+ * init() will return an error if called twice. It's an error to call any
+ * other method on this interface without first calling init().
+ */
+ [must_use] void init(in nsIFile file);
+
+ /**
+ * Alternate version of init() for use when the file is already opened;
+ * e.g., with a FileDescriptor passed over IPC.
+ */
+ [noscript, must_use] void initANSIFileDesc(in FILE file);
+
+ /**
+ * Write the given string to the file.
+ */
+ [must_use] void write(in AUTF8String str);
+
+ /*
+ * The following two overloads of Write() are C++ because we can't overload
+ * methods in XPIDL. Anyway, they don't add much functionality for JS
+ * callers.
+ */
+ %{C++
+ /**
+ * Write the given char* to the file (not including the null-terminator).
+ */
+ MOZ_MUST_USE nsresult Write(const char* str)
+ {
+ return Write(str, strlen(str));
+ }
+
+ /**
+ * Write |length| bytes of |str| to the file.
+ */
+ MOZ_MUST_USE nsresult Write(const char* str, uint32_t len)
+ {
+ return Write(nsDependentCString(str, len));
+ }
+ %}
+
+ /**
+ * Close this nsIGZFileWriter. Classes implementing nsIGZFileWriter will run
+ * this method when the underlying object is destroyed, so it's not strictly
+ * necessary to explicitly call it from your code.
+ *
+ * It's an error to call this method twice, and it's an error to call write()
+ * after finish() has been called.
+ */
+ void finish();
+};
diff --git a/xpcom/base/nsIID.h b/xpcom/base/nsIID.h
new file mode 100644
index 000000000..f2d788576
--- /dev/null
+++ b/xpcom/base/nsIID.h
@@ -0,0 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsIID_h
+#define __nsIID_h
+#include "nsID.h"
+#endif /* __nsIID_h */
diff --git a/xpcom/base/nsIInterfaceRequestor.idl b/xpcom/base/nsIInterfaceRequestor.idl
new file mode 100644
index 000000000..9727c53cb
--- /dev/null
+++ b/xpcom/base/nsIInterfaceRequestor.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
+
+/**
+ * The nsIInterfaceRequestor interface defines a generic interface for
+ * requesting interfaces that a given object might provide access to.
+ * This is very similar to QueryInterface found in nsISupports.
+ * The main difference is that interfaces returned from GetInterface()
+ * are not required to provide a way back to the object implementing this
+ * interface. The semantics of QI() dictate that given an interface A that
+ * you QI() on to get to interface B, you must be able to QI on B to get back
+ * to A. This interface however allows you to obtain an interface C from A
+ * that may or most likely will not have the ability to get back to A.
+ */
+
+[scriptable, uuid(033A1470-8B2A-11d3-AF88-00A024FFC08C)]
+interface nsIInterfaceRequestor : nsISupports
+{
+ /**
+ * Retrieves the specified interface pointer.
+ *
+ * @param uuid The IID of the interface being requested.
+ * @param result [out] The interface pointer to be filled in if
+ * the interface is accessible.
+ * @throws NS_NOINTERFACE - interface not accessible.
+ * @throws NS_ERROR* - method failure.
+ */
+ void getInterface(in nsIIDRef uuid,
+ [iid_is(uuid),retval] out nsQIResult result);
+};
+
diff --git a/xpcom/base/nsIMacUtils.idl b/xpcom/base/nsIMacUtils.idl
new file mode 100644
index 000000000..9a60df47c
--- /dev/null
+++ b/xpcom/base/nsIMacUtils.idl
@@ -0,0 +1,32 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * nsIMacUtils: Generic globally-available Mac-specific utilities.
+ */
+
+[scriptable, uuid(5E9072D7-FF95-455E-9466-8AF9841A72EC)]
+interface nsIMacUtils : nsISupports
+{
+ /**
+ * True when the main executable is a fat file supporting at least
+ * ppc and x86 (universal binary).
+ */
+ readonly attribute boolean isUniversalBinary;
+
+ /**
+ * Returns a string containing a list of architectures delimited
+ * by "-". Architecture sets are always in the same order:
+ * ppc > i386 > ppc64 > x86_64 > (future additions)
+ */
+ readonly attribute AString architecturesInBinary;
+
+ /**
+ * True when running under binary translation (Rosetta).
+ */
+ readonly attribute boolean isTranslated;
+};
diff --git a/xpcom/base/nsIMemory.idl b/xpcom/base/nsIMemory.idl
new file mode 100644
index 000000000..0c1a050b7
--- /dev/null
+++ b/xpcom/base/nsIMemory.idl
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ *
+ * nsIMemory: interface to allocate and deallocate memory. Also provides
+ * for notifications in low-memory situations.
+ *
+ * The frozen exported symbols moz_xmalloc, moz_xrealloc, and free
+ * provide a more efficient way to access XPCOM memory allocation. Using
+ * those symbols is preferred to using the methods on this interface.
+ *
+ * A client that wishes to be notified of low memory situations (for
+ * example, because the client maintains a large memory cache that
+ * could be released when memory is tight) should register with the
+ * observer service (see nsIObserverService) using the topic
+ * "memory-pressure". There are specific types of notications
+ * that can occur. These types will be passed as the |aData|
+ * parameter of the of the "memory-pressure" notification:
+ *
+ * "low-memory"
+ * This will be passed as the extra data when the pressure
+ * observer is being asked to flush for low-memory conditions.
+ *
+ * "low-memory-ongoing"
+ * This will be passed when we continue to be in a low-memory
+ * condition and we want to flush caches and do other cheap
+ * forms of memory minimization, but heavy handed approaches like
+ * a GC are unlikely to succeed.
+ *
+ * "-no-forward"
+ * This is appended to the above two parameters when the resulting
+ * notification should not be forwarded to the child processes.
+ *
+ * "heap-minimize"
+ * This will be passed as the extra data when the pressure
+ * observer is being asked to flush because of a heap minimize
+ * call.
+ *
+ * "alloc-failure"
+ * This will be passed as the extra data when the pressure
+ * observer has been asked to flush because a malloc() or
+ * realloc() has failed.
+ *
+ * "lowering-priority"
+ * This will be passed as the extra data when the priority of a child
+ * process is lowered. The pressure observers could take the chance to
+ * clear caches that could be easily regenerated. This type of
+ * notification only appears in child processes.
+ */
+
+[scriptable, uuid(1e004834-6d8f-425a-bc9c-a2812ed43bb7)]
+interface nsIMemory : nsISupports
+{
+ /**
+ * Attempts to shrink the heap.
+ * @param immediate - if true, heap minimization will occur
+ * immediately if the call was made on the main thread. If
+ * false, the flush will be scheduled to happen when the app is
+ * idle.
+ * @throws NS_ERROR_FAILURE if 'immediate' is set an the call
+ * was not on the application's main thread.
+ */
+ void heapMinimize(in boolean immediate);
+
+ /**
+ * This predicate can be used to determine if the platform is a "low-memory"
+ * platform. Callers may use this to dynamically tune their behaviour
+ * to favour reduced memory usage at the expense of performance. The value
+ * returned by this function will not change over the lifetime of the process.
+ */
+ boolean isLowMemoryPlatform();
+};
+
diff --git a/xpcom/base/nsIMemoryInfoDumper.idl b/xpcom/base/nsIMemoryInfoDumper.idl
new file mode 100644
index 000000000..7e08503a4
--- /dev/null
+++ b/xpcom/base/nsIMemoryInfoDumper.idl
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 50; 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 "nsISupports.idl"
+
+interface nsIFile;
+interface nsICycleCollectorLogSink;
+
+[scriptable, function, uuid(2dea18fc-fbfa-4bf7-ad45-0efaf5495f5e)]
+interface nsIFinishDumpingCallback : nsISupports
+{
+ void callback(in nsISupports data);
+};
+
+/**
+ * Callback interface for |dumpGCAndCCLogsToFile|, below. Note that
+ * these method calls can occur before |dumpGCAndCCLogsToFile|
+ * returns.
+ */
+[scriptable, uuid(dc1b2b24-65bd-441b-b6bd-cb5825a7ed14)]
+interface nsIDumpGCAndCCLogsCallback : nsISupports
+{
+ /**
+ * Called whenever a process has successfully finished dumping its GC/CC logs.
+ * Incomplete dumps (e.g., if the child crashes or is killed due to memory
+ * exhaustion) are not reported.
+ *
+ * @param aGCLog The file that the GC log was written to.
+ *
+ * @param aCCLog The file that the CC log was written to.
+ *
+ * @param aIsParent indicates whether this log file pair is from the
+ * parent process.
+ */
+ void onDump(in nsIFile aGCLog,
+ in nsIFile aCCLog,
+ in bool aIsParent);
+
+ /**
+ * Called when GC/CC logging has finished, after all calls to |onDump|.
+ */
+ void onFinish();
+};
+
+[scriptable, builtinclass, uuid(48541b74-47ee-4a62-9557-7f4b809bda5c)]
+interface nsIMemoryInfoDumper : nsISupports
+{
+ /**
+ * This dumps gzipped memory reports for this process and its child
+ * processes. If a file of the given name exists, it will be overwritten.
+ *
+ * @param aFilename The output file.
+ *
+ * @param aFinishDumping The callback called on completion.
+ *
+ * @param aFinishDumpingData The environment for the callback.
+ *
+ * @param aAnonymize Should the reports be anonymized?
+ *
+ * Sample output, annotated with comments for explanatory purposes.
+ *
+ * {
+ * // The version number of the format, which will be incremented each time
+ * // backwards-incompatible changes are made. A mandatory integer.
+ * "version": 1
+ *
+ * // Equal to nsIMemoryReporterManager::hasMozMallocUsableSize. A
+ * // mandatory boolean.
+ * "hasMozMallocUsableSize": true,
+ *
+ * // The memory reports. A mandatory array.
+ * "reports": [
+ * // The properties correspond to the arguments of
+ * // nsIHandleReportCallback::callback. Every one is mandatory.
+ * {"process":"Main Process (pid 12345)", "path":"explicit/foo/bar",
+ * "kind":1, "units":0, "amount":2000000, "description":"Foo bar."},
+ * {"process":"Main Process (pid 12345)", "path":"heap-allocated",
+ * "kind":1, "units":0, "amount":3000000, "description":"Heap allocated."},
+ * {"process":"Main Process (pid 12345)", "path":"vsize",
+ * "kind":1, "units":0, "amount":10000000, "description":"Vsize."}
+ * ]
+ * }
+ */
+ void dumpMemoryReportsToNamedFile(in AString aFilename,
+ in nsIFinishDumpingCallback aFinishDumping,
+ in nsISupports aFinishDumpingData,
+ in boolean aAnonymize);
+
+ /**
+ * Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
+ * reports for this process and its child processes to files in the tmp
+ * directory called memory-reports-<identifier>-<pid>.json.gz (or something
+ * similar, such as memory-reports-<identifier>-<pid>-1.json.gz; no existing
+ * file will be overwritten).
+ *
+ * If DMD is enabled, this method also dumps gzipped DMD output for this
+ * process and its child processes to files in the tmp directory called
+ * dmd-<identifier>-<pid>.txt.gz (or something similar; again, no existing
+ * file will be overwritten).
+ *
+ * @param aIdentifier this identifier will appear in the filename of our
+ * about:memory dump and those of our children.
+ *
+ * If the identifier is empty, the implementation may set it arbitrarily
+ * and use that new value for its own dump and the dumps of its child
+ * processes. For example, the implementation may set |aIdentifier| to the
+ * number of seconds since the epoch.
+ *
+ * @param aAnonymize Should the reports be anonymized?
+ *
+ * @param aMinimizeMemoryUsage indicates whether we should run a series of
+ * gc/cc's in an attempt to reduce our memory usage before collecting our
+ * memory report.
+ */
+ void dumpMemoryInfoToTempDir(
+ in AString aIdentifier,
+ in boolean aAnonymize,
+ in boolean aMinimizeMemoryUsage);
+
+ /**
+ * Dump GC and CC logs to files in the OS's temp directory (or in
+ * $MOZ_CC_LOG_DIRECTORY, if that environment variable is specified).
+ *
+ * @param aIdentifier If aIdentifier is non-empty, this string will appear in
+ * the filenames of the logs we create (both for this process and, if
+ * aDumpChildProcesses is true, for our child processes).
+ *
+ * If aIdentifier is empty, the implementation may set it to an
+ * arbitrary value; for example, it may set aIdentifier to the number
+ * of seconds since the epoch.
+ *
+ * @param aDumpAllTraces indicates whether we should run an all-traces CC
+ * log. An all-traces log visits all objects currently eligible for cycle
+ * collection, while a non-all-traces log avoids visiting some objects
+ * which we know are reachable.
+ *
+ * All-traces logs are much bigger than the alternative, but they may be
+ * helpful when trying to understand why a particular object is alive. For
+ * example, a non-traces-log will skip references held by an active
+ * document; if your object is being held alive by such a document, you
+ * probably want to see those references.
+ *
+ * @param aDumpChildProcesses indicates whether we should call
+ * DumpGCAndCCLogsToFile in our child processes. If so, the child processes
+ * will dump their children, and so on.
+ *
+ */
+ void dumpGCAndCCLogsToFile(in AString aIdentifier,
+ in bool aDumpAllTraces,
+ in bool aDumpChildProcesses,
+ in nsIDumpGCAndCCLogsCallback aCallback);
+
+ /**
+ * Like |dumpGCAndCCLogsToFile|, but sends the logs to the given log
+ * sink object instead of accessing the filesystem directly, and
+ * dumps the current process only.
+ */
+ void dumpGCAndCCLogsToSink(in bool aDumpAllTraces,
+ in nsICycleCollectorLogSink aSink);
+};
diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl
new file mode 100644
index 000000000..9617877df
--- /dev/null
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+%{C++
+#include <stdio.h>
+%}
+
+interface mozIDOMWindowProxy;
+interface nsIRunnable;
+interface nsISimpleEnumerator;
+[ptr] native FILE(FILE);
+
+/*
+ * Memory reporters measure Firefox's memory usage. They are primarily used to
+ * generate the about:memory page. You should read
+ * https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Memory_reporting
+ * before writing a memory reporter.
+ */
+
+[scriptable, function, uuid(62ef0e1c-dbd6-11e3-aa75-3c970e9f4238)]
+interface nsIMemoryReporterCallback : nsISupports
+{
+ /*
+ * The arguments to the callback are as follows.
+ *
+ *
+ * |process| The name of the process containing this reporter. Each
+ * reporter initially has "" in this field, indicating that it applies to the
+ * current process. (This is true even for reporters in a child process.)
+ * When a reporter from a child process is copied into the main process, the
+ * copy has its 'process' field set appropriately.
+ *
+ *
+ * |path| The path that this memory usage should be reported under. Paths
+ * are '/'-delimited, eg. "a/b/c".
+ *
+ * Each reporter can be viewed as representing a leaf node in a tree.
+ * Internal nodes of the tree don't have reporters. So, for example, the
+ * reporters "explicit/a/b", "explicit/a/c", "explicit/d/e", and
+ * "explicit/d/f" define this tree:
+ *
+ * explicit
+ * |--a
+ * | |--b [*]
+ * | \--c [*]
+ * \--d
+ * |--e [*]
+ * \--f [*]
+ *
+ * Nodes marked with a [*] have a reporter. Notice that the internal
+ * nodes are implicitly defined by the paths.
+ *
+ * Nodes within a tree should not overlap measurements, otherwise the
+ * parent node measurements will be double-counted. So in the example
+ * above, |b| should not count any allocations counted by |c|, and vice
+ * versa.
+ *
+ * All nodes within each tree must have the same units.
+ *
+ * If you want to include a '/' not as a path separator, e.g. because the
+ * path contains a URL, you need to convert each '/' in the URL to a '\'.
+ * Consumers of the path will undo this change. Any other '\' character
+ * in a path will also be changed. This is clumsy but hasn't caused any
+ * problems so far.
+ *
+ * The paths of all reporters form a set of trees. Trees can be
+ * "degenerate", i.e. contain a single entry with no '/'.
+ *
+ *
+ * |kind| There are three kinds of memory reporters.
+ *
+ * - HEAP: reporters measuring memory allocated by the heap allocator,
+ * e.g. by calling malloc, calloc, realloc, memalign, operator new, or
+ * operator new[]. Reporters in this category must have units
+ * UNITS_BYTES.
+ *
+ * - NONHEAP: reporters measuring memory which the program explicitly
+ * allocated, but does not live on the heap. Such memory is commonly
+ * allocated by calling one of the OS's memory-mapping functions (e.g.
+ * mmap, VirtualAlloc, or vm_allocate). Reporters in this category
+ * must have units UNITS_BYTES.
+ *
+ * - OTHER: reporters which don't fit into either of these categories.
+ * They can have any units.
+ *
+ * The kind only matters for reporters in the "explicit" tree;
+ * aboutMemory.js uses it to calculate "heap-unclassified".
+ *
+ *
+ * |units| The units on the reporter's amount. One of the following.
+ *
+ * - BYTES: The amount contains a number of bytes.
+ *
+ * - COUNT: The amount is an instantaneous count of things currently in
+ * existence. For instance, the number of tabs currently open would have
+ * units COUNT.
+ *
+ * - COUNT_CUMULATIVE: The amount contains the number of times some event
+ * has occurred since the application started up. For instance, the
+ * number of times the user has opened a new tab would have units
+ * COUNT_CUMULATIVE.
+ *
+ * The amount returned by a reporter with units COUNT_CUMULATIVE must
+ * never decrease over the lifetime of the application.
+ *
+ * - PERCENTAGE: The amount contains a fraction that should be expressed as
+ * a percentage. NOTE! The |amount| field should be given a value 100x
+ * the actual percentage; this number will be divided by 100 when shown.
+ * This allows a fractional percentage to be shown even though |amount| is
+ * an integer. E.g. if the actual percentage is 12.34%, |amount| should
+ * be 1234.
+ *
+ * Values greater than 100% are allowed.
+ *
+ *
+ * |amount| The numeric value reported by this memory reporter. Accesses
+ * can fail if something goes wrong when getting the amount.
+ *
+ *
+ * |description| A human-readable description of this memory usage report.
+ */
+ void callback(in ACString process, in AUTF8String path, in int32_t kind,
+ in int32_t units, in int64_t amount,
+ in AUTF8String description, in nsISupports data);
+};
+
+/*
+ * An nsIMemoryReporter reports one or more memory measurements via a
+ * callback function which is called once for each measurement.
+ *
+ * An nsIMemoryReporter that reports a single measurement is sometimes called a
+ * "uni-reporter". One that reports multiple measurements is sometimes called
+ * a "multi-reporter".
+ *
+ * aboutMemory.js is the most important consumer of memory reports. It
+ * places the following constraints on reports.
+ *
+ * - All reports within a single sub-tree must have the same units.
+ *
+ * - There may be an "explicit" tree. If present, it represents
+ * non-overlapping regions of memory that have been explicitly allocated with
+ * an OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
+ * heap-level allocation (e.g. malloc/calloc/operator new). Reporters in
+ * this tree must have kind HEAP or NONHEAP, units BYTES.
+ *
+ * It is preferred, but not required, that report descriptions use complete
+ * sentences (i.e. start with a capital letter and end with a period, or
+ * similar).
+ */
+[scriptable, uuid(92a36db1-46bd-4fe6-988e-47db47236d8b)]
+interface nsIMemoryReporter : nsISupports
+{
+ /*
+ * Run the reporter.
+ *
+ * If |anonymize| is true, the memory reporter should anonymize any
+ * privacy-sensitive details in memory report paths, by replacing them with a
+ * string such as "<anonymized>". Anonymized memory reports may be sent
+ * automatically via crash reports or telemetry.
+ *
+ * The following things are considered privacy-sensitive.
+ *
+ * - Content domains and URLs, and information derived from them.
+ * - Content data, such as strings.
+ * - Details about content code, such as filenames, function names or stack
+ * traces.
+ * - Details about or data from the user's system, such as filenames.
+ * - Running apps.
+ *
+ * In short, anything that could identify parts of the user's browsing
+ * history is considered privacy-sensitive.
+ *
+ * The following thing are not considered privacy-sensitive.
+ *
+ * - Chrome domains and URLs.
+ * - Information about installed extensions.
+ */
+ void collectReports(in nsIMemoryReporterCallback callback,
+ in nsISupports data,
+ in boolean anonymize);
+
+ /*
+ * Kinds. See the |kind| comment in nsIMemoryReporterCallback.
+ */
+ const int32_t KIND_NONHEAP = 0;
+ const int32_t KIND_HEAP = 1;
+ const int32_t KIND_OTHER = 2;
+
+ /*
+ * Units. See the |units| comment in nsIMemoryReporterCallback.
+ */
+ const int32_t UNITS_BYTES = 0;
+ const int32_t UNITS_COUNT = 1;
+ const int32_t UNITS_COUNT_CUMULATIVE = 2;
+ const int32_t UNITS_PERCENTAGE = 3;
+};
+
+[scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
+interface nsIFinishReportingCallback : nsISupports
+{
+ void callback(in nsISupports data);
+};
+
+[scriptable, builtinclass, uuid(2998574d-8993-407a-b1a5-8ad7417653e1)]
+interface nsIMemoryReporterManager : nsISupports
+{
+ /*
+ * Initialize.
+ */
+ [must_use] void init();
+
+ /*
+ * Register the given nsIMemoryReporter. The Manager service will hold a
+ * strong reference to the given reporter, and will be responsible for freeing
+ * the reporter at shutdown. You may manually unregister the reporter with
+ * unregisterStrongReporter() at any point.
+ */
+ void registerStrongReporter(in nsIMemoryReporter reporter);
+ void registerStrongAsyncReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * Like registerReporter, but the Manager service will hold a weak reference
+ * via a raw pointer to the given reporter. The reporter should be
+ * unregistered before shutdown.
+ * You cannot register JavaScript components with this function! Always
+ * register your JavaScript components with registerStrongReporter().
+ */
+ void registerWeakReporter(in nsIMemoryReporter reporter);
+ void registerWeakAsyncReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * Unregister the given memory reporter, which must have been registered with
+ * registerStrongReporter(). You normally don't need to unregister your
+ * strong reporters, as nsIMemoryReporterManager will take care of that at
+ * shutdown.
+ */
+ void unregisterStrongReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * Unregister the given memory reporter, which must have been registered with
+ * registerWeakReporter().
+ */
+ void unregisterWeakReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * These functions should only be used for testing purposes.
+ */
+ void blockRegistrationAndHideExistingReporters();
+ void unblockRegistrationAndRestoreOriginalReporters();
+ void registerStrongReporterEvenIfBlocked(in nsIMemoryReporter aReporter);
+
+ /*
+ * Get memory reports for the current process and all child processes.
+ * |handleReport| is called for each report, and |finishReporting| is called
+ * once all reports have been handled.
+ *
+ * |finishReporting| is called even if, for example, some child processes
+ * fail to report back. However, calls to this method will silently and
+ * immediately abort -- and |finishReporting| will not be called -- if a
+ * previous getReports() call is still in flight, i.e. if it has not yet
+ * finished invoking |finishReporting|. The silent abort is because the
+ * in-flight request will finish soon, and the caller would very likely just
+ * catch and ignore any error anyway.
+ *
+ * If |anonymize| is true, it indicates that the memory reporters should
+ * anonymize any privacy-sensitive data (see above).
+ */
+ void getReports(in nsIMemoryReporterCallback handleReport,
+ in nsISupports handleReportData,
+ in nsIFinishReportingCallback finishReporting,
+ in nsISupports finishReportingData,
+ in boolean anonymize);
+
+ /*
+ * As above, but: If |minimizeMemoryUsage| is true, then each process will
+ * minimize its memory usage (see the |minimizeMemoryUsage| method) before
+ * gathering its report. If DMD is enabled and |DMDDumpIdent| is non-empty
+ * then write a DMD report to a file in the usual temporary directory (see
+ * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
+ */
+ [noscript] void
+ getReportsExtended(in nsIMemoryReporterCallback handleReport,
+ in nsISupports handleReportData,
+ in nsIFinishReportingCallback finishReporting,
+ in nsISupports finishReportingData,
+ in boolean anonymize,
+ in boolean minimizeMemoryUsage,
+ in AString DMDDumpIdent);
+
+ /*
+ * As above, but if DMD is enabled and |DMDFile| is non-null then
+ * write a DMD report to that file and close it.
+ */
+ [noscript] void
+ getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
+ in nsISupports handleReportData,
+ in boolean anonymize,
+ in FILE DMDFile,
+ in nsIFinishReportingCallback finishReporting,
+ in nsISupports finishReportingData);
+
+ /*
+ * Called by an asynchronous memory reporter upon completion.
+ */
+ [noscript] void endReport();
+
+ /*
+ * The memory reporter manager, for the most part, treats reporters
+ * registered with it as a black box. However, there are some
+ * "distinguished" amounts (as could be reported by a memory reporter) that
+ * the manager provides as attributes, because they are sufficiently
+ * interesting that we want external code (e.g. telemetry) to be able to rely
+ * on them.
+ *
+ * Note that these are not reporters and so getReports() does not look at
+ * them. However, distinguished amounts can be embedded in a reporter.
+ *
+ * Access to these attributes can fail. In particular, some of them are not
+ * available on all platforms.
+ *
+ * If you add a new distinguished amount, please update
+ * toolkit/components/aboutmemory/tests/test_memoryReporters.xul.
+ *
+ * |vsize| (UNITS_BYTES) The virtual size, i.e. the amount of address space
+ * taken up.
+ *
+ * |vsizeMaxContiguous| (UNITS_BYTES) The size of the largest contiguous
+ * block of virtual memory.
+ *
+ * |resident| (UNITS_BYTES) The resident size (a.k.a. RSS or physical memory
+ * used).
+ *
+ * |residentFast| (UNITS_BYTES) This is like |resident|, but on Mac OS
+ * |resident| can purge pages, which is slow. It also affects the result of
+ * |residentFast|, and so |resident| and |residentFast| should not be used
+ * together.
+ *
+ * |residentPeak| (UNITS_BYTES) The peak resident size.
+ *
+ * |residentUnique| (UNITS_BYTES) The unique set size (a.k.a. USS).
+ *
+ * |heapAllocated| (UNITS_BYTES) Memory mapped by the heap allocator.
+ *
+ * |heapOverheadFraction| (UNITS_PERCENTAGE) In the heap allocator, this is
+ * the fraction of committed heap bytes that are overhead. Like all
+ * UNITS_PERCENTAGE measurements, its amount is multiplied by 100x so it can
+ * be represented by an int64_t.
+ *
+ * |JSMainRuntimeGCHeap| (UNITS_BYTES) Size of the main JS runtime's GC
+ * heap.
+ *
+ * |JSMainRuntimeTemporaryPeak| (UNITS_BYTES) Peak size of the transient
+ * storage in the main JSRuntime.
+ *
+ * |JSMainRuntimeCompartments{System,User}| (UNITS_COUNT) The number of
+ * {system,user} compartments in the main JS runtime.
+ *
+ * |imagesContentUsedUncompressed| (UNITS_BYTES) Memory used for decoded
+ * raster images in content.
+ *
+ * |storageSQLite| (UNITS_BYTES) Memory used by SQLite.
+ *
+ * |lowMemoryEvents{Virtual,Physical}| (UNITS_COUNT_CUMULATIVE) The number
+ * of low-{virtual,physical}-memory events that have occurred since the
+ * process started.
+ *
+ * |ghostWindows| (UNITS_COUNT) The number of ghost windows.
+ *
+ * |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a.
+ * major) page faults that have occurred since the process started.
+ */
+ [must_use] readonly attribute int64_t vsize;
+ [must_use] readonly attribute int64_t vsizeMaxContiguous;
+ [must_use] readonly attribute int64_t resident;
+ [must_use] readonly attribute int64_t residentFast;
+ [must_use] readonly attribute int64_t residentPeak;
+ [must_use] readonly attribute int64_t residentUnique;
+
+ [must_use] readonly attribute int64_t heapAllocated;
+ [must_use] readonly attribute int64_t heapOverheadFraction;
+
+ [must_use] readonly attribute int64_t JSMainRuntimeGCHeap;
+ [must_use] readonly attribute int64_t JSMainRuntimeTemporaryPeak;
+ [must_use] readonly attribute int64_t JSMainRuntimeCompartmentsSystem;
+ [must_use] readonly attribute int64_t JSMainRuntimeCompartmentsUser;
+
+ [must_use] readonly attribute int64_t imagesContentUsedUncompressed;
+
+ [must_use] readonly attribute int64_t storageSQLite;
+
+ [must_use] readonly attribute int64_t lowMemoryEventsVirtual;
+ [must_use] readonly attribute int64_t lowMemoryEventsPhysical;
+
+ [must_use] readonly attribute int64_t ghostWindows;
+
+ [must_use] readonly attribute int64_t pageFaultsHard;
+
+ /*
+ * This attribute indicates if moz_malloc_usable_size() works.
+ */
+ [infallible] readonly attribute boolean hasMozMallocUsableSize;
+
+ /*
+ * These attributes indicate DMD's status. "Enabled" means enabled at
+ * build-time.
+ */
+ [infallible] readonly attribute boolean isDMDEnabled;
+ [infallible] readonly attribute boolean isDMDRunning;
+
+ /*
+ * Run a series of GC/CC's in an attempt to minimize the application's memory
+ * usage. When we're finished, we invoke the given runnable if it's not
+ * null.
+ */
+ [must_use] void minimizeMemoryUsage(in nsIRunnable callback);
+
+ /*
+ * Measure the memory that is known to be owned by this tab, split up into
+ * several broad categories. Note that this will be an underestimate of the
+ * true number, due to imperfect memory reporter coverage (corresponding to
+ * about:memory's "heap-unclassified"), and due to some memory shared between
+ * tabs not being counted.
+ *
+ * The time taken for the measurement (split into JS and non-JS parts) is
+ * also returned.
+ */
+ [must_use]
+ void sizeOfTab(in mozIDOMWindowProxy window,
+ out int64_t jsObjectsSize, out int64_t jsStringsSize,
+ out int64_t jsOtherSize, out int64_t domSize,
+ out int64_t styleSize, out int64_t otherSize,
+ out int64_t totalSize,
+ out double jsMilliseconds, out double nonJSMilliseconds);
+};
+
+%{C++
+
+#include "js/TypeDecls.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+class nsPIDOMWindowOuter;
+
+// nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback
+// around for backwards compatibility.
+typedef nsIMemoryReporterCallback nsIHandleReportCallback;
+
+namespace mozilla {
+
+// All the following registration/unregistration functions don't use
+// MOZ_MUST_USE because ignoring failures is common and reasonable.
+
+// Register a memory reporter. The manager service will hold a strong
+// reference to this reporter.
+XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
+XPCOM_API(nsresult) RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter);
+
+// Register a memory reporter. The manager service will hold a weak reference
+// to this reporter.
+XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
+XPCOM_API(nsresult) RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter);
+
+// Unregister a strong memory reporter.
+XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
+
+// Unregister a weak memory reporter.
+XPCOM_API(nsresult) UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
+
+// The memory reporter manager provides access to several distinguished
+// amounts via attributes. Some of these amounts are provided by Gecko
+// components that cannot be accessed directly from XPCOM code. So we provide
+// the following functions for those components to be registered with the
+// manager.
+
+typedef int64_t (*InfallibleAmountFn)();
+
+#define DECL_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
+ nsresult Register##name##DistinguishedAmount(kind##AmountFn aAmountFn);
+#define DECL_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
+ nsresult Unregister##name##DistinguishedAmount();
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
+DECL_UNREGISTER_DISTINGUISHED_AMOUNT(ImagesContentUsedUncompressed)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
+DECL_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
+
+#undef DECL_REGISTER_DISTINGUISHED_AMOUNT
+#undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT
+
+// Likewise for per-tab measurement.
+
+typedef MOZ_MUST_USE nsresult (*JSSizeOfTabFn)(JSObject* aObj,
+ size_t* aJsObjectsSize,
+ size_t* aJsStringSize,
+ size_t* aJsPrivateSize,
+ size_t* aJsOtherSize);
+typedef MOZ_MUST_USE nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindowOuter* aWindow,
+ size_t* aDomSize,
+ size_t* aStyleSize,
+ size_t* aOtherSize);
+
+nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn);
+nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
+
+}
+
+#if defined(MOZ_DMD)
+#if !defined(MOZ_MEMORY)
+#error "MOZ_DMD requires MOZ_MEMORY"
+#endif
+
+#include "DMD.h"
+
+#define MOZ_REPORT(ptr) mozilla::dmd::Report(ptr)
+#define MOZ_REPORT_ON_ALLOC(ptr) mozilla::dmd::ReportOnAlloc(ptr)
+
+#else
+
+#define MOZ_REPORT(ptr)
+#define MOZ_REPORT_ON_ALLOC(ptr)
+
+#endif // defined(MOZ_DMD)
+
+// Functions generated via this macro should be used by all traversal-based
+// memory reporters. Such functions return |moz_malloc_size_of(ptr)|; this
+// will always be zero on some obscure platforms.
+//
+// You might be wondering why we have a macro that creates multiple functions
+// that differ only in their name, instead of a single MallocSizeOf function.
+// It's mostly to help with DMD integration, though it sometimes also helps
+// with debugging and temporary ad hoc profiling. The function name chosen
+// doesn't matter greatly, but it's best to make it similar to the path used by
+// the relevant memory reporter(s).
+#define MOZ_DEFINE_MALLOC_SIZE_OF(fn) \
+ static size_t fn(const void* aPtr) \
+ { \
+ MOZ_REPORT(aPtr); \
+ return moz_malloc_size_of(aPtr); \
+ }
+
+// Functions generated by the next two macros should be used by wrapping
+// allocators that report heap blocks as soon as they are allocated and
+// unreport them as soon as they are freed. Such allocators are used in cases
+// where we have third-party code that we cannot modify. The two functions
+// must always be used in tandem.
+#define MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(fn) \
+ static size_t fn(const void* aPtr) \
+ { \
+ MOZ_REPORT_ON_ALLOC(aPtr); \
+ return moz_malloc_size_of(aPtr); \
+ }
+#define MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(fn) \
+ static size_t fn(const void* aPtr) \
+ { \
+ return moz_malloc_size_of(aPtr); \
+ }
+
+// This macro assumes the presence of appropriate |aHandleReport| and |aData|
+// variables. The (void) is there because we should always ignore the return
+// value of the callback, because callback failures aren't fatal.
+#define MOZ_COLLECT_REPORT(path, kind, units, amount, description) \
+ (void)aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(path), \
+ kind, units, amount, \
+ NS_LITERAL_CSTRING(description), aData)
+
+%}
diff --git a/xpcom/base/nsIMessageLoop.idl b/xpcom/base/nsIMessageLoop.idl
new file mode 100644
index 000000000..b10d1c576
--- /dev/null
+++ b/xpcom/base/nsIMessageLoop.idl
@@ -0,0 +1,36 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsIRunnable;
+
+/**
+ * This service allows access to the current thread's Chromium MessageLoop
+ * instance, with some extra sugar added. If you're calling from C++, it may
+ * or may not make sense for you to use this interface. If you're calling from
+ * JS, you don't have a choice!
+ *
+ * Right now, you can only call PostIdleTask(), and the wrath of khuey is
+ * stopping you from adding other methods.
+ *
+ * nsIMessageLoop's contractid is "@mozilla.org/message-loop;1".
+ */
+[scriptable, uuid(3E8C58E8-E52C-43E0-8E66-669CA788FF5F)]
+interface nsIMessageLoop : nsISupports
+{
+ /**
+ * Posts a task to be run when this thread's message loop is idle, or after
+ * ensureRunsAfterMS milliseconds have elapsed. (That is, the task is
+ * guaranteed to run /eventually/.)
+ *
+ * Note that if the event loop is busy, we will hold a reference to the task
+ * until ensureRunsAfterMS milliseconds have elapsed. Be careful when
+ * specifying long timeouts and tasks which hold references to windows or
+ * other large objects, because you can leak memory in a difficult-to-detect
+ * way!
+ */
+ void postIdleTask(in nsIRunnable task, in uint32_t ensureRunsAfterMS);
+};
diff --git a/xpcom/base/nsIMutable.idl b/xpcom/base/nsIMutable.idl
new file mode 100644
index 000000000..c5c7baab0
--- /dev/null
+++ b/xpcom/base/nsIMutable.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * nsIMutable defines an interface to be implemented by objects which
+ * can be made immutable.
+ */
+[scriptable, uuid(321578d0-03c1-4d95-8821-021ac612d18d)]
+interface nsIMutable : nsISupports
+{
+ /**
+ * Control whether or not this object can be modified. If the flag is
+ * false, no modification is allowed. Once the flag has been set to false,
+ * it cannot be reset back to true -- attempts to do so throw
+ * NS_ERROR_INVALID_ARG.
+ */
+ attribute boolean mutable;
+};
diff --git a/xpcom/base/nsIProgrammingLanguage.idl b/xpcom/base/nsIProgrammingLanguage.idl
new file mode 100644
index 000000000..57621168b
--- /dev/null
+++ b/xpcom/base/nsIProgrammingLanguage.idl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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"
+
+/**
+ * Legacy constants for specifying programming languages.
+ *
+ * JAVASCRIPT is needed to avoid breaking addons that use it in nsIClassInfo
+ * to define fields that are no longer needed.
+ *
+ * UNKNOWN and JAVASCRIPT are also used in implementations of
+ * nsIStackFrame::language.
+ */
+
+[scriptable, uuid(02ad9f22-3c98-46f3-be4e-2f5c9299e29a)]
+interface nsIProgrammingLanguage : nsISupports
+{
+ const uint32_t UNKNOWN = 0;
+ // 1 is unused.
+ const uint32_t JAVASCRIPT = 2;
+};
diff --git a/xpcom/base/nsISecurityConsoleMessage.idl b/xpcom/base/nsISecurityConsoleMessage.idl
new file mode 100644
index 000000000..917968d83
--- /dev/null
+++ b/xpcom/base/nsISecurityConsoleMessage.idl
@@ -0,0 +1,20 @@
+/* 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"
+
+/*
+ * Holds localization message tag and message category
+ * for security related console messages.
+ */
+[uuid(FE9FC9B6-DDE2-11E2-A8F1-0A326188709B)]
+interface nsISecurityConsoleMessage : nsISupports
+{
+ attribute AString tag;
+ attribute AString category;
+};
+
+%{ C++
+#define NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID "@mozilla.org/securityconsole/message;1"
+%}
diff --git a/xpcom/base/nsISizeOf.h b/xpcom/base/nsISizeOf.h
new file mode 100644
index 000000000..35f5de44c
--- /dev/null
+++ b/xpcom/base/nsISizeOf.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsISizeOf_h___
+#define nsISizeOf_h___
+
+#include "mozilla/MemoryReporting.h"
+#include "nsISupports.h"
+
+#define NS_ISIZEOF_IID \
+ {0x61d05579, 0xd7ec, 0x485c, \
+ { 0xa4, 0x0c, 0x31, 0xc7, 0x9a, 0x5c, 0xf9, 0xf3 }}
+
+class nsISizeOf : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISIZEOF_IID)
+
+ /**
+ * Measures the size of the things pointed to by the object.
+ */
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+
+ /**
+ * Like SizeOfExcludingThis, but also includes the size of the object itself.
+ */
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISizeOf, NS_ISIZEOF_IID)
+
+#endif /* nsISizeOf_h___ */
diff --git a/xpcom/base/nsIStatusReporter.idl b/xpcom/base/nsIStatusReporter.idl
new file mode 100644
index 000000000..9f9245f49
--- /dev/null
+++ b/xpcom/base/nsIStatusReporter.idl
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsISimpleEnumerator;
+
+/*
+ * Status reporters show Firefox's service status.
+ */
+
+[scriptable, uuid(ffcb716c-deeb-44ea-9d9d-ab25dc6980a8)]
+interface nsIStatusReporter : nsISupports
+{
+ readonly attribute ACString name;
+ /*
+ * The name of the process containing this reporter. Each reporter initially
+ * has "" in this field, indicating that it applies to the current process.
+ */
+ readonly attribute ACString process;
+ /*
+ * A human-readable status description.
+ */
+ readonly attribute AUTF8String description;
+};
+
+[scriptable, uuid(fd531273-3319-4fcd-90f2-9f53876c3828)]
+interface nsIStatusReporterManager : nsISupports
+{
+
+ /*
+ * Return an enumerator of nsIStatusReporters that are currently registered.
+ */
+ nsISimpleEnumerator enumerateReporters();
+
+ /*
+ * Register the given nsIStatusReporter. After a reporter is registered,
+ * it will be available via enumerateReporters(). The Manager service
+ * will hold a strong reference to the given reporter.
+ */
+ void registerReporter(in nsIStatusReporter reporter);
+
+ /*
+ * Unregister the given status reporter.
+ */
+ void unregisterReporter(in nsIStatusReporter reporter);
+
+ /*
+ * Initialize.
+ */
+ void init();
+
+ /*
+ * Dump service status as a json file
+ */
+ void dumpReports();
+};
+
+%{C++
+
+/*
+ * Note that this defaults 'process' to "", which is usually what's desired.
+ */
+#define NS_STATUS_REPORTER_IMPLEMENT(_classname, _name, _desc_Function) \
+ class StatusReporter_##_classname final : public nsIStatusReporter { \
+ ~StatusReporter_##_classname() {} \
+ public: \
+ NS_DECL_ISUPPORTS \
+ NS_IMETHOD GetName(nsACString &name) override \
+ { name.AssignLiteral(_name); return NS_OK; } \
+ NS_IMETHOD GetProcess(nsACString &process) override \
+ { process.Truncate(); return NS_OK; } \
+ NS_IMETHOD GetDescription(nsACString &desc) override \
+ { _desc_Function(desc); return NS_OK; } \
+ }; \
+ NS_IMPL_ISUPPORTS(StatusReporter_##_classname, nsIStatusReporter)
+
+#define NS_STATUS_REPORTER_NAME(_classname) StatusReporter_##_classname
+
+nsresult NS_RegisterStatusReporter(nsIStatusReporter *reporter);
+nsresult NS_UnregisterStatusReporter(nsIStatusReporter *reporter);
+nsresult NS_DumpStatusReporter();
+
+#define NS_STATUS_REPORTER_MANAGER_CID \
+{ 0xe8eb4e7e, 0xf2cf, 0x45e5, \
+{ 0xb8, 0xa4, 0x6a, 0x0f, 0x50, 0x18, 0x84, 0x63 } }
+%}
diff --git a/xpcom/base/nsISupports.idl b/xpcom/base/nsISupports.idl
new file mode 100644
index 000000000..45ea44ecb
--- /dev/null
+++ b/xpcom/base/nsISupports.idl
@@ -0,0 +1,44 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 mother of all xpcom interfaces.
+ */
+
+/* In order to get both the right typelib and the right header we force
+* the 'real' output from xpidl to be commented out in the generated header
+* and includes a copy of the original nsISupports.h. This is all just to deal
+* with the Mac specific ": public __comobject" thing.
+*/
+
+#include "nsrootidl.idl"
+
+%{C++
+/*
+ * Start commenting out the C++ versions of the below in the output header
+ */
+#if 0
+%}
+
+[scriptable, uuid(00000000-0000-0000-c000-000000000046)]
+interface nsISupports {
+ void QueryInterface(in nsIIDRef uuid,
+ [iid_is(uuid),retval] out nsQIResult result);
+ [noscript, notxpcom] nsrefcnt AddRef();
+ [noscript, notxpcom] nsrefcnt Release();
+};
+
+%{C++
+/*
+ * End commenting out the C++ versions of the above in the output header
+ */
+#endif
+%}
+
+
+%{C++
+#include "nsISupportsBase.h"
+#include "nsISupportsUtils.h"
+%}
diff --git a/xpcom/base/nsISupportsBase.h b/xpcom/base/nsISupportsBase.h
new file mode 100644
index 000000000..bf5518b81
--- /dev/null
+++ b/xpcom/base/nsISupportsBase.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsISupports.h"
+
+#ifndef nsISupportsBase_h__
+#define nsISupportsBase_h__
+
+#ifndef nscore_h___
+#include "nscore.h"
+#endif
+
+#ifndef nsID_h__
+#include "nsID.h"
+#endif
+
+
+/*@{*/
+/**
+ * IID for the nsISupports interface
+ * {00000000-0000-0000-c000-000000000046}
+ *
+ * To maintain binary compatibility with COM's IUnknown, we define the IID
+ * of nsISupports to be the same as that of COM's IUnknown.
+ */
+#define NS_ISUPPORTS_IID \
+ { 0x00000000, 0x0000, 0x0000, \
+ {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} }
+
+/**
+ * Basic component object model interface. Objects which implement
+ * this interface support runtime interface discovery (QueryInterface)
+ * and a reference counted memory model (AddRef/Release). This is
+ * modelled after the win32 IUnknown API.
+ */
+class NS_NO_VTABLE nsISupports
+{
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISUPPORTS_IID)
+
+ /**
+ * @name Methods
+ */
+
+ //@{
+ /**
+ * A run time mechanism for interface discovery.
+ * @param aIID [in] A requested interface IID
+ * @param aInstancePtr [out] A pointer to an interface pointer to
+ * receive the result.
+ * @return <b>NS_OK</b> if the interface is supported by the associated
+ * instance, <b>NS_NOINTERFACE</b> if it is not.
+ *
+ * aInstancePtr must not be null.
+ */
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) = 0;
+ /**
+ * Increases the reference count for this interface.
+ * The associated instance will not be deleted unless
+ * the reference count is returned to zero.
+ *
+ * @return The resulting reference count.
+ */
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+
+ /**
+ * Decreases the reference count for this interface.
+ * Generally, if the reference count returns to zero,
+ * the associated instance is deleted.
+ *
+ * @return The resulting reference count.
+ */
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ //@}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISupports, NS_ISUPPORTS_IID)
+
+/*@}*/
+
+#endif
diff --git a/xpcom/base/nsIUUIDGenerator.idl b/xpcom/base/nsIUUIDGenerator.idl
new file mode 100644
index 000000000..8715ed622
--- /dev/null
+++ b/xpcom/base/nsIUUIDGenerator.idl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 50; 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 "nsISupports.idl"
+
+[ptr] native nsNonConstIDPtr(nsID);
+
+/**
+ * nsIUUIDGenerator is implemented by a service that can generate
+ * universally unique identifiers, ideally using any platform-native
+ * method for generating UUIDs.
+ */
+[scriptable, uuid(138ad1b2-c694-41cc-b201-333ce936d8b8)]
+interface nsIUUIDGenerator : nsISupports
+{
+ /**
+ * Obtains a new UUID using appropriate platform-specific methods to
+ * obtain a nsID that can be considered to be globally unique.
+ *
+ * @returns an nsID filled in with a new UUID.
+ *
+ * @throws NS_ERROR_FAILURE if a UUID cannot be generated (e.g. if
+ * an underlying source of randomness is not available)
+ */
+ nsIDPtr generateUUID();
+
+ /**
+ * Obtain a new UUID like the generateUUID method, but place it in
+ * the provided nsID pointer instead of allocating a new nsID.
+ *
+ * @param id an existing nsID pointer where the UUID will be stored.
+ *
+ * @throws NS_ERROR_FAILURE if a UUID cannot be generated (e.g. if
+ * an underlying source of randomness is not available)
+ */
+ [noscript] void generateUUIDInPlace(in nsNonConstIDPtr id);
+};
diff --git a/xpcom/base/nsIVersionComparator.idl b/xpcom/base/nsIVersionComparator.idl
new file mode 100644
index 000000000..1a33cb74e
--- /dev/null
+++ b/xpcom/base/nsIVersionComparator.idl
@@ -0,0 +1,49 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+/**
+ * Version strings are dot-separated sequences of version-parts.
+ *
+ * A version-part consists of up to four parts, all of which are optional:
+ *
+ * <number-a><string-b><number-c><string-d (everything else)>
+ *
+ * A version-part may also consist of a single asterisk "*" which indicates
+ * "infinity".
+ *
+ * Numbers are base-10, and are zero if left out.
+ * Strings are compared bytewise.
+ *
+ * For additional backwards compatibility, if "string-b" is "+" then
+ * "number-a" is incremented by 1 and "string-b" becomes "pre".
+ *
+ * 1.0pre1
+ * < 1.0pre2
+ * < 1.0 == 1.0.0 == 1.0.0.0
+ * < 1.1pre == 1.1pre0 == 1.0+
+ * < 1.1pre1a
+ * < 1.1pre1
+ * < 1.1pre10a
+ * < 1.1pre10
+ *
+ * Although not required by this interface, it is recommended that
+ * numbers remain within the limits of a signed char, i.e. -127 to 128.
+ */
+[scriptable, uuid(e6cd620a-edbb-41d2-9e42-9a2ffc8107f3)]
+interface nsIVersionComparator : nsISupports
+{
+ /**
+ * Compare two version strings
+ * @param A The first version
+ * @param B The second version
+ * @returns < 0 if A < B
+ * = 0 if A == B
+ * > 0 if A > B
+ */
+ long compare(in ACString A, in ACString B);
+};
+
diff --git a/xpcom/base/nsIWeakReference.idl b/xpcom/base/nsIWeakReference.idl
new file mode 100644
index 000000000..73390b15f
--- /dev/null
+++ b/xpcom/base/nsIWeakReference.idl
@@ -0,0 +1,74 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include "mozilla/MemoryReporting.h"
+%}
+
+/**
+ * An instance of |nsIWeakReference| is a proxy object that cooperates with
+ * its referent to give clients a non-owning, non-dangling reference. Clients
+ * own the proxy, and should generally manage it with an |nsCOMPtr| (see the
+ * type |nsWeakPtr| for a |typedef| name that stands out) as they would any
+ * other XPCOM object. The |QueryReferent| member function provides a
+ * (hopefully short-lived) owning reference on demand, through which clients
+ * can get useful access to the referent, while it still exists.
+ *
+ * @version 1.0
+ * @see nsISupportsWeakReference
+ * @see nsWeakReference
+ * @see nsWeakPtr
+ */
+[scriptable, uuid(9188bc85-f92e-11d2-81ef-0060083a0bcf)]
+interface nsIWeakReference : nsISupports
+ {
+ /**
+ * |QueryReferent| queries the referent, if it exists, and like |QueryInterface|, produces
+ * an owning reference to the desired interface. It is designed to look and act exactly
+ * like (a proxied) |QueryInterface|. Don't hold on to the produced interface permanently;
+ * that would defeat the purpose of using a non-owning |nsIWeakReference| in the first place.
+ */
+ void QueryReferent( in nsIIDRef uuid, [iid_is(uuid), retval] out nsQIResult result );
+
+%{C++
+ virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+%}
+ };
+
+
+/**
+ * |nsISupportsWeakReference| is a factory interface which produces appropriate
+ * instances of |nsIWeakReference|. Weak references in this scheme can only be
+ * produced for objects that implement this interface.
+ *
+ * @version 1.0
+ * @see nsIWeakReference
+ * @see nsSupportsWeakReference
+ */
+[scriptable, uuid(9188bc86-f92e-11d2-81ef-0060083a0bcf)]
+interface nsISupportsWeakReference : nsISupports
+ {
+ /**
+ * |GetWeakReference| produces an appropriate instance of |nsIWeakReference|.
+ * As with all good XPCOM `getters', you own the resulting interface and should
+ * manage it with an |nsCOMPtr|.
+ *
+ * @see nsIWeakReference
+ * @see nsWeakPtr
+ * @see nsCOMPtr
+ */
+ nsIWeakReference GetWeakReference();
+ };
+
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+#include "nsIWeakReferenceUtils.h"
+#endif
+%}
+
diff --git a/xpcom/base/nsInterfaceRequestorAgg.cpp b/xpcom/base/nsInterfaceRequestorAgg.cpp
new file mode 100644
index 000000000..7e5cd83da
--- /dev/null
+++ b/xpcom/base/nsInterfaceRequestorAgg.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsInterfaceRequestorAgg.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+
+class nsInterfaceRequestorAgg final : public nsIInterfaceRequestor
+{
+public:
+ // XXX This needs to support threadsafe refcounting until we fix bug 243591.
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ nsInterfaceRequestorAgg(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIEventTarget* aConsumerTarget = nullptr)
+ : mFirst(aFirst)
+ , mSecond(aSecond)
+ , mConsumerTarget(aConsumerTarget)
+ {
+ if (!mConsumerTarget) {
+ mConsumerTarget = NS_GetCurrentThread();
+ }
+ }
+
+private:
+ ~nsInterfaceRequestorAgg();
+
+ nsCOMPtr<nsIInterfaceRequestor> mFirst, mSecond;
+ nsCOMPtr<nsIEventTarget> mConsumerTarget;
+};
+
+NS_IMPL_ISUPPORTS(nsInterfaceRequestorAgg, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+nsInterfaceRequestorAgg::GetInterface(const nsIID& aIID, void** aResult)
+{
+ nsresult rv = NS_ERROR_NO_INTERFACE;
+ if (mFirst) {
+ rv = mFirst->GetInterface(aIID, aResult);
+ }
+ if (mSecond && NS_FAILED(rv)) {
+ rv = mSecond->GetInterface(aIID, aResult);
+ }
+ return rv;
+}
+
+nsInterfaceRequestorAgg::~nsInterfaceRequestorAgg()
+{
+ NS_ProxyRelease(mConsumerTarget, mFirst.forget());
+ NS_ProxyRelease(mConsumerTarget, mSecond.forget());
+}
+
+nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIInterfaceRequestor** aResult)
+{
+ *aResult = new nsInterfaceRequestorAgg(aFirst, aSecond);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIEventTarget* aTarget,
+ nsIInterfaceRequestor** aResult)
+{
+ *aResult = new nsInterfaceRequestorAgg(aFirst, aSecond, aTarget);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
diff --git a/xpcom/base/nsInterfaceRequestorAgg.h b/xpcom/base/nsInterfaceRequestorAgg.h
new file mode 100644
index 000000000..62b4f61f9
--- /dev/null
+++ b/xpcom/base/nsInterfaceRequestorAgg.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsInterfaceRequestorAgg_h__
+#define nsInterfaceRequestorAgg_h__
+
+#include "nsError.h"
+
+class nsIEventTarget;
+class nsIInterfaceRequestor;
+
+/**
+ * This function returns an instance of nsIInterfaceRequestor that aggregates
+ * two nsIInterfaceRequestor instances. Its GetInterface method queries
+ * aFirst for the requested interface and will query aSecond only if aFirst
+ * failed to supply the requested interface. Both aFirst and aSecond may
+ * be null, and will be released on the main thread when the aggregator is
+ * destroyed.
+ */
+extern nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIInterfaceRequestor** aResult);
+
+/**
+ * Like the previous method, but aFirst and aSecond will be released on the
+ * provided target thread.
+ */
+extern nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIEventTarget* aTarget,
+ nsIInterfaceRequestor** aResult);
+
+#endif // !defined( nsInterfaceRequestorAgg_h__ )
diff --git a/xpcom/base/nsMacUtilsImpl.cpp b/xpcom/base/nsMacUtilsImpl.cpp
new file mode 100644
index 000000000..e2706047a
--- /dev/null
+++ b/xpcom/base/nsMacUtilsImpl.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMacUtilsImpl.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+NS_IMPL_ISUPPORTS(nsMacUtilsImpl, nsIMacUtils)
+
+nsresult
+nsMacUtilsImpl::GetArchString(nsAString& aArchString)
+{
+ if (!mBinaryArchs.IsEmpty()) {
+ aArchString.Assign(mBinaryArchs);
+ return NS_OK;
+ }
+
+ aArchString.Truncate();
+
+ bool foundPPC = false,
+ foundX86 = false,
+ foundPPC64 = false,
+ foundX86_64 = false;
+
+ CFBundleRef mainBundle = ::CFBundleGetMainBundle();
+ if (!mainBundle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFArrayRef archList = ::CFBundleCopyExecutableArchitectures(mainBundle);
+ if (!archList) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFIndex archCount = ::CFArrayGetCount(archList);
+ for (CFIndex i = 0; i < archCount; i++) {
+ CFNumberRef arch =
+ static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(archList, i));
+
+ int archInt = 0;
+ if (!::CFNumberGetValue(arch, kCFNumberIntType, &archInt)) {
+ ::CFRelease(archList);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (archInt == kCFBundleExecutableArchitecturePPC) {
+ foundPPC = true;
+ } else if (archInt == kCFBundleExecutableArchitectureI386) {
+ foundX86 = true;
+ } else if (archInt == kCFBundleExecutableArchitecturePPC64) {
+ foundPPC64 = true;
+ } else if (archInt == kCFBundleExecutableArchitectureX86_64) {
+ foundX86_64 = true;
+ }
+ }
+
+ ::CFRelease(archList);
+
+ // The order in the string must always be the same so
+ // don't do this in the loop.
+ if (foundPPC) {
+ mBinaryArchs.AppendLiteral("ppc");
+ }
+
+ if (foundX86) {
+ if (!mBinaryArchs.IsEmpty()) {
+ mBinaryArchs.Append('-');
+ }
+ mBinaryArchs.AppendLiteral("i386");
+ }
+
+ if (foundPPC64) {
+ if (!mBinaryArchs.IsEmpty()) {
+ mBinaryArchs.Append('-');
+ }
+ mBinaryArchs.AppendLiteral("ppc64");
+ }
+
+ if (foundX86_64) {
+ if (!mBinaryArchs.IsEmpty()) {
+ mBinaryArchs.Append('-');
+ }
+ mBinaryArchs.AppendLiteral("x86_64");
+ }
+
+ aArchString.Assign(mBinaryArchs);
+
+ return (aArchString.IsEmpty() ? NS_ERROR_FAILURE : NS_OK);
+}
+
+NS_IMETHODIMP
+nsMacUtilsImpl::GetIsUniversalBinary(bool* aIsUniversalBinary)
+{
+ if (NS_WARN_IF(!aIsUniversalBinary)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aIsUniversalBinary = false;
+
+ nsAutoString archString;
+ nsresult rv = GetArchString(archString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // The delimiter char in the arch string is '-', so if that character
+ // is in the string we know we have multiple architectures.
+ *aIsUniversalBinary = (archString.Find("-") > -1);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacUtilsImpl::GetArchitecturesInBinary(nsAString& aArchString)
+{
+ return GetArchString(aArchString);
+}
+
+// True when running under binary translation (Rosetta).
+NS_IMETHODIMP
+nsMacUtilsImpl::GetIsTranslated(bool* aIsTranslated)
+{
+#ifdef __ppc__
+ static bool sInitialized = false;
+
+ // Initialize sIsNative to 1. If the sysctl fails because it doesn't
+ // exist, then translation is not possible, so the process must not be
+ // running translated.
+ static int32_t sIsNative = 1;
+
+ if (!sInitialized) {
+ size_t sz = sizeof(sIsNative);
+ sysctlbyname("sysctl.proc_native", &sIsNative, &sz, nullptr, 0);
+ sInitialized = true;
+ }
+
+ *aIsTranslated = !sIsNative;
+#else
+ // Translation only exists for ppc code. Other architectures aren't
+ // translated.
+ *aIsTranslated = false;
+#endif
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsMacUtilsImpl.h b/xpcom/base/nsMacUtilsImpl.h
new file mode 100644
index 000000000..4a480ffe5
--- /dev/null
+++ b/xpcom/base/nsMacUtilsImpl.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMacUtilsImpl_h___
+#define nsMacUtilsImpl_h___
+
+#include "nsIMacUtils.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+
+class nsMacUtilsImpl final : public nsIMacUtils
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMACUTILS
+
+ nsMacUtilsImpl()
+ {
+ }
+
+private:
+ ~nsMacUtilsImpl()
+ {
+ }
+
+ nsresult GetArchString(nsAString& aArchString);
+
+ // A string containing a "-" delimited list of architectures
+ // in our binary.
+ nsString mBinaryArchs;
+};
+
+// Global singleton service
+// 697BD3FD-43E5-41CE-AD5E-C339175C0818
+#define NS_MACUTILSIMPL_CID \
+ {0x697BD3FD, 0x43E5, 0x41CE, {0xAD, 0x5E, 0xC3, 0x39, 0x17, 0x5C, 0x08, 0x18}}
+#define NS_MACUTILSIMPL_CONTRACTID "@mozilla.org/xpcom/mac-utils;1"
+
+#endif /* nsMacUtilsImpl_h___ */
diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp
new file mode 100644
index 000000000..1d1576fbd
--- /dev/null
+++ b/xpcom/base/nsMemoryImpl.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMemoryImpl.h"
+#include "nsThreadUtils.h"
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/Services.h"
+
+#ifdef ANDROID
+#include <stdio.h>
+
+// Minimum memory threshold for a device to be considered
+// a low memory platform. This value has be in sync with
+// Java's equivalent threshold, defined in
+// mobile/android/base/util/HardwareUtils.java
+#define LOW_MEMORY_THRESHOLD_KB (384 * 1024)
+#endif
+
+static nsMemoryImpl sGlobalMemory;
+
+NS_IMPL_QUERY_INTERFACE(nsMemoryImpl, nsIMemory)
+
+NS_IMETHODIMP
+nsMemoryImpl::HeapMinimize(bool aImmediate)
+{
+ return FlushMemory(u"heap-minimize", aImmediate);
+}
+
+NS_IMETHODIMP
+nsMemoryImpl::IsLowMemoryPlatform(bool* aResult)
+{
+#ifdef ANDROID
+ static int sLowMemory = -1; // initialize to unknown, lazily evaluate to 0 or 1
+ if (sLowMemory == -1) {
+ sLowMemory = 0; // assume "not low memory" in case file operations fail
+ *aResult = false;
+
+ // check if MemTotal from /proc/meminfo is less than LOW_MEMORY_THRESHOLD_KB
+ FILE* fd = fopen("/proc/meminfo", "r");
+ if (!fd) {
+ return NS_OK;
+ }
+ uint64_t mem = 0;
+ int rv = fscanf(fd, "MemTotal: %llu kB", &mem);
+ if (fclose(fd)) {
+ return NS_OK;
+ }
+ if (rv != 1) {
+ return NS_OK;
+ }
+ sLowMemory = (mem < LOW_MEMORY_THRESHOLD_KB) ? 1 : 0;
+ }
+ *aResult = (sLowMemory == 1);
+#else
+ *aResult = false;
+#endif
+ return NS_OK;
+}
+
+/*static*/ nsresult
+nsMemoryImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ return sGlobalMemory.QueryInterface(aIID, aResult);
+}
+
+nsresult
+nsMemoryImpl::FlushMemory(const char16_t* aReason, bool aImmediate)
+{
+ nsresult rv = NS_OK;
+
+ if (aImmediate) {
+ // They've asked us to run the flusher *immediately*. We've
+ // got to be on the UI main thread for us to be able to do
+ // that...are we?
+ if (!NS_IsMainThread()) {
+ NS_ERROR("can't synchronously flush memory: not on UI thread");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ bool lastVal = sIsFlushing.exchange(true);
+ if (lastVal) {
+ return NS_OK;
+ }
+
+ PRIntervalTime now = PR_IntervalNow();
+
+ // Run the flushers immediately if we can; otherwise, proxy to the
+ // UI thread an run 'em asynchronously.
+ if (aImmediate) {
+ rv = RunFlushers(aReason);
+ } else {
+ // Don't broadcast more than once every 1000ms to avoid being noisy
+ if (PR_IntervalToMicroseconds(now - sLastFlushTime) > 1000) {
+ sFlushEvent.mReason = aReason;
+ rv = NS_DispatchToMainThread(&sFlushEvent);
+ }
+ }
+
+ sLastFlushTime = now;
+ return rv;
+}
+
+nsresult
+nsMemoryImpl::RunFlushers(const char16_t* aReason)
+{
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+
+ // Instead of:
+ // os->NotifyObservers(this, "memory-pressure", aReason);
+ // we are going to do this manually to see who/what is
+ // deallocating.
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ os->EnumerateObservers("memory-pressure", getter_AddRefs(e));
+
+ if (e) {
+ nsCOMPtr<nsIObserver> observer;
+ bool loop = true;
+
+ while (NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) {
+ nsCOMPtr<nsISupports> supports;
+ e->GetNext(getter_AddRefs(supports));
+
+ if (!supports) {
+ continue;
+ }
+
+ observer = do_QueryInterface(supports);
+ observer->Observe(observer, "memory-pressure", aReason);
+ }
+ }
+ }
+
+ sIsFlushing = false;
+ return NS_OK;
+}
+
+// XXX need NS_IMPL_STATIC_ADDREF/RELEASE
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsMemoryImpl::FlushEvent::AddRef()
+{
+ return 2;
+}
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsMemoryImpl::FlushEvent::Release()
+{
+ return 1;
+}
+NS_IMPL_QUERY_INTERFACE(nsMemoryImpl::FlushEvent, nsIRunnable)
+
+NS_IMETHODIMP
+nsMemoryImpl::FlushEvent::Run()
+{
+ sGlobalMemory.RunFlushers(mReason);
+ return NS_OK;
+}
+
+mozilla::Atomic<bool>
+nsMemoryImpl::sIsFlushing;
+
+PRIntervalTime
+nsMemoryImpl::sLastFlushTime = 0;
+
+nsMemoryImpl::FlushEvent
+nsMemoryImpl::sFlushEvent;
+
+nsresult
+NS_GetMemoryManager(nsIMemory** aResult)
+{
+ return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**)aResult);
+}
diff --git a/xpcom/base/nsMemoryImpl.h b/xpcom/base/nsMemoryImpl.h
new file mode 100644
index 000000000..9e2d46d38
--- /dev/null
+++ b/xpcom/base/nsMemoryImpl.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMemoryImpl_h__
+#define nsMemoryImpl_h__
+
+#include "mozilla/Atomics.h"
+
+#include "nsIMemory.h"
+#include "nsIRunnable.h"
+
+// nsMemoryImpl is a static object. We can do this because it doesn't have
+// a constructor/destructor or any instance members. Please don't add
+// instance member variables, only static member variables.
+
+class nsMemoryImpl : public nsIMemory
+{
+public:
+ // We don't use the generic macros because we are a special static object
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aResult) override;
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override
+ {
+ return 1;
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override
+ {
+ return 1;
+ }
+
+ NS_DECL_NSIMEMORY
+
+ static nsresult Create(nsISupports* aOuter,
+ const nsIID& aIID, void** aResult);
+
+ nsresult FlushMemory(const char16_t* aReason, bool aImmediate);
+ nsresult RunFlushers(const char16_t* aReason);
+
+protected:
+ struct FlushEvent : public nsIRunnable
+ {
+ constexpr FlushEvent() : mReason(nullptr) {}
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIRUNNABLE
+ const char16_t* mReason;
+ };
+
+ static mozilla::Atomic<bool> sIsFlushing;
+ static FlushEvent sFlushEvent;
+ static PRIntervalTime sLastFlushTime;
+};
+
+#endif // nsMemoryImpl_h__
diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp
new file mode 100644
index 000000000..06453b126
--- /dev/null
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -0,0 +1,830 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/JSONWriter.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/nsMemoryInfoDumper.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDumpUtils.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsIConsoleService.h"
+#include "nsCycleCollector.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIMemoryReporter.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsGZFileWriter.h"
+#include "nsJSEnvironment.h"
+#include "nsPrintfCString.h"
+#include "nsISimpleEnumerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIFile.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#ifndef getpid
+#define getpid _getpid
+#endif
+#else
+#include <unistd.h>
+#endif
+
+#ifdef XP_UNIX
+#define MOZ_SUPPORTS_FIFO 1
+#endif
+
+#if defined(XP_LINUX) || defined(__FreeBSD__)
+#define MOZ_SUPPORTS_RT_SIGNALS 1
+#endif
+
+#if defined(MOZ_SUPPORTS_RT_SIGNALS)
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#if defined(MOZ_SUPPORTS_FIFO)
+#include "mozilla/Preferences.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace {
+
+class DumpMemoryInfoToTempDirRunnable : public Runnable
+{
+public:
+ DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
+ bool aAnonymize, bool aMinimizeMemoryUsage)
+ : mIdentifier(aIdentifier)
+ , mAnonymize(aAnonymize)
+ , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+ dumper->DumpMemoryInfoToTempDir(mIdentifier, mAnonymize,
+ mMinimizeMemoryUsage);
+ return NS_OK;
+ }
+
+private:
+ const nsString mIdentifier;
+ const bool mAnonymize;
+ const bool mMinimizeMemoryUsage;
+};
+
+class GCAndCCLogDumpRunnable final
+ : public Runnable
+ , public nsIDumpGCAndCCLogsCallback
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
+ bool aDumpAllTraces,
+ bool aDumpChildProcesses)
+ : mIdentifier(aIdentifier)
+ , mDumpAllTraces(aDumpAllTraces)
+ , mDumpChildProcesses(aDumpChildProcesses)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+
+ dumper->DumpGCAndCCLogsToFile(mIdentifier, mDumpAllTraces,
+ mDumpChildProcesses, this);
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
+ {
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnFinish() override
+ {
+ return NS_OK;
+ }
+
+private:
+ ~GCAndCCLogDumpRunnable() {}
+
+ const nsString mIdentifier;
+ const bool mDumpAllTraces;
+ const bool mDumpChildProcesses;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable, Runnable,
+ nsIDumpGCAndCCLogsCallback)
+
+} // namespace
+
+#if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
+namespace {
+
+/*
+ * The following code supports dumping about:memory upon receiving a signal.
+ *
+ * We listen for the following signals:
+ *
+ * - SIGRTMIN: Dump our memory reporters (and those of our child
+ * processes),
+ * - SIGRTMIN + 1: Dump our memory reporters (and those of our child
+ * processes) after minimizing memory usage, and
+ * - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
+ *
+ * When we receive one of these signals, we write the signal number to a pipe.
+ * The IO thread then notices that the pipe has been written to, and kicks off
+ * the appropriate task on the main thread.
+ *
+ * This scheme is similar to using signalfd(), except it's portable and it
+ * doesn't require the use of sigprocmask, which is problematic because it
+ * masks signals received by child processes.
+ *
+ * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
+ * But that uses libevent, which does not handle the realtime signals (bug
+ * 794074).
+ */
+
+// It turns out that at least on some systems, SIGRTMIN is not a compile-time
+// constant, so these have to be set at runtime.
+static uint8_t sDumpAboutMemorySignum; // SIGRTMIN
+static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
+static uint8_t sGCAndCCDumpSignum; // SIGRTMIN + 2
+
+void doMemoryReport(const uint8_t aRecvSig)
+{
+ // Dump our memory reports (but run this on the main thread!).
+ bool minimize = aRecvSig == sDumpAboutMemoryAfterMMUSignum;
+ LOG("SignalWatcher(sig %d) dispatching memory report runnable.", aRecvSig);
+ RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
+ new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
+ /* anonymize = */ false,
+ minimize);
+ NS_DispatchToMainThread(runnable);
+}
+
+void doGCCCDump(const uint8_t aRecvSig)
+{
+ LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig);
+ // Dump GC and CC logs (from the main thread).
+ RefPtr<GCAndCCLogDumpRunnable> runnable =
+ new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
+ /* allTraces = */ true,
+ /* dumpChildProcesses = */ true);
+ NS_DispatchToMainThread(runnable);
+}
+
+} // namespace
+#endif // MOZ_SUPPORTS_RT_SIGNALS }
+
+#if defined(MOZ_SUPPORTS_FIFO) // {
+namespace {
+
+void
+doMemoryReport(const nsCString& aInputStr)
+{
+ bool minimize = aInputStr.EqualsLiteral("minimize memory report");
+ LOG("FifoWatcher(command:%s) dispatching memory report runnable.",
+ aInputStr.get());
+ RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
+ new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
+ /* anonymize = */ false,
+ minimize);
+ NS_DispatchToMainThread(runnable);
+}
+
+void
+doGCCCDump(const nsCString& aInputStr)
+{
+ bool doAllTracesGCCCDump = aInputStr.EqualsLiteral("gc log");
+ LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", aInputStr.get());
+ RefPtr<GCAndCCLogDumpRunnable> runnable =
+ new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
+ doAllTracesGCCCDump,
+ /* dumpChildProcesses = */ true);
+ NS_DispatchToMainThread(runnable);
+}
+
+bool
+SetupFifo()
+{
+#ifdef DEBUG
+ static bool fifoCallbacksRegistered = false;
+#endif
+
+ if (!FifoWatcher::MaybeCreate()) {
+ return false;
+ }
+
+ MOZ_ASSERT(!fifoCallbacksRegistered,
+ "FifoWatcher callbacks should be registered only once");
+
+ FifoWatcher* fw = FifoWatcher::GetSingleton();
+ // Dump our memory reports (but run this on the main thread!).
+ fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
+ doMemoryReport);
+ fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
+ doMemoryReport);
+ // Dump GC and CC logs (from the main thread).
+ fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
+ doGCCCDump);
+ fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
+ doGCCCDump);
+
+#ifdef DEBUG
+ fifoCallbacksRegistered = true;
+#endif
+ return true;
+}
+
+void
+OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
+{
+ LOG("%s changed", FifoWatcher::kPrefName);
+ if (SetupFifo()) {
+ Preferences::UnregisterCallback(OnFifoEnabledChange,
+ FifoWatcher::kPrefName,
+ nullptr);
+ }
+}
+
+} // namespace
+#endif // MOZ_SUPPORTS_FIFO }
+
+NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
+
+nsMemoryInfoDumper::nsMemoryInfoDumper()
+{
+}
+
+nsMemoryInfoDumper::~nsMemoryInfoDumper()
+{
+}
+
+/* static */ void
+nsMemoryInfoDumper::Initialize()
+{
+#if defined(MOZ_SUPPORTS_RT_SIGNALS)
+ SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
+
+ // Dump memory reporters (and those of our child processes)
+ sDumpAboutMemorySignum = SIGRTMIN;
+ sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
+ // Dump our memory reporters after minimizing memory usage
+ sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
+ sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
+ // Dump the GC and CC logs in this and our child processes.
+ sGCAndCCDumpSignum = SIGRTMIN + 2;
+ sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
+#endif
+
+#if defined(MOZ_SUPPORTS_FIFO)
+ if (!SetupFifo()) {
+ // NB: This gets loaded early enough that it's possible there is a user pref
+ // set to enable the fifo watcher that has not been loaded yet. Register
+ // to attempt to initialize if the fifo watcher becomes enabled by
+ // a user pref.
+ Preferences::RegisterCallback(OnFifoEnabledChange,
+ FifoWatcher::kPrefName,
+ nullptr);
+ }
+#endif
+}
+
+static void
+EnsureNonEmptyIdentifier(nsAString& aIdentifier)
+{
+ if (!aIdentifier.IsEmpty()) {
+ return;
+ }
+
+ // If the identifier is empty, set it to the number of whole seconds since the
+ // epoch. This identifier will appear in the files that this process
+ // generates and also the files generated by this process's children, allowing
+ // us to identify which files are from the same memory report request.
+ aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
+}
+
+// Use XPCOM refcounting to fire |onFinish| when all reference-holders
+// (remote dump actors or the |DumpGCAndCCLogsToFile| activation itself)
+// have gone away.
+class nsDumpGCAndCCLogsCallbackHolder final
+ : public nsIDumpGCAndCCLogsCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit nsDumpGCAndCCLogsCallbackHolder(nsIDumpGCAndCCLogsCallback* aCallback)
+ : mCallback(aCallback)
+ {
+ }
+
+ NS_IMETHOD OnFinish() override
+ {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
+ {
+ return mCallback->OnDump(aGCLog, aCCLog, aIsParent);
+ }
+
+private:
+ ~nsDumpGCAndCCLogsCallbackHolder()
+ {
+ Unused << mCallback->OnFinish();
+ }
+
+ nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
+};
+
+NS_IMPL_ISUPPORTS(nsDumpGCAndCCLogsCallbackHolder, nsIDumpGCAndCCLogsCallback)
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
+ bool aDumpAllTraces,
+ bool aDumpChildProcesses,
+ nsIDumpGCAndCCLogsCallback* aCallback)
+{
+ nsString identifier(aIdentifier);
+ EnsureNonEmptyIdentifier(identifier);
+ nsCOMPtr<nsIDumpGCAndCCLogsCallback> callbackHolder =
+ new nsDumpGCAndCCLogsCallbackHolder(aCallback);
+
+ if (aDumpChildProcesses) {
+ nsTArray<ContentParent*> children;
+ ContentParent::GetAll(children);
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ ContentParent* cp = children[i];
+ nsCOMPtr<nsICycleCollectorLogSink> logSink =
+ nsCycleCollector_createLogSink();
+
+ logSink->SetFilenameIdentifier(identifier);
+ logSink->SetProcessIdentifier(cp->Pid());
+
+ Unused << cp->CycleCollectWithLogs(aDumpAllTraces, logSink,
+ callbackHolder);
+ }
+ }
+
+ nsCOMPtr<nsICycleCollectorListener> logger =
+ do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
+
+ if (aDumpAllTraces) {
+ nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
+ logger->AllTraces(getter_AddRefs(allTracesLogger));
+ logger = allTracesLogger;
+ }
+
+ nsCOMPtr<nsICycleCollectorLogSink> logSink;
+ logger->GetLogSink(getter_AddRefs(logSink));
+
+ logSink->SetFilenameIdentifier(identifier);
+
+ nsJSContext::CycleCollectNow(logger);
+
+ nsCOMPtr<nsIFile> gcLog, ccLog;
+ logSink->GetGcLog(getter_AddRefs(gcLog));
+ logSink->GetCcLog(getter_AddRefs(ccLog));
+ callbackHolder->OnDump(gcLog, ccLog, /* parent = */ true);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpGCAndCCLogsToSink(bool aDumpAllTraces,
+ nsICycleCollectorLogSink* aSink)
+{
+ nsCOMPtr<nsICycleCollectorListener> logger =
+ do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
+
+ if (aDumpAllTraces) {
+ nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
+ logger->AllTraces(getter_AddRefs(allTracesLogger));
+ logger = allTracesLogger;
+ }
+
+ logger->SetLogSink(aSink);
+
+ nsJSContext::CycleCollectNow(logger);
+
+ return NS_OK;
+}
+
+static void
+MakeFilename(const char* aPrefix, const nsAString& aIdentifier,
+ int aPid, const char* aSuffix, nsACString& aResult)
+{
+ aResult = nsPrintfCString("%s-%s-%d.%s",
+ aPrefix,
+ NS_ConvertUTF16toUTF8(aIdentifier).get(),
+ aPid, aSuffix);
+}
+
+// This class wraps GZFileWriter so it can be used with JSONWriter, overcoming
+// the following two problems:
+// - It provides a JSONWriterFunc::Write() that calls nsGZFileWriter::Write().
+// - It can be stored as a UniquePtr, whereas nsGZFileWriter is refcounted.
+class GZWriterWrapper : public JSONWriteFunc
+{
+public:
+ explicit GZWriterWrapper(nsGZFileWriter* aGZWriter)
+ : mGZWriter(aGZWriter)
+ {}
+
+ void Write(const char* aStr)
+ {
+ // Ignore any failure because JSONWriteFunc doesn't have a mechanism for
+ // handling errors.
+ Unused << mGZWriter->Write(aStr);
+ }
+
+ nsresult Finish() { return mGZWriter->Finish(); }
+
+private:
+ RefPtr<nsGZFileWriter> mGZWriter;
+};
+
+// We need two callbacks: one that handles reports, and one that is called at
+// the end of reporting. Both the callbacks need access to the same JSONWriter,
+// so we implement both of them in this one class.
+class HandleReportAndFinishReportingCallbacks final
+ : public nsIHandleReportCallback, public nsIFinishReportingCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ HandleReportAndFinishReportingCallbacks(UniquePtr<JSONWriter> aWriter,
+ nsIFinishDumpingCallback* aFinishDumping,
+ nsISupports* aFinishDumpingData)
+ : mWriter(Move(aWriter))
+ , mFinishDumping(aFinishDumping)
+ , mFinishDumpingData(aFinishDumpingData)
+ {
+ }
+
+ // This is the callback for nsIHandleReportCallback.
+ NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
+ int32_t aKind, int32_t aUnits, int64_t aAmount,
+ const nsACString& aDescription,
+ nsISupports* aData) override
+ {
+ nsAutoCString process;
+ if (aProcess.IsEmpty()) {
+ // If the process is empty, the report originated with the process doing
+ // the dumping. In that case, generate the process identifier, which is
+ // of the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we
+ // don't have a process name. If we're the main process, we let
+ // $PROCESS_NAME be "Main Process".
+ if (XRE_IsParentProcess()) {
+ // We're the main process.
+ process.AssignLiteral("Main Process");
+ } else if (ContentChild* cc = ContentChild::GetSingleton()) {
+ // Try to get the process name from ContentChild.
+ cc->GetProcessName(process);
+ }
+ ContentChild::AppendProcessId(process);
+
+ } else {
+ // Otherwise, the report originated with another process and already has a
+ // process name. Just use that.
+ process = aProcess;
+ }
+
+ mWriter->StartObjectElement();
+ {
+ mWriter->StringProperty("process", process.get());
+ mWriter->StringProperty("path", PromiseFlatCString(aPath).get());
+ mWriter->IntProperty("kind", aKind);
+ mWriter->IntProperty("units", aUnits);
+ mWriter->IntProperty("amount", aAmount);
+ mWriter->StringProperty("description",
+ PromiseFlatCString(aDescription).get());
+ }
+ mWriter->EndObject();
+
+ return NS_OK;
+ }
+
+ // This is the callback for nsIFinishReportingCallback.
+ NS_IMETHOD Callback(nsISupports* aData) override
+ {
+ mWriter->EndArray(); // end of "reports" array
+ mWriter->End();
+
+ // The call to Finish() deallocates the memory allocated by the first Write
+ // call. Because that memory was live while the memory reporters ran and
+ // was measured by them -- by "heap-allocated" if nothing else -- we want
+ // DMD to see it as well. So we deliberately don't call Finish() until
+ // after DMD finishes.
+ nsresult rv = static_cast<GZWriterWrapper*>(mWriter->WriteFunc())->Finish();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mFinishDumping) {
+ return NS_OK;
+ }
+
+ return mFinishDumping->Callback(mFinishDumpingData);
+ }
+
+private:
+ ~HandleReportAndFinishReportingCallbacks() {}
+
+ UniquePtr<JSONWriter> mWriter;
+ nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
+ nsCOMPtr<nsISupports> mFinishDumpingData;
+};
+
+NS_IMPL_ISUPPORTS(HandleReportAndFinishReportingCallbacks,
+ nsIHandleReportCallback, nsIFinishReportingCallback)
+
+class TempDirFinishCallback final : public nsIFinishDumpingCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ TempDirFinishCallback(nsIFile* aReportsTmpFile,
+ const nsCString& aReportsFinalFilename)
+ : mReportsTmpFile(aReportsTmpFile)
+ , mReportsFilename(aReportsFinalFilename)
+ {
+ }
+
+ NS_IMETHOD Callback(nsISupports* aData) override
+ {
+ // Rename the memory reports file, now that we're done writing all the
+ // files. Its final name is "memory-report<-identifier>-<pid>.json.gz".
+
+ nsCOMPtr<nsIFile> reportsFinalFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(reportsFinalFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ #ifdef ANDROID
+ rv = reportsFinalFile->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ #endif
+
+ rv = reportsFinalFile->AppendNative(mReportsFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = reportsFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoString reportsFinalFilename;
+ rv = reportsFinalFile->GetLeafName(reportsFinalFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = mReportsTmpFile->MoveTo(/* directory */ nullptr,
+ reportsFinalFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Write a message to the console.
+
+ nsCOMPtr<nsIConsoleService> cs =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString path;
+ mReportsTmpFile->GetPath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString msg = NS_LITERAL_STRING("nsIMemoryInfoDumper dumped reports to ");
+ msg.Append(path);
+ return cs->LogStringMessage(msg.get());
+ }
+
+private:
+ ~TempDirFinishCallback() {}
+
+ nsCOMPtr<nsIFile> mReportsTmpFile;
+ nsCString mReportsFilename;
+};
+
+NS_IMPL_ISUPPORTS(TempDirFinishCallback, nsIFinishDumpingCallback)
+
+static nsresult
+DumpMemoryInfoToFile(
+ nsIFile* aReportsFile,
+ nsIFinishDumpingCallback* aFinishDumping,
+ nsISupports* aFinishDumpingData,
+ bool aAnonymize,
+ bool aMinimizeMemoryUsage,
+ nsAString& aDMDIdentifier)
+{
+ RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
+ nsresult rv = gzWriter->Init(aReportsFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ auto jsonWriter =
+ MakeUnique<JSONWriter>(MakeUnique<GZWriterWrapper>(gzWriter));
+
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+ // This is the first write to the file, and it causes |aWriter| to allocate
+ // over 200 KiB of memory.
+ jsonWriter->Start();
+ {
+ // Increment this number if the format changes.
+ jsonWriter->IntProperty("version", 1);
+ jsonWriter->BoolProperty("hasMozMallocUsableSize",
+ mgr->GetHasMozMallocUsableSize());
+ jsonWriter->StartArrayProperty("reports");
+ }
+
+ RefPtr<HandleReportAndFinishReportingCallbacks>
+ handleReportAndFinishReporting =
+ new HandleReportAndFinishReportingCallbacks(Move(jsonWriter),
+ aFinishDumping,
+ aFinishDumpingData);
+ rv = mgr->GetReportsExtended(handleReportAndFinishReporting, nullptr,
+ handleReportAndFinishReporting, nullptr,
+ aAnonymize,
+ aMinimizeMemoryUsage,
+ aDMDIdentifier);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
+ const nsAString& aFilename,
+ nsIFinishDumpingCallback* aFinishDumping,
+ nsISupports* aFinishDumpingData,
+ bool aAnonymize)
+{
+ MOZ_ASSERT(!aFilename.IsEmpty());
+
+ // Create the file.
+
+ nsCOMPtr<nsIFile> reportsFile;
+ nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(reportsFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ reportsFile->InitWithPath(aFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = reportsFile->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!exists) {
+ rv = reportsFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ nsString dmdIdent = EmptyString();
+ return DumpMemoryInfoToFile(reportsFile, aFinishDumping, aFinishDumpingData,
+ aAnonymize, /* minimizeMemoryUsage = */ false,
+ dmdIdent);
+}
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
+ bool aAnonymize,
+ bool aMinimizeMemoryUsage)
+{
+ nsString identifier(aIdentifier);
+ EnsureNonEmptyIdentifier(identifier);
+
+ // Open a new file named something like
+ //
+ // incomplete-memory-report-<identifier>-<pid>.json.gz
+ //
+ // in NS_OS_TEMP_DIR for writing. When we're finished writing the report,
+ // we'll rename this file and get rid of the "incomplete-" prefix.
+ //
+ // We do this because we don't want scripts which poll the filesystem
+ // looking for memory report dumps to grab a file before we're finished
+ // writing to it.
+
+ // The "unified" indicates that we merge the memory reports from all
+ // processes and write out one file, rather than a separate file for
+ // each process as was the case before bug 946407. This is so that
+ // the get_about_memory.py script in the B2G repository can
+ // determine when it's done waiting for files to appear.
+ nsCString reportsFinalFilename;
+ MakeFilename("unified-memory-report", identifier, getpid(), "json.gz",
+ reportsFinalFilename);
+
+ nsCOMPtr<nsIFile> reportsTmpFile;
+ nsresult rv;
+ // In Android case, this function will open a file named aFilename under
+ // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
+ // open a file named aFilename under "NS_OS_TEMP_DIR".
+ rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
+ reportsFinalFilename,
+ getter_AddRefs(reportsTmpFile),
+ NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ RefPtr<TempDirFinishCallback> finishDumping =
+ new TempDirFinishCallback(reportsTmpFile, reportsFinalFilename);
+
+ return DumpMemoryInfoToFile(reportsTmpFile, finishDumping, nullptr,
+ aAnonymize, aMinimizeMemoryUsage, identifier);
+}
+
+#ifdef MOZ_DMD
+dmd::DMDFuncs::Singleton dmd::DMDFuncs::sSingleton;
+
+nsresult
+nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
+ FILE** aOutFile)
+{
+ if (!dmd::IsRunning()) {
+ *aOutFile = nullptr;
+ return NS_OK;
+ }
+
+ // Create a filename like dmd-<identifier>-<pid>.json.gz, which will be used
+ // if DMD is enabled.
+ nsCString dmdFilename;
+ MakeFilename("dmd", aIdentifier, aPid, "json.gz", dmdFilename);
+
+ // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
+ // and dump DMD output to it. This must occur after the memory reporters
+ // have been run (above), but before the memory-reports file has been
+ // renamed (so scripts can detect the DMD file, if present).
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> dmdFile;
+ rv = nsDumpUtils::OpenTempFile(dmdFilename,
+ getter_AddRefs(dmdFile),
+ NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = dmdFile->OpenANSIFileDesc("wb", aOutFile);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "OpenANSIFileDesc failed");
+
+ // Print the path, because on some platforms (e.g. Mac) it's not obvious.
+ nsCString path;
+ rv = dmdFile->GetNativePath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ dmd::StatusMsg("opened %s for writing\n", path.get());
+
+ return rv;
+}
+
+nsresult
+nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
+{
+ RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
+ nsresult rv = gzWriter->InitANSIFileDesc(aFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Dump DMD's memory reports analysis to the file.
+ dmd::Analyze(MakeUnique<GZWriterWrapper>(gzWriter));
+
+ rv = gzWriter->Finish();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Finish failed");
+ return rv;
+}
+#endif // MOZ_DMD
+
diff --git a/xpcom/base/nsMemoryInfoDumper.h b/xpcom/base/nsMemoryInfoDumper.h
new file mode 100644
index 000000000..6bba176f2
--- /dev/null
+++ b/xpcom/base/nsMemoryInfoDumper.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_nsMemoryInfoDumper_h
+#define mozilla_nsMemoryInfoDumper_h
+
+#include "nsIMemoryInfoDumper.h"
+#include <stdio.h>
+
+/**
+ * This class facilitates dumping information about our memory usage to disk.
+ *
+ * Its cpp file also has Linux-only code which watches various OS signals and
+ * dumps memory info upon receiving a signal. You can activate these listeners
+ * by calling Initialize().
+ */
+class nsMemoryInfoDumper : public nsIMemoryInfoDumper
+{
+ virtual ~nsMemoryInfoDumper();
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYINFODUMPER
+
+ nsMemoryInfoDumper();
+
+ static void Initialize();
+
+#ifdef MOZ_DMD
+ // Open an appropriately named file for a DMD report. If DMD is
+ // disabled, return a null FILE* instead.
+ static nsresult OpenDMDFile(const nsAString& aIdentifier, int aPid,
+ FILE** aOutFile);
+ // Write a DMD report to the given file and close it.
+ static nsresult DumpDMDToFile(FILE* aFile);
+#endif
+};
+
+#define NS_MEMORY_INFO_DUMPER_CID \
+{ 0x00bd71fb, 0x7f09, 0x4ec3, \
+{ 0x96, 0xaf, 0xa0, 0xb5, 0x22, 0xb7, 0x79, 0x69 } }
+
+#endif
diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp
new file mode 100644
index 000000000..aa3d74dfd
--- /dev/null
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -0,0 +1,2717 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAtomTable.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsPrintfCString.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMemoryReporterManager.h"
+#include "nsITimer.h"
+#include "nsThreadUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsIObserverService.h"
+#include "nsIGlobalObject.h"
+#include "nsIXPConnect.h"
+#if defined(XP_UNIX) || defined(MOZ_DMD)
+#include "nsMemoryInfoDumper.h"
+#endif
+#include "mozilla/Attributes.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/dom/PMemoryReportRequestParent.h" // for dom::MemoryReport
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#ifndef getpid
+#define getpid _getpid
+#endif
+#else
+#include <unistd.h>
+#endif
+
+using namespace mozilla;
+
+#if defined(MOZ_MEMORY)
+# define HAVE_JEMALLOC_STATS 1
+# include "mozmemory.h"
+#endif // MOZ_MEMORY
+
+#if defined(XP_LINUX)
+
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+
+static MOZ_MUST_USE nsresult
+GetProcSelfStatmField(int aField, int64_t* aN)
+{
+ // There are more than two fields, but we're only interested in the first
+ // two.
+ static const int MAX_FIELD = 2;
+ size_t fields[MAX_FIELD];
+ MOZ_ASSERT(aField < MAX_FIELD, "bad field number");
+ FILE* f = fopen("/proc/self/statm", "r");
+ if (f) {
+ int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]);
+ fclose(f);
+ if (nread == MAX_FIELD) {
+ *aN = fields[aField] * getpagesize();
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+static MOZ_MUST_USE nsresult
+GetProcSelfSmapsPrivate(int64_t* aN)
+{
+ // You might be tempted to calculate USS by subtracting the "shared" value
+ // from the "resident" value in /proc/<pid>/statm. But at least on Linux,
+ // statm's "shared" value actually counts pages backed by files, which has
+ // little to do with whether the pages are actually shared. /proc/self/smaps
+ // on the other hand appears to give us the correct information.
+
+ FILE* f = fopen("/proc/self/smaps", "r");
+ if (NS_WARN_IF(!f)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // We carry over the end of the buffer to the beginning to make sure we only
+ // interpret complete lines.
+ static const uint32_t carryOver = 32;
+ static const uint32_t readSize = 4096;
+
+ int64_t amount = 0;
+ char buffer[carryOver + readSize + 1];
+
+ // Fill the beginning of the buffer with spaces, as a sentinel for the first
+ // iteration.
+ memset(buffer, ' ', carryOver);
+
+ for (;;) {
+ size_t bytes = fread(buffer + carryOver, sizeof(*buffer), readSize, f);
+ char* end = buffer + bytes;
+ char* ptr = buffer;
+ end[carryOver] = '\0';
+ // We are looking for lines like "Private_{Clean,Dirty}: 4 kB".
+ while ((ptr = strstr(ptr, "Private"))) {
+ if (ptr >= end) {
+ break;
+ }
+ ptr += sizeof("Private_Xxxxx:");
+ amount += strtol(ptr, nullptr, 10);
+ }
+ if (bytes < readSize) {
+ // We do not expect any match within the end of the buffer.
+ MOZ_ASSERT(!strstr(end, "Private"));
+ break;
+ }
+ // Carry the end of the buffer over to the beginning.
+ memcpy(buffer, end, carryOver);
+ }
+
+ fclose(f);
+ // Convert from kB to bytes.
+ *aN = amount * 1024;
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ return GetProcSelfStatmField(0, aN);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ return GetProcSelfStatmField(1, aN);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+static MOZ_MUST_USE nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+ return GetProcSelfSmapsPrivate(aN);
+}
+
+#ifdef HAVE_MALLINFO
+#define HAVE_SYSTEM_HEAP_REPORTER 1
+static MOZ_MUST_USE nsresult
+SystemHeapSize(int64_t* aSizeOut)
+{
+ struct mallinfo info = mallinfo();
+
+ // The documentation in the glibc man page makes it sound like |uordblks|
+ // would suffice, but that only gets the small allocations that are put in
+ // the brk heap. We need |hblkhd| as well to get the larger allocations
+ // that are mmapped.
+ //
+ // The fields in |struct mallinfo| are all |int|, <sigh>, so it is
+ // unreliable if memory usage gets high. However, the system heap size on
+ // Linux should usually be zero (so long as jemalloc is enabled) so that
+ // shouldn't be a problem. Nonetheless, cast the |int|s to |size_t| before
+ // adding them to provide a small amount of extra overflow protection.
+ *aSizeOut = size_t(info.hblkhd) + size_t(info.uordblks);
+ return NS_OK;
+}
+#endif
+
+#elif defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__) \
+ || defined(__FreeBSD_kernel__)
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/user.h>
+#endif
+
+#include <unistd.h>
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(__DragonFly__)
+#define KP_SIZE(kp) (kp.kp_vm_map_size)
+#define KP_RSS(kp) (kp.kp_vm_rssize * getpagesize())
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define KP_SIZE(kp) (kp.ki_size)
+#define KP_RSS(kp) (kp.ki_rssize * getpagesize())
+#elif defined(__NetBSD__)
+#define KP_SIZE(kp) (kp.p_vm_msize * getpagesize())
+#define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
+#elif defined(__OpenBSD__)
+#define KP_SIZE(kp) ((kp.p_vm_dsize + kp.p_vm_ssize \
+ + kp.p_vm_tsize) * getpagesize())
+#define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
+#endif
+
+static MOZ_MUST_USE nsresult
+GetKinfoProcSelf(KINFO_PROC* aProc)
+{
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int miblen = sizeof(mib) / sizeof(mib[0]);
+ size_t size = sizeof(KINFO_PROC);
+ if (sysctl(mib, miblen, aProc, &size, nullptr, 0)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ KINFO_PROC proc;
+ nsresult rv = GetKinfoProcSelf(&proc);
+ if (NS_SUCCEEDED(rv)) {
+ *aN = KP_SIZE(proc);
+ }
+ return rv;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ KINFO_PROC proc;
+ nsresult rv = GetKinfoProcSelf(&proc);
+ if (NS_SUCCEEDED(rv)) {
+ *aN = KP_RSS(proc);
+ }
+ return rv;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#ifdef __FreeBSD__
+#include <libutil.h>
+#include <algorithm>
+
+static MOZ_MUST_USE nsresult
+GetKinfoVmentrySelf(int64_t* aPrss, uint64_t* aMaxreg)
+{
+ int cnt;
+ struct kinfo_vmentry* vmmap;
+ struct kinfo_vmentry* kve;
+ if (!(vmmap = kinfo_getvmmap(getpid(), &cnt))) {
+ return NS_ERROR_FAILURE;
+ }
+ if (aPrss) {
+ *aPrss = 0;
+ }
+ if (aMaxreg) {
+ *aMaxreg = 0;
+ }
+
+ for (int i = 0; i < cnt; i++) {
+ kve = &vmmap[i];
+ if (aPrss) {
+ *aPrss += kve->kve_private_resident;
+ }
+ if (aMaxreg) {
+ *aMaxreg = std::max(*aMaxreg, kve->kve_end - kve->kve_start);
+ }
+ }
+
+ free(vmmap);
+ return NS_OK;
+}
+
+#define HAVE_PRIVATE_REPORTER 1
+static MOZ_MUST_USE nsresult
+PrivateDistinguishedAmount(int64_t* aN)
+{
+ int64_t priv;
+ nsresult rv = GetKinfoVmentrySelf(&priv, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aN = priv * getpagesize();
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
+static MOZ_MUST_USE nsresult
+VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
+{
+ uint64_t biggestRegion;
+ nsresult rv = GetKinfoVmentrySelf(nullptr, &biggestRegion);
+ if (NS_SUCCEEDED(rv)) {
+ *aN = biggestRegion;
+ }
+ return NS_OK;
+}
+#endif // FreeBSD
+
+#elif defined(SOLARIS)
+
+#include <procfs.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+static void
+XMappingIter(int64_t& aVsize, int64_t& aResident)
+{
+ aVsize = -1;
+ aResident = -1;
+ int mapfd = open("/proc/self/xmap", O_RDONLY);
+ struct stat st;
+ prxmap_t* prmapp = nullptr;
+ if (mapfd >= 0) {
+ if (!fstat(mapfd, &st)) {
+ int nmap = st.st_size / sizeof(prxmap_t);
+ while (1) {
+ // stat(2) on /proc/<pid>/xmap returns an incorrect value,
+ // prior to the release of Solaris 11.
+ // Here is a workaround for it.
+ nmap *= 2;
+ prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
+ if (!prmapp) {
+ // out of memory
+ break;
+ }
+ int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
+ if (n < 0) {
+ break;
+ }
+ if (nmap >= n / sizeof(prxmap_t)) {
+ aVsize = 0;
+ aResident = 0;
+ for (int i = 0; i < n / sizeof(prxmap_t); i++) {
+ aVsize += prmapp[i].pr_size;
+ aResident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
+ }
+ break;
+ }
+ free(prmapp);
+ }
+ free(prmapp);
+ }
+ close(mapfd);
+ }
+}
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ int64_t vsize, resident;
+ XMappingIter(vsize, resident);
+ if (vsize == -1) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = vsize;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ int64_t vsize, resident;
+ XMappingIter(vsize, resident);
+ if (resident == -1) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = resident;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#elif defined(XP_MACOSX)
+
+#include <mach/mach_init.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <mach/task.h>
+#include <sys/sysctl.h>
+
+static MOZ_MUST_USE bool
+GetTaskBasicInfo(struct task_basic_info* aTi)
+{
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
+ (task_info_t)aTi, &count);
+ return kr == KERN_SUCCESS;
+}
+
+// The VSIZE figure on Mac includes huge amounts of shared memory and is always
+// absurdly high, eg. 2GB+ even at start-up. But both 'top' and 'ps' report
+// it, so we might as well too.
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ task_basic_info ti;
+ if (!GetTaskBasicInfo(&ti)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = ti.virtual_size;
+ return NS_OK;
+}
+
+// If we're using jemalloc on Mac, we need to instruct jemalloc to purge the
+// pages it has madvise(MADV_FREE)'d before we read our RSS in order to get
+// an accurate result. The OS will take away MADV_FREE'd pages when there's
+// memory pressure, so ideally, they shouldn't count against our RSS.
+//
+// Purging these pages can take a long time for some users (see bug 789975),
+// so we provide the option to get the RSS without purging first.
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmountHelper(int64_t* aN, bool aDoPurge)
+{
+#ifdef HAVE_JEMALLOC_STATS
+#ifndef MOZ_JEMALLOC4
+ if (aDoPurge) {
+ Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
+ jemalloc_purge_freed_pages();
+ }
+#endif
+#endif
+
+ task_basic_info ti;
+ if (!GetTaskBasicInfo(&ti)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = ti.resident_size;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ false);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true);
+}
+
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+
+static bool
+InSharedRegion(mach_vm_address_t aAddr, cpu_type_t aType)
+{
+ mach_vm_address_t base;
+ mach_vm_address_t size;
+
+ switch (aType) {
+ case CPU_TYPE_ARM:
+ base = SHARED_REGION_BASE_ARM;
+ size = SHARED_REGION_SIZE_ARM;
+ break;
+ case CPU_TYPE_I386:
+ base = SHARED_REGION_BASE_I386;
+ size = SHARED_REGION_SIZE_I386;
+ break;
+ case CPU_TYPE_X86_64:
+ base = SHARED_REGION_BASE_X86_64;
+ size = SHARED_REGION_SIZE_X86_64;
+ break;
+ default:
+ return false;
+ }
+
+ return base <= aAddr && aAddr < (base + size);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+ if (!aN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ cpu_type_t cpu_type;
+ size_t len = sizeof(cpu_type);
+ if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Roughly based on libtop_update_vm_regions in
+ // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
+ size_t privatePages = 0;
+ mach_vm_size_t size = 0;
+ for (mach_vm_address_t addr = MACH_VM_MIN_ADDRESS; ; addr += size) {
+ vm_region_top_info_data_t info;
+ mach_msg_type_number_t infoCount = VM_REGION_TOP_INFO_COUNT;
+ mach_port_t objectName;
+
+ kern_return_t kr =
+ mach_vm_region(mach_task_self(), &addr, &size, VM_REGION_TOP_INFO,
+ reinterpret_cast<vm_region_info_t>(&info),
+ &infoCount, &objectName);
+ if (kr == KERN_INVALID_ADDRESS) {
+ // Done iterating VM regions.
+ break;
+ } else if (kr != KERN_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (InSharedRegion(addr, cpu_type) && info.share_mode != SM_PRIVATE) {
+ continue;
+ }
+
+ switch (info.share_mode) {
+ case SM_LARGE_PAGE:
+ // NB: Large pages are not shareable and always resident.
+ case SM_PRIVATE:
+ privatePages += info.private_pages_resident;
+ privatePages += info.shared_pages_resident;
+ break;
+ case SM_COW:
+ privatePages += info.private_pages_resident;
+ if (info.ref_count == 1) {
+ // Treat copy-on-write pages as private if they only have one reference.
+ privatePages += info.shared_pages_resident;
+ }
+ break;
+ case SM_SHARED:
+ default:
+ break;
+ }
+ }
+
+ vm_size_t pageSize;
+ if (host_page_size(mach_host_self(), &pageSize) != KERN_SUCCESS) {
+ pageSize = PAGE_SIZE;
+ }
+
+ *aN = privatePages * pageSize;
+ return NS_OK;
+}
+
+#elif defined(XP_WIN)
+
+#include <windows.h>
+#include <psapi.h>
+#include <algorithm>
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ MEMORYSTATUSEX s;
+ s.dwLength = sizeof(s);
+
+ if (!GlobalMemoryStatusEx(&s)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aN = s.ullTotalVirtual - s.ullAvailVirtual;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ PROCESS_MEMORY_COUNTERS pmc;
+ pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
+
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aN = pmc.WorkingSetSize;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+
+static MOZ_MUST_USE nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+ // Determine how many entries we need.
+ PSAPI_WORKING_SET_INFORMATION tmp;
+ DWORD tmpSize = sizeof(tmp);
+ memset(&tmp, 0, tmpSize);
+
+ HANDLE proc = GetCurrentProcess();
+ QueryWorkingSet(proc, &tmp, tmpSize);
+
+ // Fudge the size in case new entries are added between calls.
+ size_t entries = tmp.NumberOfEntries * 2;
+
+ if (!entries) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DWORD infoArraySize = tmpSize + (entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+ UniqueFreePtr<PSAPI_WORKING_SET_INFORMATION> infoArray(
+ static_cast<PSAPI_WORKING_SET_INFORMATION*>(malloc(infoArraySize)));
+
+ if (!infoArray) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!QueryWorkingSet(proc, infoArray.get(), infoArraySize)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ entries = static_cast<size_t>(infoArray->NumberOfEntries);
+ size_t privatePages = 0;
+ for (size_t i = 0; i < entries; i++) {
+ // Count shared pages that only one process is using as private.
+ if (!infoArray->WorkingSetInfo[i].Shared ||
+ infoArray->WorkingSetInfo[i].ShareCount <= 1) {
+ privatePages++;
+ }
+ }
+
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+
+ *aN = privatePages * si.dwPageSize;
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
+static MOZ_MUST_USE nsresult
+VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
+{
+ SIZE_T biggestRegion = 0;
+ MEMORY_BASIC_INFORMATION vmemInfo = { 0 };
+ for (size_t currentAddress = 0; ; ) {
+ if (!VirtualQuery((LPCVOID)currentAddress, &vmemInfo, sizeof(vmemInfo))) {
+ // Something went wrong, just return whatever we've got already.
+ break;
+ }
+
+ if (vmemInfo.State == MEM_FREE) {
+ biggestRegion = std::max(biggestRegion, vmemInfo.RegionSize);
+ }
+
+ SIZE_T lastAddress = currentAddress;
+ currentAddress += vmemInfo.RegionSize;
+
+ // If we overflow, we've examined all of the address space.
+ if (currentAddress < lastAddress) {
+ break;
+ }
+ }
+
+ *aN = biggestRegion;
+ return NS_OK;
+}
+
+#define HAVE_PRIVATE_REPORTER 1
+static MOZ_MUST_USE nsresult
+PrivateDistinguishedAmount(int64_t* aN)
+{
+ PROCESS_MEMORY_COUNTERS_EX pmcex;
+ pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
+
+ if (!GetProcessMemoryInfo(GetCurrentProcess(),
+ (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aN = pmcex.PrivateUsage;
+ return NS_OK;
+}
+
+#define HAVE_SYSTEM_HEAP_REPORTER 1
+// Windows can have multiple separate heaps. During testing there were multiple
+// heaps present but the non-default ones had sizes no more than a few 10s of
+// KiBs. So we combine their sizes into a single measurement.
+static MOZ_MUST_USE nsresult
+SystemHeapSize(int64_t* aSizeOut)
+{
+ // Get the number of heaps.
+ DWORD nHeaps = GetProcessHeaps(0, nullptr);
+ NS_ENSURE_TRUE(nHeaps != 0, NS_ERROR_FAILURE);
+
+ // Get handles to all heaps, checking that the number of heaps hasn't
+ // changed in the meantime.
+ UniquePtr<HANDLE[]> heaps(new HANDLE[nHeaps]);
+ DWORD nHeaps2 = GetProcessHeaps(nHeaps, heaps.get());
+ NS_ENSURE_TRUE(nHeaps2 != 0 && nHeaps2 == nHeaps, NS_ERROR_FAILURE);
+
+ // Lock and iterate over each heap to get its size.
+ int64_t heapsSize = 0;
+ for (DWORD i = 0; i < nHeaps; i++) {
+ HANDLE heap = heaps[i];
+
+ NS_ENSURE_TRUE(HeapLock(heap), NS_ERROR_FAILURE);
+
+ int64_t heapSize = 0;
+ PROCESS_HEAP_ENTRY entry;
+ entry.lpData = nullptr;
+ while (HeapWalk(heap, &entry)) {
+ // We don't count entry.cbOverhead, because we just want to measure the
+ // space available to the program.
+ if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) {
+ heapSize += entry.cbData;
+ }
+ }
+
+ // Check this result only after unlocking the heap, so that we don't leave
+ // the heap locked if there was an error.
+ DWORD lastError = GetLastError();
+
+ // I have no idea how things would proceed if unlocking this heap failed...
+ NS_ENSURE_TRUE(HeapUnlock(heap), NS_ERROR_FAILURE);
+
+ NS_ENSURE_TRUE(lastError == ERROR_NO_MORE_ITEMS, NS_ERROR_FAILURE);
+
+ heapsSize += heapSize;
+ }
+
+ *aSizeOut = heapsSize;
+ return NS_OK;
+}
+
+struct SegmentKind
+{
+ DWORD mState;
+ DWORD mType;
+ DWORD mProtect;
+ int mIsStack;
+};
+
+struct SegmentEntry : public PLDHashEntryHdr
+{
+ static PLDHashNumber HashKey(const void* aKey)
+ {
+ auto kind = static_cast<const SegmentKind*>(aKey);
+ return mozilla::HashGeneric(kind->mState, kind->mType, kind->mProtect,
+ kind->mIsStack);
+ }
+
+ static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
+ {
+ auto kind = static_cast<const SegmentKind*>(aKey);
+ auto entry = static_cast<const SegmentEntry*>(aEntry);
+ return kind->mState == entry->mKind.mState &&
+ kind->mType == entry->mKind.mType &&
+ kind->mProtect == entry->mKind.mProtect &&
+ kind->mIsStack == entry->mKind.mIsStack;
+ }
+
+ static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
+ {
+ auto kind = static_cast<const SegmentKind*>(aKey);
+ auto entry = static_cast<SegmentEntry*>(aEntry);
+ entry->mKind = *kind;
+ entry->mCount = 0;
+ entry->mSize = 0;
+ }
+
+ static const PLDHashTableOps Ops;
+
+ SegmentKind mKind; // The segment kind.
+ uint32_t mCount; // The number of segments of this kind.
+ size_t mSize; // The combined size of segments of this kind.
+};
+
+/* static */ const PLDHashTableOps SegmentEntry::Ops = {
+ SegmentEntry::HashKey,
+ SegmentEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ SegmentEntry::InitEntry
+};
+
+class WindowsAddressSpaceReporter final : public nsIMemoryReporter
+{
+ ~WindowsAddressSpaceReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ // First iterate over all the segments and record how many of each kind
+ // there were and their aggregate sizes. We use a hash table for this
+ // because there are a couple of dozen different kinds possible.
+
+ PLDHashTable table(&SegmentEntry::Ops, sizeof(SegmentEntry));
+ MEMORY_BASIC_INFORMATION info = { 0 };
+ bool isPrevSegStackGuard = false;
+ for (size_t currentAddress = 0; ; ) {
+ if (!VirtualQuery((LPCVOID)currentAddress, &info, sizeof(info))) {
+ // Something went wrong, just return whatever we've got already.
+ break;
+ }
+
+ size_t size = info.RegionSize;
+
+ // Note that |type| and |protect| are ignored in some cases.
+ DWORD state = info.State;
+ DWORD type =
+ (state == MEM_RESERVE || state == MEM_COMMIT) ? info.Type : 0;
+ DWORD protect = (state == MEM_COMMIT) ? info.Protect : 0;
+ bool isStack = isPrevSegStackGuard &&
+ state == MEM_COMMIT &&
+ type == MEM_PRIVATE &&
+ protect == PAGE_READWRITE;
+
+ SegmentKind kind = { state, type, protect, isStack ? 1 : 0 };
+ auto entry =
+ static_cast<SegmentEntry*>(table.Add(&kind, mozilla::fallible));
+ if (entry) {
+ entry->mCount += 1;
+ entry->mSize += size;
+ }
+
+ isPrevSegStackGuard = info.State == MEM_COMMIT &&
+ info.Type == MEM_PRIVATE &&
+ info.Protect == (PAGE_READWRITE|PAGE_GUARD);
+
+ size_t lastAddress = currentAddress;
+ currentAddress += size;
+
+ // If we overflow, we've examined all of the address space.
+ if (currentAddress < lastAddress) {
+ break;
+ }
+ }
+
+ // Then iterate over the hash table and report the details for each segment
+ // kind.
+
+ for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // For each range of pages, we consider one or more of its State, Type
+ // and Protect values. These are documented at
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366775%28v=vs.85%29.aspx
+ // (for State and Type) and
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786%28v=vs.85%29.aspx
+ // (for Protect).
+ //
+ // Not all State values have accompanying Type and Protection values.
+ bool doType = false;
+ bool doProtect = false;
+
+ auto entry = static_cast<const SegmentEntry*>(iter.Get());
+
+ nsCString path("address-space");
+
+ switch (entry->mKind.mState) {
+ case MEM_FREE:
+ path.AppendLiteral("/free");
+ break;
+
+ case MEM_RESERVE:
+ path.AppendLiteral("/reserved");
+ doType = true;
+ break;
+
+ case MEM_COMMIT:
+ path.AppendLiteral("/commit");
+ doType = true;
+ doProtect = true;
+ break;
+
+ default:
+ // Should be impossible, but handle it just in case.
+ path.AppendLiteral("/???");
+ break;
+ }
+
+ if (doType) {
+ switch (entry->mKind.mType) {
+ case MEM_IMAGE:
+ path.AppendLiteral("/image");
+ break;
+
+ case MEM_MAPPED:
+ path.AppendLiteral("/mapped");
+ break;
+
+ case MEM_PRIVATE:
+ path.AppendLiteral("/private");
+ break;
+
+ default:
+ // Should be impossible, but handle it just in case.
+ path.AppendLiteral("/???");
+ break;
+ }
+ }
+
+ if (doProtect) {
+ DWORD protect = entry->mKind.mProtect;
+ // Basic attributes. Exactly one of these should be set.
+ if (protect & PAGE_EXECUTE) {
+ path.AppendLiteral("/execute");
+ }
+ if (protect & PAGE_EXECUTE_READ) {
+ path.AppendLiteral("/execute-read");
+ }
+ if (protect & PAGE_EXECUTE_READWRITE) {
+ path.AppendLiteral("/execute-readwrite");
+ }
+ if (protect & PAGE_EXECUTE_WRITECOPY) {
+ path.AppendLiteral("/execute-writecopy");
+ }
+ if (protect & PAGE_NOACCESS) {
+ path.AppendLiteral("/noaccess");
+ }
+ if (protect & PAGE_READONLY) {
+ path.AppendLiteral("/readonly");
+ }
+ if (protect & PAGE_READWRITE) {
+ path.AppendLiteral("/readwrite");
+ }
+ if (protect & PAGE_WRITECOPY) {
+ path.AppendLiteral("/writecopy");
+ }
+
+ // Modifiers. At most one of these should be set.
+ if (protect & PAGE_GUARD) {
+ path.AppendLiteral("+guard");
+ }
+ if (protect & PAGE_NOCACHE) {
+ path.AppendLiteral("+nocache");
+ }
+ if (protect & PAGE_WRITECOMBINE) {
+ path.AppendLiteral("+writecombine");
+ }
+
+ // Annotate likely stack segments, too.
+ if (entry->mKind.mIsStack) {
+ path.AppendLiteral("+stack");
+ }
+ }
+
+ // Append the segment count.
+ path.AppendPrintf("(segments=%u)", entry->mCount);
+
+ aHandleReport->Callback(
+ EmptyCString(), path, KIND_OTHER, UNITS_BYTES, entry->mSize,
+ NS_LITERAL_CSTRING("From MEMORY_BASIC_INFORMATION."), aData);
+ }
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(WindowsAddressSpaceReporter, nsIMemoryReporter)
+
+#endif // XP_<PLATFORM>
+
+#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
+class VsizeMaxContiguousReporter final : public nsIMemoryReporter
+{
+ ~VsizeMaxContiguousReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(VsizeMaxContiguousDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
+ "Size of the maximum contiguous block of available virtual memory.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(VsizeMaxContiguousReporter, nsIMemoryReporter)
+#endif
+
+#ifdef HAVE_PRIVATE_REPORTER
+class PrivateReporter final : public nsIMemoryReporter
+{
+ ~PrivateReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(PrivateDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "private", KIND_OTHER, UNITS_BYTES, amount,
+"Memory that cannot be shared with other processes, including memory that is "
+"committed and marked MEM_PRIVATE, data that is not mapped, and executable "
+"pages that have been written to.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(PrivateReporter, nsIMemoryReporter)
+#endif
+
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+class VsizeReporter final : public nsIMemoryReporter
+{
+ ~VsizeReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(VsizeDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "vsize", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process, including code and data segments, the heap, "
+"thread stacks, memory explicitly mapped by the process via mmap and similar "
+"operations, and memory shared with other processes. This is the vsize figure "
+"as reported by 'top' and 'ps'. This figure is of limited use on Mac, where "
+"processes share huge amounts of memory with one another. But even on other "
+"operating systems, 'resident' is a much better measure of the memory "
+"resources used by the process.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(VsizeReporter, nsIMemoryReporter)
+
+class ResidentReporter final : public nsIMemoryReporter
+{
+ ~ResidentReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(ResidentDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "resident", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process that is present in physical memory, also known "
+"as the resident set size (RSS). This is the best single figure to use when "
+"considering the memory resources used by the process, but it depends both on "
+"other processes being run and details of the OS kernel and so is best used "
+"for comparing the memory usage of a single process at different points in "
+"time.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(ResidentReporter, nsIMemoryReporter)
+
+#endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS
+
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+class ResidentUniqueReporter final : public nsIMemoryReporter
+{
+ ~ResidentUniqueReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount = 0;
+ if (NS_SUCCEEDED(ResidentUniqueDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process that is present in physical memory and not "
+"shared with any other processes. This is also known as the process's unique "
+"set size (USS). This is the amount of RAM we'd expect to be freed if we "
+"closed this process.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter)
+
+#endif // HAVE_RESIDENT_UNIQUE_REPORTER
+
+#ifdef HAVE_SYSTEM_HEAP_REPORTER
+
+class SystemHeapReporter final : public nsIMemoryReporter
+{
+ ~SystemHeapReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(SystemHeapSize(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "system-heap-allocated", KIND_OTHER, UNITS_BYTES, amount,
+"Memory used by the system allocator that is currently allocated to the "
+"application. This is distinct from the jemalloc heap that Firefox uses for "
+"most or all of its heap allocations. Ideally this number is zero, but "
+"on some platforms we cannot force every heap allocation through jemalloc.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(SystemHeapReporter, nsIMemoryReporter)
+
+#endif // HAVE_SYSTEM_HEAP_REPORTER
+
+#ifdef XP_UNIX
+
+#include <sys/resource.h>
+
+#define HAVE_RESIDENT_PEAK_REPORTER 1
+
+static MOZ_MUST_USE nsresult
+ResidentPeakDistinguishedAmount(int64_t* aN)
+{
+ struct rusage usage;
+ if (0 == getrusage(RUSAGE_SELF, &usage)) {
+ // The units for ru_maxrrs:
+ // - Mac: bytes
+ // - Solaris: pages? But some sources it actually always returns 0, so
+ // check for that
+ // - Linux, {Net/Open/Free}BSD, DragonFly: KiB
+#ifdef XP_MACOSX
+ *aN = usage.ru_maxrss;
+#elif defined(SOLARIS)
+ *aN = usage.ru_maxrss * getpagesize();
+#else
+ *aN = usage.ru_maxrss * 1024;
+#endif
+ if (*aN > 0) {
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+class ResidentPeakReporter final : public nsIMemoryReporter
+{
+ ~ResidentPeakReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount = 0;
+ if (NS_SUCCEEDED(ResidentPeakDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "resident-peak", KIND_OTHER, UNITS_BYTES, amount,
+"The peak 'resident' value for the lifetime of the process.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(ResidentPeakReporter, nsIMemoryReporter)
+
+#define HAVE_PAGE_FAULT_REPORTERS 1
+
+class PageFaultsSoftReporter final : public nsIMemoryReporter
+{
+ ~PageFaultsSoftReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ struct rusage usage;
+ int err = getrusage(RUSAGE_SELF, &usage);
+ if (err == 0) {
+ int64_t amount = usage.ru_minflt;
+ MOZ_COLLECT_REPORT(
+ "page-faults-soft", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
+"The number of soft page faults (also known as 'minor page faults') that "
+"have occurred since the process started. A soft page fault occurs when the "
+"process tries to access a page which is present in physical memory but is "
+"not mapped into the process's address space. For instance, a process might "
+"observe soft page faults when it loads a shared library which is already "
+"present in physical memory. A process may experience many thousands of soft "
+"page faults even when the machine has plenty of available physical memory, "
+"and because the OS services a soft page fault without accessing the disk, "
+"they impact performance much less than hard page faults.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(PageFaultsSoftReporter, nsIMemoryReporter)
+
+static MOZ_MUST_USE nsresult
+PageFaultsHardDistinguishedAmount(int64_t* aAmount)
+{
+ struct rusage usage;
+ int err = getrusage(RUSAGE_SELF, &usage);
+ if (err != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ *aAmount = usage.ru_majflt;
+ return NS_OK;
+}
+
+class PageFaultsHardReporter final : public nsIMemoryReporter
+{
+ ~PageFaultsHardReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount = 0;
+ if (NS_SUCCEEDED(PageFaultsHardDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
+"The number of hard page faults (also known as 'major page faults') that have "
+"occurred since the process started. A hard page fault occurs when a process "
+"tries to access a page which is not present in physical memory. The "
+"operating system must access the disk in order to fulfill a hard page fault. "
+"When memory is plentiful, you should see very few hard page faults. But if "
+"the process tries to use more memory than your machine has available, you "
+"may see many thousands of hard page faults. Because accessing the disk is up "
+"to a million times slower than accessing RAM, the program may run very "
+"slowly when it is experiencing more than 100 or so hard page faults a "
+"second.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(PageFaultsHardReporter, nsIMemoryReporter)
+
+#endif // XP_UNIX
+
+/**
+ ** memory reporter implementation for jemalloc and OSX malloc,
+ ** to obtain info on total memory in use (that we know about,
+ ** at least -- on OSX, there are sometimes other zones in use).
+ **/
+
+#ifdef HAVE_JEMALLOC_STATS
+
+static size_t
+HeapOverhead(jemalloc_stats_t* aStats)
+{
+ return aStats->waste + aStats->bookkeeping +
+ aStats->page_cache + aStats->bin_unused;
+}
+
+// This has UNITS_PERCENTAGE, so it is multiplied by 100x *again* on top of the
+// 100x for the percentage.
+static int64_t
+HeapOverheadFraction(jemalloc_stats_t* aStats)
+{
+ size_t heapOverhead = HeapOverhead(aStats);
+ size_t heapCommitted = aStats->allocated + heapOverhead;
+ return int64_t(10000 * (heapOverhead / (double)heapCommitted));
+}
+
+class JemallocHeapReporter final : public nsIMemoryReporter
+{
+ ~JemallocHeapReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ jemalloc_stats_t stats;
+ jemalloc_stats(&stats);
+
+ MOZ_COLLECT_REPORT(
+ "heap-committed/allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
+"Memory mapped by the heap allocator that is currently allocated to the "
+"application. This may exceed the amount of memory requested by the "
+"application because the allocator regularly rounds up request sizes. (The "
+"exact amount requested is not recorded.)");
+
+ MOZ_COLLECT_REPORT(
+ "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
+"The same as 'heap-committed/allocated'.");
+
+ // We mark this and the other heap-overhead reporters as KIND_NONHEAP
+ // because KIND_HEAP memory means "counted in heap-allocated", which
+ // this is not.
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/bin-unused", KIND_NONHEAP, UNITS_BYTES,
+ stats.bin_unused,
+"Unused bytes due to fragmentation in the bins used for 'small' (<= 2 KiB) "
+"allocations. These bytes will be used if additional allocations occur.");
+
+ if (stats.waste > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES,
+ stats.waste,
+"Committed bytes which do not correspond to an active allocation and which the "
+"allocator is not intentionally keeping alive (i.e., not "
+"'explicit/heap-overhead/{bookkeeping,page-cache,bin-unused}').");
+ }
+
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES,
+ stats.bookkeeping,
+"Committed bytes which the heap allocator uses for internal data structures.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/page-cache", KIND_NONHEAP, UNITS_BYTES,
+ stats.page_cache,
+"Memory which the allocator could return to the operating system, but hasn't. "
+"The allocator keeps this memory around as an optimization, so it doesn't "
+"have to ask the OS the next time it needs to fulfill a request. This value "
+"is typically not larger than a few megabytes.");
+
+ MOZ_COLLECT_REPORT(
+ "heap-committed/overhead", KIND_OTHER, UNITS_BYTES,
+ HeapOverhead(&stats),
+"The sum of 'explicit/heap-overhead/*'.");
+
+ MOZ_COLLECT_REPORT(
+ "heap-mapped", KIND_OTHER, UNITS_BYTES, stats.mapped,
+"Amount of memory currently mapped. Includes memory that is uncommitted, i.e. "
+"neither in physical memory nor paged to disk.");
+
+ MOZ_COLLECT_REPORT(
+ "heap-chunksize", KIND_OTHER, UNITS_BYTES, stats.chunksize,
+ "Size of chunks.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter)
+
+#endif // HAVE_JEMALLOC_STATS
+
+// Why is this here? At first glance, you'd think it could be defined and
+// registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
+// However, the obvious time to register it is when the table is initialized,
+// and that happens before XPCOM components are initialized, which means the
+// RegisterStrongMemoryReporter call fails. So instead we do it here.
+class AtomTablesReporter final : public nsIMemoryReporter
+{
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+ ~AtomTablesReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ size_t Main, Static;
+ NS_SizeOfAtomTablesIncludingThis(MallocSizeOf, &Main, &Static);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/atom-tables/main", KIND_HEAP, UNITS_BYTES, Main,
+ "Memory used by the main atoms table.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/atom-tables/static", KIND_HEAP, UNITS_BYTES, Static,
+ "Memory used by the static atoms table.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
+
+#ifdef DEBUG
+
+// Ideally, this would be implemented in BlockingResourceBase.cpp.
+// However, this ends up breaking the linking step of various unit tests due
+// to adding a new dependency to libdmd for a commonly used feature (mutexes)
+// in DMD builds. So instead we do it here.
+class DeadlockDetectorReporter final : public nsIMemoryReporter
+{
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+ ~DeadlockDetectorReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/deadlock-detector", KIND_HEAP, UNITS_BYTES,
+ BlockingResourceBase::SizeOfDeadlockDetector(MallocSizeOf),
+ "Memory used by the deadlock detector.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(DeadlockDetectorReporter, nsIMemoryReporter)
+
+#endif
+
+#ifdef MOZ_DMD
+
+namespace mozilla {
+namespace dmd {
+
+class DMDReporter final : public nsIMemoryReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ dmd::Sizes sizes;
+ dmd::SizeOf(&sizes);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/stack-traces/used", KIND_HEAP, UNITS_BYTES,
+ sizes.mStackTracesUsed,
+ "Memory used by stack traces which correspond to at least "
+ "one heap block DMD is tracking.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/stack-traces/unused", KIND_HEAP, UNITS_BYTES,
+ sizes.mStackTracesUnused,
+ "Memory used by stack traces which don't correspond to any heap "
+ "blocks DMD is currently tracking.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/stack-traces/table", KIND_HEAP, UNITS_BYTES,
+ sizes.mStackTraceTable,
+ "Memory used by DMD's stack trace table.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/live-block-table", KIND_HEAP, UNITS_BYTES,
+ sizes.mLiveBlockTable,
+ "Memory used by DMD's live block table.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/dead-block-list", KIND_HEAP, UNITS_BYTES,
+ sizes.mDeadBlockTable,
+ "Memory used by DMD's dead block list.");
+
+ return NS_OK;
+ }
+
+private:
+ ~DMDReporter() {}
+};
+NS_IMPL_ISUPPORTS(DMDReporter, nsIMemoryReporter)
+
+} // namespace dmd
+} // namespace mozilla
+
+#endif // MOZ_DMD
+
+/**
+ ** nsMemoryReporterManager implementation
+ **/
+
+NS_IMPL_ISUPPORTS(nsMemoryReporterManager, nsIMemoryReporterManager)
+
+NS_IMETHODIMP
+nsMemoryReporterManager::Init()
+{
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ // Under normal circumstances this function is only called once. However,
+ // we've (infrequently) seen memory report dumps in crash reports that
+ // suggest that this function is sometimes called multiple times. That in
+ // turn means that multiple reporters of each kind are registered, which
+ // leads to duplicated reports of individual measurements such as "resident",
+ // "vsize", etc.
+ //
+ // It's unclear how these multiple calls can occur. The only plausible theory
+ // so far is badly-written extensions, because this function is callable from
+ // JS code via nsIMemoryReporter.idl.
+ //
+ // Whatever the cause, it's a bad thing. So we protect against it with the
+ // following check.
+ static bool isInited = false;
+ if (isInited) {
+ NS_WARNING("nsMemoryReporterManager::Init() has already been called!");
+ return NS_OK;
+ }
+ isInited = true;
+
+#if defined(HAVE_JEMALLOC_STATS) && defined(MOZ_GLUE_IN_PROGRAM)
+ if (!jemalloc_stats) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+#ifdef HAVE_JEMALLOC_STATS
+ RegisterStrongReporter(new JemallocHeapReporter());
+#endif
+
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ RegisterStrongReporter(new VsizeReporter());
+ RegisterStrongReporter(new ResidentReporter());
+#endif
+
+#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
+ RegisterStrongReporter(new VsizeMaxContiguousReporter());
+#endif
+
+#ifdef HAVE_RESIDENT_PEAK_REPORTER
+ RegisterStrongReporter(new ResidentPeakReporter());
+#endif
+
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+ RegisterStrongReporter(new ResidentUniqueReporter());
+#endif
+
+#ifdef HAVE_PAGE_FAULT_REPORTERS
+ RegisterStrongReporter(new PageFaultsSoftReporter());
+ RegisterStrongReporter(new PageFaultsHardReporter());
+#endif
+
+#ifdef HAVE_PRIVATE_REPORTER
+ RegisterStrongReporter(new PrivateReporter());
+#endif
+
+#ifdef HAVE_SYSTEM_HEAP_REPORTER
+ RegisterStrongReporter(new SystemHeapReporter());
+#endif
+
+ RegisterStrongReporter(new AtomTablesReporter());
+
+#ifdef DEBUG
+ RegisterStrongReporter(new DeadlockDetectorReporter());
+#endif
+
+#ifdef MOZ_DMD
+ RegisterStrongReporter(new mozilla::dmd::DMDReporter());
+#endif
+
+#ifdef XP_WIN
+ RegisterStrongReporter(new WindowsAddressSpaceReporter());
+#endif
+
+#ifdef XP_UNIX
+ nsMemoryInfoDumper::Initialize();
+#endif
+
+ return NS_OK;
+}
+
+nsMemoryReporterManager::nsMemoryReporterManager()
+ : mMutex("nsMemoryReporterManager::mMutex")
+ , mIsRegistrationBlocked(false)
+ , mStrongReporters(new StrongReportersTable())
+ , mWeakReporters(new WeakReportersTable())
+ , mSavedStrongReporters(nullptr)
+ , mSavedWeakReporters(nullptr)
+ , mNextGeneration(1)
+ , mPendingProcessesState(nullptr)
+ , mPendingReportersState(nullptr)
+{
+}
+
+nsMemoryReporterManager::~nsMemoryReporterManager()
+{
+ delete mStrongReporters;
+ delete mWeakReporters;
+ NS_ASSERTION(!mSavedStrongReporters, "failed to restore strong reporters");
+ NS_ASSERTION(!mSavedWeakReporters, "failed to restore weak reporters");
+}
+
+#ifdef MOZ_WIDGET_GONK
+#define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1
+#endif
+
+#ifdef DEBUG_CHILD_PROCESS_MEMORY_REPORTING
+#define MEMORY_REPORTING_LOG(format, ...) \
+ printf_stderr("++++ MEMORY REPORTING: " format, ##__VA_ARGS__);
+#else
+#define MEMORY_REPORTING_LOG(...)
+#endif
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReports(
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ bool aAnonymize)
+{
+ return GetReportsExtended(aHandleReport, aHandleReportData,
+ aFinishReporting, aFinishReportingData,
+ aAnonymize,
+ /* minimize = */ false,
+ /* DMDident = */ EmptyString());
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReportsExtended(
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ bool aAnonymize,
+ bool aMinimize,
+ const nsAString& aDMDDumpIdent)
+{
+ nsresult rv;
+
+ // Memory reporters are not necessarily threadsafe, so this function must
+ // be called from the main thread.
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ uint32_t generation = mNextGeneration++;
+
+ if (mPendingProcessesState) {
+ // A request is in flight. Don't start another one. And don't report
+ // an error; just ignore it, and let the in-flight request finish.
+ MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
+ generation, mPendingProcessesState->mGeneration);
+ return NS_OK;
+ }
+
+ MEMORY_REPORTING_LOG("GetReports (gen=%u)\n", generation);
+
+ uint32_t concurrency = Preferences::GetUint("memory.report_concurrency", 1);
+ MOZ_ASSERT(concurrency >= 1);
+ if (concurrency < 1) {
+ concurrency = 1;
+ }
+ mPendingProcessesState = new PendingProcessesState(generation,
+ aAnonymize,
+ aMinimize,
+ concurrency,
+ aHandleReport,
+ aHandleReportData,
+ aFinishReporting,
+ aFinishReportingData,
+ aDMDDumpIdent);
+
+ if (aMinimize) {
+ nsCOMPtr<nsIRunnable> callback =
+ NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports);
+ rv = MinimizeMemoryUsage(callback);
+ } else {
+ rv = StartGettingReports();
+ }
+ return rv;
+}
+
+nsresult
+nsMemoryReporterManager::StartGettingReports()
+{
+ PendingProcessesState* s = mPendingProcessesState;
+ nsresult rv;
+
+ // Get reports for this process.
+ FILE* parentDMDFile = nullptr;
+#ifdef MOZ_DMD
+ if (!s->mDMDDumpIdent.IsEmpty()) {
+ rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
+ &parentDMDFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Proceed with the memory report as if DMD were disabled.
+ parentDMDFile = nullptr;
+ }
+ }
+#endif
+
+ // This is async.
+ GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
+ s->mAnonymize, parentDMDFile,
+ s->mFinishReporting, s->mFinishReportingData);
+
+ nsTArray<ContentParent*> childWeakRefs;
+ ContentParent::GetAll(childWeakRefs);
+ if (!childWeakRefs.IsEmpty()) {
+ // Request memory reports from child processes. This happens
+ // after the parent report so that the parent's main thread will
+ // be free to process the child reports, instead of causing them
+ // to be buffered and consume (possibly scarce) memory.
+
+ for (size_t i = 0; i < childWeakRefs.Length(); ++i) {
+ s->mChildrenPending.AppendElement(childWeakRefs[i]);
+ }
+
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ // Don't use NS_ENSURE_* here; can't return until the report is finished.
+ if (NS_WARN_IF(!timer)) {
+ FinishReporting();
+ return NS_ERROR_FAILURE;
+ }
+ rv = timer->InitWithFuncCallback(TimeoutCallback,
+ this, kTimeoutLengthMS,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ FinishReporting();
+ return rv;
+ }
+
+ MOZ_ASSERT(!s->mTimer);
+ s->mTimer.swap(timer);
+ }
+
+ return NS_OK;
+}
+
+void
+nsMemoryReporterManager::DispatchReporter(
+ nsIMemoryReporter* aReporter, bool aIsAsync,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ bool aAnonymize)
+{
+ MOZ_ASSERT(mPendingReportersState);
+
+ // Grab refs to everything used in the lambda function.
+ RefPtr<nsMemoryReporterManager> self = this;
+ nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
+ nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
+ nsCOMPtr<nsISupports> handleReportData = aHandleReportData;
+
+ nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
+ [self, reporter, aIsAsync, handleReport, handleReportData, aAnonymize] () {
+ reporter->CollectReports(handleReport, handleReportData, aAnonymize);
+ if (!aIsAsync) {
+ self->EndReport();
+ }
+ });
+
+ NS_DispatchToMainThread(event);
+ mPendingReportersState->mReportsPending++;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReportsForThisProcessExtended(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
+ bool aAnonymize, FILE* aDMDFile,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData)
+{
+ // Memory reporters are not necessarily threadsafe, so this function must
+ // be called from the main thread.
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ if (NS_WARN_IF(mPendingReportersState)) {
+ // Report is already in progress.
+ return NS_ERROR_IN_PROGRESS;
+ }
+
+#ifdef MOZ_DMD
+ if (aDMDFile) {
+ // Clear DMD's reportedness state before running the memory
+ // reporters, to avoid spurious twice-reported warnings.
+ dmd::ClearReports();
+ }
+#else
+ MOZ_ASSERT(!aDMDFile);
+#endif
+
+ mPendingReportersState = new PendingReportersState(
+ aFinishReporting, aFinishReportingData, aDMDFile);
+
+ {
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
+ DispatchReporter(iter.Key(), iter.Data(),
+ aHandleReport, aHandleReportData, aAnonymize);
+ }
+
+ for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
+ nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
+ DispatchReporter(reporter, iter.Data(),
+ aHandleReport, aHandleReportData, aAnonymize);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::EndReport()
+{
+ if (--mPendingReportersState->mReportsPending == 0) {
+#ifdef MOZ_DMD
+ if (mPendingReportersState->mDMDFile) {
+ nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile);
+ }
+#endif
+ if (mPendingProcessesState) {
+ // This is the parent process.
+ EndProcessReport(mPendingProcessesState->mGeneration, true);
+ } else {
+ mPendingReportersState->mFinishReporting->Callback(
+ mPendingReportersState->mFinishReportingData);
+ }
+
+ delete mPendingReportersState;
+ mPendingReportersState = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsMemoryReporterManager::PendingProcessesState*
+nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
+{
+ // Memory reporting only happens on the main thread.
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ PendingProcessesState* s = mPendingProcessesState;
+
+ if (!s) {
+ // If we reach here, then:
+ //
+ // - A child process reported back too late, and no subsequent request
+ // is in flight.
+ //
+ // So there's nothing to be done. Just ignore it.
+ MEMORY_REPORTING_LOG(
+ "HandleChildReports: no request in flight (aGen=%u)\n",
+ aGeneration);
+ return nullptr;
+ }
+
+ if (aGeneration != s->mGeneration) {
+ // If we reach here, a child process must have reported back, too late,
+ // while a subsequent (higher-numbered) request is in flight. Again,
+ // ignore it.
+ MOZ_ASSERT(aGeneration < s->mGeneration);
+ MEMORY_REPORTING_LOG(
+ "HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
+ aGeneration, s->mGeneration);
+ return nullptr;
+ }
+
+ return s;
+}
+
+// This function has no return value. If something goes wrong, there's no
+// clear place to report the problem to, but that's ok -- we will end up
+// hitting the timeout and executing TimeoutCallback().
+void
+nsMemoryReporterManager::HandleChildReport(
+ uint32_t aGeneration,
+ const dom::MemoryReport& aChildReport)
+{
+ PendingProcessesState* s = GetStateForGeneration(aGeneration);
+ if (!s) {
+ return;
+ }
+
+ // Child reports should have a non-empty process.
+ MOZ_ASSERT(!aChildReport.process().IsEmpty());
+
+ // If the call fails, ignore and continue.
+ s->mHandleReport->Callback(aChildReport.process(),
+ aChildReport.path(),
+ aChildReport.kind(),
+ aChildReport.units(),
+ aChildReport.amount(),
+ aChildReport.desc(),
+ s->mHandleReportData);
+}
+
+/* static */ bool
+nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
+ const PendingProcessesState* aState)
+{
+ if (!aChild->IsAlive()) {
+ MEMORY_REPORTING_LOG("StartChildReports (gen=%u): child exited before"
+ " its report was started\n",
+ aState->mGeneration);
+ return false;
+ }
+
+ mozilla::dom::MaybeFileDesc dmdFileDesc = void_t();
+#ifdef MOZ_DMD
+ if (!aState->mDMDDumpIdent.IsEmpty()) {
+ FILE *dmdFile = nullptr;
+ nsresult rv = nsMemoryInfoDumper::OpenDMDFile(aState->mDMDDumpIdent,
+ aChild->Pid(), &dmdFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Proceed with the memory report as if DMD were disabled.
+ dmdFile = nullptr;
+ }
+ if (dmdFile) {
+ dmdFileDesc = mozilla::ipc::FILEToFileDescriptor(dmdFile);
+ fclose(dmdFile);
+ }
+ }
+#endif
+ return aChild->SendPMemoryReportRequestConstructor(
+ aState->mGeneration, aState->mAnonymize, aState->mMinimize, dmdFileDesc);
+}
+
+void
+nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
+{
+ PendingProcessesState* s = GetStateForGeneration(aGeneration);
+ if (!s) {
+ return;
+ }
+
+ MOZ_ASSERT(s->mNumProcessesRunning > 0);
+ s->mNumProcessesRunning--;
+ s->mNumProcessesCompleted++;
+ MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): process %u %s"
+ " (%u running, %u pending)\n",
+ aGeneration, s->mNumProcessesCompleted,
+ aSuccess ? "completed" : "exited during report",
+ s->mNumProcessesRunning,
+ static_cast<unsigned>(s->mChildrenPending.Length()));
+
+ // Start pending children up to the concurrency limit.
+ while (s->mNumProcessesRunning < s->mConcurrencyLimit &&
+ !s->mChildrenPending.IsEmpty()) {
+ // Pop last element from s->mChildrenPending
+ RefPtr<ContentParent> nextChild;
+ nextChild.swap(s->mChildrenPending.LastElement());
+ s->mChildrenPending.TruncateLength(s->mChildrenPending.Length() - 1);
+ // Start report (if the child is still alive).
+ if (StartChildReport(nextChild, s)) {
+ ++s->mNumProcessesRunning;
+ MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report"
+ " (%u running, %u pending)\n",
+ aGeneration, s->mNumProcessesRunning,
+ static_cast<unsigned>(s->mChildrenPending.Length()));
+ }
+ }
+
+ // If all the child processes (if any) have reported, we can cancel
+ // the timer (if started) and finish up. Otherwise, just return.
+ if (s->mNumProcessesRunning == 0) {
+ MOZ_ASSERT(s->mChildrenPending.IsEmpty());
+ if (s->mTimer) {
+ s->mTimer->Cancel();
+ }
+ FinishReporting();
+ }
+}
+
+/* static */ void
+nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
+{
+ nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
+ PendingProcessesState* s = mgr->mPendingProcessesState;
+
+ // Release assert because: if the pointer is null we're about to
+ // crash regardless of DEBUG, and this way the compiler doesn't
+ // complain about unused variables.
+ MOZ_RELEASE_ASSERT(s, "mgr->mPendingProcessesState");
+ MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u; %u running, %u pending)\n",
+ s->mGeneration, s->mNumProcessesRunning,
+ static_cast<unsigned>(s->mChildrenPending.Length()));
+
+ // We don't bother sending any kind of cancellation message to the child
+ // processes that haven't reported back.
+ mgr->FinishReporting();
+}
+
+nsresult
+nsMemoryReporterManager::FinishReporting()
+{
+ // Memory reporting only happens on the main thread.
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ MOZ_ASSERT(mPendingProcessesState);
+ MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n",
+ mPendingProcessesState->mGeneration,
+ mPendingProcessesState->mNumProcessesCompleted);
+
+ // Call this before deleting |mPendingProcessesState|. That way, if
+ // |mFinishReportData| calls GetReports(), it will silently abort, as
+ // required.
+ nsresult rv = mPendingProcessesState->mFinishReporting->Callback(
+ mPendingProcessesState->mFinishReportingData);
+
+ delete mPendingProcessesState;
+ mPendingProcessesState = nullptr;
+ return rv;
+}
+
+nsMemoryReporterManager::PendingProcessesState::PendingProcessesState(
+ uint32_t aGeneration, bool aAnonymize, bool aMinimize,
+ uint32_t aConcurrencyLimit,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ const nsAString& aDMDDumpIdent)
+ : mGeneration(aGeneration)
+ , mAnonymize(aAnonymize)
+ , mMinimize(aMinimize)
+ , mChildrenPending()
+ , mNumProcessesRunning(1) // reporting starts with the parent
+ , mNumProcessesCompleted(0)
+ , mConcurrencyLimit(aConcurrencyLimit)
+ , mHandleReport(aHandleReport)
+ , mHandleReportData(aHandleReportData)
+ , mFinishReporting(aFinishReporting)
+ , mFinishReportingData(aFinishReportingData)
+ , mDMDDumpIdent(aDMDDumpIdent)
+{
+}
+
+static void
+CrashIfRefcountIsZero(nsISupports* aObj)
+{
+ // This will probably crash if the object's refcount is 0.
+ uint32_t refcnt = NS_ADDREF(aObj);
+ if (refcnt <= 1) {
+ MOZ_CRASH("CrashIfRefcountIsZero: refcount is zero");
+ }
+ NS_RELEASE(aObj);
+}
+
+nsresult
+nsMemoryReporterManager::RegisterReporterHelper(
+ nsIMemoryReporter* aReporter, bool aForce, bool aStrong, bool aIsAsync)
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ if (mIsRegistrationBlocked && !aForce) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mStrongReporters->Contains(aReporter) ||
+ mWeakReporters->Contains(aReporter)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // If |aStrong| is true, |aReporter| may have a refcnt of 0, so we take
+ // a kung fu death grip before calling PutEntry. Otherwise, if PutEntry
+ // addref'ed and released |aReporter| before finally addref'ing it for
+ // good, it would free aReporter! The kung fu death grip could itself be
+ // problematic if PutEntry didn't addref |aReporter| (because then when the
+ // death grip goes out of scope, we would delete the reporter). In debug
+ // mode, we check that this doesn't happen.
+ //
+ // If |aStrong| is false, we require that |aReporter| have a non-zero
+ // refcnt.
+ //
+ if (aStrong) {
+ nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
+ mStrongReporters->Put(aReporter, aIsAsync);
+ CrashIfRefcountIsZero(aReporter);
+ } else {
+ CrashIfRefcountIsZero(aReporter);
+ nsCOMPtr<nsIXPConnectWrappedJS> jsComponent = do_QueryInterface(aReporter);
+ if (jsComponent) {
+ // We cannot allow non-native reporters (WrappedJS), since we'll be
+ // holding onto a raw pointer, which would point to the wrapper,
+ // and that wrapper is likely to go away as soon as this register
+ // call finishes. This would then lead to subsequent crashes in
+ // CollectReports().
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+ mWeakReporters->Put(aReporter, aIsAsync);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ true,
+ /* async = */ false);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterStrongAsyncReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ true,
+ /* async = */ true);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ false,
+ /* async = */ false);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterWeakAsyncReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ false,
+ /* async = */ true);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
+ nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ true,
+ /* strong = */ true,
+ /* async = */ false);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
+
+ if (mStrongReporters->Contains(aReporter)) {
+ mStrongReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ // We don't register new reporters when the block is in place, but we do
+ // unregister existing reporters. This is so we don't keep holding strong
+ // references that these reporters aren't expecting (which can keep them
+ // alive longer than intended).
+ if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) {
+ mSavedStrongReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
+
+ if (mWeakReporters->Contains(aReporter)) {
+ mWeakReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ // We don't register new reporters when the block is in place, but we do
+ // unregister existing reporters. This is so we don't keep holding weak
+ // references that the old reporters aren't expecting (which can end up as
+ // dangling pointers that lead to use-after-frees).
+ if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) {
+ mSavedWeakReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::BlockRegistrationAndHideExistingReporters()
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (mIsRegistrationBlocked) {
+ return NS_ERROR_FAILURE;
+ }
+ mIsRegistrationBlocked = true;
+
+ // Hide the existing reporters, saving them for later restoration.
+ MOZ_ASSERT(!mSavedStrongReporters);
+ MOZ_ASSERT(!mSavedWeakReporters);
+ mSavedStrongReporters = mStrongReporters;
+ mSavedWeakReporters = mWeakReporters;
+ mStrongReporters = new StrongReportersTable();
+ mWeakReporters = new WeakReportersTable();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (!mIsRegistrationBlocked) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Banish the current reporters, and restore the hidden ones.
+ delete mStrongReporters;
+ delete mWeakReporters;
+ mStrongReporters = mSavedStrongReporters;
+ mWeakReporters = mSavedWeakReporters;
+ mSavedStrongReporters = nullptr;
+ mSavedWeakReporters = nullptr;
+
+ mIsRegistrationBlocked = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetVsize(int64_t* aVsize)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ return VsizeDistinguishedAmount(aVsize);
+#else
+ *aVsize = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetVsizeMaxContiguous(int64_t* aAmount)
+{
+#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
+ return VsizeMaxContiguousDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResident(int64_t* aAmount)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ return ResidentDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResidentFast(int64_t* aAmount)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ return ResidentFastDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+/*static*/ int64_t
+nsMemoryReporterManager::ResidentFast()
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ int64_t amount;
+ nsresult rv = ResidentFastDistinguishedAmount(&amount);
+ NS_ENSURE_SUCCESS(rv, 0);
+ return amount;
+#else
+ return 0;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResidentPeak(int64_t* aAmount)
+{
+#ifdef HAVE_RESIDENT_PEAK_REPORTER
+ return ResidentPeakDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+/*static*/ int64_t
+nsMemoryReporterManager::ResidentPeak()
+{
+#ifdef HAVE_RESIDENT_PEAK_REPORTER
+ int64_t amount = 0;
+ nsresult rv = ResidentPeakDistinguishedAmount(&amount);
+ NS_ENSURE_SUCCESS(rv, 0);
+ return amount;
+#else
+ return 0;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResidentUnique(int64_t* aAmount)
+{
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+ return ResidentUniqueDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+/*static*/ int64_t
+nsMemoryReporterManager::ResidentUnique()
+{
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+ int64_t amount = 0;
+ nsresult rv = ResidentUniqueDistinguishedAmount(&amount);
+ NS_ENSURE_SUCCESS(rv, 0);
+ return amount;
+#else
+ return 0;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount)
+{
+#ifdef HAVE_JEMALLOC_STATS
+ jemalloc_stats_t stats;
+ jemalloc_stats(&stats);
+ *aAmount = stats.allocated;
+ return NS_OK;
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+// This has UNITS_PERCENTAGE, so it is multiplied by 100x.
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHeapOverheadFraction(int64_t* aAmount)
+{
+#ifdef HAVE_JEMALLOC_STATS
+ jemalloc_stats_t stats;
+ jemalloc_stats(&stats);
+ *aAmount = HeapOverheadFraction(&stats);
+ return NS_OK;
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+static MOZ_MUST_USE nsresult
+GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount)
+{
+ if (aAmountFn) {
+ *aAmount = aAmountFn();
+ return NS_OK;
+ }
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem,
+ aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser,
+ aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed,
+ aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetPageFaultsHard(int64_t* aAmount)
+{
+#ifdef HAVE_PAGE_FAULT_REPORTERS
+ return PageFaultsHardDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
+{
+ void* p = malloc(16);
+ if (!p) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ size_t usable = moz_malloc_usable_size(p);
+ free(p);
+ *aHas = !!(usable > 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetIsDMDEnabled(bool* aIsEnabled)
+{
+#ifdef MOZ_DMD
+ *aIsEnabled = true;
+#else
+ *aIsEnabled = false;
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetIsDMDRunning(bool* aIsRunning)
+{
+#ifdef MOZ_DMD
+ *aIsRunning = dmd::IsRunning();
+#else
+ *aIsRunning = false;
+#endif
+ return NS_OK;
+}
+
+namespace {
+
+/**
+ * This runnable lets us implement
+ * nsIMemoryReporterManager::MinimizeMemoryUsage(). We fire a heap-minimize
+ * notification, spin the event loop, and repeat this process a few times.
+ *
+ * When this sequence finishes, we invoke the callback function passed to the
+ * runnable's constructor.
+ */
+class MinimizeMemoryUsageRunnable : public Runnable
+{
+public:
+ explicit MinimizeMemoryUsageRunnable(nsIRunnable* aCallback)
+ : mCallback(aCallback)
+ , mRemainingIters(sNumIters)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (!os) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mRemainingIters == 0) {
+ os->NotifyObservers(nullptr, "after-minimize-memory-usage",
+ u"MinimizeMemoryUsageRunnable");
+ if (mCallback) {
+ mCallback->Run();
+ }
+ return NS_OK;
+ }
+
+ os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
+ mRemainingIters--;
+ NS_DispatchToMainThread(this);
+
+ return NS_OK;
+ }
+
+private:
+ // Send sNumIters heap-minimize notifications, spinning the event
+ // loop after each notification (see bug 610166 comment 12 for an
+ // explanation), because one notification doesn't cut it.
+ static const uint32_t sNumIters = 3;
+
+ nsCOMPtr<nsIRunnable> mCallback;
+ uint32_t mRemainingIters;
+};
+
+} // namespace
+
+NS_IMETHODIMP
+nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback)
+{
+ RefPtr<MinimizeMemoryUsageRunnable> runnable =
+ new MinimizeMemoryUsageRunnable(aCallback);
+
+ return NS_DispatchToMainThread(runnable);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::SizeOfTab(mozIDOMWindowProxy* aTopWindow,
+ int64_t* aJSObjectsSize,
+ int64_t* aJSStringsSize,
+ int64_t* aJSOtherSize,
+ int64_t* aDomSize,
+ int64_t* aStyleSize,
+ int64_t* aOtherSize,
+ int64_t* aTotalSize,
+ double* aJSMilliseconds,
+ double* aNonJSMilliseconds)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
+ auto* piWindow = nsPIDOMWindowOuter::From(aTopWindow);
+ if (NS_WARN_IF(!global) || NS_WARN_IF(!piWindow)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ TimeStamp t1 = TimeStamp::Now();
+
+ // Measure JS memory consumption (and possibly some non-JS consumption, via
+ // |jsPrivateSize|).
+ size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
+ nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
+ &jsObjectsSize, &jsStringsSize,
+ &jsPrivateSize, &jsOtherSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ TimeStamp t2 = TimeStamp::Now();
+
+ // Measure non-JS memory consumption.
+ size_t domSize, styleSize, otherSize;
+ rv = mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ TimeStamp t3 = TimeStamp::Now();
+
+ *aTotalSize = 0;
+#define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
+ DO(aJSObjectsSize, jsObjectsSize);
+ DO(aJSStringsSize, jsStringsSize);
+ DO(aJSOtherSize, jsOtherSize);
+ DO(aDomSize, jsPrivateSize + domSize);
+ DO(aStyleSize, styleSize);
+ DO(aOtherSize, otherSize);
+#undef DO
+
+ *aJSMilliseconds = (t2 - t1).ToMilliseconds();
+ *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
+
+ return NS_OK;
+}
+
+namespace mozilla {
+
+#define GET_MEMORY_REPORTER_MANAGER(mgr) \
+ RefPtr<nsMemoryReporterManager> mgr = \
+ nsMemoryReporterManager::GetOrCreate(); \
+ if (!mgr) { \
+ return NS_ERROR_FAILURE; \
+ }
+
+nsresult
+RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ // Hold a strong reference to the argument to make sure it gets released if
+ // we return early below.
+ nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterStrongReporter(reporter);
+}
+
+nsresult
+RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ // Hold a strong reference to the argument to make sure it gets released if
+ // we return early below.
+ nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterStrongAsyncReporter(reporter);
+}
+
+nsresult
+RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterWeakReporter(aReporter);
+}
+
+nsresult
+RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterWeakAsyncReporter(aReporter);
+}
+
+nsresult
+UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->UnregisterStrongReporter(aReporter);
+}
+
+nsresult
+UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->UnregisterWeakReporter(aReporter);
+}
+
+// Macro for generating functions that register distinguished amount functions
+// with the memory reporter manager.
+#define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
+ nsresult \
+ Register##name##DistinguishedAmount(kind##AmountFn aAmountFn) \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mAmountFns.m##name = aAmountFn; \
+ return NS_OK; \
+ }
+
+// Macro for generating functions that unregister distinguished amount
+// functions with the memory reporter manager.
+#define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
+ nsresult \
+ Unregister##name##DistinguishedAmount() \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mAmountFns.m##name = nullptr; \
+ return NS_OK; \
+ }
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
+DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(ImagesContentUsedUncompressed)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
+DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
+
+#undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
+#undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
+
+#define DEFINE_REGISTER_SIZE_OF_TAB(name) \
+ nsresult \
+ Register##name##SizeOfTab(name##SizeOfTabFn aSizeOfTabFn) \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mSizeOfTabFns.m##name = aSizeOfTabFn; \
+ return NS_OK; \
+ }
+
+DEFINE_REGISTER_SIZE_OF_TAB(JS);
+DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
+
+#undef DEFINE_REGISTER_SIZE_OF_TAB
+
+#undef GET_MEMORY_REPORTER_MANAGER
+
+} // namespace mozilla
diff --git a/xpcom/base/nsMemoryReporterManager.h b/xpcom/base/nsMemoryReporterManager.h
new file mode 100644
index 000000000..8240cbe34
--- /dev/null
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMemoryReporterManager_h__
+#define nsMemoryReporterManager_h__
+
+#include "mozilla/Mutex.h"
+#include "nsHashKeys.h"
+#include "nsIMemoryReporter.h"
+#include "nsITimer.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+class MemoryReport;
+} // namespace dom
+} // namespace mozilla
+
+class nsITimer;
+
+class nsMemoryReporterManager final : public nsIMemoryReporterManager
+{
+ virtual ~nsMemoryReporterManager();
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTERMANAGER
+
+ nsMemoryReporterManager();
+
+ // Gets the memory reporter manager service.
+ static nsMemoryReporterManager* GetOrCreate()
+ {
+ nsCOMPtr<nsIMemoryReporterManager> imgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+ return static_cast<nsMemoryReporterManager*>(imgr.get());
+ }
+
+ typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable;
+ typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable;
+
+ // Inter-process memory reporting proceeds as follows.
+ //
+ // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
+ // synchronously gets memory reports for the current process, sets up some
+ // state (mPendingProcessesState) for when child processes report back --
+ // including a timer -- and starts telling child processes to get memory
+ // reports. Control then returns to the main event loop.
+ //
+ // The number of concurrent child process reports is limited by the pref
+ // "memory.report_concurrency" in order to prevent the memory overhead of
+ // memory reporting from causing problems, especially on B2G when swapping
+ // to compressed RAM; see bug 1154053.
+ //
+ // - HandleChildReport() is called (asynchronously) once per child process
+ // reporter callback.
+ //
+ // - EndProcessReport() is called (asynchronously) once per process that
+ // finishes reporting back, including the parent. If all processes do so
+ // before time-out, the timer is cancelled. If there are child processes
+ // whose requests have not yet been sent, they will be started until the
+ // concurrency limit is (again) reached.
+ //
+ // - TimeoutCallback() is called (asynchronously) if all the child processes
+ // don't respond within the time threshold.
+ //
+ // - FinishReporting() finishes things off. It is *always* called -- either
+ // from EndChildReport() (if all child processes have reported back) or
+ // from TimeoutCallback() (if time-out occurs).
+ //
+ // All operations occur on the main thread.
+ //
+ // The above sequence of steps is a "request". A partially-completed request
+ // is described as "in flight".
+ //
+ // Each request has a "generation", a unique number that identifies it. This
+ // is used to ensure that each reports from a child process corresponds to
+ // the appropriate request from the parent process. (It's easier to
+ // implement a generation system than to implement a child report request
+ // cancellation mechanism.)
+ //
+ // Failures are mostly ignored, because it's (a) typically the most sensible
+ // thing to do, and (b) often hard to do anything else. The following are
+ // the failure cases of note.
+ //
+ // - If a request is made while the previous request is in flight, the new
+ // request is ignored, as per getReports()'s specification. No error is
+ // reported, because the previous request will complete soon enough.
+ //
+ // - If one or more child processes fail to respond within the time limit,
+ // things will proceed as if they don't exist. No error is reported,
+ // because partial information is better than nothing.
+ //
+ // - If a child process reports after the time-out occurs, it is ignored.
+ // (Generation checking will ensure it is ignored even if a subsequent
+ // request is in flight; this is the main use of generations.) No error
+ // is reported, because there's nothing sensible to be done about it at
+ // this late stage.
+ //
+ // - If the time-out occurs after a child process has sent some reports but
+ // before it has signaled completion (see bug 1151597), then what it
+ // successfully sent will be included, with no explicit indication that it
+ // is incomplete.
+ //
+ // Now, what what happens if a child process is created/destroyed in the
+ // middle of a request? Well, PendingProcessesState is initialized with an array
+ // of child process actors as of when the report started. So...
+ //
+ // - If a process is created after reporting starts, it won't be sent a
+ // request for reports. So the reported data will reflect how things were
+ // when the request began.
+ //
+ // - If a process is destroyed before it starts reporting back, the reported
+ // data will reflect how things are when the request ends.
+ //
+ // - If a process is destroyed after it starts reporting back but before it
+ // finishes, the reported data will contain a partial report for it.
+ //
+ // - If a process is destroyed after reporting back, but before all other
+ // child processes have reported back, it will be included in the reported
+ // data. So the reported data will reflect how things were when the
+ // request began.
+ //
+ // The inconsistencies between these cases are unfortunate but difficult to
+ // avoid. It's enough of an edge case to not be worth doing more.
+ //
+ void HandleChildReport(uint32_t aGeneration,
+ const mozilla::dom::MemoryReport& aChildReport);
+ void EndProcessReport(uint32_t aGeneration, bool aSuccess);
+
+ // Functions that (a) implement distinguished amounts, and (b) are outside of
+ // this module.
+ struct AmountFns
+ {
+ mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
+ mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
+ mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
+ mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
+
+ mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
+
+ mozilla::InfallibleAmountFn mStorageSQLite;
+
+ mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
+ mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
+
+ mozilla::InfallibleAmountFn mGhostWindows;
+
+ AmountFns()
+ {
+ mozilla::PodZero(this);
+ }
+ };
+ AmountFns mAmountFns;
+
+ // Convenience function to get RSS easily from other code. This is useful
+ // when debugging transient memory spikes with printf instrumentation.
+ static int64_t ResidentFast();
+
+ // Convenience function to get peak RSS easily from other code.
+ static int64_t ResidentPeak();
+
+ // Convenience function to get USS easily from other code. This is useful
+ // when debugging unshared memory pages for forked processes.
+ static int64_t ResidentUnique();
+
+ // Functions that measure per-tab memory consumption.
+ struct SizeOfTabFns
+ {
+ mozilla::JSSizeOfTabFn mJS;
+ mozilla::NonJSSizeOfTabFn mNonJS;
+
+ SizeOfTabFns()
+ {
+ mozilla::PodZero(this);
+ }
+ };
+ SizeOfTabFns mSizeOfTabFns;
+
+private:
+ MOZ_MUST_USE nsresult
+ RegisterReporterHelper(nsIMemoryReporter* aReporter,
+ bool aForce, bool aStrongRef, bool aIsAsync);
+
+ MOZ_MUST_USE nsresult StartGettingReports();
+ // No MOZ_MUST_USE here because ignoring the result is common and reasonable.
+ nsresult FinishReporting();
+
+ void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ bool aAnonymize);
+
+ static void TimeoutCallback(nsITimer* aTimer, void* aData);
+ // Note: this timeout needs to be long enough to allow for the
+ // possibility of DMD reports and/or running on a low-end phone.
+ static const uint32_t kTimeoutLengthMS = 50000;
+
+ mozilla::Mutex mMutex;
+ bool mIsRegistrationBlocked;
+
+ StrongReportersTable* mStrongReporters;
+ WeakReportersTable* mWeakReporters;
+
+ // These two are only used for testing purposes.
+ StrongReportersTable* mSavedStrongReporters;
+ WeakReportersTable* mSavedWeakReporters;
+
+ uint32_t mNextGeneration;
+
+ // Used to keep track of state of which processes are currently running and
+ // waiting to run memory reports. Holds references to parameters needed when
+ // requesting a memory report and finishing reporting.
+ struct PendingProcessesState
+ {
+ uint32_t mGeneration;
+ bool mAnonymize;
+ bool mMinimize;
+ nsCOMPtr<nsITimer> mTimer;
+ nsTArray<RefPtr<mozilla::dom::ContentParent>> mChildrenPending;
+ uint32_t mNumProcessesRunning;
+ uint32_t mNumProcessesCompleted;
+ uint32_t mConcurrencyLimit;
+ nsCOMPtr<nsIHandleReportCallback> mHandleReport;
+ nsCOMPtr<nsISupports> mHandleReportData;
+ nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
+ nsCOMPtr<nsISupports> mFinishReportingData;
+ nsString mDMDDumpIdent;
+
+ PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
+ uint32_t aConcurrencyLimit,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ const nsAString& aDMDDumpIdent);
+ };
+
+ // Used to keep track of the state of the asynchronously run memory
+ // reporters. The callback and file handle used when all memory reporters
+ // have finished are also stored here.
+ struct PendingReportersState
+ {
+ // Number of memory reporters currently running.
+ uint32_t mReportsPending;
+
+ // Callback for when all memory reporters have completed.
+ nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
+ nsCOMPtr<nsISupports> mFinishReportingData;
+
+ // File handle to write a DMD report to if requested.
+ FILE* mDMDFile;
+
+ PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ FILE* aDMDFile)
+ : mReportsPending(0)
+ , mFinishReporting(aFinishReporting)
+ , mFinishReportingData(aFinishReportingData)
+ , mDMDFile(aDMDFile)
+ {
+ }
+ };
+
+ // When this is non-null, a request is in flight. Note: We use manual
+ // new/delete for this because its lifetime doesn't match block scope or
+ // anything like that.
+ PendingProcessesState* mPendingProcessesState;
+
+ // This is reinitialized each time a call to GetReports is initiated.
+ PendingReportersState* mPendingReportersState;
+
+ PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
+ static MOZ_MUST_USE bool
+ StartChildReport(mozilla::dom::ContentParent* aChild,
+ const PendingProcessesState* aState);
+};
+
+#define NS_MEMORY_REPORTER_MANAGER_CID \
+{ 0xfb97e4f5, 0x32dd, 0x497a, \
+{ 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
+
+#endif // nsMemoryReporterManager_h__
diff --git a/xpcom/base/nsMessageLoop.cpp b/xpcom/base/nsMessageLoop.cpp
new file mode 100644
index 000000000..fabf6b9f5
--- /dev/null
+++ b/xpcom/base/nsMessageLoop.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMessageLoop.h"
+#include "mozilla/WeakPtr.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "nsIRunnable.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+namespace {
+
+/**
+ * This Task runs its nsIRunnable when Run() is called, or after
+ * aEnsureRunsAfterMS milliseconds have elapsed since the object was
+ * constructed.
+ *
+ * Note that the MessageLoop owns this object and will delete it after it calls
+ * Run(). Tread lightly.
+ */
+class MessageLoopIdleTask
+ : public Runnable
+ , public SupportsWeakPtr<MessageLoopIdleTask>
+{
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MessageLoopIdleTask)
+ MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS);
+ NS_IMETHOD Run() override;
+
+private:
+ nsresult Init(uint32_t aEnsureRunsAfterMS);
+
+ nsCOMPtr<nsIRunnable> mTask;
+ nsCOMPtr<nsITimer> mTimer;
+
+ virtual ~MessageLoopIdleTask() {}
+};
+
+/**
+ * This timer callback calls MessageLoopIdleTask::Run() when its timer fires.
+ * (The timer can't call back into MessageLoopIdleTask directly since that's
+ * not a refcounted object; it's owned by the MessageLoop.)
+ *
+ * We keep a weak reference to the MessageLoopIdleTask, although a raw pointer
+ * should in theory suffice: When the MessageLoopIdleTask runs (right before
+ * the MessageLoop deletes it), it cancels its timer. But the weak pointer
+ * saves us from worrying about an edge case somehow messing us up here.
+ */
+class MessageLoopTimerCallback
+ : public nsITimerCallback
+{
+public:
+ explicit MessageLoopTimerCallback(MessageLoopIdleTask* aTask);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+private:
+ WeakPtr<MessageLoopIdleTask> mTask;
+
+ virtual ~MessageLoopTimerCallback() {}
+};
+
+MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask,
+ uint32_t aEnsureRunsAfterMS)
+ : mTask(aTask)
+{
+ // Init() really shouldn't fail, but if it does, we schedule our runnable
+ // immediately, because it's more important to guarantee that we run the task
+ // eventually than it is to run the task when we're idle.
+ nsresult rv = Init(aEnsureRunsAfterMS);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Running idle task early because we couldn't initialize our timer.");
+ NS_DispatchToCurrentThread(mTask);
+
+ mTask = nullptr;
+ mTimer = nullptr;
+ }
+}
+
+nsresult
+MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS)
+{
+ mTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (NS_WARN_IF(!mTimer)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<MessageLoopTimerCallback> callback =
+ new MessageLoopTimerCallback(this);
+
+ return mTimer->InitWithCallback(callback, aEnsureRunsAfterMS,
+ nsITimer::TYPE_ONE_SHOT);
+}
+
+NS_IMETHODIMP
+MessageLoopIdleTask::Run()
+{
+ // Null out our pointers because if Run() was called by the timer, this
+ // object will be kept alive by the MessageLoop until the MessageLoop calls
+ // Run().
+
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ if (mTask) {
+ mTask->Run();
+ mTask = nullptr;
+ }
+
+ return NS_OK;
+}
+
+MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask)
+ : mTask(aTask)
+{
+}
+
+NS_IMETHODIMP
+MessageLoopTimerCallback::Notify(nsITimer* aTimer)
+{
+ // We don't expect to hit the case when the timer fires but mTask has been
+ // deleted, because mTask should cancel the timer before the mTask is
+ // deleted. But you never know...
+ NS_WARNING_ASSERTION(mTask, "This timer shouldn't have fired.");
+
+ if (mTask) {
+ mTask->Run();
+ }
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback)
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop)
+
+NS_IMETHODIMP
+nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS)
+{
+ // The message loop owns MessageLoopIdleTask and deletes it after calling
+ // Run(). Be careful...
+ RefPtr<MessageLoopIdleTask> idle =
+ new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS);
+ MessageLoop::current()->PostIdleTask(idle.forget());
+
+ return NS_OK;
+}
+
+nsresult
+nsMessageLoopConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ nsISupports* messageLoop = new nsMessageLoop();
+ return messageLoop->QueryInterface(aIID, aInstancePtr);
+}
diff --git a/xpcom/base/nsMessageLoop.h b/xpcom/base/nsMessageLoop.h
new file mode 100644
index 000000000..e7b146221
--- /dev/null
+++ b/xpcom/base/nsMessageLoop.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMessageLoop.h"
+
+/*
+ * nsMessageLoop implements nsIMessageLoop, which wraps Chromium's MessageLoop
+ * class and adds a bit of sugar.
+ */
+class nsMessageLoop : public nsIMessageLoop
+{
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMESSAGELOOP
+
+private:
+ virtual ~nsMessageLoop()
+ {
+ }
+};
+
+#define NS_MESSAGE_LOOP_CID \
+{0x67b3ac0c, 0xd806, 0x4d48, \
+{0x93, 0x9e, 0x6a, 0x81, 0x9e, 0x6c, 0x24, 0x8f}}
+
+extern nsresult
+nsMessageLoopConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr);
diff --git a/xpcom/base/nsObjCExceptions.h b/xpcom/base/nsObjCExceptions.h
new file mode 100644
index 000000000..e63c92af5
--- /dev/null
+++ b/xpcom/base/nsObjCExceptions.h
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Undo the damage that exception_defines.h does.
+#undef try
+#undef catch
+
+#ifndef nsObjCExceptions_h_
+#define nsObjCExceptions_h_
+
+#import <Foundation/Foundation.h>
+
+#ifdef DEBUG
+#import <ExceptionHandling/NSExceptionHandler.h>
+#endif
+
+#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
+#include "nsICrashReporter.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#endif
+
+#include <unistd.h>
+#include <signal.h>
+#include "nsError.h"
+
+// Undo the damage that exception_defines.h does.
+#undef try
+#undef catch
+
+/* NOTE: Macros that claim to abort no longer abort, see bug 486574.
+ * If you actually want to log and abort, call "nsObjCExceptionLogAbort"
+ * from an exception handler. At some point we will fix this by replacing
+ * all macros in the tree with appropriately-named macros.
+ */
+
+// See Mozilla bug 163260.
+// This file can only be included in an Objective-C context.
+
+__attribute__((unused))
+static void
+nsObjCExceptionLog(NSException* aException)
+{
+ NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]",
+ [aException name], [aException reason]);
+
+#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
+ // Attach exception info to the crash report.
+ nsCOMPtr<nsICrashReporter> crashReporter =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (crashReporter) {
+ crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException));
+ }
+#endif
+
+#ifdef DEBUG
+ @try {
+ // Try to get stack information out of the exception. 10.5 returns the stack
+ // info with the callStackReturnAddresses selector.
+ NSArray* stackTrace = nil;
+ if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) {
+ NSArray* addresses = (NSArray*)
+ [aException performSelector:@selector(callStackReturnAddresses)];
+ if ([addresses count]) {
+ stackTrace = addresses;
+ }
+ }
+
+ // 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the
+ // stack info out of the userInfo. It might not be there, sadly :(
+ if (!stackTrace) {
+ stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey];
+ }
+
+ if (stackTrace) {
+ // The command line should look like this:
+ // /usr/bin/atos -p <pid> -printHeader <stack frame addresses>
+ NSMutableArray* args =
+ [NSMutableArray arrayWithCapacity:[stackTrace count] + 3];
+
+ [args addObject:@"-p"];
+ int pid = [[NSProcessInfo processInfo] processIdentifier];
+ [args addObject:[NSString stringWithFormat:@"%d", pid]];
+
+ [args addObject:@"-printHeader"];
+
+ unsigned int stackCount = [stackTrace count];
+ unsigned int stackIndex = 0;
+ for (; stackIndex < stackCount; stackIndex++) {
+ unsigned long address =
+ [[stackTrace objectAtIndex:stackIndex] unsignedLongValue];
+ [args addObject:[NSString stringWithFormat:@"0x%lx", address]];
+ }
+
+ NSPipe* outPipe = [NSPipe pipe];
+
+ NSTask* task = [[NSTask alloc] init];
+ [task setLaunchPath:@"/usr/bin/atos"];
+ [task setArguments:args];
+ [task setStandardOutput:outPipe];
+ [task setStandardError:outPipe];
+
+ NSLog(@"Generating stack trace for Obj-C exception...");
+
+ // This will throw an exception if the atos tool cannot be found, and in
+ // that case we'll just hit our @catch block below.
+ [task launch];
+
+ [task waitUntilExit];
+ [task release];
+
+ NSData* outData =
+ [[outPipe fileHandleForReading] readDataToEndOfFile];
+ NSString* outString =
+ [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
+
+ NSLog(@"Stack trace:\n%@", outString);
+
+ [outString release];
+ } else {
+ NSLog(@"<No stack information available for Obj-C exception>");
+ }
+ }
+ @catch (NSException* exn) {
+ NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]",
+ [exn name], [exn reason]);
+ }
+#endif
+}
+
+__attribute__((unused))
+static void
+nsObjCExceptionAbort()
+{
+ // We need to raise a mach-o signal here, the Mozilla crash reporter on
+ // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly
+ // is tricky so we do it by just derefing a null pointer.
+ int* foo = nullptr;
+ *foo = 1;
+}
+
+__attribute__((unused))
+static void
+nsObjCExceptionLogAbort(NSException* aException)
+{
+ nsObjCExceptionLog(aException);
+ nsObjCExceptionAbort();
+}
+
+#define NS_OBJC_TRY(_e, _fail) \
+@try { _e; } \
+@catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ _fail; \
+}
+
+#define NS_OBJC_TRY_EXPR(_e, _fail) \
+({ \
+ typeof(_e) _tmp; \
+ @try { _tmp = (_e); } \
+ @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ _fail; \
+ } \
+ _tmp; \
+})
+
+#define NS_OBJC_TRY_EXPR_NULL(_e) \
+NS_OBJC_TRY_EXPR(_e, 0)
+
+#define NS_OBJC_TRY_IGNORE(_e) \
+NS_OBJC_TRY(_e, )
+
+// To reduce code size the abort versions do not reuse above macros. This allows
+// catch blocks to only contain one call.
+
+#define NS_OBJC_TRY_ABORT(_e) \
+@try { _e; } \
+@catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+}
+
+#define NS_OBJC_TRY_EXPR_ABORT(_e) \
+({ \
+ typeof(_e) _tmp; \
+ @try { _tmp = (_e); } \
+ @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ _tmp; \
+})
+
+// For wrapping blocks of Obj-C calls. Does not actually terminate.
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ }
+
+// Same as above ABORT_BLOCK but returns a value after the try/catch block to
+// suppress compiler warnings. This allows us to avoid having to refactor code
+// to get scoping right when wrapping an entire method.
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return nil;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return nullptr;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return NS_ERROR_FAILURE;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn);\
+ } \
+ return _rv;
+
+#endif // nsObjCExceptions_h_
diff --git a/xpcom/base/nsQueryObject.h b/xpcom/base/nsQueryObject.h
new file mode 100644
index 000000000..a52546324
--- /dev/null
+++ b/xpcom/base/nsQueryObject.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsQueryObject_h
+#define nsQueryObject_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+/*****************************************************************************/
+
+template<class T>
+class MOZ_STACK_CLASS nsQueryObject final : public nsCOMPtr_helper
+{
+public:
+ explicit nsQueryObject(T* aRawPtr)
+ : mRawPtr(aRawPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const
+ {
+ nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
+ : NS_ERROR_NULL_POINTER;
+ return status;
+ }
+private:
+ T* MOZ_NON_OWNING_REF mRawPtr;
+};
+
+template<class T>
+class MOZ_STACK_CLASS nsQueryObjectWithError final : public nsCOMPtr_helper
+{
+public:
+ nsQueryObjectWithError(T* aRawPtr, nsresult* aErrorPtr)
+ : mRawPtr(aRawPtr), mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const
+ {
+ nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
+ : NS_ERROR_NULL_POINTER;
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+ }
+private:
+ T* MOZ_NON_OWNING_REF mRawPtr;
+ nsresult* mErrorPtr;
+};
+
+/*****************************************************************************/
+
+/*****************************************************************************/
+
+template<class T>
+inline nsQueryObject<T>
+do_QueryObject(T* aRawPtr)
+{
+ return nsQueryObject<T>(aRawPtr);
+}
+
+template<class T>
+inline nsQueryObject<T>
+do_QueryObject(nsCOMPtr<T>& aRawPtr)
+{
+ return nsQueryObject<T>(aRawPtr);
+}
+
+template<class T>
+inline nsQueryObject<T>
+do_QueryObject(RefPtr<T>& aRawPtr)
+{
+ return nsQueryObject<T>(aRawPtr);
+}
+
+template<class T>
+inline nsQueryObjectWithError<T>
+do_QueryObject(T* aRawPtr, nsresult* aErrorPtr)
+{
+ return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
+}
+
+template<class T>
+inline nsQueryObjectWithError<T>
+do_QueryObject(nsCOMPtr<T>& aRawPtr, nsresult* aErrorPtr)
+{
+ return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
+}
+
+template<class T>
+inline nsQueryObjectWithError<T>
+do_QueryObject(RefPtr<T>& aRawPtr, nsresult* aErrorPtr)
+{
+ return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
+}
+
+/*****************************************************************************/
+
+#endif // !defined(nsQueryObject_h)
diff --git a/xpcom/base/nsSecurityConsoleMessage.cpp b/xpcom/base/nsSecurityConsoleMessage.cpp
new file mode 100644
index 000000000..3e6409cf3
--- /dev/null
+++ b/xpcom/base/nsSecurityConsoleMessage.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSecurityConsoleMessage.h"
+
+NS_IMPL_ISUPPORTS(nsSecurityConsoleMessage, nsISecurityConsoleMessage)
+
+nsSecurityConsoleMessage::nsSecurityConsoleMessage()
+{
+}
+
+nsSecurityConsoleMessage::~nsSecurityConsoleMessage()
+{
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::GetTag(nsAString& aTag)
+{
+ aTag = mTag;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::SetTag(const nsAString& aTag)
+{
+ mTag = aTag;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::GetCategory(nsAString& aCategory)
+{
+ aCategory = mCategory;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::SetCategory(const nsAString& aCategory)
+{
+ mCategory = aCategory;
+ return NS_OK;
+}
diff --git a/xpcom/base/nsSecurityConsoleMessage.h b/xpcom/base/nsSecurityConsoleMessage.h
new file mode 100644
index 000000000..c93c13613
--- /dev/null
+++ b/xpcom/base/nsSecurityConsoleMessage.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSecurityConsoleMessage_h__
+#define nsSecurityConsoleMessage_h__
+#include "nsISecurityConsoleMessage.h"
+#include "nsString.h"
+
+class nsSecurityConsoleMessage final : public nsISecurityConsoleMessage
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISECURITYCONSOLEMESSAGE
+
+ nsSecurityConsoleMessage();
+
+private:
+ ~nsSecurityConsoleMessage();
+
+protected:
+ nsString mTag;
+ nsString mCategory;
+};
+
+#define NS_SECURITY_CONSOLE_MESSAGE_CID \
+ {0x43ebf210, 0x8a7b, 0x4ddb, {0xa8, 0x3d, 0xb8, 0x7c, 0x51, 0xa0, 0x58, 0xdb}}
+#endif //nsSecurityConsoleMessage_h__
diff --git a/xpcom/base/nsSetDllDirectory.h b/xpcom/base/nsSetDllDirectory.h
new file mode 100644
index 000000000..0920d5936
--- /dev/null
+++ b/xpcom/base/nsSetDllDirectory.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSetDllDirectory_h
+#define nsSetDllDirectory_h
+
+#ifndef XP_WIN
+#error This file only makes sense on Windows.
+#endif
+
+#include <windows.h>
+#include <nscore.h>
+#include <stdlib.h>
+
+namespace mozilla {
+
+static void
+SanitizeEnvironmentVariables()
+{
+ DWORD bufferSize = GetEnvironmentVariableW(L"PATH", nullptr, 0);
+ if (bufferSize) {
+ wchar_t* originalPath = new wchar_t[bufferSize];
+ if (bufferSize - 1 == GetEnvironmentVariableW(L"PATH", originalPath,
+ bufferSize)) {
+ bufferSize = ExpandEnvironmentStringsW(originalPath, nullptr, 0);
+ if (bufferSize) {
+ wchar_t* newPath = new wchar_t[bufferSize];
+ if (ExpandEnvironmentStringsW(originalPath,
+ newPath,
+ bufferSize)) {
+ SetEnvironmentVariableW(L"PATH", newPath);
+ }
+ delete[] newPath;
+ }
+ }
+ delete[] originalPath;
+ }
+}
+
+}
+
+#endif
diff --git a/xpcom/base/nsStatusReporterManager.cpp b/xpcom/base/nsStatusReporterManager.cpp
new file mode 100644
index 000000000..491e7d458
--- /dev/null
+++ b/xpcom/base/nsStatusReporterManager.cpp
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsStatusReporterManager.h"
+#include "nsCOMPtr.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsArrayEnumerator.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIFile.h"
+#include "nsDumpUtils.h"
+#include "nsIFileStreams.h"
+#include "nsPrintfCString.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#ifdef XP_UNIX
+#define DO_STATUS_REPORT 1
+#endif
+
+#ifdef DO_STATUS_REPORT // {
+namespace {
+
+class DumpStatusInfoToTempDirRunnable : public mozilla::Runnable
+{
+public:
+ DumpStatusInfoToTempDirRunnable()
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ mgr->DumpReports();
+ return NS_OK;
+ }
+};
+
+void
+doStatusReport(const nsCString& aInputStr)
+{
+ LOG("FifoWatcher(%s) dispatching status report runnable.", aInputStr.get());
+ RefPtr<DumpStatusInfoToTempDirRunnable> runnable =
+ new DumpStatusInfoToTempDirRunnable();
+ NS_DispatchToMainThread(runnable);
+}
+
+} //anonymous namespace
+#endif // DO_STATUS_REPORT }
+
+static bool gStatusReportProgress = 0;
+static int gNumReporters = 0;
+
+nsresult
+getStatus(nsACString& aDesc)
+{
+ if (!gStatusReportProgress) {
+ aDesc.AssignLiteral("Init");
+ } else {
+ aDesc.AssignLiteral("Running: There are ");
+ aDesc.AppendInt(gNumReporters);
+ aDesc.AppendLiteral(" reporters");
+ }
+ return NS_OK;
+}
+
+NS_STATUS_REPORTER_IMPLEMENT(StatusReporter, "StatusReporter State", getStatus)
+
+#define DUMP(o, s) \
+ do { \
+ const char* s2 = (s); \
+ uint32_t dummy; \
+ nsresult rvDump = (o)->Write((s2), strlen(s2), &dummy); \
+ if (NS_WARN_IF(NS_FAILED(rvDump))) \
+ return rvDump; \
+ } while (0)
+
+static nsresult
+DumpReport(nsIFileOutputStream* aOStream, const nsCString& aProcess,
+ const nsCString& aName, const nsCString& aDescription)
+{
+ if (aProcess.IsEmpty()) {
+ int pid = getpid();
+ nsPrintfCString pidStr("PID %u", pid);
+ DUMP(aOStream, "\n {\n \"Process\": \"");
+ DUMP(aOStream, pidStr.get());
+ } else {
+ DUMP(aOStream, "\n { \"Unknown Process\": \"");
+ }
+
+ DUMP(aOStream, "\",\n \"Reporter name\": \"");
+ DUMP(aOStream, aName.get());
+
+ DUMP(aOStream, "\",\n \"Status Description\": [\"");
+ nsCString desc = aDescription;
+ desc.ReplaceSubstring("|", "\",\"");
+ DUMP(aOStream, desc.get());
+
+ DUMP(aOStream, "\"]\n }");
+
+ return NS_OK;
+}
+
+/**
+ ** nsStatusReporterManager implementation
+ **/
+
+NS_IMPL_ISUPPORTS(nsStatusReporterManager, nsIStatusReporterManager)
+
+nsStatusReporterManager::nsStatusReporterManager()
+{
+}
+
+nsStatusReporterManager::~nsStatusReporterManager()
+{
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::Init()
+{
+ RegisterReporter(new NS_STATUS_REPORTER_NAME(StatusReporter));
+ gStatusReportProgress = 1;
+
+#ifdef DO_STATUS_REPORT
+ if (FifoWatcher::MaybeCreate()) {
+ FifoWatcher* fw = FifoWatcher::GetSingleton();
+ fw->RegisterCallback(NS_LITERAL_CSTRING("status report"), doStatusReport);
+ }
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::DumpReports()
+{
+ static unsigned number = 1;
+ nsresult rv;
+
+ nsCString filename("status-reports-");
+ filename.AppendInt(getpid());
+ filename.Append('-');
+ filename.AppendInt(number++);
+ filename.AppendLiteral(".json");
+
+ // Open a file in NS_OS_TEMP_DIR for writing.
+ // The file is initialized as "incomplete-status-reports-pid-number.json" in the
+ // begining, it will be rename as "status-reports-pid-number.json" in the end.
+ nsCOMPtr<nsIFile> tmpFile;
+ rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
+ filename,
+ getter_AddRefs(tmpFile),
+ NS_LITERAL_CSTRING("status-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIFileOutputStream> ostream =
+ do_CreateInstance("@mozilla.org/network/file-output-stream;1");
+ rv = ostream->Init(tmpFile, -1, -1, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ //Write the reports to the file
+
+ DUMP(ostream, "{\n\"subject\":\"about:service reports\",\n");
+ DUMP(ostream, "\"reporters\": [ ");
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ bool more, first = true;
+ EnumerateReporters(getter_AddRefs(e));
+ while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> supports;
+ e->GetNext(getter_AddRefs(supports));
+ nsCOMPtr<nsIStatusReporter> r = do_QueryInterface(supports);
+
+ nsCString process;
+ rv = r->GetProcess(process);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString name;
+ rv = r->GetName(name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString description;
+ rv = r->GetDescription(description);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ DUMP(ostream, ",");
+ }
+
+ rv = DumpReport(ostream, process, name, description);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ DUMP(ostream, "\n]\n}\n");
+
+ rv = ostream->Close();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Rename the status reports file
+ nsCOMPtr<nsIFile> srFinalFile;
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(srFinalFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifdef ANDROID
+ rv = srFinalFile->AppendNative(NS_LITERAL_CSTRING("status-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+ rv = srFinalFile->AppendNative(filename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = srFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoString srActualFinalFilename;
+ rv = srFinalFile->GetLeafName(srActualFinalFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = tmpFile->MoveTo(/* directory */ nullptr, srActualFinalFilename);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
+{
+ return NS_NewArrayEnumerator(aResult, mReporters);
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::RegisterReporter(nsIStatusReporter* aReporter)
+{
+ if (mReporters.IndexOf(aReporter) != -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mReporters.AppendObject(aReporter);
+ gNumReporters++;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::UnregisterReporter(nsIStatusReporter* aReporter)
+{
+ if (!mReporters.RemoveObject(aReporter)) {
+ return NS_ERROR_FAILURE;
+ }
+ gNumReporters--;
+ return NS_OK;
+}
+
+nsresult
+NS_RegisterStatusReporter(nsIStatusReporter* aReporter)
+{
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ if (!mgr) {
+ return NS_ERROR_FAILURE;
+ }
+ return mgr->RegisterReporter(aReporter);
+}
+
+nsresult
+NS_UnregisterStatusReporter(nsIStatusReporter* aReporter)
+{
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ if (!mgr) {
+ return NS_ERROR_FAILURE;
+ }
+ return mgr->UnregisterReporter(aReporter);
+}
+
+nsresult
+NS_DumpStatusReporter()
+{
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ if (!mgr) {
+ return NS_ERROR_FAILURE;
+ }
+ return mgr->DumpReports();
+}
diff --git a/xpcom/base/nsStatusReporterManager.h b/xpcom/base/nsStatusReporterManager.h
new file mode 100644
index 000000000..84427cfcf
--- /dev/null
+++ b/xpcom/base/nsStatusReporterManager.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsIStatusReporter.h"
+#include "nsCOMArray.h"
+#include "nsString.h"
+
+class nsStatusReporter final : public nsIStatusReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTATUSREPORTER
+
+ nsStatusReporter(nsACString& aProcess, nsACString& aDesc);
+
+private:
+ nsCString sProcess;
+ nsCString sName;
+ nsCString sDesc;
+
+ virtual ~nsStatusReporter();
+};
+
+
+class nsStatusReporterManager : public nsIStatusReporterManager
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTATUSREPORTERMANAGER
+
+ nsStatusReporterManager();
+
+private:
+ nsCOMArray<nsIStatusReporter> mReporters;
+
+ virtual ~nsStatusReporterManager();
+};
diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp
new file mode 100644
index 000000000..f6d9fd5ad
--- /dev/null
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -0,0 +1,985 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsSystemInfo.h"
+#include "prsystem.h"
+#include "prio.h"
+#include "prprf.h"
+#include "mozilla/SSE.h"
+#include "mozilla/arm.h"
+#include "mozilla/Sprintf.h"
+
+#ifdef XP_WIN
+#include <time.h>
+#include <windows.h>
+#include <winioctl.h>
+#include "base/scoped_handle_win.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIObserverService.h"
+#include "nsWindowsHelpers.h"
+#endif
+
+#ifdef XP_MACOSX
+#include "MacHelpers.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#include <gtk/gtk.h>
+#include <dlfcn.h>
+#endif
+
+#if defined (XP_LINUX) && !defined (ANDROID)
+#include <unistd.h>
+#include <fstream>
+#include "mozilla/Tokenizer.h"
+#include "nsCharSeparatedTokenizer.h"
+
+#include <map>
+#include <string>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "mozilla/dom/ContentChild.h"
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include <sys/system_properties.h>
+#include "mozilla/Preferences.h"
+#include "nsPrintfCString.h"
+#endif
+
+#ifdef ANDROID
+extern "C" {
+NS_EXPORT int android_sdk_version;
+}
+#endif
+
+#ifdef XP_MACOSX
+#include <sys/sysctl.h>
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+#include "mozilla/SandboxInfo.h"
+#endif
+
+// Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init.
+// Only set to nonzero (potentially) if XP_UNIX. On such systems, the
+// system call to discover the appropriate value is not thread-safe,
+// so we must call it before going multithreaded, but nsSystemInfo::Init
+// only happens well after that point.
+uint32_t nsSystemInfo::gUserUmask = 0;
+
+#if defined (XP_LINUX) && !defined (ANDROID)
+static void
+SimpleParseKeyValuePairs(const std::string& aFilename,
+ std::map<nsCString, nsCString>& aKeyValuePairs)
+{
+ std::ifstream input(aFilename.c_str());
+ for (std::string line; std::getline(input, line); ) {
+ nsAutoCString key, value;
+
+ nsCCharSeparatedTokenizer tokens(nsDependentCString(line.c_str()), ':');
+ if (tokens.hasMoreTokens()) {
+ key = tokens.nextToken();
+ if (tokens.hasMoreTokens()) {
+ value = tokens.nextToken();
+ }
+ // We want the value even if there was just one token, to cover the
+ // case where we had the key, and the value was blank (seems to be
+ // a valid scenario some files.)
+ aKeyValuePairs[key] = value;
+ }
+ }
+}
+#endif
+
+#if defined(XP_WIN)
+namespace {
+nsresult
+GetHDDInfo(const char* aSpecialDirName, nsAutoCString& aModel,
+ nsAutoCString& aRevision)
+{
+ aModel.Truncate();
+ aRevision.Truncate();
+
+ nsCOMPtr<nsIFile> profDir;
+ nsresult rv = NS_GetSpecialDirectory(aSpecialDirName,
+ getter_AddRefs(profDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString profDirPath;
+ rv = profDir->GetPath(profDirPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ wchar_t volumeMountPoint[MAX_PATH] = {L'\\', L'\\', L'.', L'\\'};
+ const size_t PREFIX_LEN = 4;
+ if (!::GetVolumePathNameW(profDirPath.get(), volumeMountPoint + PREFIX_LEN,
+ mozilla::ArrayLength(volumeMountPoint) -
+ PREFIX_LEN)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ size_t volumeMountPointLen = wcslen(volumeMountPoint);
+ // Since we would like to open a drive and not a directory, we need to
+ // remove any trailing backslash. A drive handle is valid for
+ // DeviceIoControl calls, a directory handle is not.
+ if (volumeMountPoint[volumeMountPointLen - 1] == L'\\') {
+ volumeMountPoint[volumeMountPointLen - 1] = L'\0';
+ }
+ ScopedHandle handle(::CreateFileW(volumeMountPoint, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, 0, nullptr));
+ if (!handle.IsValid()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ STORAGE_PROPERTY_QUERY queryParameters = {
+ StorageDeviceProperty, PropertyStandardQuery
+ };
+ STORAGE_DEVICE_DESCRIPTOR outputHeader = {sizeof(STORAGE_DEVICE_DESCRIPTOR)};
+ DWORD bytesRead = 0;
+ if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY,
+ &queryParameters, sizeof(queryParameters),
+ &outputHeader, sizeof(outputHeader), &bytesRead,
+ nullptr)) {
+ return NS_ERROR_FAILURE;
+ }
+ PSTORAGE_DEVICE_DESCRIPTOR deviceOutput =
+ (PSTORAGE_DEVICE_DESCRIPTOR)malloc(outputHeader.Size);
+ if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY,
+ &queryParameters, sizeof(queryParameters),
+ deviceOutput, outputHeader.Size, &bytesRead,
+ nullptr)) {
+ free(deviceOutput);
+ return NS_ERROR_FAILURE;
+ }
+ // Some HDDs are including product ID info in the vendor field. Since PNP
+ // IDs include vendor info and product ID concatenated together, we'll do
+ // that here and interpret the result as a unique ID for the HDD model.
+ if (deviceOutput->VendorIdOffset) {
+ aModel = reinterpret_cast<char*>(deviceOutput) +
+ deviceOutput->VendorIdOffset;
+ }
+ if (deviceOutput->ProductIdOffset) {
+ aModel += reinterpret_cast<char*>(deviceOutput) +
+ deviceOutput->ProductIdOffset;
+ }
+ aModel.CompressWhitespace();
+ if (deviceOutput->ProductRevisionOffset) {
+ aRevision = reinterpret_cast<char*>(deviceOutput) +
+ deviceOutput->ProductRevisionOffset;
+ aRevision.CompressWhitespace();
+ }
+ free(deviceOutput);
+ return NS_OK;
+}
+
+nsresult GetInstallYear(uint32_t& aYear)
+{
+ HKEY hKey;
+ LONG status = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING(
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
+ ).get(),
+ 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
+
+ if (status != ERROR_SUCCESS) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoRegKey key(hKey);
+
+ DWORD type = 0;
+ time_t raw_time = 0;
+ DWORD time_size = sizeof(time_t);
+
+ status = RegQueryValueExW(hKey, L"InstallDate",
+ nullptr, &type, (LPBYTE)&raw_time, &time_size);
+
+ if (status != ERROR_SUCCESS) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (type != REG_DWORD) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ tm time;
+ if (localtime_s(&time, &raw_time) != 0) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aYear = 1900UL + time.tm_year;
+ return NS_OK;
+}
+
+nsresult GetCountryCode(nsAString& aCountryCode)
+{
+ GEOID geoid = GetUserGeoID(GEOCLASS_NATION);
+ if (geoid == GEOID_NOT_AVAILABLE) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ // Get required length
+ int numChars = GetGeoInfoW(geoid, GEO_ISO2, nullptr, 0, 0);
+ if (!numChars) {
+ return NS_ERROR_FAILURE;
+ }
+ // Now get the string for real
+ aCountryCode.SetLength(numChars);
+ numChars = GetGeoInfoW(geoid, GEO_ISO2, wwc(aCountryCode.BeginWriting()),
+ aCountryCode.Length(), 0);
+ if (!numChars) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // numChars includes null terminator
+ aCountryCode.Truncate(numChars - 1);
+ return NS_OK;
+}
+
+} // namespace
+#endif // defined(XP_WIN)
+
+using namespace mozilla;
+
+nsSystemInfo::nsSystemInfo()
+{
+}
+
+nsSystemInfo::~nsSystemInfo()
+{
+}
+
+// CPU-specific information.
+static const struct PropItems
+{
+ const char* name;
+ bool (*propfun)(void);
+} cpuPropItems[] = {
+ // x86-specific bits.
+ { "hasMMX", mozilla::supports_mmx },
+ { "hasSSE", mozilla::supports_sse },
+ { "hasSSE2", mozilla::supports_sse2 },
+ { "hasSSE3", mozilla::supports_sse3 },
+ { "hasSSSE3", mozilla::supports_ssse3 },
+ { "hasSSE4A", mozilla::supports_sse4a },
+ { "hasSSE4_1", mozilla::supports_sse4_1 },
+ { "hasSSE4_2", mozilla::supports_sse4_2 },
+ { "hasAVX", mozilla::supports_avx },
+ { "hasAVX2", mozilla::supports_avx2 },
+ // ARM-specific bits.
+ { "hasEDSP", mozilla::supports_edsp },
+ { "hasARMv6", mozilla::supports_armv6 },
+ { "hasARMv7", mozilla::supports_armv7 },
+ { "hasNEON", mozilla::supports_neon }
+};
+
+#ifdef XP_WIN
+// Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
+// so keeping the _ instead of switching to camel case for now.
+typedef BOOL (WINAPI *LPFN_GLPI)(
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
+ PDWORD);
+static void
+GetProcessorInformation(int* physical_cpus, int* cache_size_L2, int* cache_size_L3)
+{
+ MOZ_ASSERT(physical_cpus && cache_size_L2 && cache_size_L3);
+
+ *physical_cpus = 0;
+ *cache_size_L2 = 0; // This will be in kbytes
+ *cache_size_L3 = 0; // This will be in kbytes
+
+ // GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond.
+ LPFN_GLPI glpi = reinterpret_cast<LPFN_GLPI>(GetProcAddress(
+ GetModuleHandle(L"kernel32"),
+ "GetLogicalProcessorInformation"));
+ if (nullptr == glpi) {
+ return;
+ }
+ // Determine buffer size, allocate and get processor information.
+ // Size can change between calls (unlikely), so a loop is done.
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer[32];
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION* infos = &info_buffer[0];
+ DWORD return_length = sizeof(info_buffer);
+ while (!glpi(infos, &return_length)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && infos == &info_buffer[0]) {
+ infos = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)];
+ } else {
+ return;
+ }
+ }
+
+ for (size_t i = 0;
+ i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
+ if (infos[i].Relationship == RelationProcessorCore) {
+ ++*physical_cpus;
+ } else if (infos[i].Relationship == RelationCache) {
+ // Only care about L2 and L3 cache
+ switch (infos[i].Cache.Level) {
+ case 2:
+ *cache_size_L2 = static_cast<int>(infos[i].Cache.Size/1024);
+ break;
+ case 3:
+ *cache_size_L3 = static_cast<int>(infos[i].Cache.Size/1024);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (infos != &info_buffer[0]) {
+ delete [] infos;
+ }
+ return;
+}
+#endif
+
+nsresult
+nsSystemInfo::Init()
+{
+ nsresult rv;
+
+ static const struct
+ {
+ PRSysInfo cmd;
+ const char* name;
+ } items[] = {
+ { PR_SI_SYSNAME, "name" },
+ { PR_SI_HOSTNAME, "host" },
+ { PR_SI_ARCHITECTURE, "arch" },
+ { PR_SI_RELEASE, "version" }
+ };
+
+ for (uint32_t i = 0; i < (sizeof(items) / sizeof(items[0])); i++) {
+ char buf[SYS_INFO_BUFFER_LENGTH];
+ if (PR_GetSystemInfo(items[i].cmd, buf, sizeof(buf)) == PR_SUCCESS) {
+ rv = SetPropertyAsACString(NS_ConvertASCIItoUTF16(items[i].name),
+ nsDependentCString(buf));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+ NS_WARNING("PR_GetSystemInfo failed");
+ }
+ }
+
+ rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"),
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Additional informations not available through PR_GetSystemInfo.
+ SetInt32Property(NS_LITERAL_STRING("pagesize"), PR_GetPageSize());
+ SetInt32Property(NS_LITERAL_STRING("pageshift"), PR_GetPageShift());
+ SetInt32Property(NS_LITERAL_STRING("memmapalign"), PR_GetMemMapAlignment());
+ SetUint64Property(NS_LITERAL_STRING("memsize"), PR_GetPhysicalMemorySize());
+ SetUint32Property(NS_LITERAL_STRING("umask"), nsSystemInfo::gUserUmask);
+
+ uint64_t virtualMem = 0;
+ nsAutoCString cpuVendor;
+ int cpuSpeed = -1;
+ int cpuFamily = -1;
+ int cpuModel = -1;
+ int cpuStepping = -1;
+ int logicalCPUs = -1;
+ int physicalCPUs = -1;
+ int cacheSizeL2 = -1;
+ int cacheSizeL3 = -1;
+
+#if defined (XP_WIN)
+ // Virtual memory:
+ MEMORYSTATUSEX memStat;
+ memStat.dwLength = sizeof(memStat);
+ if (GlobalMemoryStatusEx(&memStat)) {
+ virtualMem = memStat.ullTotalVirtual;
+ }
+
+ // CPU speed
+ HKEY key;
+ static const WCHAR keyName[] =
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key)
+ == ERROR_SUCCESS) {
+ DWORD data, len, vtype;
+ len = sizeof(data);
+
+ if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
+ &len) == ERROR_SUCCESS) {
+ cpuSpeed = static_cast<int>(data);
+ }
+
+ // Limit to 64 double byte characters, should be plenty, but create
+ // a buffer one larger as the result may not be null terminated. If
+ // it is more than 64, we will not get the value.
+ wchar_t cpuVendorStr[64+1];
+ len = sizeof(cpuVendorStr)-2;
+ if (RegQueryValueExW(key, L"VendorIdentifier",
+ 0, &vtype,
+ reinterpret_cast<LPBYTE>(cpuVendorStr),
+ &len) == ERROR_SUCCESS &&
+ vtype == REG_SZ && len % 2 == 0 && len > 1) {
+ cpuVendorStr[len/2] = 0; // In case it isn't null terminated
+ CopyUTF16toUTF8(nsDependentString(cpuVendorStr), cpuVendor);
+ }
+
+ RegCloseKey(key);
+ }
+
+ // Other CPU attributes:
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ logicalCPUs = si.dwNumberOfProcessors;
+ GetProcessorInformation(&physicalCPUs, &cacheSizeL2, &cacheSizeL3);
+ if (physicalCPUs <= 0) {
+ physicalCPUs = logicalCPUs;
+ }
+ cpuFamily = si.wProcessorLevel;
+ cpuModel = si.wProcessorRevision >> 8;
+ cpuStepping = si.wProcessorRevision & 0xFF;
+#elif defined (XP_MACOSX)
+ // CPU speed
+ uint64_t sysctlValue64 = 0;
+ uint32_t sysctlValue32 = 0;
+ size_t len = 0;
+ len = sizeof(sysctlValue64);
+ if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64, &len, NULL, 0)) {
+ cpuSpeed = static_cast<int>(sysctlValue64/1000000);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
+ physicalCPUs = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
+ logicalCPUs = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue64);
+ if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) {
+ cacheSizeL2 = static_cast<int>(sysctlValue64/1024);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+ len = sizeof(sysctlValue64);
+ if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) {
+ cacheSizeL3 = static_cast<int>(sysctlValue64/1024);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+ if (!sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0)) {
+ char* cpuVendorStr = new char[len];
+ if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr, &len, NULL, 0)) {
+ cpuVendor = cpuVendorStr;
+ }
+ delete [] cpuVendorStr;
+ }
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("machdep.cpu.family", &sysctlValue32, &len, NULL, 0)) {
+ cpuFamily = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) {
+ cpuModel = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
+ cpuStepping = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+#elif defined (XP_LINUX) && !defined (ANDROID)
+ // Get vendor, family, model, stepping, physical cores, L3 cache size
+ // from /proc/cpuinfo file
+ {
+ std::map<nsCString, nsCString> keyValuePairs;
+ SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
+
+ // cpuVendor from "vendor_id"
+ cpuVendor.Assign(keyValuePairs[NS_LITERAL_CSTRING("vendor_id")]);
+
+ {
+ // cpuFamily from "cpu family"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu family")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuFamily = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // cpuModel from "model"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("model")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuModel = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // cpuStepping from "stepping"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("stepping")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuStepping = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // physicalCPUs from "cpu cores"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu cores")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ physicalCPUs = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // cacheSizeL3 from "cache size"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cache size")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cacheSizeL3 = static_cast<int>(t.AsInteger());
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_WORD &&
+ t.AsString() != NS_LITERAL_CSTRING("KB")) {
+ // If we get here, there was some text after the cache size value
+ // and that text was not KB. For now, just don't report the
+ // L3 cache.
+ cacheSizeL3 = -1;
+ }
+ }
+ }
+ }
+
+ {
+ // Get cpuSpeed from another file.
+ std::ifstream input("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
+ std::string line;
+ if (getline(input, line)) {
+ Tokenizer::Token t;
+ Tokenizer p(line.c_str());
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuSpeed = static_cast<int>(t.AsInteger()/1000);
+ }
+ }
+ }
+
+ {
+ // Get cacheSizeL2 from yet another file
+ std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
+ std::string line;
+ if (getline(input, line)) {
+ Tokenizer::Token t;
+ Tokenizer p(line.c_str(), nullptr, "K");
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cacheSizeL2 = static_cast<int>(t.AsInteger());
+ }
+ }
+ }
+
+ SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
+#else
+ SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
+#endif
+
+ if (virtualMem) SetUint64Property(NS_LITERAL_STRING("virtualmemsize"), virtualMem);
+ if (cpuSpeed >= 0) SetInt32Property(NS_LITERAL_STRING("cpuspeed"), cpuSpeed);
+ if (!cpuVendor.IsEmpty()) SetPropertyAsACString(NS_LITERAL_STRING("cpuvendor"), cpuVendor);
+ if (cpuFamily >= 0) SetInt32Property(NS_LITERAL_STRING("cpufamily"), cpuFamily);
+ if (cpuModel >= 0) SetInt32Property(NS_LITERAL_STRING("cpumodel"), cpuModel);
+ if (cpuStepping >= 0) SetInt32Property(NS_LITERAL_STRING("cpustepping"), cpuStepping);
+
+ if (logicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucount"), logicalCPUs);
+ if (physicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucores"), physicalCPUs);
+
+ if (cacheSizeL2 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel2"), cacheSizeL2);
+ if (cacheSizeL3 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel3"), cacheSizeL3);
+
+ for (uint32_t i = 0; i < ArrayLength(cpuPropItems); i++) {
+ rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name),
+ cpuPropItems[i].propfun());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+#ifdef XP_WIN
+ BOOL isWow64;
+ BOOL gotWow64Value = IsWow64Process(GetCurrentProcess(), &isWow64);
+ NS_WARNING_ASSERTION(gotWow64Value, "IsWow64Process failed");
+ if (gotWow64Value) {
+ rv = SetPropertyAsBool(NS_LITERAL_STRING("isWow64"), !!isWow64);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ if (NS_FAILED(GetProfileHDDInfo())) {
+ // We might have been called before profile-do-change. We'll observe that
+ // event so that we can fill this in later.
+ nsCOMPtr<nsIObserverService> obsService =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = obsService->AddObserver(this, "profile-do-change", false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ nsAutoCString hddModel, hddRevision;
+ if (NS_SUCCEEDED(GetHDDInfo(NS_GRE_DIR, hddModel, hddRevision))) {
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("binHDDModel"), hddModel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("binHDDRevision"),
+ hddRevision);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (NS_SUCCEEDED(GetHDDInfo(NS_WIN_WINDOWS_DIR, hddModel, hddRevision))) {
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("winHDDModel"), hddModel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("winHDDRevision"),
+ hddRevision);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoString countryCode;
+ if (NS_SUCCEEDED(GetCountryCode(countryCode))) {
+ rv = SetPropertyAsAString(NS_LITERAL_STRING("countryCode"), countryCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint32_t installYear = 0;
+ if (NS_SUCCEEDED(GetInstallYear(installYear))) {
+ rv = SetPropertyAsUint32(NS_LITERAL_STRING("installYear"), installYear);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+#endif
+
+#if defined(XP_MACOSX)
+ nsAutoString countryCode;
+ if (NS_SUCCEEDED(GetSelectedCityInfo(countryCode))) {
+ rv = SetPropertyAsAString(NS_LITERAL_STRING("countryCode"), countryCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif
+
+#if defined(MOZ_WIDGET_GTK)
+ // This must be done here because NSPR can only separate OS's when compiled, not libraries.
+ // 64 bytes is going to be well enough for "GTK " followed by 3 integers
+ // separated with dots.
+ char gtkver[64];
+ ssize_t gtkver_len = 0;
+
+#if MOZ_WIDGET_GTK == 2
+ extern int gtk_read_end_of_the_pipe;
+
+ if (gtk_read_end_of_the_pipe != -1) {
+ do {
+ gtkver_len = read(gtk_read_end_of_the_pipe, &gtkver, sizeof(gtkver));
+ } while (gtkver_len < 0 && errno == EINTR);
+ close(gtk_read_end_of_the_pipe);
+ }
+#endif
+
+ if (gtkver_len <= 0) {
+ gtkver_len = SprintfLiteral(gtkver, "GTK %u.%u.%u", gtk_major_version,
+ gtk_minor_version, gtk_micro_version);
+ }
+
+ nsAutoCString secondaryLibrary;
+ if (gtkver_len > 0 && gtkver_len < int(sizeof(gtkver))) {
+ secondaryLibrary.Append(nsDependentCSubstring(gtkver, gtkver_len));
+ }
+
+ void* libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
+ const char* libpulseVersion = "not-available";
+ if (libpulse) {
+ auto pa_get_library_version = reinterpret_cast<const char* (*)()>
+ (dlsym(libpulse, "pa_get_library_version"));
+
+ if (pa_get_library_version) {
+ libpulseVersion = pa_get_library_version();
+ }
+ }
+
+ secondaryLibrary.AppendPrintf(",libpulse %s", libpulseVersion);
+
+ if (libpulse) {
+ dlclose(libpulse);
+ }
+
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("secondaryLibrary"),
+ secondaryLibrary);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+ AndroidSystemInfo info;
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* child = dom::ContentChild::GetSingleton();
+ if (child) {
+ child->SendGetAndroidSystemInfo(&info);
+ SetupAndroidInfo(info);
+ }
+ } else {
+ GetAndroidSystemInfo(&info);
+ SetupAndroidInfo(info);
+ }
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+ char sdk[PROP_VALUE_MAX];
+ if (__system_property_get("ro.build.version.sdk", sdk)) {
+ android_sdk_version = atoi(sdk);
+ SetPropertyAsInt32(NS_LITERAL_STRING("sdk_version"), android_sdk_version);
+
+ SetPropertyAsACString(NS_LITERAL_STRING("secondaryLibrary"),
+ nsPrintfCString("SDK %u", android_sdk_version));
+ }
+
+ char characteristics[PROP_VALUE_MAX];
+ if (__system_property_get("ro.build.characteristics", characteristics)) {
+ if (!strcmp(characteristics, "tablet")) {
+ SetPropertyAsBool(NS_LITERAL_STRING("tablet"), true);
+ } else if (!strcmp(characteristics, "tv")) {
+ SetPropertyAsBool(NS_LITERAL_STRING("tv"), true);
+ }
+ }
+
+ nsAutoString str;
+ rv = GetPropertyAsAString(NS_LITERAL_STRING("version"), str);
+ if (NS_SUCCEEDED(rv)) {
+ SetPropertyAsAString(NS_LITERAL_STRING("kernel_version"), str);
+ }
+
+ const nsAdoptingString& b2g_os_name =
+ mozilla::Preferences::GetString("b2g.osName");
+ if (b2g_os_name) {
+ SetPropertyAsAString(NS_LITERAL_STRING("name"), b2g_os_name);
+ }
+
+ const nsAdoptingString& b2g_version =
+ mozilla::Preferences::GetString("b2g.version");
+ if (b2g_version) {
+ SetPropertyAsAString(NS_LITERAL_STRING("version"), b2g_version);
+ }
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ SandboxInfo sandInfo = SandboxInfo::Get();
+
+ SetPropertyAsBool(NS_LITERAL_STRING("hasSeccompBPF"),
+ sandInfo.Test(SandboxInfo::kHasSeccompBPF));
+ SetPropertyAsBool(NS_LITERAL_STRING("hasSeccompTSync"),
+ sandInfo.Test(SandboxInfo::kHasSeccompTSync));
+ SetPropertyAsBool(NS_LITERAL_STRING("hasUserNamespaces"),
+ sandInfo.Test(SandboxInfo::kHasUserNamespaces));
+ SetPropertyAsBool(NS_LITERAL_STRING("hasPrivilegedUserNamespaces"),
+ sandInfo.Test(SandboxInfo::kHasPrivilegedUserNamespaces));
+
+ if (sandInfo.Test(SandboxInfo::kEnabledForContent)) {
+ SetPropertyAsBool(NS_LITERAL_STRING("canSandboxContent"),
+ sandInfo.CanSandboxContent());
+ }
+
+ if (sandInfo.Test(SandboxInfo::kEnabledForMedia)) {
+ SetPropertyAsBool(NS_LITERAL_STRING("canSandboxMedia"),
+ sandInfo.CanSandboxMedia());
+ }
+#endif // XP_LINUX && MOZ_SANDBOX
+
+ return NS_OK;
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+// Prerelease versions of Android use a letter instead of version numbers.
+// Unfortunately this breaks websites due to the user agent.
+// Chrome works around this by hardcoding an Android version when a
+// numeric version can't be obtained. We're doing the same.
+// This version will need to be updated whenever there is a new official
+// Android release.
+// See: https://cs.chromium.org/chromium/src/base/sys_info_android.cc?l=61
+#define DEFAULT_ANDROID_VERSION "6.0.99"
+
+/* static */
+void
+nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!mozilla::AndroidBridge::Bridge()) {
+ aInfo->sdk_version() = 0;
+ return;
+ }
+
+ nsAutoString str;
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build", "MODEL", str)) {
+ aInfo->device() = str;
+ }
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build", "MANUFACTURER", str)) {
+ aInfo->manufacturer() = str;
+ }
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build$VERSION", "RELEASE", str)) {
+ int major_version;
+ int minor_version;
+ int bugfix_version;
+ int num_read = sscanf(NS_ConvertUTF16toUTF8(str).get(), "%d.%d.%d", &major_version, &minor_version, &bugfix_version);
+ if (num_read == 0) {
+ aInfo->release_version() = NS_LITERAL_STRING(DEFAULT_ANDROID_VERSION);
+ } else {
+ aInfo->release_version() = str;
+ }
+ }
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build", "HARDWARE", str)) {
+ aInfo->hardware() = str;
+ }
+ int32_t sdk_version;
+ if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField(
+ "android/os/Build$VERSION", "SDK_INT", &sdk_version)) {
+ sdk_version = 0;
+ }
+ aInfo->sdk_version() = sdk_version;
+ aInfo->isTablet() = java::GeckoAppShell::IsTablet();
+}
+
+void
+nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo& aInfo)
+{
+ if (!aInfo.device().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("device"), aInfo.device());
+ }
+ if (!aInfo.manufacturer().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), aInfo.manufacturer());
+ }
+ if (!aInfo.release_version().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("release_version"), aInfo.release_version());
+ }
+ SetPropertyAsBool(NS_LITERAL_STRING("tablet"), aInfo.isTablet());
+ // NSPR "version" is the kernel version. For Android we want the Android version.
+ // Rename SDK version to version and put the kernel version into kernel_version.
+ nsAutoString str;
+ nsresult rv = GetPropertyAsAString(NS_LITERAL_STRING("version"), str);
+ if (NS_SUCCEEDED(rv)) {
+ SetPropertyAsAString(NS_LITERAL_STRING("kernel_version"), str);
+ }
+ // When AndroidBridge is not available (eg. in xpcshell tests), sdk_version is 0.
+ if (aInfo.sdk_version() != 0) {
+ android_sdk_version = aInfo.sdk_version();
+ if (android_sdk_version >= 8 && !aInfo.hardware().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("hardware"), aInfo.hardware());
+ }
+ SetPropertyAsInt32(NS_LITERAL_STRING("version"), android_sdk_version);
+ }
+}
+#endif // MOZ_WIDGET_ANDROID
+
+void
+nsSystemInfo::SetInt32Property(const nsAString& aPropertyName,
+ const int32_t aValue)
+{
+ NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
+ if (aValue > 0) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetPropertyAsInt32(aPropertyName, aValue);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
+ }
+}
+
+void
+nsSystemInfo::SetUint32Property(const nsAString& aPropertyName,
+ const uint32_t aValue)
+{
+ // Only one property is currently set via this function.
+ // It may legitimately be zero.
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetPropertyAsUint32(aPropertyName, aValue);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
+}
+
+void
+nsSystemInfo::SetUint64Property(const nsAString& aPropertyName,
+ const uint64_t aValue)
+{
+ NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
+ if (aValue > 0) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetPropertyAsUint64(aPropertyName, aValue);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
+ }
+}
+
+#if defined(XP_WIN)
+NS_IMETHODIMP
+nsSystemInfo::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, "profile-do-change")) {
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obsService =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = obsService->RemoveObserver(this, "profile-do-change");
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return GetProfileHDDInfo();
+ }
+ return NS_OK;
+}
+
+nsresult
+nsSystemInfo::GetProfileHDDInfo()
+{
+ nsAutoCString hddModel, hddRevision;
+ nsresult rv = GetHDDInfo(NS_APP_USER_PROFILE_50_DIR, hddModel, hddRevision);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("profileHDDModel"), hddModel);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("profileHDDRevision"),
+ hddRevision);
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo, nsHashPropertyBag, nsIObserver)
+#endif // defined(XP_WIN)
+
diff --git a/xpcom/base/nsSystemInfo.h b/xpcom/base/nsSystemInfo.h
new file mode 100644
index 000000000..5a7ef4424
--- /dev/null
+++ b/xpcom/base/nsSystemInfo.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _NSSYSTEMINFO_H_
+#define _NSSYSTEMINFO_H_
+
+#include "nsHashPropertyBag.h"
+#if defined(XP_WIN)
+#include "nsIObserver.h"
+#endif // defined(XP_WIN)
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "mozilla/dom/PContent.h"
+#endif // MOZ_WIDGET_ANDROID
+
+class nsSystemInfo final
+ : public nsHashPropertyBag
+#if defined(XP_WIN)
+ , public nsIObserver
+#endif // defined(XP_WIN)
+{
+public:
+#if defined(XP_WIN)
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIOBSERVER
+#endif // defined(XP_WIN)
+
+ nsSystemInfo();
+
+ nsresult Init();
+
+ // Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init.
+ // See comments above the variable definition and in NS_InitXPCOM2.
+ static uint32_t gUserUmask;
+
+#ifdef MOZ_WIDGET_ANDROID
+ static void GetAndroidSystemInfo(mozilla::dom::AndroidSystemInfo* aInfo);
+ protected:
+ void SetupAndroidInfo(const mozilla::dom::AndroidSystemInfo&);
+#endif
+
+protected:
+ void SetInt32Property(const nsAString& aPropertyName,
+ const int32_t aValue);
+ void SetUint32Property(const nsAString& aPropertyName,
+ const uint32_t aValue);
+ void SetUint64Property(const nsAString& aPropertyName,
+ const uint64_t aValue);
+
+private:
+ ~nsSystemInfo();
+
+#if defined(XP_WIN)
+ nsresult GetProfileHDDInfo();
+#endif // defined(XP_WIN)
+};
+
+#define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
+#define NS_SYSTEMINFO_CID \
+{ 0xd962398a, 0x99e5, 0x49b2, \
+{ 0x85, 0x7a, 0xc1, 0x59, 0x04, 0x9c, 0x7f, 0x6c } }
+
+#endif /* _NSSYSTEMINFO_H_ */
diff --git a/xpcom/base/nsTraceRefcnt.cpp b/xpcom/base/nsTraceRefcnt.cpp
new file mode 100644
index 000000000..3b415174b
--- /dev/null
+++ b/xpcom/base/nsTraceRefcnt.cpp
@@ -0,0 +1,1319 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTraceRefcnt.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/StaticPtr.h"
+#include "nsXPCOMPrivate.h"
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "prenv.h"
+#include "plstr.h"
+#include "prlink.h"
+#include "nsCRT.h"
+#include <math.h>
+#include "nsHashKeys.h"
+#include "mozilla/StackWalk.h"
+#include "nsThreadUtils.h"
+#include "CodeAddressService.h"
+
+#include "nsXULAppAPI.h"
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#include "mozilla/Atomics.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/PoisonIOInterposer.h"
+
+#include <string>
+#include <vector>
+
+#ifdef HAVE_DLOPEN
+#include <dlfcn.h>
+#endif
+
+#ifdef MOZ_DMD
+#include "base/process_util.h"
+#include "nsMemoryInfoDumper.h"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+#include "plhash.h"
+#include "prmem.h"
+
+#include "prthread.h"
+
+// We use a spin lock instead of a regular mutex because this lock is usually
+// only held for a very short time, and gets grabbed at a very high frequency
+// (~100000 times per second). On Mac, the overhead of using a regular lock
+// is very high, see bug 1137963.
+static mozilla::Atomic<uintptr_t, mozilla::ReleaseAcquire> gTraceLogLocked;
+
+struct MOZ_STACK_CLASS AutoTraceLogLock final
+{
+ bool doRelease;
+ AutoTraceLogLock()
+ : doRelease(true)
+ {
+ uintptr_t currentThread = reinterpret_cast<uintptr_t>(PR_GetCurrentThread());
+ if (gTraceLogLocked == currentThread) {
+ doRelease = false;
+ } else {
+ while (!gTraceLogLocked.compareExchange(0, currentThread)) {
+ PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield */
+ }
+ }
+ }
+ ~AutoTraceLogLock() { if (doRelease) gTraceLogLocked = 0; }
+};
+
+static PLHashTable* gBloatView;
+static PLHashTable* gTypesToLog;
+static PLHashTable* gObjectsToLog;
+static PLHashTable* gSerialNumbers;
+static intptr_t gNextSerialNumber;
+static bool gDumpedStatistics = false;
+
+// By default, debug builds only do bloat logging. Bloat logging
+// only tries to record when an object is created or destroyed, so we
+// optimize the common case in NS_LogAddRef and NS_LogRelease where
+// only bloat logging is enabled and no logging needs to be done.
+enum LoggingType
+{
+ NoLogging,
+ OnlyBloatLogging,
+ FullLogging
+};
+
+static LoggingType gLogging;
+
+static bool gLogLeaksOnly;
+
+#define BAD_TLS_INDEX ((unsigned)-1)
+
+// if gActivityTLS == BAD_TLS_INDEX, then we're
+// unitialized... otherwise this points to a NSPR TLS thread index
+// indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
+// activity is ok, otherwise not!
+static unsigned gActivityTLS = BAD_TLS_INDEX;
+
+static bool gInitialized;
+static nsrefcnt gInitCount;
+
+static FILE* gBloatLog = nullptr;
+static FILE* gRefcntsLog = nullptr;
+static FILE* gAllocLog = nullptr;
+static FILE* gCOMPtrLog = nullptr;
+
+static void
+WalkTheStackSavingLocations(std::vector<void*>& aLocations);
+
+struct SerialNumberRecord
+{
+ SerialNumberRecord()
+ : serialNumber(++gNextSerialNumber)
+ , refCount(0)
+ , COMPtrCount(0)
+ {}
+
+ intptr_t serialNumber;
+ int32_t refCount;
+ int32_t COMPtrCount;
+ // We use std:: classes here rather than the XPCOM equivalents because the
+ // XPCOM equivalents do leak-checking, and if you try to leak-check while
+ // leak-checking, you're gonna have a bad time.
+ std::vector<void*> allocationStack;
+};
+
+struct nsTraceRefcntStats
+{
+ uint64_t mCreates;
+ uint64_t mDestroys;
+
+ bool HaveLeaks() const
+ {
+ return mCreates != mDestroys;
+ }
+
+ void Clear()
+ {
+ mCreates = 0;
+ mDestroys = 0;
+ }
+
+ int64_t NumLeaked() const
+ {
+ return (int64_t)(mCreates - mDestroys);
+ }
+};
+
+#ifdef DEBUG
+static const char kStaticCtorDtorWarning[] =
+ "XPCOM objects created/destroyed from static ctor/dtor";
+
+static void
+AssertActivityIsLegal()
+{
+ if (gActivityTLS == BAD_TLS_INDEX || PR_GetThreadPrivate(gActivityTLS)) {
+ if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
+ NS_RUNTIMEABORT(kStaticCtorDtorWarning);
+ } else {
+ NS_WARNING(kStaticCtorDtorWarning);
+ }
+ }
+}
+# define ASSERT_ACTIVITY_IS_LEGAL \
+ PR_BEGIN_MACRO \
+ AssertActivityIsLegal(); \
+ PR_END_MACRO
+#else
+# define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
+#endif // DEBUG
+
+// These functions are copied from nsprpub/lib/ds/plhash.c, with changes
+// to the functions not called Default* to free the SerialNumberRecord or
+// the BloatEntry.
+
+static void*
+DefaultAllocTable(void* aPool, size_t aSize)
+{
+ return PR_MALLOC(aSize);
+}
+
+static void
+DefaultFreeTable(void* aPool, void* aItem)
+{
+ PR_Free(aItem);
+}
+
+static PLHashEntry*
+DefaultAllocEntry(void* aPool, const void* aKey)
+{
+ return PR_NEW(PLHashEntry);
+}
+
+static void
+SerialNumberFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
+{
+ if (aFlag == HT_FREE_ENTRY) {
+ delete static_cast<SerialNumberRecord*>(aHashEntry->value);
+ PR_Free(aHashEntry);
+ }
+}
+
+static void
+TypesToLogFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
+{
+ if (aFlag == HT_FREE_ENTRY) {
+ free(const_cast<char*>(static_cast<const char*>(aHashEntry->key)));
+ PR_Free(aHashEntry);
+ }
+}
+
+static const PLHashAllocOps serialNumberHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, SerialNumberFreeEntry
+};
+
+static const PLHashAllocOps typesToLogHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, TypesToLogFreeEntry
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class CodeAddressServiceStringTable final
+{
+public:
+ CodeAddressServiceStringTable() : mSet(32) {}
+
+ const char* Intern(const char* aString)
+ {
+ nsCharPtrHashKey* e = mSet.PutEntry(aString);
+ return e->GetKey();
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mSet.SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+ typedef nsTHashtable<nsCharPtrHashKey> StringSet;
+ StringSet mSet;
+};
+
+struct CodeAddressServiceStringAlloc final
+{
+ static char* copy(const char* aStr) { return strdup(aStr); }
+ static void free(char* aPtr) { ::free(aPtr); }
+};
+
+// WalkTheStack does not hold any locks needed by MozDescribeCodeAddress, so
+// this class does not need to do anything.
+struct CodeAddressServiceLock final
+{
+ static void Unlock() {}
+ static void Lock() {}
+ static bool IsLocked() { return true; }
+};
+
+typedef mozilla::CodeAddressService<CodeAddressServiceStringTable,
+ CodeAddressServiceStringAlloc,
+ CodeAddressServiceLock> WalkTheStackCodeAddressService;
+
+mozilla::StaticAutoPtr<WalkTheStackCodeAddressService> gCodeAddressService;
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BloatEntry
+{
+public:
+ BloatEntry(const char* aClassName, uint32_t aClassSize)
+ : mClassSize(aClassSize)
+ {
+ MOZ_ASSERT(strlen(aClassName) > 0, "BloatEntry name must be non-empty");
+ mClassName = PL_strdup(aClassName);
+ mStats.Clear();
+ mTotalLeaked = 0;
+ }
+
+ ~BloatEntry()
+ {
+ PL_strfree(mClassName);
+ }
+
+ uint32_t GetClassSize()
+ {
+ return (uint32_t)mClassSize;
+ }
+ const char* GetClassName()
+ {
+ return mClassName;
+ }
+
+ void Ctor()
+ {
+ mStats.mCreates++;
+ }
+
+ void Dtor()
+ {
+ mStats.mDestroys++;
+ }
+
+ static int DumpEntry(PLHashEntry* aHashEntry, int aIndex, void* aArg)
+ {
+ BloatEntry* entry = (BloatEntry*)aHashEntry->value;
+ if (entry) {
+ static_cast<nsTArray<BloatEntry*>*>(aArg)->AppendElement(entry);
+ }
+ return HT_ENUMERATE_NEXT;
+ }
+
+ static int TotalEntries(PLHashEntry* aHashEntry, int aIndex, void* aArg)
+ {
+ BloatEntry* entry = (BloatEntry*)aHashEntry->value;
+ if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
+ entry->Total((BloatEntry*)aArg);
+ }
+ return HT_ENUMERATE_NEXT;
+ }
+
+ void Total(BloatEntry* aTotal)
+ {
+ aTotal->mStats.mCreates += mStats.mCreates;
+ aTotal->mStats.mDestroys += mStats.mDestroys;
+ aTotal->mClassSize += mClassSize * mStats.mCreates; // adjust for average in DumpTotal
+ aTotal->mTotalLeaked += mClassSize * mStats.NumLeaked();
+ }
+
+ void DumpTotal(FILE* aOut)
+ {
+ mClassSize /= mStats.mCreates;
+ Dump(-1, aOut);
+ }
+
+ bool PrintDumpHeader(FILE* aOut, const char* aMsg)
+ {
+ fprintf(aOut, "\n== BloatView: %s, %s process %d\n", aMsg,
+ XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
+ if (gLogLeaksOnly && !mStats.HaveLeaks()) {
+ return false;
+ }
+
+ fprintf(aOut,
+ "\n" \
+ " |<----------------Class--------------->|<-----Bytes------>|<----Objects---->|\n" \
+ " | | Per-Inst Leaked| Total Rem|\n");
+
+ this->DumpTotal(aOut);
+
+ return true;
+ }
+
+ void Dump(int aIndex, FILE* aOut)
+ {
+ if (gLogLeaksOnly && !mStats.HaveLeaks()) {
+ return;
+ }
+
+ if (mStats.HaveLeaks() || mStats.mCreates != 0) {
+ fprintf(aOut, "%4d |%-38.38s| %8d %8" PRId64 "|%8" PRIu64 " %8" PRId64"|\n",
+ aIndex + 1, mClassName,
+ GetClassSize(),
+ nsCRT::strcmp(mClassName, "TOTAL") ? (mStats.NumLeaked() * GetClassSize()) : mTotalLeaked,
+ mStats.mCreates,
+ mStats.NumLeaked());
+ }
+ }
+
+protected:
+ char* mClassName;
+ double mClassSize; // This is stored as a double because of the way we compute the avg class size for total bloat.
+ int64_t mTotalLeaked; // Used only for TOTAL entry.
+ nsTraceRefcntStats mStats;
+};
+
+static void
+BloatViewFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
+{
+ if (aFlag == HT_FREE_ENTRY) {
+ BloatEntry* entry = static_cast<BloatEntry*>(aHashEntry->value);
+ delete entry;
+ PR_Free(aHashEntry);
+ }
+}
+
+const static PLHashAllocOps bloatViewHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, BloatViewFreeEntry
+};
+
+static void
+RecreateBloatView()
+{
+ gBloatView = PL_NewHashTable(256,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ &bloatViewHashAllocOps, nullptr);
+}
+
+static BloatEntry*
+GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (!gBloatView) {
+ RecreateBloatView();
+ }
+ BloatEntry* entry = nullptr;
+ if (gBloatView) {
+ entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
+ if (!entry && aInstanceSize > 0) {
+
+ entry = new BloatEntry(aTypeName, aInstanceSize);
+ PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
+ if (!e) {
+ delete entry;
+ entry = nullptr;
+ }
+ } else {
+ MOZ_ASSERT(aInstanceSize == 0 || entry->GetClassSize() == aInstanceSize,
+ "Mismatched sizes were recorded in the memory leak logging table. "
+ "The usual cause of this is having a templated class that uses "
+ "MOZ_COUNT_{C,D}TOR in the constructor or destructor, respectively. "
+ "As a workaround, the MOZ_COUNT_{C,D}TOR calls can be moved to a "
+ "non-templated base class.");
+ }
+ }
+ return entry;
+}
+
+static int
+DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
+{
+ SerialNumberRecord* record =
+ static_cast<SerialNumberRecord*>(aHashEntry->value);
+ auto* outputFile = static_cast<FILE*>(aClosure);
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ fprintf(outputFile, "%" PRIdPTR
+ " @%p (%d references; %d from COMPtrs)\n",
+ record->serialNumber,
+ aHashEntry->key,
+ record->refCount,
+ record->COMPtrCount);
+#else
+ fprintf(outputFile, "%" PRIdPTR
+ " @%p (%d references)\n",
+ record->serialNumber,
+ aHashEntry->key,
+ record->refCount);
+#endif
+ if (!record->allocationStack.empty()) {
+ static const size_t bufLen = 1024;
+ char buf[bufLen];
+ fprintf(outputFile, "allocation stack:\n");
+ for (size_t i = 0, length = record->allocationStack.size();
+ i < length;
+ ++i) {
+ gCodeAddressService->GetLocation(i, record->allocationStack[i],
+ buf, bufLen);
+ fprintf(outputFile, "%s\n", buf);
+ }
+ }
+ return HT_ENUMERATE_NEXT;
+}
+
+
+template<>
+class nsDefaultComparator<BloatEntry*, BloatEntry*>
+{
+public:
+ bool Equals(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
+ {
+ return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) == 0;
+ }
+ bool LessThan(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
+ {
+ return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) < 0;
+ }
+};
+
+
+nsresult
+nsTraceRefcnt::DumpStatistics()
+{
+ if (!gBloatLog || !gBloatView) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoTraceLogLock lock;
+
+ MOZ_ASSERT(!gDumpedStatistics,
+ "Calling DumpStatistics more than once may result in "
+ "bogus positive or negative leaks being reported");
+ gDumpedStatistics = true;
+
+ // Don't try to log while we hold the lock, we'd deadlock.
+ AutoRestore<LoggingType> saveLogging(gLogging);
+ gLogging = NoLogging;
+
+ BloatEntry total("TOTAL", 0);
+ PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
+ const char* msg;
+ if (gLogLeaksOnly) {
+ msg = "ALL (cumulative) LEAK STATISTICS";
+ } else {
+ msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
+ }
+ const bool leaked = total.PrintDumpHeader(gBloatLog, msg);
+
+ nsTArray<BloatEntry*> entries;
+ PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
+ const uint32_t count = entries.Length();
+
+ if (!gLogLeaksOnly || leaked) {
+ // Sort the entries alphabetically by classname.
+ entries.Sort();
+
+ for (uint32_t i = 0; i < count; ++i) {
+ BloatEntry* entry = entries[i];
+ entry->Dump(i, gBloatLog);
+ }
+
+ fprintf(gBloatLog, "\n");
+ }
+
+ fprintf(gBloatLog, "nsTraceRefcnt::DumpStatistics: %d entries\n", count);
+
+ if (gSerialNumbers) {
+ fprintf(gBloatLog, "\nSerial Numbers of Leaked Objects:\n");
+ PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, gBloatLog);
+ }
+
+ return NS_OK;
+}
+
+void
+nsTraceRefcnt::ResetStatistics()
+{
+ AutoTraceLogLock lock;
+ if (gBloatView) {
+ PL_HashTableDestroy(gBloatView);
+ gBloatView = nullptr;
+ }
+}
+
+static bool
+LogThisType(const char* aTypeName)
+{
+ void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
+ return he != nullptr;
+}
+
+static PLHashNumber
+HashNumber(const void* aKey)
+{
+ return PLHashNumber(NS_PTR_TO_INT32(aKey));
+}
+
+static intptr_t
+GetSerialNumber(void* aPtr, bool aCreate)
+{
+ PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
+ HashNumber(aPtr),
+ aPtr);
+ if (hep && *hep) {
+ MOZ_RELEASE_ASSERT(!aCreate, "If an object already has a serial number, we should be destroying it.");
+ return static_cast<SerialNumberRecord*>((*hep)->value)->serialNumber;
+ }
+
+ if (!aCreate) {
+ return 0;
+ }
+
+ SerialNumberRecord* record = new SerialNumberRecord();
+ WalkTheStackSavingLocations(record->allocationStack);
+ PL_HashTableRawAdd(gSerialNumbers, hep, HashNumber(aPtr),
+ aPtr, static_cast<void*>(record));
+ return gNextSerialNumber;
+}
+
+static int32_t*
+GetRefCount(void* aPtr)
+{
+ PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
+ HashNumber(aPtr),
+ aPtr);
+ if (hep && *hep) {
+ return &(static_cast<SerialNumberRecord*>((*hep)->value)->refCount);
+ } else {
+ return nullptr;
+ }
+}
+
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+static int32_t*
+GetCOMPtrCount(void* aPtr)
+{
+ PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
+ HashNumber(aPtr),
+ aPtr);
+ if (hep && *hep) {
+ return &(static_cast<SerialNumberRecord*>((*hep)->value)->COMPtrCount);
+ }
+ return nullptr;
+}
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+
+static void
+RecycleSerialNumberPtr(void* aPtr)
+{
+ PL_HashTableRemove(gSerialNumbers, aPtr);
+}
+
+static bool
+LogThisObj(intptr_t aSerialNumber)
+{
+ return (bool)PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
+}
+
+#ifdef XP_WIN
+#define FOPEN_NO_INHERIT "N"
+#else
+#define FOPEN_NO_INHERIT
+#endif
+
+static bool
+InitLog(const char* aEnvVar, const char* aMsg, FILE** aResult)
+{
+ const char* value = getenv(aEnvVar);
+ if (value) {
+ if (nsCRT::strcmp(value, "1") == 0) {
+ *aResult = stdout;
+ fprintf(stdout, "### %s defined -- logging %s to stdout\n",
+ aEnvVar, aMsg);
+ return true;
+ } else if (nsCRT::strcmp(value, "2") == 0) {
+ *aResult = stderr;
+ fprintf(stdout, "### %s defined -- logging %s to stderr\n",
+ aEnvVar, aMsg);
+ return true;
+ } else {
+ FILE* stream;
+ nsAutoCString fname(value);
+ if (!XRE_IsParentProcess()) {
+ bool hasLogExtension =
+ fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
+ if (hasLogExtension) {
+ fname.Cut(fname.Length() - 4, 4);
+ }
+ fname.Append('_');
+ fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
+ fname.AppendLiteral("_pid");
+ fname.AppendInt((uint32_t)getpid());
+ if (hasLogExtension) {
+ fname.AppendLiteral(".log");
+ }
+ }
+ stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
+ if (stream) {
+ MozillaRegisterDebugFD(fileno(stream));
+ *aResult = stream;
+ fprintf(stdout, "### %s defined -- logging %s to %s\n",
+ aEnvVar, aMsg, fname.get());
+ } else {
+ fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
+ aEnvVar, aMsg, fname.get());
+ MOZ_ASSERT(false, "Tried and failed to create an XPCOM log");
+ }
+ return stream != nullptr;
+ }
+ }
+ return false;
+}
+
+
+static void
+maybeUnregisterAndCloseFile(FILE*& aFile)
+{
+ if (!aFile) {
+ return;
+ }
+
+ MozillaUnRegisterDebugFILE(aFile);
+ fclose(aFile);
+ aFile = nullptr;
+}
+
+
+static void
+InitTraceLog()
+{
+ if (gInitialized) {
+ return;
+ }
+ gInitialized = true;
+
+ bool defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
+ if (!defined) {
+ gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
+ }
+ if (defined || gLogLeaksOnly) {
+ RecreateBloatView();
+ if (!gBloatView) {
+ NS_WARNING("out of memory");
+ maybeUnregisterAndCloseFile(gBloatLog);
+ gLogLeaksOnly = false;
+ }
+ }
+
+ InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
+
+ InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
+
+ const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
+
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ if (classes) {
+ InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
+ } else {
+ if (getenv("XPCOM_MEM_COMPTR_LOG")) {
+ fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
+ }
+ }
+#else
+ const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
+ if (comptr_log) {
+ fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
+ }
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+
+ if (classes) {
+ // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
+ // as a list of class names to track
+ gTypesToLog = PL_NewHashTable(256,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ &typesToLogHashAllocOps, nullptr);
+ if (!gTypesToLog) {
+ NS_WARNING("out of memory");
+ fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
+ } else {
+ fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
+ const char* cp = classes;
+ for (;;) {
+ char* cm = (char*)strchr(cp, ',');
+ if (cm) {
+ *cm = '\0';
+ }
+ PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1);
+ fprintf(stdout, "%s ", cp);
+ if (!cm) {
+ break;
+ }
+ *cm = ',';
+ cp = cm + 1;
+ }
+ fprintf(stdout, "\n");
+ }
+
+ gSerialNumbers = PL_NewHashTable(256,
+ HashNumber,
+ PL_CompareValues,
+ PL_CompareValues,
+ &serialNumberHashAllocOps, nullptr);
+
+
+ }
+
+ const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
+ if (objects) {
+ gObjectsToLog = PL_NewHashTable(256,
+ HashNumber,
+ PL_CompareValues,
+ PL_CompareValues,
+ nullptr, nullptr);
+
+ if (!gObjectsToLog) {
+ NS_WARNING("out of memory");
+ fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
+ } else if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
+ fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
+ } else {
+ fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
+ const char* cp = objects;
+ for (;;) {
+ char* cm = (char*)strchr(cp, ',');
+ if (cm) {
+ *cm = '\0';
+ }
+ intptr_t top = 0;
+ intptr_t bottom = 0;
+ while (*cp) {
+ if (*cp == '-') {
+ bottom = top;
+ top = 0;
+ ++cp;
+ }
+ top *= 10;
+ top += *cp - '0';
+ ++cp;
+ }
+ if (!bottom) {
+ bottom = top;
+ }
+ for (intptr_t serialno = bottom; serialno <= top; serialno++) {
+ PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
+ fprintf(stdout, "%" PRIdPTR " ", serialno);
+ }
+ if (!cm) {
+ break;
+ }
+ *cm = ',';
+ cp = cm + 1;
+ }
+ fprintf(stdout, "\n");
+ }
+ }
+
+
+ if (gBloatLog) {
+ gLogging = OnlyBloatLogging;
+ }
+
+ if (gRefcntsLog || gAllocLog || gCOMPtrLog) {
+ gLogging = FullLogging;
+ }
+}
+
+
+extern "C" {
+
+static void
+PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+ FILE* stream = (FILE*)aClosure;
+ MozCodeAddressDetails details;
+ char buf[1024];
+
+ MozDescribeCodeAddress(aPC, &details);
+ MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+ fprintf(stream, "%s\n", buf);
+ fflush(stream);
+}
+
+static void
+PrintStackFrameCached(uint32_t aFrameNumber, void* aPC, void* aSP,
+ void* aClosure)
+{
+ auto stream = static_cast<FILE*>(aClosure);
+ static const size_t buflen = 1024;
+ char buf[buflen];
+ gCodeAddressService->GetLocation(aFrameNumber, aPC, buf, buflen);
+ fprintf(stream, " %s\n", buf);
+ fflush(stream);
+}
+
+static void
+RecordStackFrame(uint32_t /*aFrameNumber*/, void* aPC, void* /*aSP*/,
+ void* aClosure)
+{
+ auto locations = static_cast<std::vector<void*>*>(aClosure);
+ locations->push_back(aPC);
+}
+
+}
+
+void
+nsTraceRefcnt::WalkTheStack(FILE* aStream)
+{
+ MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
+ 0, nullptr);
+}
+
+/**
+ * This is a variant of |WalkTheStack| that uses |CodeAddressService| to cache
+ * the results of |NS_DescribeCodeAddress|. If |WalkTheStackCached| is being
+ * called frequently, it will be a few orders of magnitude faster than
+ * |WalkTheStack|. However, the cache uses a lot of memory, which can cause
+ * OOM crashes. Therefore, this should only be used for things like refcount
+ * logging which walk the stack extremely frequently.
+ */
+static void
+WalkTheStackCached(FILE* aStream)
+{
+ if (!gCodeAddressService) {
+ gCodeAddressService = new WalkTheStackCodeAddressService();
+ }
+ MozStackWalk(PrintStackFrameCached, /* skipFrames */ 2, /* maxFrames */ 0,
+ aStream, 0, nullptr);
+}
+
+static void
+WalkTheStackSavingLocations(std::vector<void*>& aLocations)
+{
+ if (!gCodeAddressService) {
+ gCodeAddressService = new WalkTheStackCodeAddressService();
+ }
+ static const int kFramesToSkip =
+ 0 + // this frame gets inlined
+ 1 + // GetSerialNumber
+ 1; // NS_LogCtor
+ MozStackWalk(RecordStackFrame, kFramesToSkip, /* maxFrames */ 0,
+ &aLocations, 0, nullptr);
+}
+
+//----------------------------------------------------------------------
+
+EXPORT_XPCOM_API(void)
+NS_LogInit()
+{
+ NS_SetMainThread();
+
+ // FIXME: This is called multiple times, we should probably not allow that.
+ StackWalkInitCriticalAddress();
+ if (++gInitCount) {
+ nsTraceRefcnt::SetActivityIsLegal(true);
+ }
+}
+
+EXPORT_XPCOM_API(void)
+NS_LogTerm()
+{
+ mozilla::LogTerm();
+}
+
+#ifdef MOZ_DMD
+// If MOZ_DMD_SHUTDOWN_LOG is set, dump a DMD report to a file.
+// The value of this environment variable is used as the prefix
+// of the file name, so you probably want something like "/tmp/".
+// By default, this is run in all processes, but you can record a
+// log only for a specific process type by setting MOZ_DMD_LOG_PROCESS
+// to the process type you want to log, such as "default" or "tab".
+// This method can't use the higher level XPCOM file utilities
+// because it is run very late in shutdown to avoid recording
+// information about refcount logging entries.
+static void
+LogDMDFile()
+{
+ const char* dmdFilePrefix = PR_GetEnv("MOZ_DMD_SHUTDOWN_LOG");
+ if (!dmdFilePrefix) {
+ return;
+ }
+
+ const char* logProcessEnv = PR_GetEnv("MOZ_DMD_LOG_PROCESS");
+ if (logProcessEnv && !!strcmp(logProcessEnv, XRE_ChildProcessTypeToString(XRE_GetProcessType()))) {
+ return;
+ }
+
+ nsPrintfCString fileName("%sdmd-%d.log.gz", dmdFilePrefix, base::GetCurrentProcId());
+ FILE* logFile = fopen(fileName.get(), "w");
+ if (NS_WARN_IF(!logFile)) {
+ return;
+ }
+
+ nsMemoryInfoDumper::DumpDMDToFile(logFile);
+}
+#endif // MOZ_DMD
+
+namespace mozilla {
+void
+LogTerm()
+{
+ NS_ASSERTION(gInitCount > 0,
+ "NS_LogTerm without matching NS_LogInit");
+
+ if (--gInitCount == 0) {
+#ifdef DEBUG
+ /* FIXME bug 491977: This is only going to operate on the
+ * BlockingResourceBase which is compiled into
+ * libxul/libxpcom_core.so. Anyone using external linkage will
+ * have their own copy of BlockingResourceBase statics which will
+ * not be freed by this method.
+ *
+ * It sounds like what we really want is to be able to register a
+ * callback function to call at XPCOM shutdown. Note that with
+ * this solution, however, we need to guarantee that
+ * BlockingResourceBase::Shutdown() runs after all other shutdown
+ * functions.
+ */
+ BlockingResourceBase::Shutdown();
+#endif
+
+ if (gInitialized) {
+ nsTraceRefcnt::DumpStatistics();
+ nsTraceRefcnt::ResetStatistics();
+ }
+ nsTraceRefcnt::Shutdown();
+ nsTraceRefcnt::SetActivityIsLegal(false);
+ gActivityTLS = BAD_TLS_INDEX;
+
+#ifdef MOZ_DMD
+ LogDMDFile();
+#endif
+ }
+}
+
+} // namespace mozilla
+
+EXPORT_XPCOM_API(void)
+NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
+ const char* aClass, uint32_t aClassSize)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == NoLogging) {
+ return;
+ }
+ if (aRefcnt == 1 || gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ if (aRefcnt == 1 && gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aClass, aClassSize);
+ if (entry) {
+ entry->Ctor();
+ }
+ }
+
+ // Here's the case where MOZ_COUNT_CTOR was not used,
+ // yet we still want to see creation information:
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, aRefcnt == 1);
+ MOZ_ASSERT(serialno != 0,
+ "Serial number requested for unrecognized pointer! "
+ "Are you memmoving a refcounted object?");
+ int32_t* count = GetRefCount(aPtr);
+ if (count) {
+ (*count)++;
+ }
+
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+ if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Create [thread %p]\n", aClass, aPtr, serialno, PR_GetCurrentThread());
+ WalkTheStackCached(gAllocLog);
+ }
+
+ if (gRefcntsLog && loggingThisType && loggingThisObject) {
+ // Can't use MOZ_LOG(), b/c it truncates the line
+ fprintf(gRefcntsLog, "\n<%s> %p %" PRIuPTR " AddRef %" PRIuPTR " [thread %p]\n",
+ aClass, aPtr, serialno, aRefcnt, PR_GetCurrentThread());
+ WalkTheStackCached(gRefcntsLog);
+ fflush(gRefcntsLog);
+ }
+ }
+}
+
+EXPORT_XPCOM_API(void)
+NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClass)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == NoLogging) {
+ return;
+ }
+ if (aRefcnt == 0 || gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ if (aRefcnt == 0 && gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aClass, 0);
+ if (entry) {
+ entry->Dtor();
+ }
+ }
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, false);
+ MOZ_ASSERT(serialno != 0,
+ "Serial number requested for unrecognized pointer! "
+ "Are you memmoving a refcounted object?");
+ int32_t* count = GetRefCount(aPtr);
+ if (count) {
+ (*count)--;
+ }
+
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+ if (gRefcntsLog && loggingThisType && loggingThisObject) {
+ // Can't use MOZ_LOG(), b/c it truncates the line
+ fprintf(gRefcntsLog,
+ "\n<%s> %p %" PRIuPTR " Release %" PRIuPTR " [thread %p]\n",
+ aClass, aPtr, serialno, aRefcnt, PR_GetCurrentThread());
+ WalkTheStackCached(gRefcntsLog);
+ fflush(gRefcntsLog);
+ }
+
+ // Here's the case where MOZ_COUNT_DTOR was not used,
+ // yet we still want to see deletion information:
+
+ if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Destroy [thread %p]\n", aClass, aPtr, serialno, PR_GetCurrentThread());
+ WalkTheStackCached(gAllocLog);
+ }
+
+ if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
+ RecycleSerialNumberPtr(aPtr);
+ }
+ }
+}
+
+EXPORT_XPCOM_API(void)
+NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+
+ if (gLogging == NoLogging) {
+ return;
+ }
+
+ AutoTraceLogLock lock;
+
+ if (gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
+ if (entry) {
+ entry->Ctor();
+ }
+ }
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aType));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, true);
+ MOZ_ASSERT(serialno != 0, "GetSerialNumber should never return 0 when passed true");
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+ if (gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Ctor (%d)\n",
+ aType, aPtr, serialno, aInstanceSize);
+ WalkTheStackCached(gAllocLog);
+ }
+}
+
+
+EXPORT_XPCOM_API(void)
+NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+
+ if (gLogging == NoLogging) {
+ return;
+ }
+
+ AutoTraceLogLock lock;
+
+ if (gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
+ if (entry) {
+ entry->Dtor();
+ }
+ }
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aType));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, false);
+ MOZ_ASSERT(serialno != 0,
+ "Serial number requested for unrecognized pointer! "
+ "Are you memmoving a MOZ_COUNT_CTOR-tracked object?");
+ RecycleSerialNumberPtr(aPtr);
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+
+ // (If we're on a losing architecture, don't do this because we'll be
+ // using LogDeleteXPCOM instead to get file and line numbers.)
+ if (gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Dtor (%d)\n",
+ aType, aPtr, serialno, aInstanceSize);
+ WalkTheStackCached(gAllocLog);
+ }
+}
+
+
+EXPORT_XPCOM_API(void)
+NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
+{
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ // Get the most-derived object.
+ void* object = dynamic_cast<void*>(aObject);
+
+ // This is a very indirect way of finding out what the class is
+ // of the object being logged. If we're logging a specific type,
+ // then
+ if (!gTypesToLog || !gSerialNumbers) {
+ return;
+ }
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ intptr_t serialno = GetSerialNumber(object, false);
+ if (serialno == 0) {
+ return;
+ }
+
+ int32_t* count = GetCOMPtrCount(object);
+ if (count) {
+ (*count)++;
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+
+ if (gCOMPtrLog && loggingThisObject) {
+ fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n",
+ object, serialno, count ? (*count) : -1, aCOMPtr);
+ WalkTheStackCached(gCOMPtrLog);
+ }
+ }
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+}
+
+
+EXPORT_XPCOM_API(void)
+NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
+{
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ // Get the most-derived object.
+ void* object = dynamic_cast<void*>(aObject);
+
+ // This is a very indirect way of finding out what the class is
+ // of the object being logged. If we're logging a specific type,
+ // then
+ if (!gTypesToLog || !gSerialNumbers) {
+ return;
+ }
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ intptr_t serialno = GetSerialNumber(object, false);
+ if (serialno == 0) {
+ return;
+ }
+
+ int32_t* count = GetCOMPtrCount(object);
+ if (count) {
+ (*count)--;
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+
+ if (gCOMPtrLog && loggingThisObject) {
+ fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n",
+ object, serialno, count ? (*count) : -1, aCOMPtr);
+ WalkTheStackCached(gCOMPtrLog);
+ }
+ }
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+}
+
+void
+nsTraceRefcnt::Shutdown()
+{
+ gCodeAddressService = nullptr;
+ if (gBloatView) {
+ PL_HashTableDestroy(gBloatView);
+ gBloatView = nullptr;
+ }
+ if (gTypesToLog) {
+ PL_HashTableDestroy(gTypesToLog);
+ gTypesToLog = nullptr;
+ }
+ if (gObjectsToLog) {
+ PL_HashTableDestroy(gObjectsToLog);
+ gObjectsToLog = nullptr;
+ }
+ if (gSerialNumbers) {
+ PL_HashTableDestroy(gSerialNumbers);
+ gSerialNumbers = nullptr;
+ }
+ maybeUnregisterAndCloseFile(gBloatLog);
+ maybeUnregisterAndCloseFile(gRefcntsLog);
+ maybeUnregisterAndCloseFile(gAllocLog);
+ maybeUnregisterAndCloseFile(gCOMPtrLog);
+}
+
+void
+nsTraceRefcnt::SetActivityIsLegal(bool aLegal)
+{
+ if (gActivityTLS == BAD_TLS_INDEX) {
+ PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
+ }
+
+ PR_SetThreadPrivate(gActivityTLS, reinterpret_cast<void*>(!aLegal));
+}
diff --git a/xpcom/base/nsTraceRefcnt.h b/xpcom/base/nsTraceRefcnt.h
new file mode 100644
index 000000000..73e123a3f
--- /dev/null
+++ b/xpcom/base/nsTraceRefcnt.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsTraceRefcnt_h___
+#define nsTraceRefcnt_h___
+
+#include <stdio.h> // for FILE
+#include "nscore.h"
+
+class nsTraceRefcnt
+{
+public:
+ static void Shutdown();
+
+ static nsresult DumpStatistics();
+
+ static void ResetStatistics();
+
+ static void WalkTheStack(FILE* aStream);
+
+ /**
+ * Tell nsTraceRefcnt whether refcounting, allocation, and destruction
+ * activity is legal. This is used to trigger assertions for any such
+ * activity that occurs because of static constructors or destructors.
+ */
+ static void SetActivityIsLegal(bool aLegal);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// And now for that utility that you've all been asking for...
+
+extern "C" void
+NS_MeanAndStdDev(double aNumberOfValues,
+ double aSumOfValues, double aSumOfSquaredValues,
+ double* aMeanResult, double* aStdDevResult);
+
+////////////////////////////////////////////////////////////////////////////////
+#endif
diff --git a/xpcom/base/nsUUIDGenerator.cpp b/xpcom/base/nsUUIDGenerator.cpp
new file mode 100644
index 000000000..254a01322
--- /dev/null
+++ b/xpcom/base/nsUUIDGenerator.cpp
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include <objbase.h>
+#elif defined(XP_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#else
+#include <stdlib.h>
+#include "prrng.h"
+#endif
+
+#include "nsUUIDGenerator.h"
+
+#ifdef ANDROID
+extern "C" NS_EXPORT void arc4random_buf(void*, size_t);
+#endif
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsUUIDGenerator, nsIUUIDGenerator)
+
+nsUUIDGenerator::nsUUIDGenerator()
+ : mLock("nsUUIDGenerator.mLock")
+{
+}
+
+nsUUIDGenerator::~nsUUIDGenerator()
+{
+}
+
+nsresult
+nsUUIDGenerator::Init()
+{
+ // We're a service, so we're guaranteed that Init() is not going
+ // to be reentered while we're inside Init().
+
+#if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(HAVE_ARC4RANDOM)
+ /* initialize random number generator using NSPR random noise */
+ unsigned int seed;
+
+ size_t bytes = 0;
+ while (bytes < sizeof(seed)) {
+ size_t nbytes = PR_GetRandomNoise(((unsigned char*)&seed) + bytes,
+ sizeof(seed) - bytes);
+ if (nbytes == 0) {
+ return NS_ERROR_FAILURE;
+ }
+ bytes += nbytes;
+ }
+
+ /* Initialize a new RNG state, and immediately switch
+ * back to the previous one -- we want to use mState
+ * only for our own calls to random().
+ */
+ mSavedState = initstate(seed, mState, sizeof(mState));
+ setstate(mSavedState);
+
+ mRBytes = 4;
+#ifdef RAND_MAX
+ if ((unsigned long)RAND_MAX < 0xffffffffUL) {
+ mRBytes = 3;
+ }
+ if ((unsigned long)RAND_MAX < 0x00ffffffUL) {
+ mRBytes = 2;
+ }
+ if ((unsigned long)RAND_MAX < 0x0000ffffUL) {
+ mRBytes = 1;
+ }
+ if ((unsigned long)RAND_MAX < 0x000000ffUL) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+#endif /* non XP_WIN and non XP_MACOSX and non ARC4RANDOM */
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUUIDGenerator::GenerateUUID(nsID** aRet)
+{
+ nsID* id = static_cast<nsID*>(moz_xmalloc(sizeof(nsID)));
+ if (!id) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = GenerateUUIDInPlace(id);
+ if (NS_FAILED(rv)) {
+ free(id);
+ return rv;
+ }
+
+ *aRet = id;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsUUIDGenerator::GenerateUUIDInPlace(nsID* aId)
+{
+ // The various code in this method is probably not threadsafe, so lock
+ // across the whole method.
+ MutexAutoLock lock(mLock);
+
+#if defined(XP_WIN)
+ HRESULT hr = CoCreateGuid((GUID*)aId);
+ if (FAILED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+#elif defined(XP_MACOSX)
+ CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+ if (!uuid) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
+ memcpy(aId, &bytes, sizeof(nsID));
+
+ CFRelease(uuid);
+#else /* not windows or OS X; generate randomness using random(). */
+ /* XXX we should be saving the return of setstate here and switching
+ * back to it; instead, we use the value returned when we called
+ * initstate, since older glibc's have broken setstate() return values
+ */
+#ifndef HAVE_ARC4RANDOM
+ setstate(mState);
+#endif
+
+#ifdef HAVE_ARC4RANDOM_BUF
+ arc4random_buf(aId, sizeof(nsID));
+#else /* HAVE_ARC4RANDOM_BUF */
+ size_t bytesLeft = sizeof(nsID);
+ while (bytesLeft > 0) {
+#ifdef HAVE_ARC4RANDOM
+ long rval = arc4random();
+ const size_t mRBytes = 4;
+#else
+ long rval = random();
+#endif
+
+
+ uint8_t* src = (uint8_t*)&rval;
+ // We want to grab the mRBytes least significant bytes of rval, since
+ // mRBytes less than sizeof(rval) means the high bytes are 0.
+#ifdef IS_BIG_ENDIAN
+ src += sizeof(rval) - mRBytes;
+#endif
+ uint8_t* dst = ((uint8_t*)aId) + (sizeof(nsID) - bytesLeft);
+ size_t toWrite = (bytesLeft < mRBytes ? bytesLeft : mRBytes);
+ for (size_t i = 0; i < toWrite; i++) {
+ dst[i] = src[i];
+ }
+
+ bytesLeft -= toWrite;
+ }
+#endif /* HAVE_ARC4RANDOM_BUF */
+
+ /* Put in the version */
+ aId->m2 &= 0x0fff;
+ aId->m2 |= 0x4000;
+
+ /* Put in the variant */
+ aId->m3[0] &= 0x3f;
+ aId->m3[0] |= 0x80;
+
+#ifndef HAVE_ARC4RANDOM
+ /* Restore the previous RNG state */
+ setstate(mSavedState);
+#endif
+#endif
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsUUIDGenerator.h b/xpcom/base/nsUUIDGenerator.h
new file mode 100644
index 000000000..dd86093f8
--- /dev/null
+++ b/xpcom/base/nsUUIDGenerator.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _NSUUIDGENERATOR_H_
+#define _NSUUIDGENERATOR_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+
+#include "nsIUUIDGenerator.h"
+
+class nsUUIDGenerator final : public nsIUUIDGenerator
+{
+public:
+ nsUUIDGenerator();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIUUIDGENERATOR
+
+ nsresult Init();
+
+private:
+ ~nsUUIDGenerator();
+
+protected:
+
+ mozilla::Mutex mLock;
+#if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(HAVE_ARC4RANDOM)
+ char mState[128];
+ char* mSavedState;
+ uint8_t mRBytes;
+#endif
+};
+
+#define NS_UUID_GENERATOR_CONTRACTID "@mozilla.org/uuid-generator;1"
+#define NS_UUID_GENERATOR_CID \
+{ 0x706d36bb, 0xbf79, 0x4293, \
+{ 0x81, 0xf2, 0x8f, 0x68, 0x28, 0xc1, 0x8f, 0x9d } }
+
+#endif /* _NSUUIDGENERATOR_H_ */
diff --git a/xpcom/base/nsVersionComparatorImpl.cpp b/xpcom/base/nsVersionComparatorImpl.cpp
new file mode 100644
index 000000000..8a37d8b1a
--- /dev/null
+++ b/xpcom/base/nsVersionComparatorImpl.cpp
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsVersionComparatorImpl.h"
+#include "nsVersionComparator.h"
+#include "nsString.h"
+
+NS_IMPL_ISUPPORTS(nsVersionComparatorImpl, nsIVersionComparator)
+
+NS_IMETHODIMP
+nsVersionComparatorImpl::Compare(const nsACString& aStr1,
+ const nsACString& aStr2,
+ int32_t* aResult)
+{
+ *aResult = mozilla::CompareVersions(PromiseFlatCString(aStr1).get(),
+ PromiseFlatCString(aStr2).get());
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsVersionComparatorImpl.h b/xpcom/base/nsVersionComparatorImpl.h
new file mode 100644
index 000000000..84a76d1bb
--- /dev/null
+++ b/xpcom/base/nsVersionComparatorImpl.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Attributes.h"
+
+#include "nsIVersionComparator.h"
+
+class nsVersionComparatorImpl final : public nsIVersionComparator
+{
+ ~nsVersionComparatorImpl() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIVERSIONCOMPARATOR
+};
+
+#define NS_VERSIONCOMPARATOR_CONTRACTID "@mozilla.org/xpcom/version-comparator;1"
+
+// c6e47036-ca94-4be3-963a-9abd8705f7a8
+#define NS_VERSIONCOMPARATOR_CID \
+{ 0xc6e47036, 0xca94, 0x4be3, \
+ { 0x96, 0x3a, 0x9a, 0xbd, 0x87, 0x05, 0xf7, 0xa8 } }
diff --git a/xpcom/base/nsWeakPtr.h b/xpcom/base/nsWeakPtr.h
new file mode 100644
index 000000000..e2f7c37f1
--- /dev/null
+++ b/xpcom/base/nsWeakPtr.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWeakPtr_h__
+#define nsWeakPtr_h__
+
+#include "nsIWeakReference.h"
+#include "nsCOMPtr.h"
+
+// typedef nsCOMPtr<nsIWeakReference> nsWeakPtr;
+
+#endif
diff --git a/xpcom/base/nsWindowsHelpers.h b/xpcom/base/nsWindowsHelpers.h
new file mode 100644
index 000000000..66505b345
--- /dev/null
+++ b/xpcom/base/nsWindowsHelpers.h
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWindowsHelpers_h
+#define nsWindowsHelpers_h
+
+#include <windows.h>
+#include "nsAutoRef.h"
+#include "nscore.h"
+#include "mozilla/Assertions.h"
+
+// ----------------------------------------------------------------------------
+// Critical Section helper class
+// ----------------------------------------------------------------------------
+
+class AutoCriticalSection
+{
+public:
+ AutoCriticalSection(LPCRITICAL_SECTION aSection)
+ : mSection(aSection)
+ {
+ ::EnterCriticalSection(mSection);
+ }
+ ~AutoCriticalSection()
+ {
+ ::LeaveCriticalSection(mSection);
+ }
+private:
+ LPCRITICAL_SECTION mSection;
+};
+
+template<>
+class nsAutoRefTraits<HKEY>
+{
+public:
+ typedef HKEY RawRef;
+ static HKEY Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ RegCloseKey(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HDC>
+{
+public:
+ typedef HDC RawRef;
+ static HDC Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteDC(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HBRUSH>
+{
+public:
+ typedef HBRUSH RawRef;
+ static HBRUSH Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteObject(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HRGN>
+{
+public:
+ typedef HRGN RawRef;
+ static HRGN Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteObject(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HBITMAP>
+{
+public:
+ typedef HBITMAP RawRef;
+ static HBITMAP Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteObject(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<SC_HANDLE>
+{
+public:
+ typedef SC_HANDLE RawRef;
+ static SC_HANDLE Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ CloseServiceHandle(aFD);
+ }
+ }
+};
+
+template<>
+class nsSimpleRef<HANDLE>
+{
+protected:
+ typedef HANDLE RawRef;
+
+ nsSimpleRef() : mRawRef(nullptr)
+ {
+ }
+
+ nsSimpleRef(RawRef aRawRef) : mRawRef(aRawRef)
+ {
+ }
+
+ bool HaveResource() const
+ {
+ return mRawRef && mRawRef != INVALID_HANDLE_VALUE;
+ }
+
+public:
+ RawRef get() const
+ {
+ return mRawRef;
+ }
+
+ static void Release(RawRef aRawRef)
+ {
+ if (aRawRef && aRawRef != INVALID_HANDLE_VALUE) {
+ CloseHandle(aRawRef);
+ }
+ }
+ RawRef mRawRef;
+};
+
+
+template<>
+class nsAutoRefTraits<HMODULE>
+{
+public:
+ typedef HMODULE RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ FreeLibrary(aFD);
+ }
+ }
+};
+
+
+template<>
+class nsAutoRefTraits<DEVMODEW*>
+{
+public:
+ typedef DEVMODEW* RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aDevMode)
+ {
+ if (aDevMode != Void()) {
+ ::HeapFree(::GetProcessHeap(), 0, aDevMode);
+ }
+ }
+};
+
+
+// HGLOBAL is just a typedef of HANDLE which nsSimpleRef has a specialization of,
+// that means having a nsAutoRefTraits specialization for HGLOBAL is useless.
+// Therefore we create a wrapper class for HGLOBAL to make nsAutoRefTraits and
+// nsAutoRef work as intention.
+class nsHGLOBAL {
+public:
+ nsHGLOBAL(HGLOBAL hGlobal) : m_hGlobal(hGlobal)
+ {
+ }
+
+ operator HGLOBAL() const
+ {
+ return m_hGlobal;
+ }
+
+private:
+ HGLOBAL m_hGlobal;
+};
+
+
+template<>
+class nsAutoRefTraits<nsHGLOBAL>
+{
+public:
+ typedef nsHGLOBAL RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef hGlobal)
+ {
+ ::GlobalFree(hGlobal);
+ }
+};
+
+
+// Because Printer's HANDLE uses ClosePrinter and we already have nsAutoRef<HANDLE>
+// which uses CloseHandle so we need to create a wrapper class for HANDLE to have
+// another specialization for nsAutoRefTraits.
+class nsHPRINTER {
+public:
+ nsHPRINTER(HANDLE hPrinter) : m_hPrinter(hPrinter)
+ {
+ }
+
+ operator HANDLE() const
+ {
+ return m_hPrinter;
+ }
+
+ HANDLE* operator&()
+ {
+ return &m_hPrinter;
+ }
+
+private:
+ HANDLE m_hPrinter;
+};
+
+
+// winspool.h header has AddMonitor macro, it conflicts with AddMonitor member
+// function in TaskbarPreview.cpp and TaskbarTabPreview.cpp. Beside, we only
+// need ClosePrinter here for Release function, so having its prototype is enough.
+extern "C" BOOL WINAPI ClosePrinter(HANDLE hPrinter);
+
+
+template<>
+class nsAutoRefTraits<nsHPRINTER>
+{
+public:
+ typedef nsHPRINTER RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef hPrinter)
+ {
+ ::ClosePrinter(hPrinter);
+ }
+};
+
+
+typedef nsAutoRef<HKEY> nsAutoRegKey;
+typedef nsAutoRef<HDC> nsAutoHDC;
+typedef nsAutoRef<HBRUSH> nsAutoBrush;
+typedef nsAutoRef<HRGN> nsAutoRegion;
+typedef nsAutoRef<HBITMAP> nsAutoBitmap;
+typedef nsAutoRef<SC_HANDLE> nsAutoServiceHandle;
+typedef nsAutoRef<HANDLE> nsAutoHandle;
+typedef nsAutoRef<HMODULE> nsModuleHandle;
+typedef nsAutoRef<DEVMODEW*> nsAutoDevMode;
+typedef nsAutoRef<nsHGLOBAL> nsAutoGlobalMem;
+typedef nsAutoRef<nsHPRINTER> nsAutoPrinter;
+
+namespace {
+
+// Construct a path "<system32>\<aModule>". return false if the output buffer
+// is too small.
+// Note: If the system path cannot be found, or doesn't fit in the output buffer
+// with the module name, we will just ignore the system path and output the
+// module name alone;
+// this may mean using a normal search path wherever the output is used.
+bool inline
+ConstructSystem32Path(LPCWSTR aModule, WCHAR* aSystemPath, UINT aSize)
+{
+ MOZ_ASSERT(aSystemPath);
+
+ size_t fileLen = wcslen(aModule);
+ if (fileLen >= aSize) {
+ // The module name alone cannot even fit!
+ return false;
+ }
+
+ size_t systemDirLen = GetSystemDirectoryW(aSystemPath, aSize);
+
+ if (systemDirLen) {
+ if (systemDirLen < aSize - fileLen) {
+ // Make the system directory path terminate with a slash.
+ if (aSystemPath[systemDirLen - 1] != L'\\') {
+ if (systemDirLen + 1 < aSize - fileLen) {
+ aSystemPath[systemDirLen] = L'\\';
+ ++systemDirLen;
+ // No need to re-nullptr terminate.
+ } else {
+ // Couldn't fit the system path with added slash.
+ systemDirLen = 0;
+ }
+ }
+ } else {
+ // Couldn't fit the system path.
+ systemDirLen = 0;
+ }
+ }
+
+ MOZ_ASSERT(systemDirLen + fileLen < aSize);
+
+ wcsncpy(aSystemPath + systemDirLen, aModule, fileLen);
+ aSystemPath[systemDirLen + fileLen] = L'\0';
+ return true;
+}
+
+HMODULE inline
+LoadLibrarySystem32(LPCWSTR aModule)
+{
+ WCHAR systemPath[MAX_PATH + 1];
+ if (!ConstructSystem32Path(aModule, systemPath, MAX_PATH + 1)) {
+ return NULL;
+ }
+ return LoadLibraryW(systemPath);
+}
+
+}
+
+#endif
diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h
new file mode 100644
index 000000000..f6e73c6bc
--- /dev/null
+++ b/xpcom/base/nscore.h
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nscore_h___
+#define nscore_h___
+
+/**
+ * Make sure that we have the proper platform specific
+ * c++ definitions needed by nscore.h
+ */
+#ifndef _XPCOM_CONFIG_H_
+#include "xpcom-config.h"
+#endif
+
+/* Definitions of functions and operators that allocate memory. */
+#if !defined(XPCOM_GLUE) && !defined(NS_NO_XPCOM) && !defined(MOZ_NO_MOZALLOC)
+# include "mozilla/mozalloc.h"
+#endif
+
+/**
+ * Incorporate the integer data types which XPCOM uses.
+ */
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mozilla/RefCountType.h"
+
+/* Core XPCOM declarations. */
+
+/*----------------------------------------------------------------------*/
+/* Import/export defines */
+
+#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE
+#define NS_VISIBILITY_HIDDEN __attribute__ ((visibility ("hidden")))
+#else
+#define NS_VISIBILITY_HIDDEN
+#endif
+
+#if defined(HAVE_VISIBILITY_ATTRIBUTE)
+#define NS_VISIBILITY_DEFAULT __attribute__ ((visibility ("default")))
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#define NS_VISIBILITY_DEFAULT __global
+#else
+#define NS_VISIBILITY_DEFAULT
+#endif
+
+#define NS_HIDDEN_(type) NS_VISIBILITY_HIDDEN type
+#define NS_EXTERNAL_VIS_(type) NS_VISIBILITY_DEFAULT type
+
+#define NS_HIDDEN NS_VISIBILITY_HIDDEN
+#define NS_EXTERNAL_VIS NS_VISIBILITY_DEFAULT
+
+/**
+ * Mark a function as using a potentially non-standard function calling
+ * convention. This can be used on functions that are called very
+ * frequently, to reduce the overhead of the function call. It is still worth
+ * using the macro for C++ functions which take no parameters since it allows
+ * passing |this| in a register.
+ *
+ * - Do not use this on any scriptable interface method since xptcall won't be
+ * aware of the different calling convention.
+ * - This must appear on the declaration, not the definition.
+ * - Adding this to a public function _will_ break binary compatibility.
+ * - This may be used on virtual functions but you must ensure it is applied
+ * to all implementations - the compiler will _not_ warn but it will crash.
+ * - This has no effect for functions which take a variable number of
+ * arguments.
+ * - __fastcall on windows should not be applied to class
+ * constructors/destructors - use the NS_CONSTRUCTOR_FASTCALL macro for
+ * constructors/destructors.
+ *
+ * Examples: int NS_FASTCALL func1(char *foo);
+ * NS_HIDDEN_(int) NS_FASTCALL func2(char *foo);
+ */
+
+#if defined(__i386__) && defined(__GNUC__)
+#define NS_FASTCALL __attribute__ ((regparm (3), stdcall))
+#define NS_CONSTRUCTOR_FASTCALL __attribute__ ((regparm (3), stdcall))
+#elif defined(XP_WIN) && !defined(_WIN64)
+#define NS_FASTCALL __fastcall
+#define NS_CONSTRUCTOR_FASTCALL
+#else
+#define NS_FASTCALL
+#define NS_CONSTRUCTOR_FASTCALL
+#endif
+
+#ifdef XP_WIN
+
+#define NS_IMPORT __declspec(dllimport)
+#define NS_IMPORT_(type) __declspec(dllimport) type __stdcall
+#define NS_EXPORT __declspec(dllexport)
+#define NS_EXPORT_(type) __declspec(dllexport) type __stdcall
+#define NS_IMETHOD_(type) virtual type __stdcall
+#define NS_IMETHODIMP_(type) type __stdcall
+#define NS_METHOD_(type) type __stdcall
+#define NS_CALLBACK_(_type, _name) _type (__stdcall * _name)
+#ifndef _WIN64
+// Win64 has only one calling convention. __stdcall will be ignored by the compiler.
+#define NS_STDCALL __stdcall
+#define NS_HAVE_STDCALL
+#else
+#define NS_STDCALL
+#endif
+#define NS_FROZENCALL __cdecl
+
+#else
+
+#define NS_IMPORT NS_EXTERNAL_VIS
+#define NS_IMPORT_(type) NS_EXTERNAL_VIS_(type)
+#define NS_EXPORT NS_EXTERNAL_VIS
+#define NS_EXPORT_(type) NS_EXTERNAL_VIS_(type)
+#define NS_IMETHOD_(type) virtual type
+#define NS_IMETHODIMP_(type) type
+#define NS_METHOD_(type) type
+#define NS_CALLBACK_(_type, _name) _type (* _name)
+#define NS_STDCALL
+#define NS_FROZENCALL
+
+#endif
+
+/**
+ * Macro for creating typedefs for pointer-to-member types which are
+ * declared with stdcall. It is important to use this for any type which is
+ * declared as stdcall (i.e. NS_IMETHOD). For example, instead of writing:
+ *
+ * typedef nsresult (nsIFoo::*someType)(nsISupports* arg);
+ *
+ * you should write:
+ *
+ * typedef
+ * NS_STDCALL_FUNCPROTO(nsresult, someType, nsIFoo, typeFunc, (nsISupports*));
+ *
+ * where nsIFoo::typeFunc is any method declared as
+ * NS_IMETHOD typeFunc(nsISupports*);
+ *
+ * XXX this can be simplified to always use the non-typeof implementation
+ * when http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11893 is fixed.
+ */
+
+#ifdef __GNUC__
+#define NS_STDCALL_FUNCPROTO(ret, name, class, func, args) \
+ typeof(&class::func) name
+#else
+#define NS_STDCALL_FUNCPROTO(ret, name, class, func, args) \
+ ret (NS_STDCALL class::*name) args
+#endif
+
+/**
+ * Deprecated declarations.
+ */
+#ifdef __GNUC__
+# define MOZ_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+# define MOZ_DEPRECATED __declspec(deprecated)
+#else
+# define MOZ_DEPRECATED
+#endif
+
+/**
+ * Generic API modifiers which return the standard XPCOM nsresult type
+ *
+ * - NS_IMETHOD: use for in-class declarations and definitions.
+ * - NS_IMETHODIMP: use for out-of-class definitions.
+ * - NS_METHOD: usually used in conjunction with NS_CALLBACK.
+ * - NS_CALLBACK: used in some legacy situations. Best avoided.
+ */
+#define NS_IMETHOD NS_IMETHOD_(nsresult)
+#define NS_IMETHODIMP NS_IMETHODIMP_(nsresult)
+#define NS_METHOD NS_METHOD_(nsresult)
+#define NS_CALLBACK(_name) NS_CALLBACK_(nsresult, _name)
+
+/**
+ * Import/Export macros for XPCOM APIs
+ */
+
+#ifdef __cplusplus
+#define NS_EXTERN_C extern "C"
+#else
+#define NS_EXTERN_C
+#endif
+
+#define EXPORT_XPCOM_API(type) NS_EXTERN_C NS_EXPORT type NS_FROZENCALL
+#define IMPORT_XPCOM_API(type) NS_EXTERN_C NS_IMPORT type NS_FROZENCALL
+#define GLUE_XPCOM_API(type) NS_EXTERN_C NS_HIDDEN_(type) NS_FROZENCALL
+
+#ifdef IMPL_LIBXUL
+#define XPCOM_API(type) EXPORT_XPCOM_API(type)
+#elif defined(XPCOM_GLUE)
+#define XPCOM_API(type) GLUE_XPCOM_API(type)
+#else
+#define XPCOM_API(type) IMPORT_XPCOM_API(type)
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+ /*
+ The frozen string API has different definitions of nsAC?String
+ classes than the internal API. On systems that explicitly declare
+ dllexport symbols this is not a problem, but on ELF systems
+ internal symbols can accidentally "shine through"; we rename the
+ internal classes to avoid symbol conflicts.
+ */
+# define nsAString nsAString_internal
+# define nsACString nsACString_internal
+#endif
+
+#if (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+/* Make refcnt logging part of the build. This doesn't mean that
+ * actual logging will occur (that requires a separate enable; see
+ * nsTraceRefcnt and nsISupportsImpl.h for more information). */
+#define NS_BUILD_REFCNT_LOGGING
+#endif
+
+/* If NO_BUILD_REFCNT_LOGGING is defined then disable refcnt logging
+ * in the build. This overrides FORCE_BUILD_REFCNT_LOGGING. */
+#if defined(NO_BUILD_REFCNT_LOGGING)
+#undef NS_BUILD_REFCNT_LOGGING
+#endif
+
+/* If a program allocates memory for the lifetime of the app, it doesn't make
+ * sense to touch memory pages and free that memory at shutdown,
+ * unless we are running leak stats.
+ */
+#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_VALGRIND) || defined(MOZ_ASAN)
+#define NS_FREE_PERMANENT_DATA
+#endif
+
+/**
+ * NS_NO_VTABLE is emitted by xpidl in interface declarations whenever
+ * xpidl can determine that the interface can't contain a constructor.
+ * This results in some space savings and possible runtime savings -
+ * see bug 49416. We undefine it first, as xpidl-generated headers
+ * define it for IDL uses that don't include this file.
+ */
+#ifdef NS_NO_VTABLE
+#undef NS_NO_VTABLE
+#endif
+#if defined(_MSC_VER)
+#define NS_NO_VTABLE __declspec(novtable)
+#else
+#define NS_NO_VTABLE
+#endif
+
+
+/**
+ * Generic XPCOM result data type
+ */
+#include "nsError.h"
+
+typedef MozRefCountType nsrefcnt;
+
+/*
+ * Use these macros to do 64bit safe pointer conversions.
+ */
+
+#define NS_PTR_TO_INT32(x) ((int32_t)(intptr_t)(x))
+#define NS_PTR_TO_UINT32(x) ((uint32_t)(intptr_t)(x))
+#define NS_INT32_TO_PTR(x) ((void*)(intptr_t)(x))
+
+/*
+ * Use NS_STRINGIFY to form a string literal from the value of a macro.
+ */
+#define NS_STRINGIFY_HELPER(x_) #x_
+#define NS_STRINGIFY(x_) NS_STRINGIFY_HELPER(x_)
+
+/*
+ * If we're being linked as standalone glue, we don't want a dynamic
+ * dependency on NSPR libs, so we skip the debug thread-safety
+ * checks, and we cannot use the THREADSAFE_ISUPPORTS macros.
+ */
+#if defined(XPCOM_GLUE) && !defined(XPCOM_GLUE_USE_NSPR)
+#define XPCOM_GLUE_AVOID_NSPR
+#endif
+
+/*
+ * SEH exception macros.
+ */
+#ifdef HAVE_SEH_EXCEPTIONS
+#define MOZ_SEH_TRY __try
+#define MOZ_SEH_EXCEPT(expr) __except(expr)
+#else
+#define MOZ_SEH_TRY if(true)
+#define MOZ_SEH_EXCEPT(expr) else
+#endif
+
+#endif /* nscore_h___ */
diff --git a/xpcom/base/nsrootidl.idl b/xpcom/base/nsrootidl.idl
new file mode 100644
index 000000000..55795b7bc
--- /dev/null
+++ b/xpcom/base/nsrootidl.idl
@@ -0,0 +1,97 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * Root idl declarations to be used by all.
+ */
+
+%{C++
+
+#include "nscore.h"
+typedef int64_t PRTime;
+
+/*
+ * Forward declarations for new string types
+ */
+class nsAString;
+class nsACString;
+
+/*
+ * Start commenting out the C++ versions of the below in the output header
+ */
+#if 0
+%}
+
+typedef boolean bool ;
+typedef octet uint8_t ;
+typedef unsigned short uint16_t ;
+typedef unsigned short char16_t;
+typedef unsigned long uint32_t ;
+typedef unsigned long long uint64_t ;
+typedef long long PRTime ;
+typedef short int16_t ;
+typedef long int32_t ;
+typedef long long int64_t ;
+
+typedef unsigned long nsrefcnt ;
+typedef unsigned long nsresult ;
+
+// XXX need this built into xpidl compiler so that it's really size_t or size_t
+// and it's scriptable:
+typedef unsigned long size_t;
+
+[ptr] native voidPtr(void);
+[ptr] native charPtr(char);
+[ptr] native unicharPtr(char16_t);
+
+[ref, nsid] native nsIDRef(nsID);
+[ref, nsid] native nsIIDRef(nsIID);
+[ref, nsid] native nsCIDRef(nsCID);
+
+[ptr, nsid] native nsIDPtr(nsID);
+[ptr, nsid] native nsIIDPtr(nsIID);
+[ptr, nsid] native nsCIDPtr(nsCID);
+
+// NOTE: Be careful in using the following 3 types. The *Ref and *Ptr variants
+// are more commonly used (and better supported). Those variants require
+// nsMemory alloc'd copies when used as 'out' params while these types do not.
+// However, currently these types can not be used for 'in' params. And, methods
+// that use them as 'out' params *must* be declared [notxpcom] (with an explicit
+// return type of nsresult). This makes such methods implicitly not scriptable.
+// Use of these types in methods without a [notxpcom] declaration will cause
+// the xpidl compiler to raise an error.
+// See: http://bugzilla.mozilla.org/show_bug.cgi?id=93792
+
+[nsid] native nsIID(nsIID);
+[nsid] native nsID(nsID);
+[nsid] native nsCID(nsCID);
+
+[ptr] native nsQIResult(void);
+
+[ref, domstring] native DOMString(ignored);
+[ref, domstring] native DOMStringRef(ignored);
+[ptr, domstring] native DOMStringPtr(ignored);
+
+[ref, utf8string] native AUTF8String(ignored);
+[ref, utf8string] native AUTF8StringRef(ignored);
+[ptr, utf8string] native AUTF8StringPtr(ignored);
+
+[ref, cstring] native ACString(ignored);
+[ref, cstring] native ACStringRef(ignored);
+[ptr, cstring] native ACStringPtr(ignored);
+
+[ref, astring] native AString(ignored);
+[ref, astring] native AStringRef(ignored);
+[ptr, astring] native AStringPtr(ignored);
+
+[ref, jsval] native jsval(jsval);
+ native jsid(jsid);
+
+%{C++
+/*
+ * End commenting out the C++ versions of the above in the output header
+ */
+#endif
+%}
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
new file mode 100644
index 000000000..374763c79
--- /dev/null
+++ b/xpcom/build/BinaryPath.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BinaryPath_h
+#define mozilla_BinaryPath_h
+
+#include "nsXPCOMPrivate.h" // for MAXPATHLEN
+#ifdef XP_WIN
+#include <windows.h>
+#elif defined(XP_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#elif defined(XP_UNIX)
+#include <sys/stat.h>
+#include <string.h>
+#endif
+
+namespace mozilla {
+
+class BinaryPath
+{
+public:
+#ifdef XP_WIN
+ static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
+ {
+ wchar_t wide_path[MAXPATHLEN];
+ nsresult rv = GetW(argv0, wide_path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ WideCharToMultiByte(CP_UTF8, 0, wide_path, -1,
+ aResult, MAXPATHLEN, nullptr, nullptr);
+ return NS_OK;
+ }
+
+private:
+ static nsresult GetW(const char* argv0, wchar_t aResult[MAXPATHLEN])
+ {
+ if (::GetModuleFileNameW(0, aResult, MAXPATHLEN)) {
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+#elif defined(XP_MACOSX)
+ static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
+ {
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = CFBundleGetMainBundle();
+ if (!appBundle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle);
+ if (!executableURL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ if (CFURLGetFileSystemRepresentation(executableURL, false, (UInt8*)aResult,
+ MAXPATHLEN)) {
+ // Sanitize path in case the app was launched from Terminal via
+ // './firefox' for example.
+ size_t readPos = 0;
+ size_t writePos = 0;
+ while (aResult[readPos] != '\0') {
+ if (aResult[readPos] == '.' && aResult[readPos + 1] == '/') {
+ readPos += 2;
+ } else {
+ aResult[writePos] = aResult[readPos];
+ readPos++;
+ writePos++;
+ }
+ }
+ aResult[writePos] = '\0';
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ CFRelease(executableURL);
+ return rv;
+ }
+
+#elif defined(ANDROID)
+ static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
+ {
+ // On Android, we use the GRE_HOME variable that is set by the Java
+ // bootstrap code.
+ const char* greHome = getenv("GRE_HOME");
+#if defined(MOZ_WIDGET_GONK)
+ if (!greHome) {
+ greHome = "/system/b2g";
+ }
+#endif
+
+ if (!greHome) {
+ return NS_ERROR_FAILURE;
+ }
+
+ snprintf(aResult, MAXPATHLEN, "%s/%s", greHome, "dummy");
+ aResult[MAXPATHLEN - 1] = '\0';
+ return NS_OK;
+ }
+
+#elif defined(XP_UNIX)
+ static nsresult Get(const char* aArgv0, char aResult[MAXPATHLEN])
+ {
+ struct stat fileStat;
+ // on unix, there is no official way to get the path of the current binary.
+ // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to
+ // multiple applications, we will try a series of techniques:
+ //
+ // 1) use realpath() on argv[0], which works unless we're loaded from the
+ // PATH. Only do so if argv[0] looks like a path (contains a /).
+ // 2) manually walk through the PATH and look for ourself
+ // 3) give up
+ if (strchr(aArgv0, '/') && realpath(aArgv0, aResult) &&
+ stat(aResult, &fileStat) == 0) {
+ return NS_OK;
+ }
+
+ const char* path = getenv("PATH");
+ if (!path) {
+ return NS_ERROR_FAILURE;
+ }
+
+ char* pathdup = strdup(path);
+ if (!pathdup) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ bool found = false;
+ char* token = strtok(pathdup, ":");
+ while (token) {
+ char tmpPath[MAXPATHLEN];
+ sprintf(tmpPath, "%s/%s", token, aArgv0);
+ if (realpath(tmpPath, aResult) && stat(aResult, &fileStat) == 0) {
+ found = true;
+ break;
+ }
+ token = strtok(nullptr, ":");
+ }
+ free(pathdup);
+ if (found) {
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+#else
+#error Oops, you need platform-specific code here
+#endif
+
+public:
+ static nsresult GetFile(const char* aArgv0, nsIFile** aResult)
+ {
+ nsCOMPtr<nsIFile> lf;
+#ifdef XP_WIN
+ wchar_t exePath[MAXPATHLEN];
+ nsresult rv = GetW(aArgv0, exePath);
+#else
+ char exePath[MAXPATHLEN];
+ nsresult rv = Get(aArgv0, exePath);
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+#ifdef XP_WIN
+ rv = NS_NewLocalFile(nsDependentString(exePath), true,
+ getter_AddRefs(lf));
+#else
+ rv = NS_NewNativeLocalFile(nsDependentCString(exePath), true,
+ getter_AddRefs(lf));
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ NS_ADDREF(*aResult = lf);
+ return NS_OK;
+ }
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_BinaryPath_h */
diff --git a/xpcom/build/FileLocation.cpp b/xpcom/build/FileLocation.cpp
new file mode 100644
index 000000000..03c1dc027
--- /dev/null
+++ b/xpcom/build/FileLocation.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileLocation.h"
+#include "nsZipArchive.h"
+#include "nsURLHelper.h"
+
+namespace mozilla {
+
+FileLocation::FileLocation()
+{
+}
+
+FileLocation::~FileLocation()
+{
+}
+
+FileLocation::FileLocation(nsIFile* aFile)
+{
+ Init(aFile);
+}
+
+FileLocation::FileLocation(nsIFile* aFile, const char* aPath)
+{
+ Init(aFile, aPath);
+}
+
+FileLocation::FileLocation(const FileLocation& aFile, const char* aPath)
+{
+ if (aFile.IsZip()) {
+ if (aFile.mBaseFile) {
+ Init(aFile.mBaseFile, aFile.mPath.get());
+ }
+ else {
+ Init(aFile.mBaseZip, aFile.mPath.get());
+ }
+ if (aPath) {
+ int32_t i = mPath.RFindChar('/');
+ if (kNotFound == i) {
+ mPath.Truncate(0);
+ } else {
+ mPath.Truncate(i + 1);
+ }
+ mPath += aPath;
+ }
+ } else {
+ if (aPath) {
+ nsCOMPtr<nsIFile> cfile;
+ aFile.mBaseFile->GetParent(getter_AddRefs(cfile));
+
+#if defined(XP_WIN)
+ nsAutoCString pathStr(aPath);
+ char* p;
+ uint32_t len = pathStr.GetMutableData(&p);
+ for (; len; ++p, --len) {
+ if ('/' == *p) {
+ *p = '\\';
+ }
+ }
+ cfile->AppendRelativeNativePath(pathStr);
+#else
+ cfile->AppendRelativeNativePath(nsDependentCString(aPath));
+#endif
+ Init(cfile);
+ } else {
+ Init(aFile.mBaseFile);
+ }
+ }
+}
+
+void
+FileLocation::Init(nsIFile* aFile)
+{
+ mBaseZip = nullptr;
+ mBaseFile = aFile;
+ mPath.Truncate();
+}
+
+void
+FileLocation::Init(nsIFile* aFile, const char* aPath)
+{
+ mBaseZip = nullptr;
+ mBaseFile = aFile;
+ mPath = aPath;
+}
+
+void
+FileLocation::Init(nsZipArchive* aZip, const char* aPath)
+{
+ mBaseZip = aZip;
+ mBaseFile = nullptr;
+ mPath = aPath;
+}
+
+void
+FileLocation::GetURIString(nsACString& aResult) const
+{
+ if (mBaseFile) {
+ net_GetURLSpecFromActualFile(mBaseFile, aResult);
+ } else if (mBaseZip) {
+ RefPtr<nsZipHandle> handler = mBaseZip->GetFD();
+ handler->mFile.GetURIString(aResult);
+ }
+ if (IsZip()) {
+ aResult.Insert("jar:", 0);
+ aResult += "!/";
+ aResult += mPath;
+ }
+}
+
+already_AddRefed<nsIFile>
+FileLocation::GetBaseFile()
+{
+ if (IsZip() && mBaseZip) {
+ RefPtr<nsZipHandle> handler = mBaseZip->GetFD();
+ if (handler) {
+ return handler->mFile.GetBaseFile();
+ }
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file = mBaseFile;
+ return file.forget();
+}
+
+bool
+FileLocation::Equals(const FileLocation& aFile) const
+{
+ if (mPath != aFile.mPath) {
+ return false;
+ }
+
+ if (mBaseFile && aFile.mBaseFile) {
+ bool eq;
+ return NS_SUCCEEDED(mBaseFile->Equals(aFile.mBaseFile, &eq)) && eq;
+ }
+
+ const FileLocation* a = this;
+ const FileLocation* b = &aFile;
+ if (a->mBaseZip) {
+ RefPtr<nsZipHandle> handler = a->mBaseZip->GetFD();
+ a = &handler->mFile;
+ }
+ if (b->mBaseZip) {
+ RefPtr<nsZipHandle> handler = b->mBaseZip->GetFD();
+ b = &handler->mFile;
+ }
+
+ return a->Equals(*b);
+}
+
+nsresult
+FileLocation::GetData(Data& aData)
+{
+ if (!IsZip()) {
+ return mBaseFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &aData.mFd.rwget());
+ }
+ aData.mZip = mBaseZip;
+ if (!aData.mZip) {
+ aData.mZip = new nsZipArchive();
+ aData.mZip->OpenArchive(mBaseFile);
+ }
+ aData.mItem = aData.mZip->GetItem(mPath.get());
+ if (aData.mItem) {
+ return NS_OK;
+ }
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+}
+
+nsresult
+FileLocation::Data::GetSize(uint32_t* aResult)
+{
+ if (mFd) {
+ PRFileInfo64 fileInfo;
+ if (PR_SUCCESS != PR_GetOpenFileInfo64(mFd, &fileInfo)) {
+ return NS_ErrorAccordingToNSPR();
+ }
+
+ if (fileInfo.size > int64_t(UINT32_MAX)) {
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ *aResult = fileInfo.size;
+ return NS_OK;
+ }
+ else if (mItem) {
+ *aResult = mItem->RealSize();
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+nsresult
+FileLocation::Data::Copy(char* aBuf, uint32_t aLen)
+{
+ if (mFd) {
+ for (uint32_t totalRead = 0; totalRead < aLen;) {
+ int32_t read = PR_Read(mFd, aBuf + totalRead,
+ XPCOM_MIN(aLen - totalRead, uint32_t(INT32_MAX)));
+ if (read < 0) {
+ return NS_ErrorAccordingToNSPR();
+ }
+ totalRead += read;
+ }
+ return NS_OK;
+ }
+ else if (mItem) {
+ nsZipCursor cursor(mItem, mZip, reinterpret_cast<uint8_t*>(aBuf),
+ aLen, true);
+ uint32_t readLen;
+ cursor.Copy(&readLen);
+ if (readLen != aLen) {
+ nsZipArchive::sFileCorruptedReason = "FileLocation::Data: insufficient data";
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+} /* namespace mozilla */
diff --git a/xpcom/build/FileLocation.h b/xpcom/build/FileLocation.h
new file mode 100644
index 000000000..e9a3fc5d0
--- /dev/null
+++ b/xpcom/build/FileLocation.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_FileLocation_h
+#define mozilla_FileLocation_h
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIFile.h"
+#include "FileUtils.h"
+
+class nsZipArchive;
+class nsZipItem;
+
+namespace mozilla {
+
+class FileLocation
+{
+public:
+ /**
+ * FileLocation is an helper to handle different kind of file locations
+ * within Gecko:
+ * - on filesystems
+ * - in archives
+ * - in archives within archives
+ * As such, it stores a path within an archive, as well as the archive
+ * path itself, or the complete file path alone when on a filesystem.
+ * When the archive is in an archive, an nsZipArchive is stored instead
+ * of a file path.
+ */
+ FileLocation();
+ ~FileLocation();
+
+ /**
+ * Constructor for plain files
+ */
+ explicit FileLocation(nsIFile* aFile);
+
+ /**
+ * Constructors for path within an archive. The archive can be given either
+ * as nsIFile or nsZipArchive.
+ */
+ FileLocation(nsIFile* aZip, const char* aPath);
+
+ FileLocation(nsZipArchive* aZip, const char* aPath);
+
+ /**
+ * Creates a new file location relative to another one.
+ */
+ FileLocation(const FileLocation& aFile, const char* aPath = nullptr);
+
+ /**
+ * Initialization functions corresponding to constructors
+ */
+ void Init(nsIFile* aFile);
+
+ void Init(nsIFile* aZip, const char* aPath);
+
+ void Init(nsZipArchive* aZip, const char* aPath);
+
+ /**
+ * Returns an URI string corresponding to the file location
+ */
+ void GetURIString(nsACString& aResult) const;
+
+ /**
+ * Returns the base file of the location, where base file is defined as:
+ * - The file itself when the location is on a filesystem
+ * - The archive file when the location is in an archive
+ * - The outer archive file when the location is in an archive in an archive
+ */
+ already_AddRefed<nsIFile> GetBaseFile();
+
+ /**
+ * Returns whether the "base file" (see GetBaseFile) is an archive
+ */
+ bool IsZip() const { return !mPath.IsEmpty(); }
+
+ /**
+ * Returns the path within the archive, when within an archive
+ */
+ void GetPath(nsACString& aResult) const { aResult = mPath; }
+
+ /**
+ * Boolean value corresponding to whether the file location is initialized
+ * or not.
+ */
+ explicit operator bool() const { return mBaseFile || mBaseZip; }
+
+ /**
+ * Returns whether another FileLocation points to the same resource
+ */
+ bool Equals(const FileLocation& aFile) const;
+
+ /**
+ * Data associated with a FileLocation.
+ */
+ class Data
+ {
+ public:
+ /**
+ * Returns the data size
+ */
+ nsresult GetSize(uint32_t* aResult);
+
+ /**
+ * Copies the data in the given buffer
+ */
+ nsresult Copy(char* aBuf, uint32_t aLen);
+ protected:
+ friend class FileLocation;
+ nsZipItem* mItem;
+ RefPtr<nsZipArchive> mZip;
+ mozilla::AutoFDClose mFd;
+ };
+
+ /**
+ * Returns the data associated with the resource pointed at by the file
+ * location.
+ */
+ nsresult GetData(Data& aData);
+private:
+ nsCOMPtr<nsIFile> mBaseFile;
+ RefPtr<nsZipArchive> mBaseZip;
+ nsCString mPath;
+}; /* class FileLocation */
+
+} /* namespace mozilla */
+
+#endif /* mozilla_FileLocation_h */
diff --git a/xpcom/build/FrozenFunctions.cpp b/xpcom/build/FrozenFunctions.cpp
new file mode 100644
index 000000000..8a957ca45
--- /dev/null
+++ b/xpcom/build/FrozenFunctions.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsXPCOM.h"
+#include "nsXPCOMPrivate.h"
+#include "nsXPCOMStrings.h"
+#include "xptcall.h"
+
+#include <string.h>
+
+/**
+ * Private Method to register an exit routine. This method
+ * used to allow you to setup a callback that will be called from
+ * the NS_ShutdownXPCOM function after all services and
+ * components have gone away. It was fatally flawed in that the component
+ * DLL could be released before the exit function was called; it is now a
+ * stub implementation that does nothing.
+ */
+XPCOM_API(nsresult)
+NS_RegisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine, uint32_t aPriority);
+
+XPCOM_API(nsresult)
+NS_UnregisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine);
+
+static const XPCOMFunctions kFrozenFunctions = {
+ XPCOM_GLUE_VERSION,
+ sizeof(XPCOMFunctions),
+ &NS_InitXPCOM2,
+ &NS_ShutdownXPCOM,
+ &NS_GetServiceManager,
+ &NS_GetComponentManager,
+ &NS_GetComponentRegistrar,
+ &NS_GetMemoryManager,
+ &NS_NewLocalFile,
+ &NS_NewNativeLocalFile,
+ &NS_RegisterXPCOMExitRoutine,
+ &NS_UnregisterXPCOMExitRoutine,
+
+ // these functions were added post 1.4
+ &NS_GetDebug,
+ nullptr,
+
+ // these functions were added post 1.6
+ &NS_StringContainerInit,
+ &NS_StringContainerFinish,
+ &NS_StringGetData,
+ &NS_StringSetData,
+ &NS_StringSetDataRange,
+ &NS_StringCopy,
+ &NS_CStringContainerInit,
+ &NS_CStringContainerFinish,
+ &NS_CStringGetData,
+ &NS_CStringSetData,
+ &NS_CStringSetDataRange,
+ &NS_CStringCopy,
+ &NS_CStringToUTF16,
+ &NS_UTF16ToCString,
+ &NS_StringCloneData,
+ &NS_CStringCloneData,
+
+ // these functions were added post 1.7 (post Firefox 1.0)
+ &moz_xmalloc,
+ &moz_xrealloc,
+ &free,
+ &NS_StringContainerInit2,
+ &NS_CStringContainerInit2,
+ &NS_StringGetMutableData,
+ &NS_CStringGetMutableData,
+ nullptr,
+
+ // these functions were added post 1.8
+ &NS_DebugBreak,
+ &NS_LogInit,
+ &NS_LogTerm,
+ &NS_LogAddRef,
+ &NS_LogRelease,
+ &NS_LogCtor,
+ &NS_LogDtor,
+ &NS_LogCOMPtrAddRef,
+ &NS_LogCOMPtrRelease,
+ &NS_GetXPTCallStub,
+ &NS_DestroyXPTCallStub,
+ &NS_InvokeByIndex,
+ nullptr,
+ nullptr,
+ &NS_StringSetIsVoid,
+ &NS_StringGetIsVoid,
+ &NS_CStringSetIsVoid,
+ &NS_CStringGetIsVoid,
+
+ // these functions were added post 1.9, but then made obsolete
+ nullptr,
+ nullptr,
+
+ &NS_CycleCollectorSuspect3,
+};
+
+EXPORT_XPCOM_API(nsresult)
+NS_GetFrozenFunctions(XPCOMFunctions* aFunctions, const char* /* aLibraryPath */)
+{
+ if (!aFunctions) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (aFunctions->version != XPCOM_GLUE_VERSION) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t size = aFunctions->size;
+ if (size > sizeof(XPCOMFunctions)) {
+ size = sizeof(XPCOMFunctions);
+ }
+
+ size -= offsetof(XPCOMFunctions, init);
+
+ memcpy(&aFunctions->init, &kFrozenFunctions.init, size);
+
+ return NS_OK;
+}
+
+/*
+ * Stubs for nsXPCOMPrivate.h
+ */
+
+EXPORT_XPCOM_API(nsresult)
+NS_RegisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine, uint32_t aPriority)
+{
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_UnregisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine)
+{
+ return NS_OK;
+}
diff --git a/xpcom/build/IOInterposer.cpp b/xpcom/build/IOInterposer.cpp
new file mode 100644
index 000000000..1c3ff54d5
--- /dev/null
+++ b/xpcom/build/IOInterposer.cpp
@@ -0,0 +1,582 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+#include <vector>
+
+#include "IOInterposer.h"
+
+#include "IOInterposerPrivate.h"
+#include "MainThreadIOLogger.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ThreadLocal.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+#if !defined(XP_WIN)
+#include "NSPRInterposer.h"
+#endif // !defined(XP_WIN)
+#include "nsXULAppAPI.h"
+#include "PoisonIOInterposer.h"
+
+using namespace mozilla;
+
+namespace {
+
+/** Find if a vector contains a specific element */
+template<class T>
+bool
+VectorContains(const std::vector<T>& aVector, const T& aElement)
+{
+ return std::find(aVector.begin(), aVector.end(), aElement) != aVector.end();
+}
+
+/** Remove element from a vector */
+template<class T>
+void
+VectorRemove(std::vector<T>& aVector, const T& aElement)
+{
+ typename std::vector<T>::iterator newEnd =
+ std::remove(aVector.begin(), aVector.end(), aElement);
+ aVector.erase(newEnd, aVector.end());
+}
+
+/** Lists of Observers */
+struct ObserverLists
+{
+private:
+ ~ObserverLists() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists)
+
+ ObserverLists() {}
+
+ ObserverLists(ObserverLists const& aOther)
+ : mCreateObservers(aOther.mCreateObservers)
+ , mReadObservers(aOther.mReadObservers)
+ , mWriteObservers(aOther.mWriteObservers)
+ , mFSyncObservers(aOther.mFSyncObservers)
+ , mStatObservers(aOther.mStatObservers)
+ , mCloseObservers(aOther.mCloseObservers)
+ , mStageObservers(aOther.mStageObservers)
+ {
+ }
+ // Lists of observers for I/O events.
+ // These are implemented as vectors since they are allowed to survive gecko,
+ // without reporting leaks. This is necessary for the IOInterposer to be used
+ // for late-write checks.
+ std::vector<IOInterposeObserver*> mCreateObservers;
+ std::vector<IOInterposeObserver*> mReadObservers;
+ std::vector<IOInterposeObserver*> mWriteObservers;
+ std::vector<IOInterposeObserver*> mFSyncObservers;
+ std::vector<IOInterposeObserver*> mStatObservers;
+ std::vector<IOInterposeObserver*> mCloseObservers;
+ std::vector<IOInterposeObserver*> mStageObservers;
+};
+
+class PerThreadData
+{
+public:
+ explicit PerThreadData(bool aIsMainThread = false)
+ : mIsMainThread(aIsMainThread)
+ , mIsHandlingObservation(false)
+ , mCurrentGeneration(0)
+ {
+ MOZ_COUNT_CTOR(PerThreadData);
+ }
+
+ ~PerThreadData()
+ {
+ MOZ_COUNT_DTOR(PerThreadData);
+ }
+
+ void CallObservers(IOInterposeObserver::Observation& aObservation)
+ {
+ // Prevent recursive reporting.
+ if (mIsHandlingObservation) {
+ return;
+ }
+
+ mIsHandlingObservation = true;
+ // Decide which list of observers to inform
+ std::vector<IOInterposeObserver*>* observers = nullptr;
+ switch (aObservation.ObservedOperation()) {
+ case IOInterposeObserver::OpCreateOrOpen:
+ observers = &mObserverLists->mCreateObservers;
+ break;
+ case IOInterposeObserver::OpRead:
+ observers = &mObserverLists->mReadObservers;
+ break;
+ case IOInterposeObserver::OpWrite:
+ observers = &mObserverLists->mWriteObservers;
+ break;
+ case IOInterposeObserver::OpFSync:
+ observers = &mObserverLists->mFSyncObservers;
+ break;
+ case IOInterposeObserver::OpStat:
+ observers = &mObserverLists->mStatObservers;
+ break;
+ case IOInterposeObserver::OpClose:
+ observers = &mObserverLists->mCloseObservers;
+ break;
+ case IOInterposeObserver::OpNextStage:
+ observers = &mObserverLists->mStageObservers;
+ break;
+ default: {
+ // Invalid IO operation, see documentation comment for
+ // IOInterposer::Report()
+ MOZ_ASSERT(false);
+ // Just ignore it in non-debug builds.
+ return;
+ }
+ }
+ MOZ_ASSERT(observers);
+
+ // Inform observers
+ for (auto i = observers->begin(), e = observers->end(); i != e; ++i) {
+ (*i)->Observe(aObservation);
+ }
+ mIsHandlingObservation = false;
+ }
+
+ inline uint32_t GetCurrentGeneration() const { return mCurrentGeneration; }
+
+ inline bool IsMainThread() const { return mIsMainThread; }
+
+ inline void SetObserverLists(uint32_t aNewGeneration,
+ RefPtr<ObserverLists>& aNewLists)
+ {
+ mCurrentGeneration = aNewGeneration;
+ mObserverLists = aNewLists;
+ }
+
+ inline void ClearObserverLists()
+ {
+ if (mObserverLists) {
+ mCurrentGeneration = 0;
+ mObserverLists = nullptr;
+ }
+ }
+
+private:
+ bool mIsMainThread;
+ bool mIsHandlingObservation;
+ uint32_t mCurrentGeneration;
+ RefPtr<ObserverLists> mObserverLists;
+};
+
+class MasterList
+{
+public:
+ MasterList()
+ : mObservedOperations(IOInterposeObserver::OpNone)
+ , mIsEnabled(true)
+ {
+ MOZ_COUNT_CTOR(MasterList);
+ }
+
+ ~MasterList()
+ {
+ MOZ_COUNT_DTOR(MasterList);
+ }
+
+ inline void Disable() { mIsEnabled = false; }
+ inline void Enable() { mIsEnabled = true; }
+
+ void Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+ {
+ IOInterposer::AutoLock lock(mLock);
+
+ ObserverLists* newLists = nullptr;
+ if (mObserverLists) {
+ newLists = new ObserverLists(*mObserverLists);
+ } else {
+ newLists = new ObserverLists();
+ }
+ // You can register to observe multiple types of observations
+ // but you'll never be registered twice for the same observations.
+ if (aOp & IOInterposeObserver::OpCreateOrOpen &&
+ !VectorContains(newLists->mCreateObservers, aObserver)) {
+ newLists->mCreateObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpRead &&
+ !VectorContains(newLists->mReadObservers, aObserver)) {
+ newLists->mReadObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpWrite &&
+ !VectorContains(newLists->mWriteObservers, aObserver)) {
+ newLists->mWriteObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpFSync &&
+ !VectorContains(newLists->mFSyncObservers, aObserver)) {
+ newLists->mFSyncObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpStat &&
+ !VectorContains(newLists->mStatObservers, aObserver)) {
+ newLists->mStatObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpClose &&
+ !VectorContains(newLists->mCloseObservers, aObserver)) {
+ newLists->mCloseObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpNextStage &&
+ !VectorContains(newLists->mStageObservers, aObserver)) {
+ newLists->mStageObservers.push_back(aObserver);
+ }
+ mObserverLists = newLists;
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations | aOp);
+
+ mCurrentGeneration++;
+ }
+
+ void Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+ {
+ IOInterposer::AutoLock lock(mLock);
+
+ ObserverLists* newLists = nullptr;
+ if (mObserverLists) {
+ newLists = new ObserverLists(*mObserverLists);
+ } else {
+ newLists = new ObserverLists();
+ }
+
+ if (aOp & IOInterposeObserver::OpCreateOrOpen) {
+ VectorRemove(newLists->mCreateObservers, aObserver);
+ if (newLists->mCreateObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpCreateOrOpen);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpRead) {
+ VectorRemove(newLists->mReadObservers, aObserver);
+ if (newLists->mReadObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpRead);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpWrite) {
+ VectorRemove(newLists->mWriteObservers, aObserver);
+ if (newLists->mWriteObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpWrite);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpFSync) {
+ VectorRemove(newLists->mFSyncObservers, aObserver);
+ if (newLists->mFSyncObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpFSync);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpStat) {
+ VectorRemove(newLists->mStatObservers, aObserver);
+ if (newLists->mStatObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpStat);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpClose) {
+ VectorRemove(newLists->mCloseObservers, aObserver);
+ if (newLists->mCloseObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpClose);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpNextStage) {
+ VectorRemove(newLists->mStageObservers, aObserver);
+ if (newLists->mStageObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpNextStage);
+ }
+ }
+ mObserverLists = newLists;
+ mCurrentGeneration++;
+ }
+
+ void Update(PerThreadData& aPtd)
+ {
+ if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
+ return;
+ }
+ // If the generation counts don't match then we need to update the current
+ // thread's observer list with the new master list.
+ IOInterposer::AutoLock lock(mLock);
+ aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
+ }
+
+ inline bool IsObservedOperation(IOInterposeObserver::Operation aOp)
+ {
+ // The quick reader may observe that no locks are being employed here,
+ // hence the result of the operations is truly undefined. However, most
+ // computers will usually return either true or false, which is good enough.
+ // It is not a problem if we occasionally report more or less IO than is
+ // actually occurring.
+ return mIsEnabled && !!(mObservedOperations & aOp);
+ }
+
+private:
+ RefPtr<ObserverLists> mObserverLists;
+ // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked
+ // (We want to monitor IO during shutdown). Furthermore, as we may have to
+ // unregister observers during shutdown an OffTheBooksMutex is not an option
+ // either, as its base calls into sDeadlockDetector which may be nullptr
+ // during shutdown.
+ IOInterposer::Mutex mLock;
+ // Flags tracking which operations are being observed
+ IOInterposeObserver::Operation mObservedOperations;
+ // Used for quickly disabling everything by IOInterposer::Disable()
+ Atomic<bool> mIsEnabled;
+ // Used to inform threads that the master observer list has changed
+ Atomic<uint32_t> mCurrentGeneration;
+};
+
+// Special observation used by IOInterposer::EnteringNextStage()
+class NextStageObservation : public IOInterposeObserver::Observation
+{
+public:
+ NextStageObservation()
+ : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
+ "IOInterposer", false)
+ {
+ mStart = TimeStamp::Now();
+ mEnd = mStart;
+ }
+};
+
+// List of observers registered
+static StaticAutoPtr<MasterList> sMasterList;
+static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
+static bool sThreadLocalDataInitialized;
+} // namespace
+
+IOInterposeObserver::Observation::Observation(Operation aOperation,
+ const char* aReference,
+ bool aShouldReport)
+ : mOperation(aOperation)
+ , mReference(aReference)
+ , mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
+ aShouldReport)
+{
+ if (mShouldReport) {
+ mStart = TimeStamp::Now();
+ }
+}
+
+IOInterposeObserver::Observation::Observation(Operation aOperation,
+ const TimeStamp& aStart,
+ const TimeStamp& aEnd,
+ const char* aReference)
+ : mOperation(aOperation)
+ , mStart(aStart)
+ , mEnd(aEnd)
+ , mReference(aReference)
+ , mShouldReport(false)
+{
+}
+
+const char*
+IOInterposeObserver::Observation::ObservedOperationString() const
+{
+ switch (mOperation) {
+ case OpCreateOrOpen:
+ return "create/open";
+ case OpRead:
+ return "read";
+ case OpWrite:
+ return "write";
+ case OpFSync:
+ return "fsync";
+ case OpStat:
+ return "stat";
+ case OpClose:
+ return "close";
+ case OpNextStage:
+ return "NextStage";
+ default:
+ return "unknown";
+ }
+}
+
+void
+IOInterposeObserver::Observation::Report()
+{
+ if (mShouldReport) {
+ mEnd = TimeStamp::Now();
+ IOInterposer::Report(*this);
+ }
+}
+
+bool
+IOInterposer::Init()
+{
+ // Don't initialize twice...
+ if (sMasterList) {
+ return true;
+ }
+ if (!sThreadLocalData.init()) {
+ return false;
+ }
+ sThreadLocalDataInitialized = true;
+ bool isMainThread = true;
+ RegisterCurrentThread(isMainThread);
+ sMasterList = new MasterList();
+
+ MainThreadIOLogger::Init();
+
+ // Now we initialize the various interposers depending on platform
+ InitPoisonIOInterposer();
+ // We don't hook NSPR on Windows because PoisonIOInterposer captures a
+ // superset of the former's events.
+#if !defined(XP_WIN)
+ InitNSPRIOInterposing();
+#endif
+ return true;
+}
+
+bool
+IOInterposeObserver::IsMainThread()
+{
+ if (!sThreadLocalDataInitialized) {
+ return false;
+ }
+ PerThreadData* ptd = sThreadLocalData.get();
+ if (!ptd) {
+ return false;
+ }
+ return ptd->IsMainThread();
+}
+
+void
+IOInterposer::Clear()
+{
+ /* Clear() is a no-op on release builds so that we may continue to trap I/O
+ until process termination. In leak-checking builds, we need to shut down
+ IOInterposer so that all references are properly released. */
+#ifdef NS_FREE_PERMANENT_DATA
+ UnregisterCurrentThread();
+ sMasterList = nullptr;
+#endif
+}
+
+void
+IOInterposer::Disable()
+{
+ if (!sMasterList) {
+ return;
+ }
+ sMasterList->Disable();
+}
+
+void
+IOInterposer::Enable()
+{
+ if (!sMasterList) {
+ return;
+ }
+ sMasterList->Enable();
+}
+
+void
+IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
+{
+ PerThreadData* ptd = sThreadLocalData.get();
+ if (!ptd) {
+ // In this case the current thread is not registered with IOInterposer.
+ // Alternatively we could take the slow path and just lock everything if
+ // we're not registered. That could potentially perform poorly, though.
+ return;
+ }
+
+ if (!sMasterList) {
+ // If there is no longer a master list then we should clear the local one.
+ ptd->ClearObserverLists();
+ return;
+ }
+
+ sMasterList->Update(*ptd);
+
+ // Don't try to report if there's nobody listening.
+ if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
+ return;
+ }
+
+ ptd->CallObservers(aObservation);
+}
+
+bool
+IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
+{
+ return sMasterList && sMasterList->IsObservedOperation(aOp);
+}
+
+void
+IOInterposer::Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+{
+ MOZ_ASSERT(aObserver);
+ if (!sMasterList || !aObserver) {
+ return;
+ }
+
+ sMasterList->Register(aOp, aObserver);
+}
+
+void
+IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+{
+ if (!sMasterList) {
+ return;
+ }
+
+ sMasterList->Unregister(aOp, aObserver);
+}
+
+void
+IOInterposer::RegisterCurrentThread(bool aIsMainThread)
+{
+ if (!sThreadLocalDataInitialized) {
+ return;
+ }
+ MOZ_ASSERT(!sThreadLocalData.get());
+ PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
+ sThreadLocalData.set(curThreadData);
+}
+
+void
+IOInterposer::UnregisterCurrentThread()
+{
+ if (!sThreadLocalDataInitialized) {
+ return;
+ }
+ PerThreadData* curThreadData = sThreadLocalData.get();
+ MOZ_ASSERT(curThreadData);
+ sThreadLocalData.set(nullptr);
+ delete curThreadData;
+}
+
+void
+IOInterposer::EnteringNextStage()
+{
+ if (!sMasterList) {
+ return;
+ }
+ NextStageObservation observation;
+ Report(observation);
+}
+
diff --git a/xpcom/build/IOInterposer.h b/xpcom/build/IOInterposer.h
new file mode 100644
index 000000000..3634572a5
--- /dev/null
+++ b/xpcom/build/IOInterposer.h
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_IOInterposer_h
+#define mozilla_IOInterposer_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/GuardObjects.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+
+/**
+ * Interface for I/O interposer observers. This is separate from the
+ * IOInterposer because we have multiple uses for these observations.
+ */
+class IOInterposeObserver
+{
+public:
+ enum Operation
+ {
+ OpNone = 0,
+ OpCreateOrOpen = (1 << 0),
+ OpRead = (1 << 1),
+ OpWrite = (1 << 2),
+ OpFSync = (1 << 3),
+ OpStat = (1 << 4),
+ OpClose = (1 << 5),
+ OpNextStage = (1 << 6), // Meta - used when leaving startup, entering shutdown
+ OpWriteFSync = (OpWrite | OpFSync),
+ OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose),
+ OpAllWithStaging = (OpAll | OpNextStage)
+ };
+
+ /** A representation of an I/O observation */
+ class Observation
+ {
+ protected:
+ /**
+ * This constructor is for use by subclasses that are intended to take
+ * timing measurements via RAII. The |aShouldReport| parameter may be
+ * used to make the measurement and reporting conditional on the
+ * satisfaction of an arbitrary predicate that was evaluated
+ * in the subclass. Note that IOInterposer::IsObservedOperation() is
+ * always ANDed with aShouldReport, so the subclass does not need to
+ * include a call to that function explicitly.
+ */
+ Observation(Operation aOperation, const char* aReference,
+ bool aShouldReport = true);
+
+ public:
+ /**
+ * Since this constructor accepts start and end times, it does *not* take
+ * its own timings, nor does it report itself.
+ */
+ Observation(Operation aOperation, const TimeStamp& aStart,
+ const TimeStamp& aEnd, const char* aReference);
+
+ /**
+ * Operation observed, this is one of the individual Operation values.
+ * Combinations of these flags are only used when registering observers.
+ */
+ Operation ObservedOperation() const { return mOperation; }
+
+ /**
+ * Return the observed operation as a human-readable string.
+ */
+ const char* ObservedOperationString() const;
+
+ /** Time at which the I/O operation was started */
+ TimeStamp Start() const { return mStart; }
+
+ /**
+ * Time at which the I/O operation ended, for asynchronous methods this is
+ * the time at which the call initiating the asynchronous request returned.
+ */
+ TimeStamp End() const { return mEnd; }
+
+ /**
+ * Duration of the operation, for asynchronous I/O methods this is the
+ * duration of the call initiating the asynchronous request.
+ */
+ TimeDuration Duration() const { return mEnd - mStart; }
+
+ /**
+ * IO reference, function name or name of component (sqlite) that did IO
+ * this is in addition the generic operation. This attribute may be platform
+ * specific, but should only take a finite number of distinct values.
+ * E.g. sqlite-commit, CreateFile, NtReadFile, fread, fsync, mmap, etc.
+ * I.e. typically the platform specific function that did the IO.
+ */
+ const char* Reference() const { return mReference; }
+
+ /** Request filename associated with the I/O operation, null if unknown */
+ virtual const char16_t* Filename() { return nullptr; }
+
+ virtual ~Observation() {}
+
+ protected:
+ void
+ Report();
+
+ Operation mOperation;
+ TimeStamp mStart;
+ TimeStamp mEnd;
+ const char* mReference; // Identifies the source of the Observation
+ bool mShouldReport; // Measure and report if true
+ };
+
+ /**
+ * Invoked whenever an implementation of the IOInterposeObserver should
+ * observe aObservation. Implement this and do your thing...
+ * But do consider if it is wise to use IO functions in this method, they are
+ * likely to cause recursion :)
+ * At least, see PoisonIOInterposer.h and register your handle as a debug file
+ * even, if you don't initialize the poison IO interposer, someone else might.
+ *
+ * Remark: Observations may occur on any thread.
+ */
+ virtual void Observe(Observation& aObservation) = 0;
+
+ virtual ~IOInterposeObserver() {}
+
+protected:
+ /**
+ * We don't use NS_IsMainThread() because we need to be able to determine the
+ * main thread outside of XPCOM Initialization. IOInterposer observers should
+ * call this function instead.
+ */
+ static bool IsMainThread();
+};
+
+/**
+ * These functions are responsible for ensuring that events are routed to the
+ * appropriate observers.
+ */
+namespace IOInterposer {
+
+/**
+ * This function must be called from the main-thread when no other threads are
+ * running before any of the other methods on this class may be used.
+ *
+ * IO reports can however, safely assume that IsObservedOperation() will
+ * return false until the IOInterposer is initialized.
+ *
+ * Remark, it's safe to call this method multiple times, so just call it when
+ * you to utilize IO interposing.
+ *
+ * Using the IOInterposerInit class is preferred to calling this directly.
+ */
+bool Init();
+
+/**
+ * This function must be called from the main thread, and furthermore
+ * it must be called when no other threads are executing. Effectively
+ * restricting us to calling it only during shutdown.
+ *
+ * Callers should take care that no other consumers are subscribed to events,
+ * as these events will stop when this function is called.
+ *
+ * In practice, we don't use this method as the IOInterposer is used for
+ * late-write checks.
+ */
+void Clear();
+
+/**
+ * This function immediately disables IOInterposer functionality in a fast,
+ * thread-safe manner. Primarily for use by the crash reporter.
+ */
+void Disable();
+
+/**
+ * This function re-enables IOInterposer functionality in a fast, thread-safe
+ * manner. Primarily for use by the crash reporter.
+ */
+void Enable();
+
+/**
+ * Report IO to registered observers.
+ * Notice that the reported operation must be either OpRead, OpWrite or
+ * OpFSync. You are not allowed to report an observation with OpWriteFSync or
+ * OpAll, these are just auxiliary values for use with Register().
+ *
+ * If the IO call you're reporting does multiple things, write and fsync, you
+ * can choose to call Report() twice once with write and once with FSync. You
+ * may not call Report() with OpWriteFSync! The Observation::mOperation
+ * attribute is meant to be generic, not perfect.
+ *
+ * Notice that there is no reason to report an observation with an operation
+ * which is not being observed. Use IsObservedOperation() to check if the
+ * operation you are about to report is being observed. This is especially
+ * important if you are constructing expensive observations containing
+ * filename and full-path.
+ *
+ * Remark: Init() must be called before any IO is reported. But
+ * IsObservedOperation() will return false until Init() is called.
+ */
+void Report(IOInterposeObserver::Observation& aObservation);
+
+/**
+ * Return whether or not an operation is observed. Reporters should not
+ * report operations that are not being observed by anybody. This mechanism
+ * allows us to avoid reporting I/O when no observers are registered.
+ */
+bool IsObservedOperation(IOInterposeObserver::Operation aOp);
+
+/**
+ * Register IOInterposeObserver, the observer object will receive all
+ * observations for the given operation aOp.
+ *
+ * Remark: Init() must be called before observers are registered.
+ */
+void Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver);
+
+/**
+ * Unregister an IOInterposeObserver for a given operation
+ * Remark: It is always safe to unregister for all operations, even if yoú
+ * didn't register for them all.
+ * I.e. IOInterposer::Unregister(IOInterposeObserver::OpAll, aObserver)
+ *
+ * Remark: Init() must be called before observers are unregistered.
+ */
+void Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver);
+
+/**
+ * Registers the current thread with the IOInterposer. This must be done to
+ * ensure that per-thread data is created in an orderly fashion.
+ * We could have written this to initialize that data lazily, however this
+ * could have unintended consequences if a thread that is not aware of
+ * IOInterposer was implicitly registered: its per-thread data would never
+ * be deleted because it would not know to unregister itself.
+ *
+ * @param aIsMainThread true if IOInterposer should treat the current thread
+ * as the main thread.
+ */
+void RegisterCurrentThread(bool aIsMainThread = false);
+
+/**
+ * Unregisters the current thread with the IOInterposer. This is important
+ * to call when a thread is shutting down because it cleans up data that
+ * is stored in a TLS slot.
+ */
+void UnregisterCurrentThread();
+
+/**
+ * Called to inform observers that the process has transitioned out of the
+ * startup stage or into the shutdown stage. Main thread only.
+ */
+void EnteringNextStage();
+
+} // namespace IOInterposer
+
+class IOInterposerInit
+{
+public:
+ IOInterposerInit()
+ {
+#if !defined(RELEASE_OR_BETA)
+ IOInterposer::Init();
+#endif
+ }
+
+ ~IOInterposerInit()
+ {
+#if !defined(RELEASE_OR_BETA)
+ IOInterposer::Clear();
+#endif
+ }
+};
+
+class MOZ_RAII AutoIOInterposerDisable final
+{
+public:
+ explicit AutoIOInterposerDisable(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ IOInterposer::Disable();
+ }
+ ~AutoIOInterposerDisable()
+ {
+ IOInterposer::Enable();
+ }
+
+private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+} // namespace mozilla
+
+#endif // mozilla_IOInterposer_h
diff --git a/xpcom/build/IOInterposerPrivate.h b/xpcom/build/IOInterposerPrivate.h
new file mode 100644
index 000000000..549321062
--- /dev/null
+++ b/xpcom/build/IOInterposerPrivate.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef xpcom_build_IOInterposerPrivate_h
+#define xpcom_build_IOInterposerPrivate_h
+
+/* This header file contains declarations for helper classes that are
+ to be used exclusively by IOInterposer and its observers. This header
+ file is not to be used by anything else and MUST NOT be exported! */
+
+#include <prcvar.h>
+#include <prlock.h>
+
+namespace mozilla {
+namespace IOInterposer {
+
+/**
+ * The following classes are simple wrappers for PRLock and PRCondVar.
+ * IOInterposer and friends use these instead of Mozilla::Mutex et al because
+ * of the fact that IOInterposer is permitted to run until the process
+ * terminates; we can't use anything that plugs into leak checkers or deadlock
+ * detectors because IOInterposer will outlive those and generate false
+ * positives.
+ */
+
+class Monitor
+{
+public:
+ Monitor()
+ : mLock(PR_NewLock())
+ , mCondVar(PR_NewCondVar(mLock))
+ {
+ }
+
+ ~Monitor()
+ {
+ PR_DestroyCondVar(mCondVar);
+ mCondVar = nullptr;
+ PR_DestroyLock(mLock);
+ mLock = nullptr;
+ }
+
+ void Lock()
+ {
+ PR_Lock(mLock);
+ }
+
+ void Unlock()
+ {
+ PR_Unlock(mLock);
+ }
+
+ bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return PR_WaitCondVar(mCondVar, aTimeout) == PR_SUCCESS;
+ }
+
+ bool Notify()
+ {
+ return PR_NotifyCondVar(mCondVar) == PR_SUCCESS;
+ }
+
+private:
+ PRLock* mLock;
+ PRCondVar* mCondVar;
+};
+
+class MonitorAutoLock
+{
+public:
+ explicit MonitorAutoLock(Monitor& aMonitor)
+ : mMonitor(aMonitor)
+ {
+ mMonitor.Lock();
+ }
+
+ ~MonitorAutoLock()
+ {
+ mMonitor.Unlock();
+ }
+
+ bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mMonitor.Wait(aTimeout);
+ }
+
+ bool Notify()
+ {
+ return mMonitor.Notify();
+ }
+
+private:
+ Monitor& mMonitor;
+};
+
+class MonitorAutoUnlock
+{
+public:
+ explicit MonitorAutoUnlock(Monitor& aMonitor)
+ : mMonitor(aMonitor)
+ {
+ mMonitor.Unlock();
+ }
+
+ ~MonitorAutoUnlock()
+ {
+ mMonitor.Lock();
+ }
+
+private:
+ Monitor& mMonitor;
+};
+
+class Mutex
+{
+public:
+ Mutex()
+ : mPRLock(PR_NewLock())
+ {
+ }
+
+ ~Mutex()
+ {
+ PR_DestroyLock(mPRLock);
+ mPRLock = nullptr;
+ }
+
+ void Lock()
+ {
+ PR_Lock(mPRLock);
+ }
+
+ void Unlock()
+ {
+ PR_Unlock(mPRLock);
+ }
+
+private:
+ PRLock* mPRLock;
+};
+
+class AutoLock
+{
+public:
+ explicit AutoLock(Mutex& aLock)
+ : mLock(aLock)
+ {
+ mLock.Lock();
+ }
+
+ ~AutoLock()
+ {
+ mLock.Unlock();
+ }
+
+private:
+ Mutex& mLock;
+};
+
+} // namespace IOInterposer
+} // namespace mozilla
+
+#endif // xpcom_build_IOInterposerPrivate_h
+
diff --git a/xpcom/build/LateWriteChecks.cpp b/xpcom/build/LateWriteChecks.cpp
new file mode 100644
index 000000000..69dbedc0f
--- /dev/null
+++ b/xpcom/build/LateWriteChecks.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+
+#include "mozilla/IOInterposer.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsPrintfCString.h"
+#include "mozilla/StackWalk.h"
+#include "plstr.h"
+#include "prio.h"
+
+#ifdef XP_WIN
+#define NS_T(str) L ## str
+#define NS_SLASH "\\"
+#include <fcntl.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <windows.h>
+#else
+#define NS_SLASH "/"
+#endif
+
+#include "LateWriteChecks.h"
+
+#define OBSERVE_LATE_WRITES
+
+using namespace mozilla;
+
+/*************************** Auxiliary Declarations ***************************/
+
+// This a wrapper over a file descriptor that provides a Printf method and
+// computes the sha1 of the data that passes through it.
+class SHA1Stream
+{
+public:
+ explicit SHA1Stream(FILE* aStream)
+ : mFile(aStream)
+ {
+ MozillaRegisterDebugFILE(mFile);
+ }
+
+ void Printf(const char* aFormat, ...)
+ {
+ MOZ_ASSERT(mFile);
+ va_list list;
+ va_start(list, aFormat);
+ nsAutoCString str;
+ str.AppendPrintf(aFormat, list);
+ va_end(list);
+ mSHA1.update(str.get(), str.Length());
+ fwrite(str.get(), 1, str.Length(), mFile);
+ }
+ void Finish(SHA1Sum::Hash& aHash)
+ {
+ int fd = fileno(mFile);
+ fflush(mFile);
+ MozillaUnRegisterDebugFD(fd);
+ fclose(mFile);
+ mSHA1.finish(aHash);
+ mFile = nullptr;
+ }
+private:
+ FILE* mFile;
+ SHA1Sum mSHA1;
+};
+
+static void
+RecordStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+ std::vector<uintptr_t>* stack =
+ static_cast<std::vector<uintptr_t>*>(aClosure);
+ stack->push_back(reinterpret_cast<uintptr_t>(aPC));
+}
+
+/**************************** Late-Write Observer ****************************/
+
+/**
+ * An implementation of IOInterposeObserver to be registered with IOInterposer.
+ * This observer logs all writes as late writes.
+ */
+class LateWriteObserver final : public IOInterposeObserver
+{
+public:
+ explicit LateWriteObserver(const char* aProfileDirectory)
+ : mProfileDirectory(PL_strdup(aProfileDirectory))
+ {
+ }
+ ~LateWriteObserver()
+ {
+ PL_strfree(mProfileDirectory);
+ mProfileDirectory = nullptr;
+ }
+
+ void Observe(IOInterposeObserver::Observation& aObservation);
+private:
+ char* mProfileDirectory;
+};
+
+void
+LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
+{
+#ifdef OBSERVE_LATE_WRITES
+ // Crash if that is the shutdown check mode
+ if (gShutdownChecks == SCM_CRASH) {
+ MOZ_CRASH();
+ }
+
+ // If we have shutdown mode SCM_NOTHING or we can't record then abort
+ if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecordExtended()) {
+ return;
+ }
+
+ // Write the stack and loaded libraries to a file. We can get here
+ // concurrently from many writes, so we use multiple temporary files.
+ std::vector<uintptr_t> rawStack;
+
+ MozStackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+ reinterpret_cast<void*>(&rawStack), 0, nullptr);
+ Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
+
+ nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
+ NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
+ char* name;
+ nameAux.GetMutableData(&name);
+
+ // We want the sha1 of the entire file, so please don't write to fd
+ // directly; use sha1Stream.
+ FILE* stream;
+#ifdef XP_WIN
+ HANDLE hFile;
+ do {
+ // mkstemp isn't supported so keep trying until we get a file
+ int result = _mktemp_s(name, strlen(name) + 1);
+ hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ } while (GetLastError() == ERROR_FILE_EXISTS);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ NS_RUNTIMEABORT("Um, how did we get here?");
+ }
+
+ // http://support.microsoft.com/kb/139640
+ int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
+ if (fd == -1) {
+ NS_RUNTIMEABORT("Um, how did we get here?");
+ }
+
+ stream = _fdopen(fd, "w");
+#else
+ int fd = mkstemp(name);
+ stream = fdopen(fd, "w");
+#endif
+
+ SHA1Stream sha1Stream(stream);
+
+ size_t numModules = stack.GetNumModules();
+ sha1Stream.Printf("%u\n", (unsigned)numModules);
+ for (size_t i = 0; i < numModules; ++i) {
+ Telemetry::ProcessedStack::Module module = stack.GetModule(i);
+ sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
+ module.mName.c_str());
+ }
+
+ size_t numFrames = stack.GetStackSize();
+ sha1Stream.Printf("%u\n", (unsigned)numFrames);
+ for (size_t i = 0; i < numFrames; ++i) {
+ const Telemetry::ProcessedStack::Frame& frame = stack.GetFrame(i);
+ // NOTE: We write the offsets, while the atos tool expects a value with
+ // the virtual address added. For example, running otool -l on the the firefox
+ // binary shows
+ // cmd LC_SEGMENT_64
+ // cmdsize 632
+ // segname __TEXT
+ // vmaddr 0x0000000100000000
+ // so to print the line matching the offset 123 one has to run
+ // atos -o firefox 0x100000123.
+ sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
+ }
+
+ SHA1Sum::Hash sha1;
+ sha1Stream.Finish(sha1);
+
+ // Note: These files should be deleted by telemetry once it reads them. If
+ // there were no telemetry runs by the time we shut down, we just add files
+ // to the existing ones instead of replacing them. Given that each of these
+ // files is a bug to be fixed, that is probably the right thing to do.
+
+ // We append the sha1 of the contents to the file name. This provides a simple
+ // client side deduplication.
+ nsPrintfCString finalName("%s%s", mProfileDirectory,
+ "/Telemetry.LateWriteFinal-");
+ for (int i = 0; i < 20; ++i) {
+ finalName.AppendPrintf("%02x", sha1[i]);
+ }
+ PR_Delete(finalName.get());
+ PR_Rename(name, finalName.get());
+#endif
+}
+
+/******************************* Setup/Teardown *******************************/
+
+static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
+
+namespace mozilla {
+
+void
+InitLateWriteChecks()
+{
+ nsCOMPtr<nsIFile> mozFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
+ if (mozFile) {
+ nsAutoCString nativePath;
+ nsresult rv = mozFile->GetNativePath(nativePath);
+ if (NS_SUCCEEDED(rv) && nativePath.get()) {
+ sLateWriteObserver = new LateWriteObserver(nativePath.get());
+ }
+ }
+}
+
+void
+BeginLateWriteChecks()
+{
+ if (sLateWriteObserver) {
+ IOInterposer::Register(
+ IOInterposeObserver::OpWriteFSync,
+ sLateWriteObserver
+ );
+ }
+}
+
+void
+StopLateWriteChecks()
+{
+ if (sLateWriteObserver) {
+ IOInterposer::Unregister(
+ IOInterposeObserver::OpAll,
+ sLateWriteObserver
+ );
+ // Deallocation would not be thread-safe, and StopLateWriteChecks() is
+ // called at shutdown and only in special cases.
+ // sLateWriteObserver = nullptr;
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/LateWriteChecks.h b/xpcom/build/LateWriteChecks.h
new file mode 100644
index 000000000..96d981cc1
--- /dev/null
+++ b/xpcom/build/LateWriteChecks.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_LateWriteChecks_h
+#define mozilla_LateWriteChecks_h
+
+// This file, along with LateWriteChecks.cpp, serves to check for and report
+// late writes. The idea is discover writes to the file system that happens
+// during shutdown such that these maybe be moved forward and the process may be
+// killed without waiting for static destructors.
+
+namespace mozilla {
+
+/** Different shutdown check modes */
+enum ShutdownChecksMode
+{
+ SCM_CRASH, /** Crash on shutdown check failure */
+ SCM_RECORD, /** Record shutdown check violations */
+ SCM_NOTHING /** Don't attempt any shutdown checks */
+};
+
+/**
+ * Current shutdown check mode.
+ * This variable is defined and initialized in nsAppRunner.cpp
+ */
+extern ShutdownChecksMode gShutdownChecks;
+
+/**
+ * Allocate structures and acquire information from XPCOM necessary to do late
+ * write checks. This function must be invoked before BeginLateWriteChecks()
+ * and before XPCOM has stopped working.
+ */
+void InitLateWriteChecks();
+
+/**
+ * Begin recording all writes as late-writes. This function should be called
+ * when all legitimate writes have occurred. This function does not rely on
+ * XPCOM as it is designed to be invoked during XPCOM shutdown.
+ *
+ * For late-write checks to work you must initialize one or more backends that
+ * reports IO through the IOInterposer API. PoisonIOInterposer would probably
+ * be the backend of choice in this case.
+ *
+ * Note: BeginLateWriteChecks() must have been invoked before this function.
+ */
+void BeginLateWriteChecks();
+
+/**
+ * Stop recording all writes as late-writes, call this function when you want
+ * late-write checks to stop. I.e. exception handling, or the special case on
+ * Mac described in bug 826029.
+ */
+void StopLateWriteChecks();
+
+} // namespace mozilla
+
+#endif // mozilla_LateWriteChecks_h
diff --git a/xpcom/build/MainThreadIOLogger.cpp b/xpcom/build/MainThreadIOLogger.cpp
new file mode 100644
index 000000000..0ca942cb9
--- /dev/null
+++ b/xpcom/build/MainThreadIOLogger.cpp
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MainThreadIOLogger.h"
+
+#include "GeckoProfiler.h"
+#include "IOInterposerPrivate.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "nsAutoPtr.h"
+#include "nsNativeCharsetUtils.h"
+
+/**
+ * This code uses NSPR stuff and STL containers because it must be detached
+ * from leak checking code; this observer runs until the process terminates.
+ */
+
+#include <prenv.h>
+#include <prprf.h>
+#include <prthread.h>
+#include <vector>
+
+namespace {
+
+struct ObservationWithStack
+{
+ ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs,
+ ProfilerBacktrace* aStack)
+ : mObservation(aObs)
+ , mStack(aStack)
+ {
+ const char16_t* filename = aObs.Filename();
+ if (filename) {
+ mFilename = filename;
+ }
+ }
+
+ mozilla::IOInterposeObserver::Observation mObservation;
+ ProfilerBacktrace* mStack;
+ nsString mFilename;
+};
+
+class MainThreadIOLoggerImpl final : public mozilla::IOInterposeObserver
+{
+public:
+ MainThreadIOLoggerImpl();
+ ~MainThreadIOLoggerImpl();
+
+ bool Init();
+
+ void Observe(Observation& aObservation);
+
+private:
+ static void sIOThreadFunc(void* aArg);
+ void IOThreadFunc();
+
+ TimeStamp mLogStartTime;
+ const char* mFileName;
+ PRThread* mIOThread;
+ IOInterposer::Monitor mMonitor;
+ bool mShutdownRequired;
+ std::vector<ObservationWithStack> mObservations;
+};
+
+static StaticAutoPtr<MainThreadIOLoggerImpl> sImpl;
+
+MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
+ : mFileName(nullptr)
+ , mIOThread(nullptr)
+ , mShutdownRequired(false)
+{
+}
+
+MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl()
+{
+ if (!mIOThread) {
+ return;
+ }
+ {
+ // Scope for lock
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ mShutdownRequired = true;
+ lock.Notify();
+ }
+ PR_JoinThread(mIOThread);
+ mIOThread = nullptr;
+}
+
+bool
+MainThreadIOLoggerImpl::Init()
+{
+ if (mFileName) {
+ // Already initialized
+ return true;
+ }
+ mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG");
+ if (!mFileName) {
+ // Can't start
+ return false;
+ }
+ mIOThread = PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this,
+ PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (!mIOThread) {
+ return false;
+ }
+ return true;
+}
+
+/* static */ void
+MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg)
+{
+ PR_SetCurrentThreadName("MainThreadIOLogger");
+ MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg);
+ obj->IOThreadFunc();
+}
+
+void
+MainThreadIOLoggerImpl::IOThreadFunc()
+{
+ PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ PR_IRUSR | PR_IWUSR | PR_IRGRP);
+ if (!fd) {
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ mShutdownRequired = true;
+ std::vector<ObservationWithStack>().swap(mObservations);
+ return;
+ }
+ mLogStartTime = TimeStamp::Now();
+ {
+ // Scope for lock
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ while (true) {
+ while (!mShutdownRequired && mObservations.empty()) {
+ lock.Wait();
+ }
+ if (mShutdownRequired) {
+ break;
+ }
+ // Pull events off the shared array onto a local one
+ std::vector<ObservationWithStack> observationsToWrite;
+ observationsToWrite.swap(mObservations);
+
+ // Release the lock so that we're not holding anybody up during I/O
+ IOInterposer::MonitorAutoUnlock unlock(mMonitor);
+
+ // Now write the events.
+ for (auto i = observationsToWrite.begin(), e = observationsToWrite.end();
+ i != e; ++i) {
+ if (i->mObservation.ObservedOperation() == OpNextStage) {
+ PR_fprintf(fd, "%f,NEXT-STAGE\n",
+ (TimeStamp::Now() - mLogStartTime).ToMilliseconds());
+ continue;
+ }
+ double durationMs = i->mObservation.Duration().ToMilliseconds();
+ nsAutoCString nativeFilename;
+ nativeFilename.AssignLiteral("(not available)");
+ if (!i->mFilename.IsEmpty()) {
+ if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) {
+ nativeFilename.AssignLiteral("(conversion failed)");
+ }
+ }
+ /**
+ * Format:
+ * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
+ */
+ if (PR_fprintf(fd, "%f,%s,%f,%s,%s\n",
+ (i->mObservation.Start() - mLogStartTime).ToMilliseconds(),
+ i->mObservation.ObservedOperationString(), durationMs,
+ i->mObservation.Reference(), nativeFilename.get()) > 0) {
+ ProfilerBacktrace* stack = i->mStack;
+ if (stack) {
+ // TODO: Write out the callstack
+ // (This will be added in a later bug)
+ profiler_free_backtrace(stack);
+ }
+ }
+ }
+ }
+ }
+ PR_Close(fd);
+}
+
+void
+MainThreadIOLoggerImpl::Observe(Observation& aObservation)
+{
+ if (!mFileName || !IsMainThread()) {
+ return;
+ }
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ if (mShutdownRequired) {
+ // The writer thread isn't running. Don't enqueue any more data.
+ return;
+ }
+ // Passing nullptr as aStack parameter for now
+ mObservations.push_back(ObservationWithStack(aObservation, nullptr));
+ lock.Notify();
+}
+
+} // namespace
+
+namespace mozilla {
+
+namespace MainThreadIOLogger {
+
+bool
+Init()
+{
+ nsAutoPtr<MainThreadIOLoggerImpl> impl(new MainThreadIOLoggerImpl());
+ if (!impl->Init()) {
+ return false;
+ }
+ sImpl = impl.forget();
+ IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl);
+ return true;
+}
+
+} // namespace MainThreadIOLogger
+
+} // namespace mozilla
+
diff --git a/xpcom/build/MainThreadIOLogger.h b/xpcom/build/MainThreadIOLogger.h
new file mode 100644
index 000000000..df9e30b53
--- /dev/null
+++ b/xpcom/build/MainThreadIOLogger.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_MainThreadIOLogger_h
+#define mozilla_MainThreadIOLogger_h
+
+namespace mozilla {
+namespace MainThreadIOLogger {
+
+bool Init();
+
+} // namespace MainThreadIOLogger
+} // namespace mozilla
+
+#endif // mozilla_MainThreadIOLogger_h
+
diff --git a/xpcom/build/NSPRInterposer.cpp b/xpcom/build/NSPRInterposer.cpp
new file mode 100644
index 000000000..a7c53a97a
--- /dev/null
+++ b/xpcom/build/NSPRInterposer.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "IOInterposer.h"
+#include "NSPRInterposer.h"
+
+#include "prio.h"
+#include "private/pprio.h"
+#include "nsDebug.h"
+#include "nscore.h"
+
+namespace {
+
+using namespace mozilla;
+
+/* Original IO methods */
+PRCloseFN sCloseFn = nullptr;
+PRReadFN sReadFn = nullptr;
+PRWriteFN sWriteFn = nullptr;
+PRFsyncFN sFSyncFn = nullptr;
+PRFileInfoFN sFileInfoFn = nullptr;
+PRFileInfo64FN sFileInfo64Fn = nullptr;
+
+/**
+ * RAII class for timing the duration of an NSPR I/O call and reporting the
+ * result to the IOInterposeObserver API.
+ */
+class NSPRIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ explicit NSPRIOAutoObservation(IOInterposeObserver::Operation aOp)
+ : IOInterposeObserver::Observation(aOp, "NSPRIOInterposer")
+ {
+ }
+
+ ~NSPRIOAutoObservation()
+ {
+ Report();
+ }
+};
+
+PRStatus PR_CALLBACK
+interposedClose(PRFileDesc* aFd)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpClose);
+ return sCloseFn(aFd);
+}
+
+int32_t PR_CALLBACK
+interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sReadFn, "NSPR IO Interposing: sReadFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpRead);
+ return sReadFn(aFd, aBuf, aAmt);
+}
+
+int32_t PR_CALLBACK
+interposedWrite(PRFileDesc* aFd, const void* aBuf, int32_t aAmt)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sWriteFn, "NSPR IO Interposing: sWriteFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpWrite);
+ return sWriteFn(aFd, aBuf, aAmt);
+}
+
+PRStatus PR_CALLBACK
+interposedFSync(PRFileDesc* aFd)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sFSyncFn, "NSPR IO Interposing: sFSyncFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpFSync);
+ return sFSyncFn(aFd);
+}
+
+PRStatus PR_CALLBACK
+interposedFileInfo(PRFileDesc* aFd, PRFileInfo* aInfo)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpStat);
+ return sFileInfoFn(aFd, aInfo);
+}
+
+PRStatus PR_CALLBACK
+interposedFileInfo64(PRFileDesc* aFd, PRFileInfo64* aInfo)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpStat);
+ return sFileInfo64Fn(aFd, aInfo);
+}
+
+} // namespace
+
+namespace mozilla {
+
+void
+InitNSPRIOInterposing()
+{
+ // Check that we have not interposed any of the IO methods before
+ MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn &&
+ !sFileInfo64Fn);
+
+ // We can't actually use this assertion because we initialize this code
+ // before XPCOM is initialized, so NS_IsMainThread() always returns false.
+ // MOZ_ASSERT(NS_IsMainThread());
+
+ // Get IO methods from NSPR and const cast the structure so we can modify it.
+ PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
+
+ // Something is badly wrong if we don't get IO methods... However, we don't
+ // want to crash over that in non-debug builds. This is unlikely to happen
+ // so an assert is enough, no need to report it to the caller.
+ MOZ_ASSERT(methods);
+ if (!methods) {
+ return;
+ }
+
+ // Store original functions
+ sCloseFn = methods->close;
+ sReadFn = methods->read;
+ sWriteFn = methods->write;
+ sFSyncFn = methods->fsync;
+ sFileInfoFn = methods->fileInfo;
+ sFileInfo64Fn = methods->fileInfo64;
+
+ // Overwrite with our interposed functions
+ methods->close = &interposedClose;
+ methods->read = &interposedRead;
+ methods->write = &interposedWrite;
+ methods->fsync = &interposedFSync;
+ methods->fileInfo = &interposedFileInfo;
+ methods->fileInfo64 = &interposedFileInfo64;
+}
+
+void
+ClearNSPRIOInterposing()
+{
+ // If we have already cleared IO interposing, or not initialized it this is
+ // actually bad.
+ MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn &&
+ sFileInfo64Fn);
+
+ // Get IO methods from NSPR and const cast the structure so we can modify it.
+ PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
+
+ // Something is badly wrong if we don't get IO methods... However, we don't
+ // want to crash over that in non-debug builds. This is unlikely to happen
+ // so an assert is enough, no need to report it to the caller.
+ MOZ_ASSERT(methods);
+ if (!methods) {
+ return;
+ }
+
+ // Restore original functions
+ methods->close = sCloseFn;
+ methods->read = sReadFn;
+ methods->write = sWriteFn;
+ methods->fsync = sFSyncFn;
+ methods->fileInfo = sFileInfoFn;
+ methods->fileInfo64 = sFileInfo64Fn;
+
+ // Forget about original functions
+ sCloseFn = nullptr;
+ sReadFn = nullptr;
+ sWriteFn = nullptr;
+ sFSyncFn = nullptr;
+ sFileInfoFn = nullptr;
+ sFileInfo64Fn = nullptr;
+}
+
+} // namespace mozilla
+
diff --git a/xpcom/build/NSPRInterposer.h b/xpcom/build/NSPRInterposer.h
new file mode 100644
index 000000000..2b8715077
--- /dev/null
+++ b/xpcom/build/NSPRInterposer.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSPRINTERPOSER_H_
+#define NSPRINTERPOSER_H_
+
+namespace mozilla {
+
+/**
+ * Initialize IO interposing for NSPR. This will report NSPR read, writes and
+ * fsyncs to the IOInterposerObserver. It is only safe to call this from the
+ * main-thread when no other threads are running.
+ */
+void InitNSPRIOInterposing();
+
+/**
+ * Removes interception of NSPR IO methods as setup by InitNSPRIOInterposing.
+ * Note, that it is only safe to call this on the main-thread when all other
+ * threads have stopped. Which is typically the case at shutdown.
+ */
+void ClearNSPRIOInterposing();
+
+} // namespace mozilla
+
+#endif // NSPRINTERPOSER_H_
diff --git a/xpcom/build/Omnijar.cpp b/xpcom/build/Omnijar.cpp
new file mode 100644
index 000000000..e1a8a1e5b
--- /dev/null
+++ b/xpcom/build/Omnijar.cpp
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Omnijar.h"
+
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsZipArchive.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+
+StaticRefPtr<nsIFile> Omnijar::sPath[2];
+StaticRefPtr<nsZipArchive> Omnijar::sReader[2];
+StaticRefPtr<nsZipArchive> Omnijar::sOuterReader[2];
+bool Omnijar::sInitialized = false;
+bool Omnijar::sIsUnified = false;
+
+static const char* sProp[2] = {
+ NS_GRE_DIR, NS_XPCOM_CURRENT_PROCESS_DIR
+};
+
+#define SPROP(Type) ((Type == mozilla::Omnijar::GRE) ? sProp[GRE] : sProp[APP])
+
+void
+Omnijar::CleanUpOne(Type aType)
+{
+ if (sReader[aType]) {
+ sReader[aType]->CloseArchive();
+ sReader[aType] = nullptr;
+ }
+ if (sOuterReader[aType]) {
+ sOuterReader[aType]->CloseArchive();
+ sOuterReader[aType] = nullptr;
+ }
+ sPath[aType] = nullptr;
+}
+
+void
+Omnijar::InitOne(nsIFile* aPath, Type aType)
+{
+ nsCOMPtr<nsIFile> file;
+ if (aPath) {
+ file = aPath;
+ } else {
+ nsCOMPtr<nsIFile> dir;
+ nsDirectoryService::gService->Get(SPROP(aType), NS_GET_IID(nsIFile),
+ getter_AddRefs(dir));
+ NS_NAMED_LITERAL_CSTRING(kOmnijarName, NS_STRINGIFY(OMNIJAR_NAME));
+ if (NS_FAILED(dir->Clone(getter_AddRefs(file))) ||
+ NS_FAILED(file->AppendNative(kOmnijarName))) {
+ return;
+ }
+ }
+ bool isFile;
+ if (NS_FAILED(file->IsFile(&isFile)) || !isFile) {
+ // If we're not using an omni.jar for GRE, and we don't have an
+ // omni.jar for APP, check if both directories are the same.
+ if ((aType == APP) && (!sPath[GRE])) {
+ nsCOMPtr<nsIFile> greDir, appDir;
+ bool equals;
+ nsDirectoryService::gService->Get(sProp[GRE], NS_GET_IID(nsIFile),
+ getter_AddRefs(greDir));
+ nsDirectoryService::gService->Get(sProp[APP], NS_GET_IID(nsIFile),
+ getter_AddRefs(appDir));
+ if (NS_SUCCEEDED(greDir->Equals(appDir, &equals)) && equals) {
+ sIsUnified = true;
+ }
+ }
+ return;
+ }
+
+ bool equals;
+ if ((aType == APP) && (sPath[GRE]) &&
+ NS_SUCCEEDED(sPath[GRE]->Equals(file, &equals)) && equals) {
+ // If we're using omni.jar on both GRE and APP and their path
+ // is the same, we're in the unified case.
+ sIsUnified = true;
+ return;
+ }
+
+ RefPtr<nsZipArchive> zipReader = new nsZipArchive();
+ if (NS_FAILED(zipReader->OpenArchive(file))) {
+ return;
+ }
+
+ RefPtr<nsZipArchive> outerReader;
+ RefPtr<nsZipHandle> handle;
+ if (NS_SUCCEEDED(nsZipHandle::Init(zipReader, NS_STRINGIFY(OMNIJAR_NAME),
+ getter_AddRefs(handle)))) {
+ outerReader = zipReader;
+ zipReader = new nsZipArchive();
+ if (NS_FAILED(zipReader->OpenArchive(handle))) {
+ return;
+ }
+ }
+
+ CleanUpOne(aType);
+ sReader[aType] = zipReader;
+ sOuterReader[aType] = outerReader;
+ sPath[aType] = file;
+}
+
+void
+Omnijar::Init(nsIFile* aGrePath, nsIFile* aAppPath)
+{
+ InitOne(aGrePath, GRE);
+ InitOne(aAppPath, APP);
+ sInitialized = true;
+}
+
+void
+Omnijar::CleanUp()
+{
+ CleanUpOne(GRE);
+ CleanUpOne(APP);
+ sInitialized = false;
+}
+
+already_AddRefed<nsZipArchive>
+Omnijar::GetReader(nsIFile* aPath)
+{
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+
+ bool equals;
+ nsresult rv;
+
+ if (sPath[GRE]) {
+ rv = sPath[GRE]->Equals(aPath, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return IsNested(GRE) ? GetOuterReader(GRE) : GetReader(GRE);
+ }
+ }
+ if (sPath[APP]) {
+ rv = sPath[APP]->Equals(aPath, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return IsNested(APP) ? GetOuterReader(APP) : GetReader(APP);
+ }
+ }
+ return nullptr;
+}
+
+nsresult
+Omnijar::GetURIString(Type aType, nsACString& aResult)
+{
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+
+ aResult.Truncate();
+
+ // Return an empty string for APP in the unified case.
+ if ((aType == APP) && sIsUnified) {
+ return NS_OK;
+ }
+
+ nsAutoCString omniJarSpec;
+ if (sPath[aType]) {
+ nsresult rv = NS_GetURLSpecFromActualFile(sPath[aType], omniJarSpec);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ aResult = "jar:";
+ if (IsNested(aType)) {
+ aResult += "jar:";
+ }
+ aResult += omniJarSpec;
+ aResult += "!";
+ if (IsNested(aType)) {
+ aResult += "/" NS_STRINGIFY(OMNIJAR_NAME) "!";
+ }
+ } else {
+ nsCOMPtr<nsIFile> dir;
+ nsDirectoryService::gService->Get(SPROP(aType), NS_GET_IID(nsIFile),
+ getter_AddRefs(dir));
+ nsresult rv = NS_GetURLSpecFromActualFile(dir, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ aResult += "/";
+ return NS_OK;
+}
+
+} /* namespace mozilla */
diff --git a/xpcom/build/Omnijar.h b/xpcom/build/Omnijar.h
new file mode 100644
index 000000000..35a208bae
--- /dev/null
+++ b/xpcom/build/Omnijar.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Omnijar_h
+#define mozilla_Omnijar_h
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsZipArchive.h"
+
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+
+class Omnijar
+{
+private:
+ /**
+ * Store an nsIFile for an omni.jar. We can store two paths here, one
+ * for GRE (corresponding to resource://gre/) and one for APP
+ * (corresponding to resource:/// and resource://app/), but only
+ * store one when both point to the same location (unified).
+ */
+ static StaticRefPtr<nsIFile> sPath[2];
+
+ /**
+ * Cached nsZipArchives for the corresponding sPath
+ */
+ static StaticRefPtr<nsZipArchive> sReader[2];
+
+ /**
+ * Cached nsZipArchives for the outer jar, when using nested jars.
+ * Otherwise nullptr.
+ */
+ static StaticRefPtr<nsZipArchive> sOuterReader[2];
+
+ /**
+ * Has Omnijar::Init() been called?
+ */
+ static bool sInitialized;
+
+ /**
+ * Is using unified GRE/APP jar?
+ */
+ static bool sIsUnified;
+
+public:
+ enum Type
+ {
+ GRE = 0,
+ APP = 1
+ };
+
+private:
+ /**
+ * Returns whether we are using nested jars.
+ */
+ static inline bool IsNested(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ return !!sOuterReader[aType];
+ }
+
+ /**
+ * Returns a nsZipArchive pointer for the outer jar file when using nested
+ * jars. Returns nullptr in the same cases GetPath() would, or if not using
+ * nested jars.
+ */
+ static inline already_AddRefed<nsZipArchive> GetOuterReader(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ RefPtr<nsZipArchive> reader = sOuterReader[aType].get();
+ return reader.forget();
+ }
+
+public:
+ /**
+ * Returns whether SetBase has been called at least once with
+ * a valid nsIFile
+ */
+ static inline bool IsInitialized() { return sInitialized; }
+
+ /**
+ * Initializes the Omnijar API with the given directory or file for GRE and
+ * APP. Each of the paths given can be:
+ * - a file path, pointing to the omnijar file,
+ * - a directory path, pointing to a directory containing an "omni.jar" file,
+ * - nullptr for autodetection of an "omni.jar" file.
+ */
+ static void Init(nsIFile* aGrePath = nullptr, nsIFile* aAppPath = nullptr);
+
+ /**
+ * Cleans up the Omnijar API
+ */
+ static void CleanUp();
+
+ /**
+ * Returns an nsIFile pointing to the omni.jar file for GRE or APP.
+ * Returns nullptr when there is no corresponding omni.jar.
+ * Also returns nullptr for APP in the unified case.
+ */
+ static inline already_AddRefed<nsIFile> GetPath(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ nsCOMPtr<nsIFile> path = sPath[aType].get();
+ return path.forget();
+ }
+
+ /**
+ * Returns whether GRE or APP use an omni.jar. Returns PR_False for
+ * APP when using an omni.jar in the unified case.
+ */
+ static inline bool HasOmnijar(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ return !!sPath[aType];
+ }
+
+ /**
+ * Returns a nsZipArchive pointer for the omni.jar file for GRE or
+ * APP. Returns nullptr in the same cases GetPath() would.
+ */
+ static inline already_AddRefed<nsZipArchive> GetReader(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ RefPtr<nsZipArchive> reader = sReader[aType].get();
+ return reader.forget();
+ }
+
+ /**
+ * Returns a nsZipArchive pointer for the given path IAOI the given
+ * path is the omni.jar for either GRE or APP.
+ */
+ static already_AddRefed<nsZipArchive> GetReader(nsIFile* aPath);
+
+ /**
+ * Returns the URI string corresponding to the omni.jar or directory
+ * for GRE or APP. i.e. jar:/path/to/omni.jar!/ for omni.jar and
+ * /path/to/base/dir/ otherwise. Returns an empty string for APP in
+ * the unified case.
+ * The returned URI is guaranteed to end with a slash.
+ */
+ static nsresult GetURIString(Type aType, nsACString& aResult);
+
+private:
+ /**
+ * Used internally, respectively by Init() and CleanUp()
+ */
+ static void InitOne(nsIFile* aPath, Type aType);
+ static void CleanUpOne(Type aType);
+}; /* class Omnijar */
+
+} /* namespace mozilla */
+
+#endif /* mozilla_Omnijar_h */
diff --git a/xpcom/build/PoisonIOInterposer.h b/xpcom/build/PoisonIOInterposer.h
new file mode 100644
index 000000000..4a4d1426d
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposer.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_PoisonIOInterposer_h
+#define mozilla_PoisonIOInterposer_h
+
+#include "mozilla/Types.h"
+#include <stdio.h>
+
+MOZ_BEGIN_EXTERN_C
+
+/** Register file handle to be ignored by poisoning IO interposer. This function
+ * and the corresponding UnRegister function are necessary for exchange of handles
+ * between binaries not using the same CRT on Windows (which happens when one of
+ * them links the static CRT). In such cases, giving file descriptors or FILEs
+ * doesn't work because _get_osfhandle fails with "invalid parameter". */
+void MozillaRegisterDebugHandle(intptr_t aHandle);
+
+/** Register file descriptor to be ignored by poisoning IO interposer */
+void MozillaRegisterDebugFD(int aFd);
+
+/** Register file to be ignored by poisoning IO interposer */
+void MozillaRegisterDebugFILE(FILE* aFile);
+
+/** Unregister file handle from being ignored by poisoning IO interposer */
+void MozillaUnRegisterDebugHandle(intptr_t aHandle);
+
+/** Unregister file descriptor from being ignored by poisoning IO interposer */
+void MozillaUnRegisterDebugFD(int aFd);
+
+/** Unregister file from being ignored by poisoning IO interposer */
+void MozillaUnRegisterDebugFILE(FILE* aFile);
+
+MOZ_END_EXTERN_C
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+
+#ifdef __cplusplus
+namespace mozilla {
+
+/**
+ * Check if a file is registered as a debug file.
+ */
+bool IsDebugFile(intptr_t aFileID);
+
+/**
+ * Initialize IO poisoning, this is only safe to do on the main-thread when no
+ * other threads are running.
+ *
+ * Please, note that this probably has performance implications as all
+ */
+void InitPoisonIOInterposer();
+
+#ifdef XP_MACOSX
+/**
+ * Check that writes are dirty before reporting I/O (Mac OS X only)
+ * This is necessary for late-write checks on Mac OS X, but reading the buffer
+ * from file to see if we're writing dirty bits is expensive, so we don't want
+ * to do this for everything else that uses
+ */
+void OnlyReportDirtyWrites();
+#endif /* XP_MACOSX */
+
+/**
+ * Clear IO poisoning, this is only safe to do on the main-thread when no other
+ * threads are running.
+ */
+void ClearPoisonIOInterposer();
+
+} // namespace mozilla
+#endif /* __cplusplus */
+
+#else /* XP_WIN || XP_MACOSX */
+
+#ifdef __cplusplus
+namespace mozilla {
+inline bool IsDebugFile(intptr_t aFileID) { return true; }
+inline void InitPoisonIOInterposer() {}
+inline void ClearPoisonIOInterposer() {}
+#ifdef XP_MACOSX
+inline void OnlyReportDirtyWrites() {}
+#endif /* XP_MACOSX */
+} // namespace mozilla
+#endif /* __cplusplus */
+
+#endif /* XP_WIN || XP_MACOSX */
+
+#endif // mozilla_PoisonIOInterposer_h
diff --git a/xpcom/build/PoisonIOInterposerBase.cpp b/xpcom/build/PoisonIOInterposerBase.cpp
new file mode 100644
index 000000000..0e5754392
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerBase.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Mutex.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/UniquePtr.h"
+
+#include <algorithm>
+
+#include "PoisonIOInterposer.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+// Auxiliary method to convert file descriptors to ids
+#if defined(XP_WIN32)
+#include <io.h>
+inline intptr_t
+FileDescriptorToHandle(int aFd)
+{
+ return _get_osfhandle(aFd);
+}
+#else
+inline intptr_t
+FileDescriptorToHandle(int aFd)
+{
+ return aFd;
+}
+#endif /* if not XP_WIN32 */
+
+using namespace mozilla;
+
+namespace {
+struct DebugFilesAutoLockTraits
+{
+ typedef PRLock* type;
+ typedef const PRLock* const_type;
+ static const_type empty() { return nullptr; }
+ static void release(type aL) { PR_Unlock(aL); }
+};
+
+class DebugFilesAutoLock : public Scoped<DebugFilesAutoLockTraits>
+{
+ static PRLock* Lock;
+public:
+ static void Clear();
+ static PRLock* getDebugFileIDsLock()
+ {
+ // On windows this static is not thread safe, but we know that the first
+ // call is from
+ // * An early registration of a debug FD or
+ // * The call to InitWritePoisoning.
+ // Since the early debug FDs are logs created early in the main thread
+ // and no writes are trapped before InitWritePoisoning, we are safe.
+ if (!Lock) {
+ Lock = PR_NewLock();
+ }
+
+ // We have to use something lower level than a mutex. If we don't, we
+ // can get recursive in here when called from logging a call to free.
+ return Lock;
+ }
+
+ DebugFilesAutoLock()
+ : Scoped<DebugFilesAutoLockTraits>(getDebugFileIDsLock())
+ {
+ PR_Lock(get());
+ }
+};
+
+PRLock* DebugFilesAutoLock::Lock;
+void
+DebugFilesAutoLock::Clear()
+{
+ MOZ_ASSERT(Lock != nullptr);
+ Lock = nullptr;
+}
+
+// The ChunkedList<T> class implements, at the high level, a non-iterable
+// list of instances of T. Its goal is to be somehow minimalist for the
+// use case of storing the debug files handles here, with the property of
+// not requiring a lock to look up whether it contains a specific value.
+// It is also chunked in blocks of chunk_size bytes so that its
+// initialization doesn't require a memory allocation, while keeping the
+// possibility to increase its size as necessary. Note that chunks are
+// never deallocated (except in the destructor).
+// All operations are essentially O(N) but N is not expected to be large
+// enough to matter.
+template <typename T, size_t chunk_size=64>
+class ChunkedList {
+ struct ListChunk {
+ static const size_t kLength = \
+ (chunk_size - sizeof(ListChunk*)) / sizeof(mozilla::Atomic<T>);
+
+ mozilla::Atomic<T> mElements[kLength];
+ mozilla::UniquePtr<ListChunk> mNext;
+
+ ListChunk() : mNext(nullptr) {}
+ };
+
+ ListChunk mList;
+ mozilla::Atomic<size_t> mLength;
+
+public:
+ ChunkedList() : mLength(0) {}
+
+ ~ChunkedList() {
+ // There can be writes happening after this destructor runs, so keep
+ // the list contents and don't reset mLength. But if there are more
+ // elements left than the first chunk can hold, then all hell breaks
+ // loose for any write that would happen after that because any extra
+ // chunk would be deallocated, so just crash in that case.
+ MOZ_RELEASE_ASSERT(mLength <= ListChunk::kLength);
+ }
+
+ // Add an element at the end of the last chunk of the list. Create a new
+ // chunk if there is not enough room.
+ // This is not thread-safe with another thread calling Add or Remove.
+ void Add(T aValue)
+ {
+ ListChunk *list = &mList;
+ size_t position = mLength;
+ for (; position >= ListChunk::kLength; position -= ListChunk::kLength) {
+ if (!list->mNext) {
+ list->mNext.reset(new ListChunk());
+ }
+ list = list->mNext.get();
+ }
+ // Use an order of operations that ensures any racing Contains call
+ // can't be hurt.
+ list->mElements[position] = aValue;
+ mLength++;
+ }
+
+ // Remove an element from the list by replacing it with the last element
+ // of the list, and then shrinking the list.
+ // This is not thread-safe with another thread calling Add or Remove.
+ void Remove(T aValue)
+ {
+ if (!mLength) {
+ return;
+ }
+ ListChunk *list = &mList;
+ size_t last = mLength - 1;
+ do {
+ size_t position = 0;
+ // Look for an element matching the given value.
+ for (; position < ListChunk::kLength; position++) {
+ if (aValue == list->mElements[position]) {
+ ListChunk *last_list = list;
+ // Look for the last element in the list, starting from where we are
+ // instead of starting over.
+ for (; last >= ListChunk::kLength; last -= ListChunk::kLength) {
+ last_list = last_list->mNext.get();
+ }
+ // Use an order of operations that ensures any racing Contains call
+ // can't be hurt.
+ T value = last_list->mElements[last];
+ list->mElements[position] = value;
+ mLength--;
+ return;
+ }
+ }
+ last -= ListChunk::kLength;
+ list = list->mNext.get();
+ } while (list);
+ }
+
+ // Returns whether the list contains the given value. It is meant to be safe
+ // to use without locking, with the tradeoff of being not entirely accurate
+ // if another thread adds or removes an element while this function runs.
+ bool Contains(T aValue)
+ {
+ ListChunk *list = &mList;
+ // Fix the range of the lookup to whatever the list length is when the
+ // function is called.
+ size_t length = mLength;
+ do {
+ size_t list_length = ListChunk::kLength;
+ list_length = std::min(list_length, length);
+ for (size_t position = 0; position < list_length; position++) {
+ if (aValue == list->mElements[position]) {
+ return true;
+ }
+ }
+ length -= ListChunk::kLength;
+ list = list->mNext.get();
+ } while (list);
+
+ return false;
+ }
+};
+
+typedef ChunkedList<intptr_t> FdList;
+
+// Return a list used to hold the IDs of the current debug files. On unix
+// an ID is a file descriptor. On Windows it is a file HANDLE.
+FdList&
+getDebugFileIDs()
+{
+ static FdList DebugFileIDs;
+ return DebugFileIDs;
+}
+
+
+} // namespace
+
+namespace mozilla {
+
+// Auxiliary Method to test if a file descriptor is registered to be ignored
+// by the poisoning IO interposer
+bool
+IsDebugFile(intptr_t aFileID)
+{
+ return getDebugFileIDs().Contains(aFileID);
+}
+
+} // namespace mozilla
+
+extern "C" {
+
+void
+MozillaRegisterDebugHandle(intptr_t aHandle)
+{
+ DebugFilesAutoLock lockedScope;
+ FdList& DebugFileIDs = getDebugFileIDs();
+ MOZ_ASSERT(!DebugFileIDs.Contains(aHandle));
+ DebugFileIDs.Add(aHandle);
+}
+
+void
+MozillaRegisterDebugFD(int aFd)
+{
+ MozillaRegisterDebugHandle(FileDescriptorToHandle(aFd));
+}
+
+void
+MozillaRegisterDebugFILE(FILE* aFile)
+{
+ int fd = fileno(aFile);
+ if (fd == 1 || fd == 2) {
+ return;
+ }
+ MozillaRegisterDebugFD(fd);
+}
+
+void
+MozillaUnRegisterDebugHandle(intptr_t aHandle)
+{
+ DebugFilesAutoLock lockedScope;
+ FdList& DebugFileIDs = getDebugFileIDs();
+ MOZ_ASSERT(DebugFileIDs.Contains(aHandle));
+ DebugFileIDs.Remove(aHandle);
+}
+
+void
+MozillaUnRegisterDebugFD(int aFd)
+{
+ MozillaUnRegisterDebugHandle(FileDescriptorToHandle(aFd));
+}
+
+void
+MozillaUnRegisterDebugFILE(FILE* aFile)
+{
+ int fd = fileno(aFile);
+ if (fd == 1 || fd == 2) {
+ return;
+ }
+ fflush(aFile);
+ MozillaUnRegisterDebugFD(fd);
+}
+
+} // extern "C"
+
+#ifdef MOZ_REPLACE_MALLOC
+void
+DebugFdRegistry::RegisterHandle(intptr_t aHandle)
+{
+ MozillaRegisterDebugHandle(aHandle);
+}
+
+void
+DebugFdRegistry::UnRegisterHandle(intptr_t aHandle)
+{
+ MozillaUnRegisterDebugHandle(aHandle);
+}
+#endif
diff --git a/xpcom/build/PoisonIOInterposerMac.cpp b/xpcom/build/PoisonIOInterposerMac.cpp
new file mode 100644
index 000000000..966269495
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerMac.cpp
@@ -0,0 +1,386 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PoisonIOInterposer.h"
+#include "mach_override.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsPrintfCString.h"
+#include "mozilla/StackWalk.h"
+#include "nsTraceRefcnt.h"
+#include "plstr.h"
+#include "prio.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <aio.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+namespace {
+
+using namespace mozilla;
+
+// Bit tracking if poisoned writes are enabled
+static bool sIsEnabled = false;
+
+// Check if writes are dirty before reporting IO
+static bool sOnlyReportDirtyWrites = false;
+
+// Routines for write validation
+bool IsValidWrite(int aFd, const void* aWbuf, size_t aCount);
+bool IsIPCWrite(int aFd, const struct stat& aBuf);
+
+/******************************** IO AutoTimer ********************************/
+
+/**
+ * RAII class for timing the duration of an I/O call and reporting the result
+ * to the IOInterposeObserver API.
+ */
+class MacIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd)
+ : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
+ !IsDebugFile(aFd))
+ , mFd(aFd)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ }
+
+ MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd,
+ const void* aBuf, size_t aCount)
+ : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
+ !IsDebugFile(aFd) &&
+ IsValidWrite(aFd, aBuf, aCount))
+ , mFd(aFd)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ }
+
+ // Custom implementation of IOInterposeObserver::Observation::Filename
+ const char16_t* Filename() override;
+
+ ~MacIOAutoObservation()
+ {
+ Report();
+ if (mFilename) {
+ free(mFilename);
+ mFilename = nullptr;
+ }
+ }
+
+private:
+ int mFd;
+ bool mHasQueriedFilename;
+ char16_t* mFilename;
+ static const char* sReference;
+};
+
+const char* MacIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+const char16_t*
+MacIOAutoObservation::Filename()
+{
+ // If mHasQueriedFilename is true, then we already have it
+ if (mHasQueriedFilename) {
+ return mFilename;
+ }
+ char filename[MAXPATHLEN];
+ if (fcntl(mFd, F_GETPATH, filename) != -1) {
+ mFilename = UTF8ToNewUnicode(nsDependentCString(filename));
+ } else {
+ mFilename = nullptr;
+ }
+ mHasQueriedFilename = true;
+
+ // Return filename
+ return mFilename;
+}
+
+/****************************** Write Validation ******************************/
+
+// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
+// implemented with file descriptors, so filter them out.
+bool
+IsIPCWrite(int aFd, const struct stat& aBuf)
+{
+ if ((aBuf.st_mode & S_IFMT) == S_IFIFO) {
+ return true;
+ }
+
+ if ((aBuf.st_mode & S_IFMT) != S_IFSOCK) {
+ return false;
+ }
+
+ sockaddr_storage address;
+ socklen_t len = sizeof(address);
+ if (getsockname(aFd, (sockaddr*)&address, &len) != 0) {
+ return true; // Ignore the aFd if we can't find what it is.
+ }
+
+ return address.ss_family == AF_UNIX;
+}
+
+// We want to report actual disk IO not things that don't move bits on the disk
+bool
+IsValidWrite(int aFd, const void* aWbuf, size_t aCount)
+{
+ // Ignore writes of zero bytes, Firefox does some during shutdown.
+ if (aCount == 0) {
+ return false;
+ }
+
+ {
+ struct stat buf;
+ int rv = fstat(aFd, &buf);
+ if (rv != 0) {
+ return true;
+ }
+
+ if (IsIPCWrite(aFd, buf)) {
+ return false;
+ }
+ }
+
+ // For writev we pass a nullptr aWbuf. We should only get here from
+ // dbm, and it uses write, so assert that we have aWbuf.
+ if (!aWbuf) {
+ return true;
+ }
+
+ // Break, here if we're allowed to report non-dirty writes
+ if (!sOnlyReportDirtyWrites) {
+ return true;
+ }
+
+ // As a really bad hack, accept writes that don't change the on disk
+ // content. This is needed because dbm doesn't keep track of dirty bits
+ // and can end up writing the same data to disk twice. Once when the
+ // user (nss) asks it to sync and once when closing the database.
+ auto wbuf2 = MakeUniqueFallible<char[]>(aCount);
+ if (!wbuf2) {
+ return true;
+ }
+ off_t pos = lseek(aFd, 0, SEEK_CUR);
+ if (pos == -1) {
+ return true;
+ }
+ ssize_t r = read(aFd, wbuf2.get(), aCount);
+ if (r < 0 || (size_t)r != aCount) {
+ return true;
+ }
+ int cmp = memcmp(aWbuf, wbuf2.get(), aCount);
+ if (cmp != 0) {
+ return true;
+ }
+ off_t pos2 = lseek(aFd, pos, SEEK_SET);
+ if (pos2 != pos) {
+ return true;
+ }
+
+ // Otherwise this is not a valid write
+ return false;
+}
+
+/*************************** Function Interception ***************************/
+
+/** Structure for declaration of function override */
+struct FuncData
+{
+ const char* Name; // Name of the function for the ones we use dlsym
+ const void* Wrapper; // The function that we will replace 'Function' with
+ void* Function; // The function that will be replaced with 'Wrapper'
+ void* Buffer; // Will point to the jump buffer that lets us call
+ // 'Function' after it has been replaced.
+};
+
+// Wrap aio_write. We have not seen it before, so just assert/report it.
+typedef ssize_t (*aio_write_t)(struct aiocb* aAioCbp);
+ssize_t wrap_aio_write(struct aiocb* aAioCbp);
+FuncData aio_write_data = { 0, (void*)wrap_aio_write, (void*)aio_write };
+ssize_t
+wrap_aio_write(struct aiocb* aAioCbp)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite,
+ aAioCbp->aio_fildes);
+
+ aio_write_t old_write = (aio_write_t)aio_write_data.Buffer;
+ return old_write(aAioCbp);
+}
+
+// Wrap pwrite-like functions.
+// We have not seen them before, so just assert/report it.
+typedef ssize_t (*pwrite_t)(int aFd, const void* buf, size_t aNumBytes,
+ off_t aOffset);
+template<FuncData& foo>
+ssize_t
+wrap_pwrite_temp(int aFd, const void* aBuf, size_t aNumBytes, off_t aOffset)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd);
+ pwrite_t old_write = (pwrite_t)foo.Buffer;
+ return old_write(aFd, aBuf, aNumBytes, aOffset);
+}
+
+// Define a FuncData for a pwrite-like functions.
+#define DEFINE_PWRITE_DATA(X, NAME) \
+FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> }; \
+
+// This exists everywhere.
+DEFINE_PWRITE_DATA(pwrite, "pwrite")
+// These exist on 32 bit OS X
+DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
+DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003");
+// This exists on 64 bit OS X
+DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
+
+
+typedef ssize_t (*writev_t)(int aFd, const struct iovec* aIov, int aIovCount);
+template<FuncData& foo>
+ssize_t
+wrap_writev_temp(int aFd, const struct iovec* aIov, int aIovCount)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd, nullptr,
+ aIovCount);
+ writev_t old_write = (writev_t)foo.Buffer;
+ return old_write(aFd, aIov, aIovCount);
+}
+
+// Define a FuncData for a writev-like functions.
+#define DEFINE_WRITEV_DATA(X, NAME) \
+FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \
+
+// This exists everywhere.
+DEFINE_WRITEV_DATA(writev, "writev");
+// These exist on 32 bit OS X
+DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
+DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003");
+// This exists on 64 bit OS X
+DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
+
+typedef ssize_t (*write_t)(int aFd, const void* aBuf, size_t aCount);
+template<FuncData& foo>
+ssize_t
+wrap_write_temp(int aFd, const void* aBuf, size_t aCount)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd, aBuf, aCount);
+ write_t old_write = (write_t)foo.Buffer;
+ return old_write(aFd, aBuf, aCount);
+}
+
+// Define a FuncData for a write-like functions.
+#define DEFINE_WRITE_DATA(X, NAME) \
+FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \
+
+// This exists everywhere.
+DEFINE_WRITE_DATA(write, "write");
+// These exist on 32 bit OS X
+DEFINE_WRITE_DATA(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003");
+DEFINE_WRITE_DATA(write_UNIX2003, "write$UNIX2003");
+// This exists on 64 bit OS X
+DEFINE_WRITE_DATA(write_NOCANCEL, "write$NOCANCEL");
+
+FuncData* Functions[] = {
+ &aio_write_data,
+
+ &pwrite_data,
+ &pwrite_NOCANCEL_UNIX2003_data,
+ &pwrite_UNIX2003_data,
+ &pwrite_NOCANCEL_data,
+
+ &write_data,
+ &write_NOCANCEL_UNIX2003_data,
+ &write_UNIX2003_data,
+ &write_NOCANCEL_data,
+
+ &writev_data,
+ &writev_NOCANCEL_UNIX2003_data,
+ &writev_UNIX2003_data,
+ &writev_NOCANCEL_data
+};
+
+const int NumFunctions = ArrayLength(Functions);
+
+} // namespace
+
+/******************************** IO Poisoning ********************************/
+
+namespace mozilla {
+
+void
+InitPoisonIOInterposer()
+{
+ // Enable reporting from poisoned write methods
+ sIsEnabled = true;
+
+ // Make sure we only poison writes once!
+ static bool WritesArePoisoned = false;
+ if (WritesArePoisoned) {
+ return;
+ }
+ WritesArePoisoned = true;
+
+ // stdout and stderr are OK.
+ MozillaRegisterDebugFD(1);
+ MozillaRegisterDebugFD(2);
+
+#ifdef MOZ_REPLACE_MALLOC
+ // The contract with InitDebugFd is that the given registry can be used
+ // at any moment, so the instance needs to persist longer than the scope
+ // of this functions.
+ static DebugFdRegistry registry;
+ ReplaceMalloc::InitDebugFd(registry);
+#endif
+
+ for (int i = 0; i < NumFunctions; ++i) {
+ FuncData* d = Functions[i];
+ if (!d->Function) {
+ d->Function = dlsym(RTLD_DEFAULT, d->Name);
+ }
+ if (!d->Function) {
+ continue;
+ }
+ DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
+ &d->Buffer);
+ MOZ_ASSERT(t == err_none);
+ }
+}
+
+void
+OnlyReportDirtyWrites()
+{
+ sOnlyReportDirtyWrites = true;
+}
+
+void
+ClearPoisonIOInterposer()
+{
+ // Not sure how or if we can unpoison the functions. Would be nice, but no
+ // worries we won't need to do this anyway.
+ sIsEnabled = false;
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/PoisonIOInterposerStub.cpp b/xpcom/build/PoisonIOInterposerStub.cpp
new file mode 100644
index 000000000..1f3daa353
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerStub.cpp
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+extern "C" {
+
+void MozillaRegisterDebugFD(int aFd) {}
+void MozillaRegisterDebugFILE(FILE* aFile) {}
+void MozillaUnRegisterDebugFD(int aFd) {}
+void MozillaUnRegisterDebugFILE(FILE* aFile) {}
+
+} // extern "C"
diff --git a/xpcom/build/PoisonIOInterposerWin.cpp b/xpcom/build/PoisonIOInterposerWin.cpp
new file mode 100644
index 000000000..752743b34
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -0,0 +1,501 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PoisonIOInterposer.h"
+
+#include <algorithm>
+#include <stdio.h>
+#include <vector>
+
+#include <io.h>
+#include <windows.h>
+#include <winternl.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FileUtilsWin.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTArray.h"
+#include "nsWindowsDllInterceptor.h"
+#include "plstr.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+using namespace mozilla;
+
+namespace {
+
+// Keep track of poisoned state. Notice that there is no reason to lock access
+// to this variable as it's only changed in InitPoisonIOInterposer and
+// ClearPoisonIOInterposer which may only be called on the main-thread when no
+// other threads are running.
+static bool sIOPoisoned = false;
+
+/************************ Internal NT API Declarations ************************/
+
+/*
+ * Function pointer declaration for internal NT routine to create/open files.
+ * For documentation on the NtCreateFile routine, see MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtCreateFileFn)(
+ PHANDLE aFileHandle,
+ ACCESS_MASK aDesiredAccess,
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PIO_STATUS_BLOCK aIoStatusBlock,
+ PLARGE_INTEGER aAllocationSize,
+ ULONG aFileAttributes,
+ ULONG aShareAccess,
+ ULONG aCreateDisposition,
+ ULONG aCreateOptions,
+ PVOID aEaBuffer,
+ ULONG aEaLength);
+
+/**
+ * Function pointer declaration for internal NT routine to read data from file.
+ * For documentation on the NtReadFile routine, see ZwReadFile on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtReadFileFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to read data from file.
+ * No documentation exists, see wine sources for details.
+ */
+typedef NTSTATUS (NTAPI* NtReadFileScatterFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to write data to file.
+ * For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtWriteFileFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to write data to file.
+ * No documentation exists, see wine sources for details.
+ */
+typedef NTSTATUS (NTAPI* NtWriteFileGatherFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to flush to disk.
+ * For documentation on the NtFlushBuffersFile routine, see ZwFlushBuffersFile
+ * on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtFlushBuffersFileFn)(
+ HANDLE aFileHandle,
+ PIO_STATUS_BLOCK aIoStatusBlock);
+
+typedef struct _FILE_NETWORK_OPEN_INFORMATION* PFILE_NETWORK_OPEN_INFORMATION;
+/**
+ * Function pointer delaration for internal NT routine to query file attributes.
+ * (equivalent to stat)
+ */
+typedef NTSTATUS (NTAPI* NtQueryFullAttributesFileFn)(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation);
+
+/*************************** Auxiliary Declarations ***************************/
+
+/**
+ * RAII class for timing the duration of an I/O call and reporting the result
+ * to the IOInterposeObserver API.
+ */
+class WinIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ WinIOAutoObservation(IOInterposeObserver::Operation aOp,
+ HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
+ : IOInterposeObserver::Observation(
+ aOp, sReference, !IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle)))
+ , mFileHandle(aFileHandle)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ if (mShouldReport) {
+ mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0;
+ }
+ }
+
+ WinIOAutoObservation(IOInterposeObserver::Operation aOp, nsAString& aFilename)
+ : IOInterposeObserver::Observation(aOp, sReference)
+ , mFileHandle(nullptr)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ if (mShouldReport) {
+ nsAutoString dosPath;
+ if (NtPathToDosPath(aFilename, dosPath)) {
+ mFilename = ToNewUnicode(dosPath);
+ mHasQueriedFilename = true;
+ }
+ mOffset.QuadPart = 0;
+ }
+ }
+
+ // Custom implementation of IOInterposeObserver::Observation::Filename
+ const char16_t* Filename() override;
+
+ ~WinIOAutoObservation()
+ {
+ Report();
+ if (mFilename) {
+ MOZ_ASSERT(mHasQueriedFilename);
+ free(mFilename);
+ mFilename = nullptr;
+ }
+ }
+
+private:
+ HANDLE mFileHandle;
+ LARGE_INTEGER mOffset;
+ bool mHasQueriedFilename;
+ char16_t* mFilename;
+ static const char* sReference;
+};
+
+const char* WinIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+const char16_t*
+WinIOAutoObservation::Filename()
+{
+ // If mHasQueriedFilename is true, then filename is already stored in mFilename
+ if (mHasQueriedFilename) {
+ return mFilename;
+ }
+
+ nsAutoString utf16Filename;
+ if (HandleToFilename(mFileHandle, mOffset, utf16Filename)) {
+ // Heap allocate with leakable memory
+ mFilename = ToNewUnicode(utf16Filename);
+ }
+ mHasQueriedFilename = true;
+
+ // Return filename
+ return mFilename;
+}
+
+/*************************** IO Interposing Methods ***************************/
+
+// Function pointers to original functions
+static NtCreateFileFn gOriginalNtCreateFile;
+static NtReadFileFn gOriginalNtReadFile;
+static NtReadFileScatterFn gOriginalNtReadFileScatter;
+static NtWriteFileFn gOriginalNtWriteFile;
+static NtWriteFileGatherFn gOriginalNtWriteFileGather;
+static NtFlushBuffersFileFn gOriginalNtFlushBuffersFile;
+static NtQueryFullAttributesFileFn gOriginalNtQueryFullAttributesFile;
+
+static NTSTATUS NTAPI
+InterposedNtCreateFile(PHANDLE aFileHandle,
+ ACCESS_MASK aDesiredAccess,
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PIO_STATUS_BLOCK aIoStatusBlock,
+ PLARGE_INTEGER aAllocationSize,
+ ULONG aFileAttributes,
+ ULONG aShareAccess,
+ ULONG aCreateDisposition,
+ ULONG aCreateOptions,
+ PVOID aEaBuffer,
+ ULONG aEaLength)
+{
+ // Report IO
+ const wchar_t* buf =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
+ uint32_t len =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) :
+ 0;
+ nsDependentSubstring filename(buf, len);
+ WinIOAutoObservation timer(IOInterposeObserver::OpCreateOrOpen, filename);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtCreateFile);
+
+ // Execute original function
+ return gOriginalNtCreateFile(aFileHandle,
+ aDesiredAccess,
+ aObjectAttributes,
+ aIoStatusBlock,
+ aAllocationSize,
+ aFileAttributes,
+ aShareAccess,
+ aCreateDisposition,
+ aCreateOptions,
+ aEaBuffer,
+ aEaLength);
+}
+
+static NTSTATUS NTAPI
+InterposedNtReadFile(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFile);
+
+ // Execute original function
+ return gOriginalNtReadFile(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aBuffer,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI
+InterposedNtReadFileScatter(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFileScatter);
+
+ // Execute original function
+ return gOriginalNtReadFileScatter(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aSegments,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+// Interposed NtWriteFile function
+static NTSTATUS NTAPI
+InterposedNtWriteFile(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFile);
+
+ // Execute original function
+ return gOriginalNtWriteFile(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aBuffer,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+// Interposed NtWriteFileGather function
+static NTSTATUS NTAPI
+InterposedNtWriteFileGather(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFileGather);
+
+ // Execute original function
+ return gOriginalNtWriteFileGather(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aSegments,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI
+InterposedNtFlushBuffersFile(HANDLE aFileHandle,
+ PIO_STATUS_BLOCK aIoStatusBlock)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpFSync, aFileHandle,
+ nullptr);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtFlushBuffersFile);
+
+ // Execute original function
+ return gOriginalNtFlushBuffersFile(aFileHandle,
+ aIoStatusBlock);
+}
+
+static NTSTATUS NTAPI
+InterposedNtQueryFullAttributesFile(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation)
+{
+ // Report IO
+ const wchar_t* buf =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
+ uint32_t len =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) :
+ 0;
+ nsDependentSubstring filename(buf, len);
+ WinIOAutoObservation timer(IOInterposeObserver::OpStat, filename);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtQueryFullAttributesFile);
+
+ // Execute original function
+ return gOriginalNtQueryFullAttributesFile(aObjectAttributes,
+ aFileInformation);
+}
+
+} // namespace
+
+/******************************** IO Poisoning ********************************/
+
+// Windows DLL interceptor
+static WindowsDllInterceptor sNtDllInterceptor;
+
+namespace mozilla {
+
+void
+InitPoisonIOInterposer()
+{
+ // Don't poison twice... as this function may only be invoked on the main
+ // thread when no other threads are running, it safe to allow multiple calls
+ // to InitPoisonIOInterposer() without complaining (ie. failing assertions).
+ if (sIOPoisoned) {
+ return;
+ }
+ sIOPoisoned = true;
+
+ // Stdout and Stderr are OK.
+ MozillaRegisterDebugFD(1);
+ MozillaRegisterDebugFD(2);
+
+#ifdef MOZ_REPLACE_MALLOC
+ // The contract with InitDebugFd is that the given registry can be used
+ // at any moment, so the instance needs to persist longer than the scope
+ // of this functions.
+ static DebugFdRegistry registry;
+ ReplaceMalloc::InitDebugFd(registry);
+#endif
+
+ // Initialize dll interceptor and add hooks
+ sNtDllInterceptor.Init("ntdll.dll");
+ sNtDllInterceptor.AddHook(
+ "NtCreateFile",
+ reinterpret_cast<intptr_t>(InterposedNtCreateFile),
+ reinterpret_cast<void**>(&gOriginalNtCreateFile));
+ sNtDllInterceptor.AddHook(
+ "NtReadFile",
+ reinterpret_cast<intptr_t>(InterposedNtReadFile),
+ reinterpret_cast<void**>(&gOriginalNtReadFile));
+ sNtDllInterceptor.AddHook(
+ "NtReadFileScatter",
+ reinterpret_cast<intptr_t>(InterposedNtReadFileScatter),
+ reinterpret_cast<void**>(&gOriginalNtReadFileScatter));
+ sNtDllInterceptor.AddHook(
+ "NtWriteFile",
+ reinterpret_cast<intptr_t>(InterposedNtWriteFile),
+ reinterpret_cast<void**>(&gOriginalNtWriteFile));
+ sNtDllInterceptor.AddHook(
+ "NtWriteFileGather",
+ reinterpret_cast<intptr_t>(InterposedNtWriteFileGather),
+ reinterpret_cast<void**>(&gOriginalNtWriteFileGather));
+ sNtDllInterceptor.AddHook(
+ "NtFlushBuffersFile",
+ reinterpret_cast<intptr_t>(InterposedNtFlushBuffersFile),
+ reinterpret_cast<void**>(&gOriginalNtFlushBuffersFile));
+ sNtDllInterceptor.AddHook(
+ "NtQueryFullAttributesFile",
+ reinterpret_cast<intptr_t>(InterposedNtQueryFullAttributesFile),
+ reinterpret_cast<void**>(&gOriginalNtQueryFullAttributesFile));
+}
+
+void
+ClearPoisonIOInterposer()
+{
+ MOZ_ASSERT(false);
+ if (sIOPoisoned) {
+ // Destroy the DLL interceptor
+ sIOPoisoned = false;
+ sNtDllInterceptor = WindowsDllInterceptor();
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/ServiceList.h b/xpcom/build/ServiceList.h
new file mode 100644
index 000000000..3bb8308dd
--- /dev/null
+++ b/xpcom/build/ServiceList.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "mozilla/Services.h"
+
+MOZ_SERVICE(ChromeRegistryService, nsIChromeRegistry,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(ToolkitChromeRegistryService, nsIToolkitChromeRegistry,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(XULChromeRegistryService, nsIXULChromeRegistry,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(XULOverlayProviderService, nsIXULOverlayProvider,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(IOService, nsIIOService,
+ "@mozilla.org/network/io-service;1")
+MOZ_SERVICE(ObserverService, nsIObserverService,
+ "@mozilla.org/observer-service;1")
+MOZ_SERVICE(StringBundleService, nsIStringBundleService,
+ "@mozilla.org/intl/stringbundle;1")
+MOZ_SERVICE(XPConnect, nsIXPConnect,
+ "@mozilla.org/js/xpc/XPConnect;1")
+MOZ_SERVICE(InDOMUtils, inIDOMUtils,
+ "@mozilla.org/inspector/dom-utils;1")
+MOZ_SERVICE(PermissionManager, nsIPermissionManager,
+ "@mozilla.org/permissionmanager;1");
+MOZ_SERVICE(ServiceWorkerManager, nsIServiceWorkerManager,
+ "@mozilla.org/serviceworkers/manager;1");
+MOZ_SERVICE(AsyncShutdown, nsIAsyncShutdownService,
+ "@mozilla.org/async-shutdown-service;1")
+MOZ_SERVICE(UUIDGenerator, nsIUUIDGenerator,
+ "@mozilla.org/uuid-generator;1");
+MOZ_SERVICE(GfxInfo, nsIGfxInfo,
+ "@mozilla.org/gfx/info;1");
+
+#ifdef MOZ_USE_NAMESPACE
+namespace mozilla {
+#endif
+
+MOZ_SERVICE(HistoryService, IHistory,
+ "@mozilla.org/browser/history;1")
+
+#ifdef MOZ_USE_NAMESPACE
+} // namespace mozilla
+#endif
diff --git a/xpcom/build/Services.cpp b/xpcom/build/Services.cpp
new file mode 100644
index 000000000..6c521391e
--- /dev/null
+++ b/xpcom/build/Services.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Likely.h"
+#include "mozilla/Services.h"
+#include "nsComponentManager.h"
+#include "nsIObserverService.h"
+#include "nsNetCID.h"
+#include "nsObserverService.h"
+#include "nsXPCOMPrivate.h"
+#include "nsIIOService.h"
+#include "nsIDirectoryService.h"
+#include "nsIChromeRegistry.h"
+#include "nsIStringBundle.h"
+#include "nsIToolkitChromeRegistry.h"
+#include "nsIXULOverlayProvider.h"
+#include "IHistory.h"
+#include "nsIXPConnect.h"
+#include "inIDOMUtils.h"
+#include "nsIPermissionManager.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsIAsyncShutdown.h"
+#include "nsIUUIDGenerator.h"
+#include "nsIGfxInfo.h"
+
+using namespace mozilla;
+using namespace mozilla::services;
+
+/*
+ * Define a global variable and a getter for every service in ServiceList.
+ * eg. gIOService and GetIOService()
+ */
+#define MOZ_SERVICE(NAME, TYPE, CONTRACT_ID) \
+ static TYPE* g##NAME = nullptr; \
+ \
+ already_AddRefed<TYPE> \
+ mozilla::services::Get##NAME() \
+ { \
+ if (MOZ_UNLIKELY(gXPCOMShuttingDown)) { \
+ return nullptr; \
+ } \
+ if (!g##NAME) { \
+ nsCOMPtr<TYPE> os = do_GetService(CONTRACT_ID); \
+ os.swap(g##NAME); \
+ } \
+ nsCOMPtr<TYPE> ret = g##NAME; \
+ return ret.forget(); \
+ } \
+ NS_EXPORT_(already_AddRefed<TYPE>) \
+ mozilla::services::_external_Get##NAME() \
+ { \
+ return Get##NAME(); \
+ }
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+
+/**
+ * Clears service cache, sets gXPCOMShuttingDown
+ */
+void
+mozilla::services::Shutdown()
+{
+ gXPCOMShuttingDown = true;
+#define MOZ_SERVICE(NAME, TYPE, CONTRACT_ID) NS_IF_RELEASE(g##NAME);
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+}
diff --git a/xpcom/build/Services.h b/xpcom/build/Services.h
new file mode 100644
index 000000000..84a355399
--- /dev/null
+++ b/xpcom/build/Services.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Services_h
+#define mozilla_Services_h
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+#define MOZ_USE_NAMESPACE
+#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) class TYPE;
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+#undef MOZ_USE_NAMESPACE
+
+namespace mozilla {
+namespace services {
+
+#ifdef MOZILLA_INTERNAL_API
+#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) \
+ already_AddRefed<TYPE> Get##NAME(); \
+ NS_EXPORT_(already_AddRefed<TYPE>) _external_Get##NAME();
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+#else
+#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) \
+ NS_IMPORT_(already_AddRefed<TYPE>) _external_Get##NAME(); \
+ inline already_AddRefed<TYPE> Get##NAME() \
+ { \
+ return _external_Get##NAME(); \
+ }
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+#endif
+
+} // namespace services
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/build/XPCOM.h b/xpcom/build/XPCOM.h
new file mode 100644
index 000000000..f52a87c69
--- /dev/null
+++ b/xpcom/build/XPCOM.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_XPCOM_h
+#define mozilla_XPCOM_h
+
+// NOTE: the following headers are sorted topologically, not alphabetically.
+// Do not reorder them without review from bsmedberg.
+
+// system headers required by XPCOM headers
+
+#include <string.h>
+
+// core headers required by pretty much everything else
+
+#include "nscore.h"
+
+#include "nsXPCOMCID.h"
+#include "nsXPCOM.h"
+
+#include "nsError.h"
+#include "nsDebug.h"
+#include "nsMemory.h"
+
+#include "nsID.h"
+
+#include "nsISupports.h"
+
+#include "nsTArray.h"
+#include "nsTWeakRef.h"
+
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#include "nsStringAPI.h"
+#else
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsNativeCharsetUtils.h"
+#endif
+
+#include "nsISupportsUtils.h"
+#include "nsISupportsImpl.h"
+
+// core data structures
+
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsInterfaceHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsRefPtrHashtable.h"
+
+// interfaces that inherit directly from nsISupports
+
+#include "nsIArray.h"
+#include "nsIAtom.h"
+#include "nsIAtomService.h"
+#include "nsICategoryManager.h"
+#include "nsIClassInfo.h"
+#include "nsIComponentManager.h"
+#include "nsIConsoleListener.h"
+#include "nsIConsoleMessage.h"
+#include "nsIConsoleService.h"
+#include "nsIDebug2.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIEnvironment.h"
+#include "nsIErrorService.h"
+#include "nsIEventTarget.h"
+#include "nsIException.h"
+#include "nsIFactory.h"
+#include "nsIFile.h"
+#include "nsIHashable.h"
+#include "nsIINIParser.h"
+#include "nsIInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILineInputStream.h"
+#include "nsIMemory.h"
+#include "nsIMutable.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsIProcess.h"
+#include "nsIProperties.h"
+#include "nsIPropertyBag2.h"
+#include "nsIRunnable.h"
+#include "nsISeekableStream.h"
+#include "nsISerializable.h"
+#include "nsIServiceManager.h"
+#include "nsIScriptableInputStream.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStreamBufferAccess.h"
+#include "nsIStringEnumerator.h"
+#include "nsIStorageStream.h"
+#include "nsISupportsIterators.h"
+#include "nsISupportsPrimitives.h"
+#include "nsISupportsPriority.h"
+#include "nsIThreadManager.h"
+#include "nsITimer.h"
+#include "nsIUUIDGenerator.h"
+#include "nsIUnicharInputStream.h"
+#include "nsIUnicharOutputStream.h"
+#include "nsIUnicharLineInputStream.h"
+#include "nsIVariant.h"
+#include "nsIVersionComparator.h"
+#include "nsIWritablePropertyBag2.h"
+
+// interfaces that include something above
+
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIBinaryInputStream.h"
+#include "nsIBinaryOutputStream.h"
+#include "nsIConverterInputStream.h"
+#include "nsIConverterOutputStream.h"
+#include "nsIInputStreamTee.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsIMutableArray.h"
+#include "nsIPersistentProperties2.h"
+#include "nsIStringStream.h"
+#include "nsIThread.h"
+#include "nsIThreadPool.h"
+
+// interfaces that include something above
+
+#include "nsILocalFileWin.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIPipe.h"
+
+#ifdef MOZ_WIDGET_COCOA
+#include "nsILocalFileMac.h"
+#include "nsIMacUtils.h"
+#endif
+
+// xpcom/glue utility headers
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIWeakReferenceUtils.h"
+#include "nsWeakReference.h"
+
+#include "nsArrayEnumerator.h"
+#include "nsArrayUtils.h"
+#include "nsCRTGlue.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDeque.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIClassInfoImpl.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsINIParser.h"
+#include "nsProxyRelease.h"
+#include "nsTObserverArray.h"
+#include "nsTextFormatter.h"
+#include "nsThreadUtils.h"
+#include "nsVersionComparator.h"
+#include "nsXPTCUtils.h"
+
+// xpcom/base utility headers
+
+#include "nsAgg.h"
+#include "nsAutoRef.h"
+#include "nsInterfaceRequestorAgg.h"
+
+// xpcom/io utility headers
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+
+#endif // mozilla_XPCOM_h
diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp
new file mode 100644
index 000000000..c72ea48d7
--- /dev/null
+++ b/xpcom/build/XPCOMInit.cpp
@@ -0,0 +1,1126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Poison.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/XPCOM.h"
+#include "nsXULAppAPI.h"
+
+#include "nsXPCOMPrivate.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+
+#include "prlink.h"
+
+#include "nsCycleCollector.h"
+#include "nsObserverList.h"
+#include "nsObserverService.h"
+#include "nsProperties.h"
+#include "nsPersistentProperties.h"
+#include "nsScriptableInputStream.h"
+#include "nsBinaryStream.h"
+#include "nsStorageStream.h"
+#include "nsPipe.h"
+#include "nsScriptableBase64Encoder.h"
+
+#include "nsMemoryImpl.h"
+#include "nsDebugImpl.h"
+#include "nsTraceRefcnt.h"
+#include "nsErrorService.h"
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+#include "nsSupportsArray.h"
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+
+#include "nsArray.h"
+#include "nsINIParserImpl.h"
+#include "nsSupportsPrimitives.h"
+#include "nsConsoleService.h"
+
+#include "nsComponentManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsIServiceManager.h"
+
+#include "nsThreadManager.h"
+#include "nsThreadPool.h"
+
+#include "xptinfo.h"
+#include "nsIInterfaceInfoManager.h"
+#include "xptiprivate.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+
+#include "nsTimerImpl.h"
+#include "TimerThread.h"
+
+#include "nsThread.h"
+#include "nsProcess.h"
+#include "nsEnvironment.h"
+#include "nsVersionComparatorImpl.h"
+
+#include "nsIFile.h"
+#include "nsLocalFile.h"
+#if defined(XP_UNIX)
+#include "nsNativeCharsetUtils.h"
+#endif
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsICategoryManager.h"
+#include "nsMultiplexInputStream.h"
+
+#include "nsStringStream.h"
+extern nsresult nsStringInputStreamConstructor(nsISupports*, REFNSIID, void**);
+
+#include "nsAtomService.h"
+#include "nsAtomTable.h"
+#include "nsISupportsImpl.h"
+
+#include "nsHashPropertyBag.h"
+
+#include "nsUnicharInputStream.h"
+#include "nsVariant.h"
+
+#include "nsUUIDGenerator.h"
+
+#include "nsIOUtil.h"
+
+#include "SpecialSystemDirectory.h"
+
+#if defined(XP_WIN)
+#include "mozilla/WindowsVersion.h"
+#include "nsWindowsRegKey.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "nsMacUtilsImpl.h"
+#endif
+
+#include "nsSystemInfo.h"
+#include "nsMemoryReporterManager.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsSecurityConsoleMessage.h"
+#include "nsMessageLoop.h"
+
+#include "nsStatusReporterManager.h"
+
+#include <locale.h>
+#include "mozilla/Services.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/HangMonitor.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/BackgroundHangMonitor.h"
+
+#include "nsChromeRegistry.h"
+#include "nsChromeProtocolHandler.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/LateWriteChecks.h"
+
+#include "mozilla/scache/StartupCache.h"
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/AvailableMemoryTracker.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/CountingAllocatorBase.h"
+#include "mozilla/SystemMemoryReporter.h"
+#include "mozilla/UniquePtr.h"
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+#include "ogg/ogg.h"
+#if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING)
+#if defined(HAVE_STDINT_H)
+// mozilla-config.h defines HAVE_STDINT_H, and then it's defined *again* in
+// vpx_config.h (which we include via vpx_mem.h, below). This redefinition
+// triggers a build warning on MSVC, so we have to #undef it first.
+#undef HAVE_STDINT_H
+#endif
+#include "vpx_mem/vpx_mem.h"
+#endif
+
+#include "GeckoProfiler.h"
+
+#include "jsapi.h"
+#include "js/Initialization.h"
+
+#include "gfxPlatform.h"
+
+#if EXPOSE_INTL_API
+#include "unicode/putil.h"
+#endif
+
+using namespace mozilla;
+using base::AtExitManager;
+using mozilla::ipc::BrowserProcessSubThread;
+
+namespace {
+
+static AtExitManager* sExitManager;
+static MessageLoop* sMessageLoop;
+static bool sCommandLineWasInitialized;
+static BrowserProcessSubThread* sIOThread;
+static BackgroundHangMonitor* sMainHangMonitor;
+
+} /* anonymous namespace */
+
+// Registry Factory creation function defined in nsRegistry.cpp
+// We hook into this function locally to create and register the registry
+// Since noone outside xpcom needs to know about this and nsRegistry.cpp
+// does not have a local include file, we are putting this definition
+// here rather than in nsIRegistry.h
+extern nsresult NS_RegistryGetFactory(nsIFactory** aFactory);
+extern nsresult NS_CategoryManagerGetFactory(nsIFactory**);
+
+#ifdef XP_WIN
+extern nsresult CreateAnonTempFileRemover();
+#endif
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsProcess)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsID)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsString)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsCString)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRBool)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint8)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint16)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint32)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint64)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRTime)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsChar)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt16)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt32)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt64)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsFloat)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsDouble)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsVoid)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsInterfacePointer)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsConsoleService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAtomService)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsTimer)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryOutputStream)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryInputStream)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsStorageStream)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsVersionComparatorImpl)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableBase64Encoder)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsVariantCC)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsHashPropertyBagCC)
+
+NS_GENERIC_AGGREGATED_CONSTRUCTOR(nsProperties)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUUIDGenerator, Init)
+
+#ifdef MOZ_WIDGET_COCOA
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacUtilsImpl)
+#endif
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemInfo, Init)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemoryReporterManager, Init)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMemoryInfoDumper)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStatusReporterManager, Init)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsIOUtil)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecurityConsoleMessage)
+
+static nsresult
+nsThreadManagerGetSingleton(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ NS_ASSERTION(aInstancePtr, "null outptr");
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ return nsThreadManager::get().QueryInterface(aIID, aInstancePtr);
+}
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsThreadPool)
+
+static nsresult
+nsXPTIInterfaceInfoManagerGetSingleton(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ NS_ASSERTION(aInstancePtr, "null outptr");
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsIInterfaceInfoManager> iim(XPTInterfaceInfoManager::GetSingleton());
+ if (!iim) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return iim->QueryInterface(aIID, aInstancePtr);
+}
+
+nsComponentManagerImpl* nsComponentManagerImpl::gComponentManager = nullptr;
+bool gXPCOMShuttingDown = false;
+bool gXPCOMThreadsShutDown = false;
+char16_t* gGREBinPath = nullptr;
+
+static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID);
+static NS_DEFINE_CID(kINIParserFactoryCID, NS_INIPARSERFACTORY_CID);
+
+NS_DEFINE_NAMED_CID(NS_CHROMEREGISTRY_CID);
+NS_DEFINE_NAMED_CID(NS_CHROMEPROTOCOLHANDLER_CID);
+
+NS_DEFINE_NAMED_CID(NS_SECURITY_CONSOLE_MESSAGE_CID);
+
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsChromeRegistry,
+ nsChromeRegistry::GetSingleton)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsChromeProtocolHandler)
+
+#define NS_PERSISTENTPROPERTIES_CID NS_IPERSISTENTPROPERTIES_CID /* sigh */
+
+static already_AddRefed<nsIFactory>
+CreateINIParserFactory(const mozilla::Module& aModule,
+ const mozilla::Module::CIDEntry& aEntry)
+{
+ nsCOMPtr<nsIFactory> f = new nsINIParserFactory();
+ return f.forget();
+}
+
+#define COMPONENT(NAME, Ctor) static NS_DEFINE_CID(kNS_##NAME##_CID, NS_##NAME##_CID);
+#define COMPONENT_M(NAME, Ctor, Selector) static NS_DEFINE_CID(kNS_##NAME##_CID, NS_##NAME##_CID);
+#include "XPCOMModule.inc"
+#undef COMPONENT
+#undef COMPONENT_M
+
+#define COMPONENT(NAME, Ctor) { &kNS_##NAME##_CID, false, nullptr, Ctor },
+#define COMPONENT_M(NAME, Ctor, Selector) { &kNS_##NAME##_CID, false, nullptr, Ctor, Selector },
+const mozilla::Module::CIDEntry kXPCOMCIDEntries[] = {
+ { &kComponentManagerCID, true, nullptr, nsComponentManagerImpl::Create, Module::ALLOW_IN_GPU_PROCESS },
+ { &kINIParserFactoryCID, false, CreateINIParserFactory },
+#include "XPCOMModule.inc"
+ { &kNS_CHROMEREGISTRY_CID, false, nullptr, nsChromeRegistryConstructor },
+ { &kNS_CHROMEPROTOCOLHANDLER_CID, false, nullptr, nsChromeProtocolHandlerConstructor },
+ { &kNS_SECURITY_CONSOLE_MESSAGE_CID, false, nullptr, nsSecurityConsoleMessageConstructor },
+ { nullptr }
+};
+#undef COMPONENT
+#undef COMPONENT_M
+
+#define COMPONENT(NAME, Ctor) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID },
+#define COMPONENT_M(NAME, Ctor, Selector) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID, Selector },
+const mozilla::Module::ContractIDEntry kXPCOMContracts[] = {
+#include "XPCOMModule.inc"
+ { NS_CHROMEREGISTRY_CONTRACTID, &kNS_CHROMEREGISTRY_CID },
+ { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "chrome", &kNS_CHROMEPROTOCOLHANDLER_CID },
+ { NS_INIPARSERFACTORY_CONTRACTID, &kINIParserFactoryCID },
+ { NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &kNS_SECURITY_CONSOLE_MESSAGE_CID },
+ { nullptr }
+};
+#undef COMPONENT
+#undef COMPONENT_M
+
+const mozilla::Module kXPCOMModule = {
+ mozilla::Module::kVersion, kXPCOMCIDEntries, kXPCOMContracts,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Module::ALLOW_IN_GPU_PROCESS
+};
+
+// gDebug will be freed during shutdown.
+static nsIDebug2* gDebug = nullptr;
+
+EXPORT_XPCOM_API(nsresult)
+NS_GetDebug(nsIDebug2** aResult)
+{
+ return nsDebugImpl::Create(nullptr, NS_GET_IID(nsIDebug2), (void**)aResult);
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InitXPCOM(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory)
+{
+ return NS_InitXPCOM2(aResult, aBinDirectory, nullptr);
+}
+
+class ICUReporter final
+ : public nsIMemoryReporter
+ , public CountingAllocatorBase<ICUReporter>
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static void* Alloc(const void*, size_t aSize)
+ {
+ return CountingMalloc(aSize);
+ }
+
+ static void* Realloc(const void*, void* aPtr, size_t aSize)
+ {
+ return CountingRealloc(aPtr, aSize);
+ }
+
+ static void Free(const void*, void* aPtr)
+ {
+ return CountingFree(aPtr);
+ }
+
+private:
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/icu", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
+ "Memory used by ICU, a Unicode and globalization support library.");
+
+ return NS_OK;
+ }
+
+ ~ICUReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(ICUReporter, nsIMemoryReporter)
+
+/* static */ template<> Atomic<size_t>
+CountingAllocatorBase<ICUReporter>::sAmount(0);
+
+class OggReporter final
+ : public nsIMemoryReporter
+ , public CountingAllocatorBase<OggReporter>
+{
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/media/libogg", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
+ "Memory allocated through libogg for Ogg, Theora, and related media "
+ "files.");
+
+ return NS_OK;
+ }
+
+ ~OggReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(OggReporter, nsIMemoryReporter)
+
+/* static */ template<> Atomic<size_t>
+CountingAllocatorBase<OggReporter>::sAmount(0);
+
+#ifdef MOZ_VPX
+class VPXReporter final
+ : public nsIMemoryReporter
+ , public CountingAllocatorBase<VPXReporter>
+{
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/media/libvpx", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
+ "Memory allocated through libvpx for WebM media files.");
+
+ return NS_OK;
+ }
+
+ ~VPXReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(VPXReporter, nsIMemoryReporter)
+
+/* static */ template<> Atomic<size_t>
+CountingAllocatorBase<VPXReporter>::sAmount(0);
+#endif /* MOZ_VPX */
+
+static double
+TimeSinceProcessCreation()
+{
+ bool ignore;
+ return (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+}
+
+static bool sInitializedJS = false;
+
+// Note that on OSX, aBinDirectory will point to .app/Contents/Resources/browser
+EXPORT_XPCOM_API(nsresult)
+NS_InitXPCOM2(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider)
+{
+ static bool sInitialized = false;
+ if (sInitialized) {
+ return NS_ERROR_FAILURE;
+ }
+
+ sInitialized = true;
+
+ mozPoisonValueInit();
+
+ NS_LogInit();
+
+ NS_InitAtomTable();
+
+ mozilla::LogModule::Init();
+
+ JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation);
+
+ char aLocal;
+ profiler_init(&aLocal);
+ nsresult rv = NS_OK;
+
+ // We are not shutting down
+ gXPCOMShuttingDown = false;
+
+ // Initialize the available memory tracker before other threads have had a
+ // chance to start up, because the initialization is not thread-safe.
+ mozilla::AvailableMemoryTracker::Init();
+
+#ifdef XP_UNIX
+ // Discover the current value of the umask, and save it where
+ // nsSystemInfo::Init can retrieve it when necessary. There is no way
+ // to read the umask without changing it, and the setting is process-
+ // global, so this must be done while we are still single-threaded; the
+ // nsSystemInfo object is typically created much later, when some piece
+ // of chrome JS wants it. The system call is specified as unable to fail.
+ nsSystemInfo::gUserUmask = ::umask(0777);
+ ::umask(nsSystemInfo::gUserUmask);
+#endif
+
+ // Set up chromium libs
+ NS_ASSERTION(!sExitManager && !sMessageLoop, "Bad logic!");
+
+ if (!AtExitManager::AlreadyRegistered()) {
+ sExitManager = new AtExitManager();
+ }
+
+ MessageLoop* messageLoop = MessageLoop::current();
+ if (!messageLoop) {
+ sMessageLoop = new MessageLoopForUI(MessageLoop::TYPE_MOZILLA_PARENT);
+ sMessageLoop->set_thread_name("Gecko");
+ // Set experimental values for main thread hangs:
+ // 128ms for transient hangs and 8192ms for permanent hangs
+ sMessageLoop->set_hang_timeouts(128, 8192);
+ } else if (messageLoop->type() == MessageLoop::TYPE_MOZILLA_CHILD) {
+ messageLoop->set_thread_name("Gecko_Child");
+ messageLoop->set_hang_timeouts(128, 8192);
+ }
+
+ if (XRE_IsParentProcess() &&
+ !BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO)) {
+ UniquePtr<BrowserProcessSubThread> ioThread = MakeUnique<BrowserProcessSubThread>(BrowserProcessSubThread::IO);
+
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ if (NS_WARN_IF(!ioThread->StartWithOptions(options))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ sIOThread = ioThread.release();
+ }
+
+ // Establish the main thread here.
+ rv = nsThreadManager::get().Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Set up the timer globals/timer thread
+ rv = nsTimerImpl::Startup();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifndef ANDROID
+ // If the locale hasn't already been setup by our embedder,
+ // get us out of the "C" locale and into the system
+ if (strcmp(setlocale(LC_ALL, nullptr), "C") == 0) {
+ setlocale(LC_ALL, "");
+ }
+#endif
+
+#if defined(XP_UNIX)
+ NS_StartupNativeCharsetUtils();
+#endif
+
+ NS_StartupLocalFile();
+
+ StartupSpecialSystemDirectory();
+
+ nsDirectoryService::RealInit();
+
+ bool value;
+
+ if (aBinDirectory) {
+ rv = aBinDirectory->IsDirectory(&value);
+
+ if (NS_SUCCEEDED(rv) && value) {
+ nsDirectoryService::gService->Set(NS_XPCOM_INIT_CURRENT_PROCESS_DIR,
+ aBinDirectory);
+ }
+ }
+
+ if (aAppFileLocationProvider) {
+ rv = nsDirectoryService::gService->RegisterProvider(aAppFileLocationProvider);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsIFile> xpcomLib;
+ nsDirectoryService::gService->Get(NS_GRE_BIN_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(xpcomLib));
+ MOZ_ASSERT(xpcomLib);
+
+ // set gGREBinPath
+ nsAutoString path;
+ xpcomLib->GetPath(path);
+ gGREBinPath = ToNewUnicode(path);
+
+ xpcomLib->AppendNative(nsDependentCString(XPCOM_DLL));
+ nsDirectoryService::gService->Set(NS_XPCOM_LIBRARY_FILE, xpcomLib);
+
+ if (!mozilla::Omnijar::IsInitialized()) {
+ mozilla::Omnijar::Init();
+ }
+
+ if ((sCommandLineWasInitialized = !CommandLine::IsInitialized())) {
+#ifdef OS_WIN
+ CommandLine::Init(0, nullptr);
+#else
+ nsCOMPtr<nsIFile> binaryFile;
+ nsDirectoryService::gService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(binaryFile));
+ if (NS_WARN_IF(!binaryFile)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = binaryFile->AppendNative(NS_LITERAL_CSTRING("nonexistent-executable"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString binaryPath;
+ rv = binaryFile->GetNativePath(binaryPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ static char const* const argv = { strdup(binaryPath.get()) };
+ CommandLine::Init(1, &argv);
+#endif
+ }
+
+ NS_ASSERTION(nsComponentManagerImpl::gComponentManager == nullptr,
+ "CompMgr not null at init");
+
+ // Create the Component/Service Manager
+ nsComponentManagerImpl::gComponentManager = new nsComponentManagerImpl();
+ NS_ADDREF(nsComponentManagerImpl::gComponentManager);
+
+ // Global cycle collector initialization.
+ if (!nsCycleCollector_init()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // And start it up for this thread too.
+ nsCycleCollector_startup();
+
+ // Register ICU memory functions. This really shouldn't be necessary: the
+ // JS engine should do this on its own inside JS_Init, and memory-reporting
+ // code should call a JSAPI function to observe ICU memory usage. But we
+ // can't define the alloc/free functions in the JS engine, because it can't
+ // depend on the XPCOM-based memory reporting goop. So for now, we have
+ // this oddness.
+ mozilla::SetICUMemoryFunctions();
+
+ // Do the same for libogg.
+ ogg_set_mem_functions(OggReporter::CountingMalloc,
+ OggReporter::CountingCalloc,
+ OggReporter::CountingRealloc,
+ OggReporter::CountingFree);
+
+#if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING)
+ // And for VPX.
+ vpx_mem_set_functions(VPXReporter::CountingMalloc,
+ VPXReporter::CountingCalloc,
+ VPXReporter::CountingRealloc,
+ VPXReporter::CountingFree,
+ memcpy,
+ memset,
+ memmove);
+#endif
+
+#if EXPOSE_INTL_API && defined(MOZ_ICU_DATA_ARCHIVE)
+ nsCOMPtr<nsIFile> greDir;
+ nsDirectoryService::gService->Get(NS_GRE_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(greDir));
+ MOZ_ASSERT(greDir);
+ nsAutoCString nativeGREPath;
+ greDir->GetNativePath(nativeGREPath);
+ u_setDataDirectory(nativeGREPath.get());
+#endif
+
+ // Initialize the JS engine.
+ const char* jsInitFailureReason = JS_InitWithFailureDiagnostic();
+ if (jsInitFailureReason) {
+ NS_RUNTIMEABORT(jsInitFailureReason);
+ }
+ sInitializedJS = true;
+
+ // Init AbstractThread.
+ AbstractThread::InitStatics();
+
+ rv = nsComponentManagerImpl::gComponentManager->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(nsComponentManagerImpl::gComponentManager);
+ return rv;
+ }
+
+ if (aResult) {
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ }
+
+ // The iimanager constructor searches and registers XPT files.
+ // (We trigger the singleton's lazy construction here to make that happen.)
+ (void)XPTInterfaceInfoManager::GetSingleton();
+
+ // After autoreg, but before we actually instantiate any components,
+ // add any services listed in the "xpcom-directory-providers" category
+ // to the directory service.
+ nsDirectoryService::gService->RegisterCategoryProviders();
+
+ // Init SharedThreadPool (which needs the service manager).
+ SharedThreadPool::InitStatics();
+
+ // Force layout to spin up so that nsContentUtils is available for cx stack
+ // munging. Note that layout registers a number of static atoms, and also
+ // seals the static atom table, so NS_RegisterStaticAtom may not be called
+ // beyond this point.
+ nsCOMPtr<nsISupports> componentLoader =
+ do_GetService("@mozilla.org/moz/jsloader;1");
+
+ mozilla::scache::StartupCache::GetSingleton();
+ mozilla::AvailableMemoryTracker::Activate();
+
+ // Notify observers of xpcom autoregistration start
+ NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY,
+ nullptr,
+ NS_XPCOM_STARTUP_OBSERVER_ID);
+#ifdef XP_WIN
+ CreateAnonTempFileRemover();
+#endif
+
+ // We only want the SystemMemoryReporter running in one process, because it
+ // profiles the entire system. The main process is the obvious place for
+ // it.
+ if (XRE_IsParentProcess()) {
+ mozilla::SystemMemoryReporter::Init();
+ }
+
+ // The memory reporter manager is up and running -- register our reporters.
+ RegisterStrongMemoryReporter(new ICUReporter());
+ RegisterStrongMemoryReporter(new OggReporter());
+#ifdef MOZ_VPX
+ RegisterStrongMemoryReporter(new VPXReporter());
+#endif
+
+ mozilla::Telemetry::Init();
+
+ mozilla::HangMonitor::Startup();
+ mozilla::BackgroundHangMonitor::Startup();
+
+ const MessageLoop* const loop = MessageLoop::current();
+ sMainHangMonitor = new mozilla::BackgroundHangMonitor(
+ loop->thread_name().c_str(),
+ loop->transient_hang_timeout(),
+ loop->permanent_hang_timeout());
+
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InitMinimalXPCOM()
+{
+ mozPoisonValueInit();
+ NS_SetMainThread();
+ mozilla::TimeStamp::Startup();
+ NS_LogInit();
+ NS_InitAtomTable();
+ mozilla::LogModule::Init();
+
+ char aLocal;
+ profiler_init(&aLocal);
+
+ nsresult rv = nsThreadManager::get().Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Set up the timer globals/timer thread.
+ rv = nsTimerImpl::Startup();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Create the Component/Service Manager
+ nsComponentManagerImpl::gComponentManager = new nsComponentManagerImpl();
+ NS_ADDREF(nsComponentManagerImpl::gComponentManager);
+
+ rv = nsComponentManagerImpl::gComponentManager->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(nsComponentManagerImpl::gComponentManager);
+ return rv;
+ }
+
+ // Global cycle collector initialization.
+ if (!nsCycleCollector_init()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AbstractThread::InitStatics();
+ SharedThreadPool::InitStatics();
+ mozilla::Telemetry::Init();
+ mozilla::HangMonitor::Startup();
+ mozilla::BackgroundHangMonitor::Startup();
+
+ return NS_OK;
+}
+
+//
+// NS_ShutdownXPCOM()
+//
+// The shutdown sequence for xpcom would be
+//
+// - Notify "xpcom-shutdown" for modules to release primary (root) references
+// - Shutdown XPCOM timers
+// - Notify "xpcom-shutdown-threads" for thread joins
+// - Shutdown the event queues
+// - Release the Global Service Manager
+// - Release all service instances held by the global service manager
+// - Release the Global Service Manager itself
+// - Release the Component Manager
+// - Release all factories cached by the Component Manager
+// - Notify module loaders to shut down
+// - Unload Libraries
+// - Release Contractid Cache held by Component Manager
+// - Release dll abstraction held by Component Manager
+// - Release the Registry held by Component Manager
+// - Finally, release the component manager itself
+//
+EXPORT_XPCOM_API(nsresult)
+NS_ShutdownXPCOM(nsIServiceManager* aServMgr)
+{
+ return mozilla::ShutdownXPCOM(aServMgr);
+}
+
+namespace mozilla {
+
+void
+SetICUMemoryFunctions()
+{
+ static bool sICUReporterInitialized = false;
+ if (!sICUReporterInitialized) {
+ if (!JS_SetICUMemoryFunctions(ICUReporter::Alloc, ICUReporter::Realloc,
+ ICUReporter::Free)) {
+ NS_RUNTIMEABORT("JS_SetICUMemoryFunctions failed.");
+ }
+ sICUReporterInitialized = true;
+ }
+}
+
+nsresult
+ShutdownXPCOM(nsIServiceManager* aServMgr)
+{
+ // Make sure the hang monitor is enabled for shutdown.
+ HangMonitor::NotifyActivity();
+
+ if (!NS_IsMainThread()) {
+ NS_RUNTIMEABORT("Shutdown on wrong thread");
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISimpleEnumerator> moduleLoaders;
+
+ // Notify observers of xpcom shutting down
+ {
+ // Block it so that the COMPtr will get deleted before we hit
+ // servicemanager shutdown
+
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ if (NS_WARN_IF(!thread)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<nsObserverService> observerService;
+ CallGetService("@mozilla.org/observer-service;1",
+ (nsObserverService**)getter_AddRefs(observerService));
+
+ if (observerService) {
+ mozilla::KillClearOnShutdown(ShutdownPhase::WillShutdown);
+ observerService->NotifyObservers(nullptr,
+ NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
+ nullptr);
+
+ nsCOMPtr<nsIServiceManager> mgr;
+ rv = NS_GetServiceManager(getter_AddRefs(mgr));
+ if (NS_SUCCEEDED(rv)) {
+ mozilla::KillClearOnShutdown(ShutdownPhase::Shutdown);
+ observerService->NotifyObservers(mgr, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ nullptr);
+ }
+ }
+
+ // This must happen after the shutdown of media and widgets, which
+ // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
+ NS_ProcessPendingEvents(thread);
+ gfxPlatform::ShutdownLayersIPC();
+ mozilla::dom::VideoDecoderManagerChild::Shutdown();
+
+ mozilla::scache::StartupCache::DeleteSingleton();
+ if (observerService)
+ {
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownThreads);
+ observerService->NotifyObservers(nullptr,
+ NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
+ nullptr);
+ }
+
+ gXPCOMThreadsShutDown = true;
+ NS_ProcessPendingEvents(thread);
+
+ // Shutdown the timer thread and all timers that might still be alive before
+ // shutting down the component manager
+ nsTimerImpl::Shutdown();
+
+ NS_ProcessPendingEvents(thread);
+
+ // Shutdown all remaining threads. This method does not return until
+ // all threads created using the thread manager (with the exception of
+ // the main thread) have exited.
+ nsThreadManager::get().Shutdown();
+
+ NS_ProcessPendingEvents(thread);
+
+ HangMonitor::NotifyActivity();
+
+ // Late-write checks needs to find the profile directory, so it has to
+ // be initialized before mozilla::services::Shutdown or (because of
+ // xpcshell tests replacing the service) modules being unloaded.
+ mozilla::InitLateWriteChecks();
+
+ // We save the "xpcom-shutdown-loaders" observers to notify after
+ // the observerservice is gone.
+ if (observerService) {
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownLoaders);
+ observerService->EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID,
+ getter_AddRefs(moduleLoaders));
+
+ observerService->Shutdown();
+ }
+ }
+
+ // Free ClearOnShutdown()'ed smart pointers. This needs to happen *after*
+ // we've finished notifying observers of XPCOM shutdown, because shutdown
+ // observers themselves might call ClearOnShutdown().
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
+
+ // XPCOM is officially in shutdown mode NOW
+ // Set this only after the observers have been notified as this
+ // will cause servicemanager to become inaccessible.
+ mozilla::services::Shutdown();
+
+ // We may have AddRef'd for the caller of NS_InitXPCOM, so release it
+ // here again:
+ NS_IF_RELEASE(aServMgr);
+
+ // Shutdown global servicemanager
+ if (nsComponentManagerImpl::gComponentManager) {
+ nsComponentManagerImpl::gComponentManager->FreeServices();
+ }
+
+ // Release the directory service
+ nsDirectoryService::gService = nullptr;
+
+ free(gGREBinPath);
+ gGREBinPath = nullptr;
+
+ if (moduleLoaders) {
+ bool more;
+ nsCOMPtr<nsISupports> el;
+ while (NS_SUCCEEDED(moduleLoaders->HasMoreElements(&more)) && more) {
+ moduleLoaders->GetNext(getter_AddRefs(el));
+
+ // Don't worry about weak-reference observers here: there is
+ // no reason for weak-ref observers to register for
+ // xpcom-shutdown-loaders
+
+ // FIXME: This can cause harmless writes from sqlite committing
+ // log files. We have to ignore them before we can move
+ // the mozilla::PoisonWrite call before this point. See bug
+ // 834945 for the details.
+ nsCOMPtr<nsIObserver> obs(do_QueryInterface(el));
+ if (obs) {
+ obs->Observe(nullptr, NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID, nullptr);
+ }
+ }
+
+ moduleLoaders = nullptr;
+ }
+
+ bool shutdownCollect;
+#ifdef NS_FREE_PERMANENT_DATA
+ shutdownCollect = true;
+#else
+ shutdownCollect = !!PR_GetEnv("MOZ_CC_RUN_DURING_SHUTDOWN");
+#endif
+ nsCycleCollector_shutdown(shutdownCollect);
+
+ PROFILER_MARKER("Shutdown xpcom");
+ // If we are doing any shutdown checks, poison writes.
+ if (gShutdownChecks != SCM_NOTHING) {
+#ifdef XP_MACOSX
+ mozilla::OnlyReportDirtyWrites();
+#endif /* XP_MACOSX */
+ mozilla::BeginLateWriteChecks();
+ }
+
+ // Shutdown nsLocalFile string conversion
+ NS_ShutdownLocalFile();
+#ifdef XP_UNIX
+ NS_ShutdownNativeCharsetUtils();
+#endif
+
+ // Shutdown xpcom. This will release all loaders and cause others holding
+ // a refcount to the component manager to release it.
+ if (nsComponentManagerImpl::gComponentManager) {
+ rv = (nsComponentManagerImpl::gComponentManager)->Shutdown();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Component Manager shutdown failed.");
+ } else {
+ NS_WARNING("Component Manager was never created ...");
+ }
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ // In optimized builds we don't do shutdown collections by default, so
+ // uncollected (garbage) objects may keep the nsXPConnect singleton alive,
+ // and its XPCJSContext along with it. However, we still destroy various
+ // bits of state in JS_ShutDown(), so we need to make sure the profiler
+ // can't access them when it shuts down. This call nulls out the
+ // JS pseudo-stack's internal reference to the main thread JSContext,
+ // duplicating the call in XPCJSContext::~XPCJSContext() in case that
+ // never fired.
+ if (PseudoStack* stack = mozilla_get_pseudo_stack()) {
+ stack->sampleContext(nullptr);
+ }
+#endif
+
+ if (sInitializedJS) {
+ // Shut down the JS engine.
+ JS_ShutDown();
+ sInitializedJS = false;
+ }
+
+ // Release our own singletons
+ // Do this _after_ shutting down the component manager, because the
+ // JS component loader will use XPConnect to call nsIModule::canUnload,
+ // and that will spin up the InterfaceInfoManager again -- bad mojo
+ XPTInterfaceInfoManager::FreeInterfaceInfoManager();
+
+ // Finally, release the component manager last because it unloads the
+ // libraries:
+ if (nsComponentManagerImpl::gComponentManager) {
+ nsrefcnt cnt;
+ NS_RELEASE2(nsComponentManagerImpl::gComponentManager, cnt);
+ NS_ASSERTION(cnt == 0, "Component Manager being held past XPCOM shutdown.");
+ }
+ nsComponentManagerImpl::gComponentManager = nullptr;
+ nsCategoryManager::Destroy();
+
+ NS_ShutdownAtomTable();
+
+ NS_IF_RELEASE(gDebug);
+
+ delete sIOThread;
+ sIOThread = nullptr;
+
+ delete sMessageLoop;
+ sMessageLoop = nullptr;
+
+ if (sCommandLineWasInitialized) {
+ CommandLine::Terminate();
+ sCommandLineWasInitialized = false;
+ }
+
+ delete sExitManager;
+ sExitManager = nullptr;
+
+ Omnijar::CleanUp();
+
+ HangMonitor::Shutdown();
+
+ delete sMainHangMonitor;
+ sMainHangMonitor = nullptr;
+
+ BackgroundHangMonitor::Shutdown();
+
+ profiler_shutdown();
+
+ NS_LogTerm();
+
+#if defined(MOZ_WIDGET_GONK)
+ // This _exit(0) call is intended to be temporary, to get shutdown leak
+ // checking working on non-B2G platforms.
+ // On debug B2G, the child process crashes very late. Instead, just
+ // give up so at least we exit cleanly. See bug 1071866.
+ if (XRE_IsContentProcess()) {
+ NS_WARNING("Exiting child process early!");
+ _exit(0);
+ }
+#endif
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/XPCOMModule.inc b/xpcom/build/XPCOMModule.inc
new file mode 100644
index 000000000..5ac2885e2
--- /dev/null
+++ b/xpcom/build/XPCOMModule.inc
@@ -0,0 +1,81 @@
+ COMPONENT_M(MEMORY, nsMemoryImpl::Create, Module::ALLOW_IN_GPU_PROCESS)
+ COMPONENT_M(DEBUG, nsDebugImpl::Create, Module::ALLOW_IN_GPU_PROCESS)
+ COMPONENT(ERRORSERVICE, nsErrorService::Create)
+
+ COMPONENT_M(CATEGORYMANAGER, nsCategoryManager::Create, Module::ALLOW_IN_GPU_PROCESS)
+
+ COMPONENT(SCRIPTABLEINPUTSTREAM, nsScriptableInputStream::Create)
+ COMPONENT(BINARYINPUTSTREAM, nsBinaryInputStreamConstructor)
+ COMPONENT(BINARYOUTPUTSTREAM, nsBinaryOutputStreamConstructor)
+ COMPONENT(STORAGESTREAM, nsStorageStreamConstructor)
+ COMPONENT(VERSIONCOMPARATOR, nsVersionComparatorImplConstructor)
+ COMPONENT(SCRIPTABLEBASE64ENCODER, nsScriptableBase64EncoderConstructor)
+ COMPONENT(PIPE, nsPipeConstructor)
+
+ COMPONENT(PROPERTIES, nsPropertiesConstructor)
+
+ COMPONENT(PERSISTENTPROPERTIES, nsPersistentProperties::Create)
+
+ COMPONENT(SUPPORTSARRAY, nsSupportsArray::Create)
+ COMPONENT(ARRAY, nsArrayBase::XPCOMConstructor)
+ COMPONENT(CONSOLESERVICE, nsConsoleServiceConstructor)
+ COMPONENT(ATOMSERVICE, nsAtomServiceConstructor)
+ COMPONENT_M(OBSERVERSERVICE, nsObserverService::Create, Module::ALLOW_IN_GPU_PROCESS)
+
+ COMPONENT_M(TIMER, nsTimerConstructor, Module::ALLOW_IN_GPU_PROCESS)
+
+#define COMPONENT_SUPPORTS(TYPE, Type) \
+ COMPONENT(SUPPORTS_##TYPE, nsSupports##Type##Constructor)
+
+ COMPONENT_SUPPORTS(ID, ID)
+ COMPONENT_SUPPORTS(STRING, String)
+ COMPONENT_SUPPORTS(CSTRING, CString)
+ COMPONENT_SUPPORTS(PRBOOL, PRBool)
+ COMPONENT_SUPPORTS(PRUINT8, PRUint8)
+ COMPONENT_SUPPORTS(PRUINT16, PRUint16)
+ COMPONENT_SUPPORTS(PRUINT32, PRUint32)
+ COMPONENT_SUPPORTS(PRUINT64, PRUint64)
+ COMPONENT_SUPPORTS(PRTIME, PRTime)
+ COMPONENT_SUPPORTS(CHAR, Char)
+ COMPONENT_SUPPORTS(PRINT16, PRInt16)
+ COMPONENT_SUPPORTS(PRINT32, PRInt32)
+ COMPONENT_SUPPORTS(PRINT64, PRInt64)
+ COMPONENT_SUPPORTS(FLOAT, Float)
+ COMPONENT_SUPPORTS(DOUBLE, Double)
+ COMPONENT_SUPPORTS(VOID, Void)
+ COMPONENT_SUPPORTS(INTERFACE_POINTER, InterfacePointer)
+
+#undef COMPONENT_SUPPORTS
+ COMPONENT(LOCAL_FILE, nsLocalFile::nsLocalFileConstructor)
+ COMPONENT(DIRECTORY_SERVICE, nsDirectoryService::Create)
+ COMPONENT(PROCESS, nsProcessConstructor)
+ COMPONENT(ENVIRONMENT, nsEnvironment::Create)
+
+ COMPONENT(THREADMANAGER, nsThreadManagerGetSingleton)
+ COMPONENT_M(THREADPOOL, nsThreadPoolConstructor, Module::ALLOW_IN_GPU_PROCESS)
+
+ COMPONENT(STRINGINPUTSTREAM, nsStringInputStreamConstructor)
+ COMPONENT(MULTIPLEXINPUTSTREAM, nsMultiplexInputStreamConstructor)
+
+ COMPONENT(VARIANT, nsVariantCCConstructor)
+ COMPONENT(INTERFACEINFOMANAGER_SERVICE, nsXPTIInterfaceInfoManagerGetSingleton)
+
+ COMPONENT(HASH_PROPERTY_BAG, nsHashPropertyBagCCConstructor)
+
+ COMPONENT(UUID_GENERATOR, nsUUIDGeneratorConstructor)
+
+#if defined(XP_WIN)
+ COMPONENT(WINDOWSREGKEY, nsWindowsRegKeyConstructor)
+#endif
+
+#if defined(MOZ_WIDGET_COCOA)
+ COMPONENT(MACUTILSIMPL, nsMacUtilsImplConstructor)
+#endif
+
+ COMPONENT(SYSTEMINFO, nsSystemInfoConstructor)
+ COMPONENT(MEMORY_REPORTER_MANAGER, nsMemoryReporterManagerConstructor)
+ COMPONENT(MEMORY_INFO_DUMPER, nsMemoryInfoDumperConstructor)
+ COMPONENT(IOUTIL, nsIOUtilConstructor)
+ COMPONENT(CYCLE_COLLECTOR_LOGGER, nsCycleCollectorLoggerConstructor)
+ COMPONENT(MESSAGE_LOOP, nsMessageLoopConstructor)
+ COMPONENT(STATUS_REPORTER_MANAGER, nsStatusReporterManagerConstructor)
diff --git a/xpcom/build/XREChildData.h b/xpcom/build/XREChildData.h
new file mode 100644
index 000000000..487fede94
--- /dev/null
+++ b/xpcom/build/XREChildData.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef XREChildData_h
+#define XREChildData_h
+
+#include "mozilla/UniquePtr.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#include "mozilla/sandboxing/loggingTypes.h"
+
+namespace sandbox {
+class TargetServices;
+}
+#endif
+
+namespace mozilla {
+namespace gmp {
+class GMPLoader;
+}
+}
+
+/**
+ * Data needed to start a child process.
+ */
+struct XREChildData
+{
+#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
+ /**
+ * Used to load the GMP binary.
+ */
+ mozilla::UniquePtr<mozilla::gmp::GMPLoader> gmpLoader;
+#endif
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox TargetServices.
+ */
+ sandbox::TargetServices* sandboxTargetServices = nullptr;
+
+ /**
+ * Function to provide a logging function to the chromium sandbox code.
+ */
+ mozilla::sandboxing::ProvideLogFunctionCb ProvideLogFunction = nullptr;
+#endif
+};
+
+#endif // XREChildData_h
diff --git a/xpcom/build/XREShellData.h b/xpcom/build/XREShellData.h
new file mode 100644
index 000000000..11bc162d9
--- /dev/null
+++ b/xpcom/build/XREShellData.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef XREShellData_h
+#define XREShellData_h
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+namespace sandbox {
+class BrokerServices;
+}
+#endif
+
+/**
+ * Data needed by XRE_XPCShellMain.
+ */
+struct XREShellData
+{
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox BrokerServices.
+ */
+ sandbox::BrokerServices* sandboxBrokerServices;
+#endif
+};
+
+#endif // XREShellData_h
diff --git a/xpcom/build/mach_override.c b/xpcom/build/mach_override.c
new file mode 100644
index 000000000..9e4940d29
--- /dev/null
+++ b/xpcom/build/mach_override.c
@@ -0,0 +1,789 @@
+// Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443.
+// mach_override.c semver:1.2.0
+// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
+// Some rights reserved: http://opensource.org/licenses/mit
+// https://github.com/rentzsch/mach_override
+
+#include "mach_override.h"
+
+#include <mach-o/dyld.h>
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#include <sys/mman.h>
+
+#include <CoreServices/CoreServices.h>
+
+/**************************
+*
+* Constants
+*
+**************************/
+#pragma mark -
+#pragma mark (Constants)
+
+#define kPageSize 4096
+#if defined(__ppc__) || defined(__POWERPC__)
+
+long kIslandTemplate[] = {
+ 0x9001FFFC, // stw r0,-4(SP)
+ 0x3C00DEAD, // lis r0,0xDEAD
+ 0x6000BEEF, // ori r0,r0,0xBEEF
+ 0x7C0903A6, // mtctr r0
+ 0x8001FFFC, // lwz r0,-4(SP)
+ 0x60000000, // nop ; optionally replaced
+ 0x4E800420 // bctr
+};
+
+#define kAddressHi 3
+#define kAddressLo 5
+#define kInstructionHi 10
+#define kInstructionLo 11
+
+#elif defined(__i386__)
+
+#define kOriginalInstructionsSize 16
+// On X86 we migh need to instert an add with a 32 bit immediate after the
+// original instructions.
+#define kMaxFixupSizeIncrease 5
+
+unsigned char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
+};
+
+#define kInstructions 0
+#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
+#elif defined(__x86_64__)
+
+#define kOriginalInstructionsSize 32
+// On X86-64 we never need to instert a new instruction.
+#define kMaxFixupSizeIncrease 0
+
+#define kJumpAddress kOriginalInstructionsSize + 6
+
+unsigned char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+#endif
+
+/**************************
+*
+* Data Types
+*
+**************************/
+#pragma mark -
+#pragma mark (Data Types)
+
+typedef struct {
+ char instructions[sizeof(kIslandTemplate)];
+} BranchIsland;
+
+/**************************
+*
+* Funky Protos
+*
+**************************/
+#pragma mark -
+#pragma mark (Funky Protos)
+
+static mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ void *originalFunctionAddress);
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island );
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction );
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions );
+void
+atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value );
+
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions,
+ int *originalInstructionCount,
+ uint8_t *originalInstructionSizes );
+
+ static void
+fixupInstructions(
+ uint32_t offset,
+ void *instructionsToFix,
+ int instructionCount,
+ uint8_t *instructionSizes );
+#endif
+
+/*******************************************************************************
+*
+* Interface
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Interface)
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t makeIslandExecutable(void *address) {
+ mach_error_t err = err_none;
+ uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1);
+ int e = err_none;
+ e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
+ e |= msync((void *)page, kPageSize, MS_INVALIDATE );
+ if (e) {
+ err = err_cannot_override;
+ }
+ return err;
+}
+#endif
+
+ mach_error_t
+mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland )
+{
+ assert( originalFunctionAddress );
+ assert( overrideFunctionAddress );
+
+ // this addresses overriding such functions as AudioOutputUnitStart()
+ // test with modified DefaultOutputUnit project
+#if defined(__x86_64__)
+ for(;;){
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
+ originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
+ else break;
+ }
+#elif defined(__i386__)
+ for(;;){
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
+ originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
+ else break;
+ }
+#endif
+
+ long *originalFunctionPtr = (long*) originalFunctionAddress;
+ mach_error_t err = err_none;
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Ensure first instruction isn't 'mfctr'.
+ #define kMFCTRMask 0xfc1fffff
+ #define kMFCTRInstruction 0x7c0903a6
+
+ long originalInstruction = *originalFunctionPtr;
+ if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
+ err = err_cannot_override;
+#elif defined(__i386__) || defined(__x86_64__)
+ int eatenCount = 0;
+ int originalInstructionCount = 0;
+ char originalInstructions[kOriginalInstructionsSize];
+ uint8_t originalInstructionSizes[kOriginalInstructionsSize];
+ uint64_t jumpRelativeInstruction = 0; // JMP
+
+ Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
+ &jumpRelativeInstruction, &eatenCount,
+ originalInstructions, &originalInstructionCount,
+ originalInstructionSizes );
+ if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) {
+ //printf ("Too many instructions eaten\n");
+ overridePossible = false;
+ }
+ if (!overridePossible) err = err_cannot_override;
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+#endif
+
+ // Make the original function implementation writable.
+ if( !err ) {
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, 8, false,
+ (VM_PROT_ALL | VM_PROT_COPY) );
+ if( err )
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, 8, false,
+ (VM_PROT_DEFAULT | VM_PROT_COPY) );
+ }
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+ // Allocate and target the escape island to the overriding function.
+ BranchIsland *escapeIsland = NULL;
+ if( !err )
+ err = allocateBranchIsland( &escapeIsland, originalFunctionAddress );
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ if( !err )
+ err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
+
+ // Build the branch absolute instruction to the escape island.
+ long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
+ if( !err ) {
+ long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
+ branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+ if( !err )
+ err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
+
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+ // Build the jump relative instruction to the escape island
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (!err) {
+ uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
+ addressOffset = OSSwapInt32(addressOffset);
+
+ jumpRelativeInstruction |= 0xE900000000000000LL;
+ jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
+ jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
+ }
+#endif
+
+ // Optionally allocate & return the reentry island. This may contain relocated
+ // jmp instructions and so has all the same addressing reachability requirements
+ // the escape island has to the original function, except the escape island is
+ // technically our original function.
+ BranchIsland *reentryIsland = NULL;
+ if( !err && originalFunctionReentryIsland ) {
+ err = allocateBranchIsland( &reentryIsland, escapeIsland);
+ if( !err )
+ *originalFunctionReentryIsland = reentryIsland;
+ }
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instruction into the reentry island.
+ // o Target the reentry island at the 2nd instruction of the
+ // original function.
+ // o Replace the original instruction with the branch absolute.
+ if( !err ) {
+ int escapeIslandEngaged = false;
+ do {
+ if( reentryIsland )
+ err = setBranchIslandTarget( reentryIsland,
+ (void*) (originalFunctionPtr+1), originalInstruction );
+ if( !err ) {
+ escapeIslandEngaged = CompareAndSwap( originalInstruction,
+ branchAbsoluteInstruction,
+ (UInt32*)originalFunctionPtr );
+ if( !escapeIslandEngaged ) {
+ // Someone replaced the instruction out from under us,
+ // re-read the instruction, make sure it's still not
+ // 'mfctr' and try again.
+ originalInstruction = *originalFunctionPtr;
+ if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
+ err = err_cannot_override;
+ }
+ }
+ } while( !err && !escapeIslandEngaged );
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instructions into the reentry island.
+ // o Target the reentry island at the first non-replaced
+ // instruction of the original function.
+ // o Replace the original first instructions with the jump relative.
+ //
+ // Note that on i386, we do not support someone else changing the code under our feet
+ if ( !err ) {
+ uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland;
+ fixupInstructions(offset, originalInstructions,
+ originalInstructionCount, originalInstructionSizes );
+
+ if( reentryIsland )
+ err = setBranchIslandTarget_i386( reentryIsland,
+ (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
+ // try making islands executable before planting the jmp
+#if defined(__x86_64__) || defined(__i386__)
+ if( !err )
+ err = makeIslandExecutable(escapeIsland);
+ if( !err && reentryIsland )
+ err = makeIslandExecutable(reentryIsland);
+#endif
+ if ( !err )
+ atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
+ }
+#endif
+
+ // Clean up on error.
+ if( err ) {
+ if( reentryIsland )
+ freeBranchIsland( reentryIsland );
+ if( escapeIsland )
+ freeBranchIsland( escapeIsland );
+ }
+
+ return err;
+}
+
+/*******************************************************************************
+*
+* Implementation
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Implementation)
+
+static bool jump_in_range(intptr_t from, intptr_t to) {
+ intptr_t field_value = to - from - 5;
+ int32_t field_value_32 = field_value;
+ return field_value == field_value_32;
+}
+
+/*******************************************************************************
+ Implementation: Allocates memory for a branch island.
+
+ @param island <- The allocated island.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+static mach_error_t
+allocateBranchIslandAux(
+ BranchIsland **island,
+ void *originalFunctionAddress,
+ bool forward)
+{
+ assert( island );
+ assert( sizeof( BranchIsland ) <= kPageSize );
+
+ vm_map_t task_self = mach_task_self();
+ vm_address_t original_address = (vm_address_t) originalFunctionAddress;
+ vm_address_t address = original_address;
+
+ for (;;) {
+ vm_size_t vmsize = 0;
+ memory_object_name_t object = 0;
+ kern_return_t kr = 0;
+ vm_region_flavor_t flavor = VM_REGION_BASIC_INFO;
+ // Find the region the address is in.
+#if __WORDSIZE == 32
+ vm_region_basic_info_data_t info;
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
+ kr = vm_region(task_self, &address, &vmsize, flavor,
+ (vm_region_info_t)&info, &info_count, &object);
+#else
+ vm_region_basic_info_data_64_t info;
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
+ kr = vm_region_64(task_self, &address, &vmsize, flavor,
+ (vm_region_info_t)&info, &info_count, &object);
+#endif
+ if (kr != KERN_SUCCESS)
+ return kr;
+ assert((address & (kPageSize - 1)) == 0);
+
+ // Go to the first page before or after this region
+ vm_address_t new_address = forward ? address + vmsize : address - kPageSize;
+#if __WORDSIZE == 64
+ if(!jump_in_range(original_address, new_address))
+ break;
+#endif
+ address = new_address;
+
+ // Try to allocate this page.
+ kr = vm_allocate(task_self, &address, kPageSize, 0);
+ if (kr == KERN_SUCCESS) {
+ *island = (BranchIsland*) address;
+ return err_none;
+ }
+ if (kr != KERN_NO_SPACE)
+ return kr;
+ }
+
+ return KERN_NO_SPACE;
+}
+
+static mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ void *originalFunctionAddress)
+{
+ mach_error_t err =
+ allocateBranchIslandAux(island, originalFunctionAddress, true);
+ if (!err)
+ return err;
+ return allocateBranchIslandAux(island, originalFunctionAddress, false);
+}
+
+
+/*******************************************************************************
+ Implementation: Deallocates memory for a branch island.
+
+ @param island -> The island to deallocate.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island )
+{
+ assert( island );
+ assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
+ assert( sizeof( BranchIsland ) <= kPageSize );
+ return vm_deallocate( mach_task_self(), (vm_address_t) island,
+ kPageSize );
+}
+
+/*******************************************************************************
+ Implementation: Sets the branch island's target, with an optional
+ instruction.
+
+ @param island -> The branch island to insert target into.
+ @param branchTo -> The address of the target.
+ @param instruction -> Optional instruction to execute prior to branch. Set
+ to zero for nop.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Fill in the address.
+ ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
+ ((short*)island->instructions)[kAddressHi]
+ = (((long) branchTo) >> 16) & 0x0000FFFF;
+
+ // Fill in the (optional) instuction.
+ if( instruction != 0 ) {
+ ((short*)island->instructions)[kInstructionLo]
+ = instruction & 0x0000FFFF;
+ ((short*)island->instructions)[kInstructionHi]
+ = (instruction >> 16) & 0x0000FFFF;
+ }
+
+ //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+#if defined(__i386__)
+ mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // copy original instructions
+ if (instructions) {
+ bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
+ *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
+
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+ return err_none;
+}
+
+#elif defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Copy original instructions.
+ if (instructions) {
+ bcopy (instructions, island->instructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+// simplistic instruction matching
+typedef struct {
+ unsigned int length; // max 15
+ unsigned char mask[15]; // sequence of bytes in memory order
+ unsigned char constraint[15]; // sequence of bytes in memory order
+} AsmInstructionMatch;
+
+#if defined(__i386__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
+ { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xFF}, {0x55} }, // push %esp
+ { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
+ { 0x1, {0xFF}, {0x53} }, // push %ebx
+ { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
+ { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
+ { 0x1, {0xFF}, {0x57} }, // push %edi
+ { 0x1, {0xFF}, {0x56} }, // push %esi
+ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
+ { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
+ { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
+ { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax
+ { 0x0 }
+};
+#elif defined(__x86_64__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xF8}, {0x50} }, // push %rX
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
+ { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp
+ { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx
+ { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
+ { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
+ { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
+ { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
+ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
+ { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax
+
+ //leaq offset(%rip),%rax
+ { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} },
+
+ { 0x0 }
+};
+#endif
+
+static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
+{
+ Boolean match = true;
+
+ size_t i;
+ for (i=0; i<instruction->length; i++) {
+ unsigned char mask = instruction->mask[i];
+ unsigned char constraint = instruction->constraint[i];
+ unsigned char codeValue = code[i];
+
+ match = ((codeValue & mask) == constraint);
+ if (!match) break;
+ }
+
+ return match;
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions,
+ int *originalInstructionCount,
+ uint8_t *originalInstructionSizes )
+{
+ Boolean allInstructionsKnown = true;
+ int totalEaten = 0;
+ unsigned char* ptr = code;
+ int remainsToEat = 5; // a JMP instruction takes 5 bytes
+ int instructionIndex = 0;
+
+ if (howManyEaten) *howManyEaten = 0;
+ if (originalInstructionCount) *originalInstructionCount = 0;
+ while (remainsToEat > 0) {
+ Boolean curInstructionKnown = false;
+
+ // See if instruction matches one we know
+ AsmInstructionMatch* curInstr = possibleInstructions;
+ do {
+ if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
+ curInstr++;
+ } while (curInstr->length > 0);
+
+ // if all instruction matches failed, we don't know current instruction then, stop here
+ if (!curInstructionKnown) {
+ allInstructionsKnown = false;
+ fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
+ break;
+ }
+
+ // At this point, we've matched curInstr
+ int eaten = curInstr->length;
+ ptr += eaten;
+ remainsToEat -= eaten;
+ totalEaten += eaten;
+
+ if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
+ instructionIndex += 1;
+ if (originalInstructionCount) *originalInstructionCount = instructionIndex;
+ }
+
+
+ if (howManyEaten) *howManyEaten = totalEaten;
+
+ if (originalInstructions) {
+ Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
+
+ if (enoughSpaceForOriginalInstructions) {
+ memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
+ bcopy(code, originalInstructions, totalEaten);
+ } else {
+ // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
+ return false;
+ }
+ }
+
+ if (allInstructionsKnown) {
+ // save last 3 bytes of first 64bits of codre we'll replace
+ uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
+ currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
+ currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
+
+ // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
+ *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
+ *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
+ }
+
+ return allInstructionsKnown;
+}
+
+ static void
+fixupInstructions(
+ uint32_t offset,
+ void *instructionsToFix,
+ int instructionCount,
+ uint8_t *instructionSizes )
+{
+ // The start of "leaq offset(%rip),%rax"
+ static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05};
+
+ int index;
+ for (index = 0;index < instructionCount;index += 1)
+ {
+ if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
+ {
+ uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
+ *jumpOffsetPtr += offset;
+ }
+
+ // leaq offset(%rip),%rax
+ if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) {
+ uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3);
+ *LeaqOffsetPtr += offset;
+ }
+
+ // 32-bit call relative to the next addr; pop %eax
+ if (*(uint8_t*)instructionsToFix == 0xE8)
+ {
+ // Just this call is larger than the jump we use, so we
+ // know this is the last instruction.
+ assert(index == (instructionCount - 1));
+ assert(instructionSizes[index] == 6);
+
+ // Insert "addl $offset, %eax" in the end so that when
+ // we jump to the rest of the function %eax has the
+ // value it would have if eip had been pushed by the
+ // call in its original position.
+ uint8_t *op = instructionsToFix;
+ op += 6;
+ *op = 0x05; // addl
+ uint32_t *addImmPtr = (uint32_t*)(op + 1);
+ *addImmPtr = offset;
+ }
+
+ instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
+ }
+}
+#endif
+
+#if defined(__i386__)
+__asm(
+ ".text;"
+ ".align 2, 0x90;"
+ "_atomic_mov64:;"
+ " pushl %ebp;"
+ " movl %esp, %ebp;"
+ " pushl %esi;"
+ " pushl %ebx;"
+ " pushl %ecx;"
+ " pushl %eax;"
+ " pushl %edx;"
+
+ // atomic push of value to an address
+ // we use cmpxchg8b, which compares content of an address with
+ // edx:eax. If they are equal, it atomically puts 64bit value
+ // ecx:ebx in address.
+ // We thus put contents of address in edx:eax to force ecx:ebx
+ // in address
+ " mov 8(%ebp), %esi;" // esi contains target address
+ " mov 12(%ebp), %ebx;"
+ " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
+ " mov (%esi), %eax;"
+ " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
+ " lock; cmpxchg8b (%esi);" // atomic move.
+
+ // restore registers
+ " popl %edx;"
+ " popl %eax;"
+ " popl %ecx;"
+ " popl %ebx;"
+ " popl %esi;"
+ " popl %ebp;"
+ " ret"
+);
+#elif defined(__x86_64__)
+void atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value )
+{
+ *targetAddress = value;
+}
+#endif
+#endif
diff --git a/xpcom/build/mach_override.h b/xpcom/build/mach_override.h
new file mode 100644
index 000000000..d9be988a3
--- /dev/null
+++ b/xpcom/build/mach_override.h
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ mach_override.h
+ Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+ Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
+ ***************************************************************************/
+
+/***************************************************************************//**
+ @mainpage mach_override
+ @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+
+ This package, coded in C to the Mach API, allows you to override ("patch")
+ program- and system-supplied functions at runtime. You can fully replace
+ functions with your implementations, or merely head- or tail-patch the
+ original implementations.
+
+ Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
+
+ @todo Discontinue use of Carbon's MakeDataExecutable() and
+ CompareAndSwap() calls and start using the Mach equivalents, if they
+ exist. If they don't, write them and roll them in. That way, this
+ code will be pure Mach, which will make it easier to use everywhere.
+ Update: MakeDataExecutable() has been replaced by
+ msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
+ I'm currently unsure if I can link against it. May have to roll in
+ my own version...
+ @todo Stop using an entire 4K high-allocated VM page per 28-byte escape
+ branch island. Done right, this will dramatically speed up escape
+ island allocations when they number over 250. Then again, if you're
+ overriding more than 250 functions, maybe speed isn't your main
+ concern...
+ @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
+ first-instructions. Initially, we should refuse to override
+ functions beginning with these instructions. Eventually, we should
+ dynamically rewrite them to make them position-independent.
+ @todo Write mach_unoverride(), which would remove an override placed on a
+ function. Must be multiple-override aware, which means an almost
+ complete rewrite under the covers, because the target address can't
+ be spread across two load instructions like it is now since it will
+ need to be atomically updatable.
+ @todo Add non-rentry variants of overrides to test_mach_override.
+
+ ***************************************************************************/
+
+#ifndef _mach_override_
+#define _mach_override_
+
+#include <sys/types.h>
+#include <mach/error.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ Returned if the function to be overrided begins with a 'mfctr' instruction.
+*/
+#define err_cannot_override (err_local|1)
+
+/************************************************************************************//**
+ Dynamically overrides the function implementation referenced by
+ originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
+ Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
+ the original implementation.
+
+ @param originalFunctionAddress -> Required address of the function to
+ override (with overrideFunctionAddress).
+ @param overrideFunctionAddress -> Required address to the overriding
+ function.
+ @param originalFunctionReentryIsland <- Optional pointer to pointer to the
+ reentry island. Can be nullptr.
+ @result <- err_cannot_override if the original
+ function's implementation begins with
+ the 'mfctr' instruction.
+
+ ************************************************************************************/
+
+ mach_error_t
+mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland );
+
+/************************************************************************************//**
+
+
+ ************************************************************************************/
+
+#ifdef __cplusplus
+
+#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
+ { \
+ static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
+ static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
+ class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
+ public: \
+ static kern_return_t override(void *originalFunctionPtr) { \
+ kern_return_t result = err_none; \
+ if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
+ ORIGINAL_FUNCTION_NAME##_overriden = true; \
+ result = mach_override_ptr( (void*)originalFunctionPtr, \
+ (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
+ (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
+ } \
+ return result; \
+ } \
+ static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
+
+#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
+ } \
+ }; \
+ \
+ err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
+ }
+
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+#endif // _mach_override_
diff --git a/xpcom/build/moz.build b/xpcom/build/moz.build
new file mode 100644
index 000000000..68bd001a2
--- /dev/null
+++ b/xpcom/build/moz.build
@@ -0,0 +1,102 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'nsXPCOM.h',
+ 'nsXPCOMCID.h',
+ 'nsXPCOMCIDInternal.h',
+ 'nsXREAppData.h',
+ 'nsXULAppAPI.h',
+ 'XREChildData.h',
+ 'xrecore.h',
+ 'XREShellData.h',
+]
+
+EXPORTS.mozilla += [
+ 'FileLocation.h',
+ 'IOInterposer.h',
+ 'LateWriteChecks.h',
+ 'Omnijar.h',
+ 'PoisonIOInterposer.h',
+ 'ServiceList.h',
+ 'Services.h',
+ 'XPCOM.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS += ['nsWindowsDllInterceptor.h']
+ EXPORTS.mozilla += ['perfprobe.h']
+ SOURCES += [
+ 'perfprobe.cpp',
+ 'PoisonIOInterposerBase.cpp',
+ 'PoisonIOInterposerWin.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'PoisonIOInterposerBase.cpp',
+ 'PoisonIOInterposerMac.cpp',
+ ]
+ SOURCES += ['mach_override.c']
+ SOURCES['mach_override.c'].flags += ['-Wno-unused-function']
+else:
+ SOURCES += ['PoisonIOInterposerStub.cpp']
+
+include('../glue/objs.mozbuild')
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ 'FrozenFunctions.cpp',
+ 'IOInterposer.cpp',
+ 'LateWriteChecks.cpp',
+ 'MainThreadIOLogger.cpp',
+ 'nsXPCOMStrings.cpp',
+ 'Services.cpp',
+ 'XPCOMInit.cpp',
+]
+
+if CONFIG['OS_ARCH'] != 'WINNT':
+ SOURCES += [
+ 'NSPRInterposer.cpp',
+ ]
+
+# FileLocation.cpp and Omnijar.cpp cannot be built in unified mode because they
+# use plarena.h.
+SOURCES += [
+ 'FileLocation.cpp',
+ 'Omnijar.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+DEFINES['_IMPL_NS_STRINGAPI'] = True
+DEFINES['OMNIJAR_NAME'] = CONFIG['OMNIJAR_NAME']
+if CONFIG['MOZ_ICU_DATA_ARCHIVE']:
+ DEFINES['MOZ_ICU_DATA_ARCHIVE'] = True
+
+LOCAL_INCLUDES += [
+ '!..',
+ '../base',
+ '../components',
+ '../ds',
+ '../glue',
+ '../io',
+ '../reflect/xptinfo',
+ '../threads',
+ '/chrome',
+ '/docshell/base',
+]
+
+if CONFIG['MOZ_VPX']:
+ LOCAL_INCLUDES += [
+ '/media/libvpx',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/xpcom/build/nsWindowsDllInterceptor.h b/xpcom/build/nsWindowsDllInterceptor.h
new file mode 100644
index 000000000..f7b7c93fb
--- /dev/null
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -0,0 +1,1127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NS_WINDOWS_DLL_INTERCEPTOR_H_
+#define NS_WINDOWS_DLL_INTERCEPTOR_H_
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "nsWindowsHelpers.h"
+
+#include <wchar.h>
+#include <windows.h>
+#include <winternl.h>
+
+/*
+ * Simple function interception.
+ *
+ * We have two separate mechanisms for intercepting a function: We can use the
+ * built-in nop space, if it exists, or we can create a detour.
+ *
+ * Using the built-in nop space works as follows: On x86-32, DLL functions
+ * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
+ * NOP instructions.
+ *
+ * When we detect a function with this prelude, we do the following:
+ *
+ * 1. Write a long jump to our interceptor function into the five bytes of NOPs
+ * before the function.
+ *
+ * 2. Write a short jump -5 into the two-byte nop at the beginning of the function.
+ *
+ * This mechanism is nice because it's thread-safe. It's even safe to do if
+ * another thread is currently running the function we're modifying!
+ *
+ * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump
+ * but not the long jump, so re-intercepting the same function won't work,
+ * because its prelude won't match.
+ *
+ *
+ * Unfortunately nop space patching doesn't work on functions which don't have
+ * this magic prelude (and in particular, x86-64 never has the prelude). So
+ * when we can't use the built-in nop space, we fall back to using a detour,
+ * which works as follows:
+ *
+ * 1. Save first N bytes of OrigFunction to trampoline, where N is a
+ * number of bytes >= 5 that are instruction aligned.
+ *
+ * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook
+ * function.
+ *
+ * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to
+ * continue original program flow.
+ *
+ * 4. Hook function needs to call the trampoline during its execution,
+ * to invoke the original function (so address of trampoline is
+ * returned).
+ *
+ * When the WindowsDllDetourPatcher object is destructed, OrigFunction is
+ * patched again to jump directly to the trampoline instead of going through
+ * the hook function. As such, re-intercepting the same function won't work, as
+ * jump instructions are not supported.
+ *
+ * Note that this is not thread-safe. Sad day.
+ *
+ */
+
+#include <stdint.h>
+
+namespace mozilla {
+namespace internal {
+
+class AutoVirtualProtect
+{
+public:
+ AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect)
+ : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0),
+ mSuccess(false)
+ {}
+
+ ~AutoVirtualProtect()
+ {
+ if (mSuccess) {
+ VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect,
+ &mOldProtect);
+ }
+ }
+
+ bool Protect()
+ {
+ mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize,
+ mNewProtect, &mOldProtect);
+ if (!mSuccess) {
+ // printf("VirtualProtectEx failed! %d\n", GetLastError());
+ }
+ return mSuccess;
+ }
+
+private:
+ void* const mFunc;
+ size_t const mSize;
+ DWORD const mNewProtect;
+ DWORD mOldProtect;
+ bool mSuccess;
+};
+
+class WindowsDllNopSpacePatcher
+{
+ typedef uint8_t* byteptr_t;
+ HMODULE mModule;
+
+ // Dumb array for remembering the addresses of functions we've patched.
+ // (This should be nsTArray, but non-XPCOM code uses this class.)
+ static const size_t maxPatchedFns = 128;
+ byteptr_t mPatchedFns[maxPatchedFns];
+ int mPatchedFnsLen;
+
+public:
+ WindowsDllNopSpacePatcher()
+ : mModule(0)
+ , mPatchedFnsLen(0)
+ {}
+
+#if defined(_M_IX86)
+ ~WindowsDllNopSpacePatcher()
+ {
+ // Restore the mov edi, edi to the beginning of each function we patched.
+
+ for (int i = 0; i < mPatchedFnsLen; i++) {
+ byteptr_t fn = mPatchedFns[i];
+
+ // Ensure we can write to the code.
+ AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE);
+ if (!protect.Protect()) {
+ continue;
+ }
+
+ // mov edi, edi
+ *((uint16_t*)fn) = 0xff8b;
+
+ // I don't think this is actually necessary, but it can't hurt.
+ FlushInstructionCache(GetCurrentProcess(),
+ /* ignored */ nullptr,
+ /* ignored */ 0);
+ }
+ }
+
+ void Init(const char* aModuleName)
+ {
+ if (!IsCompatible()) {
+#if defined(MOZILLA_INTERNAL_API)
+ NS_WARNING("NOP space patching is unavailable for compatibility reasons");
+#endif
+ return;
+ }
+
+ mModule = LoadLibraryExA(aModuleName, nullptr, 0);
+ if (!mModule) {
+ //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
+ return;
+ }
+ }
+
+ /**
+ * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
+ * in our address space. There is a bug in Detours 2.x that causes it to
+ * patch at the wrong address when attempting to detour code that is already
+ * NOP space patched. This function is an effort to detect the presence of
+ * this NVIDIA code in our address space and disable NOP space patching if it
+ * is. We also check AppInit_DLLs since this is the mechanism that the Optimus
+ * drivers use to inject into our process.
+ */
+ static bool IsCompatible()
+ {
+ // These DLLs are known to have bad interactions with this style of patching
+ const wchar_t* kIncompatibleDLLs[] = {
+ L"detoured.dll",
+ L"_etoured.dll",
+ L"nvd3d9wrap.dll",
+ L"nvdxgiwrap.dll"
+ };
+ // See if the infringing DLLs are already loaded
+ for (unsigned int i = 0; i < mozilla::ArrayLength(kIncompatibleDLLs); ++i) {
+ if (GetModuleHandleW(kIncompatibleDLLs[i])) {
+ return false;
+ }
+ }
+ if (GetModuleHandleW(L"user32.dll")) {
+ // user32 is loaded but the infringing DLLs are not, assume we're safe to
+ // proceed.
+ return true;
+ }
+ // If user32 has not loaded yet, check AppInit_DLLs to ensure that Optimus
+ // won't be loaded once user32 is initialized.
+ HKEY hkey = NULL;
+ if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
+ 0, KEY_QUERY_VALUE, &hkey)) {
+ nsAutoRegKey key(hkey);
+ DWORD numBytes = 0;
+ const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
+ // Query for required buffer size
+ LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
+ nullptr, nullptr, &numBytes);
+ mozilla::UniquePtr<wchar_t[]> data;
+ if (!status) {
+ // Allocate the buffer and query for the actual data
+ data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
+ status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
+ nullptr, (LPBYTE)data.get(), &numBytes);
+ }
+ if (!status) {
+ // For each token, split up the filename components and then check the
+ // name of the file.
+ const wchar_t kDelimiters[] = L", ";
+ wchar_t* tokenContext = nullptr;
+ wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
+ while (token) {
+ wchar_t fname[_MAX_FNAME] = {0};
+ if (!_wsplitpath_s(token, nullptr, 0, nullptr, 0,
+ fname, mozilla::ArrayLength(fname),
+ nullptr, 0)) {
+ // nvinit.dll is responsible for bootstrapping the DLL injection, so
+ // that is the library that we check for here
+ const wchar_t kNvInitName[] = L"nvinit";
+ if (!_wcsnicmp(fname, kNvInitName,
+ mozilla::ArrayLength(kNvInitName))) {
+ return false;
+ }
+ }
+ token = wcstok_s(nullptr, kDelimiters, &tokenContext);
+ }
+ }
+ }
+ return true;
+ }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ if (!mModule) {
+ return false;
+ }
+
+ if (!IsCompatible()) {
+#if defined(MOZILLA_INTERNAL_API)
+ NS_WARNING("NOP space patching is unavailable for compatibility reasons");
+#endif
+ return false;
+ }
+
+ if (mPatchedFnsLen == maxPatchedFns) {
+ // printf ("No space for hook in mPatchedFns.\n");
+ return false;
+ }
+
+ byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
+ if (!fn) {
+ //printf ("GetProcAddress failed\n");
+ return false;
+ }
+
+ fn = ResolveRedirectedAddress(fn);
+
+ // Ensure we can read and write starting at fn - 5 (for the long jmp we're
+ // going to write) and ending at fn + 2 (for the short jmp up to the long
+ // jmp). These bytes may span two pages with different protection.
+ AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE);
+ AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE);
+ if (!protectBefore.Protect() || !protectAfter.Protect()) {
+ return false;
+ }
+
+ bool rv = WriteHook(fn, aHookDest, aOrigFunc);
+
+ if (rv) {
+ mPatchedFns[mPatchedFnsLen] = fn;
+ mPatchedFnsLen++;
+ }
+
+ return rv;
+ }
+
+ bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Check that the 5 bytes before aFn are NOP's or INT 3's,
+ // and that the 2 bytes after aFn are mov(edi, edi).
+ //
+ // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE
+ // before calling WriteHook.
+
+ for (int i = -5; i <= -1; i++) {
+ if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3
+ return false;
+ }
+ }
+
+ // mov edi, edi. Yes, there are two ways to encode the same thing:
+ //
+ // 0x89ff == mov r/m, r
+ // 0x8bff == mov r, r/m
+ //
+ // where "r" is register and "r/m" is register or memory. Windows seems to
+ // use 8bff; I include 89ff out of paranoia.
+ if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) {
+ return false;
+ }
+
+ // Write a long jump into the space above the function.
+ aFn[-5] = 0xe9; // jmp
+ *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement
+
+ // Set aOrigFunc here, because after this point, aHookDest might be called,
+ // and aHookDest might use the aOrigFunc pointer.
+ *aOrigFunc = aFn + 2;
+
+ // Short jump up into our long jump.
+ *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5
+
+ // I think this routine is safe without this, but it can't hurt.
+ FlushInstructionCache(GetCurrentProcess(),
+ /* ignored */ nullptr,
+ /* ignored */ 0);
+
+ return true;
+ }
+
+private:
+ static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
+ {
+ // If function entry is jmp [disp32] such as used by kernel32,
+ // we resolve redirected address from import table.
+ if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
+ return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
+ }
+
+ return aOriginalFunction;
+ }
+#else
+ void Init(const char* aModuleName)
+ {
+ // Not implemented except on x86-32.
+ }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Not implemented except on x86-32.
+ return false;
+ }
+#endif
+};
+
+class WindowsDllDetourPatcher
+{
+ typedef unsigned char* byteptr_t;
+public:
+ WindowsDllDetourPatcher()
+ : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0)
+ {
+ }
+
+ ~WindowsDllDetourPatcher()
+ {
+ int i;
+ byteptr_t p;
+ for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) {
+#if defined(_M_IX86)
+ size_t nBytes = 1 + sizeof(intptr_t);
+#elif defined(_M_X64)
+ size_t nBytes = 2 + sizeof(intptr_t);
+#else
+#error "Unknown processor type"
+#endif
+ byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p));
+
+ // ensure we can modify the original code
+ AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE);
+ if (!protect.Protect()) {
+ continue;
+ }
+
+ // Remove the hook by making the original function jump directly
+ // in the trampoline.
+ intptr_t dest = (intptr_t)(p + sizeof(void*));
+#if defined(_M_IX86)
+ // Ensure the JMP from CreateTrampoline is where we expect it to be.
+ if (origBytes[0] != 0xE9)
+ continue;
+ *((intptr_t*)(origBytes + 1)) =
+ dest - (intptr_t)(origBytes + 5); // target displacement
+#elif defined(_M_X64)
+ // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
+ if (origBytes[0] != 0x49 || origBytes[1] != 0xBB)
+ continue;
+ *((intptr_t*)(origBytes + 2)) = dest;
+#else
+#error "Unknown processor type"
+#endif
+ }
+ }
+
+ void Init(const char* aModuleName, int aNumHooks = 0)
+ {
+ if (mModule) {
+ return;
+ }
+
+ mModule = LoadLibraryExA(aModuleName, nullptr, 0);
+ if (!mModule) {
+ //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
+ return;
+ }
+
+ int hooksPerPage = 4096 / kHookSize;
+ if (aNumHooks == 0) {
+ aNumHooks = hooksPerPage;
+ }
+
+ mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks);
+
+ mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr,
+ mMaxHooks * kHookSize,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READ);
+ if (!mHookPage) {
+ mModule = 0;
+ return;
+ }
+ }
+
+ bool Initialized() { return !!mModule; }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ if (!mModule) {
+ return false;
+ }
+
+ void* pAddr = (void*)GetProcAddress(mModule, aName);
+ if (!pAddr) {
+ //printf ("GetProcAddress failed\n");
+ return false;
+ }
+
+ pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
+
+ CreateTrampoline(pAddr, aHookDest, aOrigFunc);
+ if (!*aOrigFunc) {
+ //printf ("CreateTrampoline failed\n");
+ return false;
+ }
+
+ return true;
+ }
+
+protected:
+ const static int kPageSize = 4096;
+ const static int kHookSize = 128;
+
+ HMODULE mModule;
+ byteptr_t mHookPage;
+ int mMaxHooks;
+ int mCurHooks;
+
+ // rex bits
+ static const BYTE kMaskHighNibble = 0xF0;
+ static const BYTE kRexOpcode = 0x40;
+ static const BYTE kMaskRexW = 0x08;
+ static const BYTE kMaskRexR = 0x04;
+ static const BYTE kMaskRexX = 0x02;
+ static const BYTE kMaskRexB = 0x01;
+
+ // mod r/m bits
+ static const BYTE kRegFieldShift = 3;
+ static const BYTE kMaskMod = 0xC0;
+ static const BYTE kMaskReg = 0x38;
+ static const BYTE kMaskRm = 0x07;
+ static const BYTE kRmNeedSib = 0x04;
+ static const BYTE kModReg = 0xC0;
+ static const BYTE kModDisp32 = 0x80;
+ static const BYTE kModDisp8 = 0x40;
+ static const BYTE kModNoRegDisp = 0x00;
+ static const BYTE kRmNoRegDispDisp32 = 0x05;
+
+ // sib bits
+ static const BYTE kMaskSibScale = 0xC0;
+ static const BYTE kMaskSibIndex = 0x38;
+ static const BYTE kMaskSibBase = 0x07;
+ static const BYTE kSibBaseEbp = 0x05;
+
+ int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr)
+ {
+ if (!aModRm) {
+ return -1;
+ }
+ int numBytes = 1; // Start with 1 for mod r/m byte itself
+ switch (*aModRm & kMaskMod) {
+ case kModReg:
+ return numBytes;
+ case kModDisp8:
+ numBytes += 1;
+ break;
+ case kModDisp32:
+ numBytes += 4;
+ break;
+ case kModNoRegDisp:
+ if ((*aModRm & kMaskRm) == kRmNoRegDispDisp32) {
+#if defined(_M_X64)
+ // RIP-relative on AMD64, currently unsupported
+ return -1;
+#else
+ // On IA-32, all ModR/M instruction modes address memory relative to 0
+ numBytes += 4;
+#endif
+ } else if (((*aModRm & kMaskRm) == kRmNeedSib &&
+ (*(aModRm + 1) & kMaskSibBase) == kSibBaseEbp)) {
+ numBytes += 4;
+ }
+ break;
+ default:
+ // This should not be reachable
+ MOZ_ASSERT_UNREACHABLE("Impossible value for modr/m byte mod bits");
+ return -1;
+ }
+ if ((*aModRm & kMaskRm) == kRmNeedSib) {
+ // SIB byte
+ numBytes += 1;
+ }
+ if (aSubOpcode) {
+ *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
+ }
+ return numBytes;
+ }
+
+#if defined(_M_X64)
+ // To patch for JMP and JE
+
+ enum JumpType {
+ Je,
+ Jmp
+ };
+
+ struct JumpPatch {
+ JumpPatch()
+ : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
+ {
+ }
+
+ JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
+ : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
+ {
+ }
+
+ void AddJumpPatch(size_t aHookOffset, intptr_t aAbsJumpAddress,
+ JumpType aType = JumpType::Jmp)
+ {
+ mHookOffset = aHookOffset;
+ mJumpAddress = aAbsJumpAddress;
+ mType = aType;
+ }
+
+ size_t GenerateJump(uint8_t* aCode)
+ {
+ size_t offset = mHookOffset;
+ if (mType == JumpType::Je) {
+ // JNE RIP+14
+ aCode[offset] = 0x75;
+ aCode[offset + 1] = 14;
+ offset += 2;
+ }
+
+ // JMP [RIP+0]
+ aCode[offset] = 0xff;
+ aCode[offset + 1] = 0x25;
+ *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
+
+ // Jump table
+ *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
+
+ return offset + 2 + 4 + 8;
+ }
+
+ bool HasJumpPatch() const
+ {
+ return !!mJumpAddress;
+ }
+
+ size_t mHookOffset;
+ intptr_t mJumpAddress;
+ JumpType mType;
+ };
+
+#endif
+
+ enum ePrefixGroupBits
+ {
+ eNoPrefixes = 0,
+ ePrefixGroup1 = (1 << 0),
+ ePrefixGroup2 = (1 << 1),
+ ePrefixGroup3 = (1 << 2),
+ ePrefixGroup4 = (1 << 3)
+ };
+
+ int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex,
+ unsigned char* aOutGroupBits)
+ {
+ unsigned char& groupBits = *aOutGroupBits;
+ groupBits = eNoPrefixes;
+ int index = aBytesIndex;
+ while (true) {
+ switch (aBytes[index]) {
+ // Group 1
+ case 0xF0: // LOCK
+ case 0xF2: // REPNZ
+ case 0xF3: // REP / REPZ
+ if (groupBits & ePrefixGroup1) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup1;
+ ++index;
+ break;
+
+ // Group 2
+ case 0x2E: // CS override / branch not taken
+ case 0x36: // SS override
+ case 0x3E: // DS override / branch taken
+ case 0x64: // FS override
+ case 0x65: // GS override
+ if (groupBits & ePrefixGroup2) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup2;
+ ++index;
+ break;
+
+ // Group 3
+ case 0x66: // operand size override
+ if (groupBits & ePrefixGroup3) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup3;
+ ++index;
+ break;
+
+ // Group 4
+ case 0x67: // Address size override
+ if (groupBits & ePrefixGroup4) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup4;
+ ++index;
+ break;
+
+ default:
+ return index - aBytesIndex;
+ }
+ }
+ }
+
+ void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
+ {
+ *aOutTramp = nullptr;
+
+ AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize,
+ PAGE_EXECUTE_READWRITE);
+ if (!protectHookPage.Protect()) {
+ return;
+ }
+
+ byteptr_t tramp = FindTrampolineSpace();
+ if (!tramp) {
+ return;
+ }
+
+ byteptr_t origBytes = (byteptr_t)aOrigFunction;
+
+ int nBytes = 0;
+
+#if defined(_M_IX86)
+ int pJmp32 = -1;
+ while (nBytes < 5) {
+ // Understand some simple instructions that might be found in a
+ // prologue; we might need to extend this as necessary.
+ //
+ // Note! If we ever need to understand jump instructions, we'll
+ // need to rewrite the displacement argument.
+ unsigned char prefixGroups;
+ int numPrefixBytes = CountPrefixBytes(origBytes, nBytes, &prefixGroups);
+ if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) {
+ // Either the prefix sequence was bad, or there are prefixes that
+ // we don't currently support (groups 3 and 4)
+ return;
+ }
+ nBytes += numPrefixBytes;
+ if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) {
+ // various MOVs
+ ++nBytes;
+ int len = CountModRmSib(origBytes + nBytes);
+ if (len < 0) {
+ return;
+ }
+ nBytes += len;
+ } else if (origBytes[nBytes] == 0xA1) {
+ // MOV eax, [seg:offset]
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0xB8) {
+ // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0x83) {
+ // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8
+ unsigned char b = origBytes[nBytes + 1];
+ if ((b & 0xc0) == 0xc0) {
+ // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8
+ nBytes += 3;
+ } else {
+ // bail
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x68) {
+ // PUSH with 4-byte operand
+ nBytes += 5;
+ } else if ((origBytes[nBytes] & 0xf0) == 0x50) {
+ // 1-byte PUSH/POP
+ nBytes++;
+ } else if (origBytes[nBytes] == 0x6A) {
+ // PUSH imm8
+ nBytes += 2;
+ } else if (origBytes[nBytes] == 0xe9) {
+ pJmp32 = nBytes;
+ // jmp 32bit offset
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0xff && origBytes[nBytes + 1] == 0x25) {
+ // jmp [disp32]
+ nBytes += 6;
+ } else {
+ //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nBytes]);
+ return;
+ }
+ }
+#elif defined(_M_X64)
+ JumpPatch jump;
+
+ while (nBytes < 13) {
+
+ // if found JMP 32bit offset, next bytes must be NOP or INT3
+ if (jump.HasJumpPatch()) {
+ if (origBytes[nBytes] == 0x90 || origBytes[nBytes] == 0xcc) {
+ nBytes++;
+ continue;
+ }
+ return;
+ }
+ if (origBytes[nBytes] == 0x0f) {
+ nBytes++;
+ if (origBytes[nBytes] == 0x1f) {
+ // nop (multibyte)
+ nBytes++;
+ if ((origBytes[nBytes] & 0xc0) == 0x40 &&
+ (origBytes[nBytes] & 0x7) == 0x04) {
+ nBytes += 3;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x05) {
+ // syscall
+ nBytes++;
+ } else if (origBytes[nBytes] == 0x84) {
+ // je rel32
+ jump.AddJumpPatch(nBytes - 1,
+ (intptr_t)
+ origBytes + nBytes + 5 +
+ *(reinterpret_cast<int32_t*>(origBytes +
+ nBytes + 1)),
+ JumpType::Je);
+ nBytes += 5;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x40 ||
+ origBytes[nBytes] == 0x41) {
+ // Plain REX or REX.B
+ nBytes++;
+
+ if ((origBytes[nBytes] & 0xf0) == 0x50) {
+ // push/pop with Rx register
+ nBytes++;
+ } else if (origBytes[nBytes] >= 0xb8 && origBytes[nBytes] <= 0xbf) {
+ // mov r32, imm32
+ nBytes += 5;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x45) {
+ // REX.R & REX.B
+ nBytes++;
+
+ if (origBytes[nBytes] == 0x33) {
+ // xor r32, r32
+ nBytes += 2;
+ } else {
+ return;
+ }
+ } else if ((origBytes[nBytes] & 0xfb) == 0x48) {
+ // REX.W | REX.WR
+ nBytes++;
+
+ if (origBytes[nBytes] == 0x81 &&
+ (origBytes[nBytes + 1] & 0xf8) == 0xe8) {
+ // sub r, dword
+ nBytes += 6;
+ } else if (origBytes[nBytes] == 0x83 &&
+ (origBytes[nBytes + 1] & 0xf8) == 0xe8) {
+ // sub r, byte
+ nBytes += 3;
+ } else if (origBytes[nBytes] == 0x83 &&
+ (origBytes[nBytes + 1] & 0xf8) == 0x60) {
+ // and [r+d], imm8
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0x85) {
+ // 85 /r => TEST r/m32, r32
+ if ((origBytes[nBytes + 1] & 0xc0) == 0xc0) {
+ nBytes += 2;
+ } else {
+ return;
+ }
+ } else if ((origBytes[nBytes] & 0xfd) == 0x89) {
+ ++nBytes;
+ // MOV r/m64, r64 | MOV r64, r/m64
+ int len = CountModRmSib(origBytes + nBytes);
+ if (len < 0) {
+ return;
+ }
+ nBytes += len;
+ } else if (origBytes[nBytes] == 0xc7) {
+ // MOV r/m64, imm32
+ if (origBytes[nBytes + 1] == 0x44) {
+ // MOV [r64+disp8], imm32
+ // ModR/W + SIB + disp8 + imm32
+ nBytes += 8;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0xff) {
+ // JMP /4
+ if ((origBytes[nBytes + 1] & 0xc0) == 0x0 &&
+ (origBytes[nBytes + 1] & 0x07) == 0x5) {
+ // [rip+disp32]
+ // convert JMP 32bit offset to JMP 64bit direct
+ jump.AddJumpPatch(nBytes - 1,
+ *reinterpret_cast<intptr_t*>(
+ origBytes + nBytes + 6 +
+ *reinterpret_cast<int32_t*>(origBytes + nBytes +
+ 2)));
+ nBytes += 6;
+ } else {
+ // not support yet!
+ return;
+ }
+ } else {
+ // not support yet!
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x66) {
+ // operand override prefix
+ nBytes += 1;
+ // This is the same as the x86 version
+ if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) {
+ // various MOVs
+ unsigned char b = origBytes[nBytes + 1];
+ if (((b & 0xc0) == 0xc0) ||
+ (((b & 0xc0) == 0x00) &&
+ ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) {
+ // REG=r, R/M=r or REG=r, R/M=[r]
+ nBytes += 2;
+ } else if ((b & 0xc0) == 0x40) {
+ if ((b & 0x07) == 0x04) {
+ // REG=r, R/M=[SIB + disp8]
+ nBytes += 4;
+ } else {
+ // REG=r, R/M=[r + disp8]
+ nBytes += 3;
+ }
+ } else {
+ // complex MOV, bail
+ return;
+ }
+ }
+ } else if ((origBytes[nBytes] & 0xf0) == 0x50) {
+ // 1-byte push/pop
+ nBytes++;
+ } else if (origBytes[nBytes] == 0x65) {
+ // GS prefix
+ //
+ // The entry of GetKeyState on Windows 10 has the following code.
+ // 65 48 8b 04 25 30 00 00 00 mov rax,qword ptr gs:[30h]
+ // (GS prefix + REX + MOV (0x8b) ...)
+ if (origBytes[nBytes + 1] == 0x48 &&
+ (origBytes[nBytes + 2] >= 0x88 && origBytes[nBytes + 2] <= 0x8b)) {
+ nBytes += 3;
+ int len = CountModRmSib(origBytes + nBytes);
+ if (len < 0) {
+ // no way to support this yet.
+ return;
+ }
+ nBytes += len;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x90) {
+ // nop
+ nBytes++;
+ } else if (origBytes[nBytes] == 0xb8) {
+ // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0x33) {
+ // xor r32, r/m32
+ nBytes += 2;
+ } else if (origBytes[nBytes] == 0xf6) {
+ // test r/m8, imm8 (used by ntdll on Windows 10 x64)
+ // (no flags are affected by near jmp since there is no task switch,
+ // so it is ok for a jmp to be written immediately after a test)
+ BYTE subOpcode = 0;
+ int nModRmSibBytes = CountModRmSib(&origBytes[nBytes + 1], &subOpcode);
+ if (nModRmSibBytes < 0 || subOpcode != 0) {
+ // Unsupported
+ return;
+ }
+ nBytes += 2 + nModRmSibBytes;
+ } else if (origBytes[nBytes] == 0xc3) {
+ // ret
+ nBytes++;
+ } else if (origBytes[nBytes] == 0xcc) {
+ // int 3
+ nBytes++;
+ } else if (origBytes[nBytes] == 0xe9) {
+ // jmp 32bit offset
+ jump.AddJumpPatch(nBytes,
+ // convert JMP 32bit offset to JMP 64bit direct
+ (intptr_t)
+ origBytes + nBytes + 5 +
+ *(reinterpret_cast<int32_t*>(origBytes + nBytes + 1)));
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0xff) {
+ nBytes++;
+ if ((origBytes[nBytes] & 0xf8) == 0xf0) {
+ // push r64
+ nBytes++;
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+#else
+#error "Unknown processor type"
+#endif
+
+ if (nBytes > 100) {
+ //printf ("Too big!");
+ return;
+ }
+
+ // We keep the address of the original function in the first bytes of
+ // the trampoline buffer
+ *((void**)tramp) = EncodePointer(aOrigFunction);
+ tramp += sizeof(void*);
+
+ memcpy(tramp, aOrigFunction, nBytes);
+
+ // OrigFunction+N, the target of the trampoline
+ byteptr_t trampDest = origBytes + nBytes;
+
+#if defined(_M_IX86)
+ if (pJmp32 >= 0) {
+ // Jump directly to the original target of the jump instead of jumping to the
+ // original function.
+ // Adjust jump target displacement to jump location in the trampoline.
+ *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
+ } else {
+ tramp[nBytes] = 0xE9; // jmp
+ *((intptr_t*)(tramp + nBytes + 1)) =
+ (intptr_t)trampDest - (intptr_t)(tramp + nBytes + 5); // target displacement
+ }
+#elif defined(_M_X64)
+ // If JMP/JE opcode found, we don't insert to trampoline jump
+ if (jump.HasJumpPatch()) {
+ size_t offset = jump.GenerateJump(tramp);
+ if (jump.mType != JumpType::Jmp) {
+ JumpPatch patch(offset, reinterpret_cast<intptr_t>(trampDest));
+ patch.GenerateJump(tramp);
+ }
+ } else {
+ JumpPatch patch(nBytes, reinterpret_cast<intptr_t>(trampDest));
+ patch.GenerateJump(tramp);
+ }
+#endif
+
+ // The trampoline is now valid.
+ *aOutTramp = tramp;
+
+ // ensure we can modify the original code
+ AutoVirtualProtect protect(aOrigFunction, nBytes, PAGE_EXECUTE_READWRITE);
+ if (!protect.Protect()) {
+ return;
+ }
+
+#if defined(_M_IX86)
+ // now modify the original bytes
+ origBytes[0] = 0xE9; // jmp
+ *((intptr_t*)(origBytes + 1)) =
+ aDest - (intptr_t)(origBytes + 5); // target displacement
+#elif defined(_M_X64)
+ // mov r11, address
+ origBytes[0] = 0x49;
+ origBytes[1] = 0xbb;
+
+ *((intptr_t*)(origBytes + 2)) = aDest;
+
+ // jmp r11
+ origBytes[10] = 0x41;
+ origBytes[11] = 0xff;
+ origBytes[12] = 0xe3;
+#endif
+ }
+
+ byteptr_t FindTrampolineSpace()
+ {
+ if (mCurHooks >= mMaxHooks) {
+ return 0;
+ }
+
+ byteptr_t p = mHookPage + mCurHooks * kHookSize;
+
+ mCurHooks++;
+
+ return p;
+ }
+
+ static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
+ {
+#if defined(_M_IX86)
+ // If function entry is jmp [disp32] such as used by kernel32,
+ // we resolve redirected address from import table.
+ if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
+ return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
+ }
+#elif defined(_M_X64)
+ if (aOriginalFunction[0] == 0xe9) {
+ // require for TestDllInterceptor with --disable-optimize
+ int32_t offset = *((int32_t*)(aOriginalFunction + 1));
+ return aOriginalFunction + 5 + offset;
+ }
+#endif
+
+ return aOriginalFunction;
+ }
+};
+
+} // namespace internal
+
+class WindowsDllInterceptor
+{
+ internal::WindowsDllNopSpacePatcher mNopSpacePatcher;
+ internal::WindowsDllDetourPatcher mDetourPatcher;
+
+ const char* mModuleName;
+ int mNHooks;
+
+public:
+ WindowsDllInterceptor()
+ : mModuleName(nullptr)
+ , mNHooks(0)
+ {}
+
+ void Init(const char* aModuleName, int aNumHooks = 0)
+ {
+ if (mModuleName) {
+ return;
+ }
+
+ mModuleName = aModuleName;
+ mNHooks = aNumHooks;
+ mNopSpacePatcher.Init(aModuleName);
+
+ // Lazily initialize mDetourPatcher, since it allocates memory and we might
+ // not need it.
+ }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Use a nop space patch if possible, otherwise fall back to a detour.
+ // This should be the preferred method for adding hooks.
+
+ if (!mModuleName) {
+ return false;
+ }
+
+ if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) {
+ return true;
+ }
+
+ return AddDetour(aName, aHookDest, aOrigFunc);
+ }
+
+ bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Generally, code should not call this method directly. Use AddHook unless
+ // there is a specific need to avoid nop space patches.
+
+ if (!mModuleName) {
+ return false;
+ }
+
+ if (!mDetourPatcher.Initialized()) {
+ mDetourPatcher.Init(mModuleName, mNHooks);
+ }
+
+ return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc);
+ }
+};
+
+} // namespace mozilla
+
+#endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */
diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h
new file mode 100644
index 000000000..925ca6901
--- /dev/null
+++ b/xpcom/build/nsXPCOM.h
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOM_h__
+#define nsXPCOM_h__
+
+#include "nscore.h"
+#include "nsXPCOMCID.h"
+
+#ifdef __cplusplus
+#define DECL_CLASS(c) class c
+#define DECL_STRUCT(c) struct c
+#else
+#define DECL_CLASS(c) typedef struct c c
+#define DECL_STRUCT(c) typedef struct c c
+#endif
+
+DECL_CLASS(nsAString);
+DECL_CLASS(nsACString);
+
+DECL_CLASS(nsISupports);
+DECL_CLASS(nsIModule);
+DECL_CLASS(nsIComponentManager);
+DECL_CLASS(nsIComponentRegistrar);
+DECL_CLASS(nsIServiceManager);
+DECL_CLASS(nsIFile);
+DECL_CLASS(nsILocalFile);
+DECL_CLASS(nsIDirectoryServiceProvider);
+DECL_CLASS(nsIMemory);
+DECL_CLASS(nsIDebug2);
+
+#ifdef __cplusplus
+namespace mozilla {
+struct Module;
+} // namespace mozilla
+#endif
+
+/**
+ * Initialises XPCOM. You must call one of the NS_InitXPCOM methods
+ * before proceeding to use xpcom. The one exception is that you may
+ * call NS_NewLocalFile to create a nsIFile.
+ *
+ * @note Use <CODE>NS_NewLocalFile</CODE> or <CODE>NS_NewNativeLocalFile</CODE>
+ * to create the file object you supply as the bin directory path in this
+ * call. The function may be safely called before the rest of XPCOM or
+ * embedding has been initialised.
+ *
+ * @param aResult The service manager. You may pass null.
+ *
+ * @param aBinDirectory The directory containing the component
+ * registry and runtime libraries;
+ * or use <CODE>nullptr</CODE> to use the working
+ * directory.
+ *
+ * @param aAppFileLocationProvider The object to be used by Gecko that
+ * specifies to Gecko where to find profiles, the
+ * component registry preferences and so on; or use
+ * <CODE>nullptr</CODE> for the default behaviour.
+ *
+ * @see NS_NewLocalFile
+ * @see nsIFile
+ * @see nsIDirectoryServiceProvider
+ *
+ * @return NS_OK for success;
+ * NS_ERROR_NOT_INITIALIZED if static globals were not initialized,
+ * which can happen if XPCOM is reloaded, but did not completly
+ * shutdown. Other error codes indicate a failure during
+ * initialisation.
+ */
+XPCOM_API(nsresult)
+NS_InitXPCOM2(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider);
+
+/**
+ * Initialize only minimal components of XPCOM. This ensures nsThreadManager,
+ * logging, and timers will work.
+ */
+XPCOM_API(nsresult)
+NS_InitMinimalXPCOM();
+
+/**
+ * Shutdown XPCOM. You must call this method after you are finished
+ * using xpcom.
+ *
+ * @param aServMgr The service manager which was returned by NS_InitXPCOM.
+ * This will release servMgr. You may pass null.
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_ShutdownXPCOM(nsIServiceManager* aServMgr);
+
+
+/**
+ * Public Method to access to the service manager.
+ *
+ * @param aResult Interface pointer to the service manager
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetServiceManager(nsIServiceManager** aResult);
+
+/**
+ * Public Method to access to the component manager.
+ *
+ * @param aResult Interface pointer to the service
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetComponentManager(nsIComponentManager** aResult);
+
+
+/**
+ * Public Method to access to the component registration manager.
+ *
+ * @param aResult Interface pointer to the service
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetComponentRegistrar(nsIComponentRegistrar** aResult);
+
+/**
+ * Public Method to access to the memory manager. See nsIMemory
+ *
+ * @param aResult Interface pointer to the memory manager
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetMemoryManager(nsIMemory** aResult);
+
+/**
+ * Public Method to create an instance of a nsIFile. This function
+ * may be called prior to NS_InitXPCOM.
+ *
+ * @param aPath
+ * A string which specifies a full file path to a
+ * location. Relative paths will be treated as an
+ * error (NS_ERROR_FILE_UNRECOGNIZED_PATH).
+ * |NS_NewNativeLocalFile|'s path must be in the
+ * filesystem charset.
+ * @param aFollowLinks
+ * This attribute will determine if the nsLocalFile will auto
+ * resolve symbolic links. By default, this value will be false
+ * on all non unix systems. On unix, this attribute is effectively
+ * a noop.
+ * @param aResult Interface pointer to a new instance of an nsIFile
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure.
+ */
+
+#ifdef __cplusplus
+
+XPCOM_API(nsresult) NS_NewLocalFile(const nsAString& aPath,
+ bool aFollowLinks,
+ nsIFile** aResult);
+
+XPCOM_API(nsresult) NS_NewNativeLocalFile(const nsACString& aPath,
+ bool aFollowLinks,
+ nsIFile** aResult);
+
+#endif
+
+/**
+ * Allocator functions for the standalone glue.
+ * Do not use outside the xpcom glue code.
+ * Use moz_xmalloc/moz_xrealloc/free, or new/delete instead.
+ */
+#ifdef XPCOM_GLUE
+/**
+ * Allocates a block of memory of a particular size. If the memory cannot
+ * be allocated (because of an out-of-memory condition), the process aborts.
+ *
+ * @param aSize The size of the block to allocate
+ * @result The block of memory
+ * @note This function is thread-safe.
+ */
+XPCOM_API(void*) NS_Alloc(size_t aSize);
+
+/**
+ * Reallocates a block of memory to a new size.
+ *
+ * @param aPtr The block of memory to reallocate. This block must originally
+ have been allocated by NS_Alloc or NS_Realloc
+ * @param aSize The new size. If 0, frees the block like NS_Free
+ * @result The reallocated block of memory
+ * @note This function is thread-safe.
+ *
+ * If aPtr is null, this function behaves like NS_Alloc.
+ * If s is the size of the block to which aPtr points, the first min(s, size)
+ * bytes of aPtr's block are copied to the new block. If the allocation
+ * succeeds, aPtr is freed and a pointer to the new block is returned. If the
+ * allocation fails, the process aborts.
+ */
+XPCOM_API(void*) NS_Realloc(void* aPtr, size_t aSize);
+
+/**
+ * Frees a block of memory. Null is a permissible value, in which case no
+ * action is taken.
+ *
+ * @param aPtr The block of memory to free. This block must originally have
+ * been allocated by NS_Alloc or NS_Realloc
+ * @note This function is thread-safe.
+ */
+XPCOM_API(void) NS_Free(void* aPtr);
+#else
+#define NS_Alloc moz_xmalloc
+#define NS_Realloc moz_xrealloc
+#define NS_Free free
+#endif
+
+/**
+ * Support for warnings, assertions, and debugging breaks.
+ */
+
+enum
+{
+ NS_DEBUG_WARNING = 0,
+ NS_DEBUG_ASSERTION = 1,
+ NS_DEBUG_BREAK = 2,
+ NS_DEBUG_ABORT = 3
+};
+
+/**
+ * Print a runtime assertion. This function is available in both debug and
+ * release builds.
+ *
+ * @note Based on the value of aSeverity and the XPCOM_DEBUG_BREAK
+ * environment variable, this function may cause the application to
+ * print the warning, print a stacktrace, break into a debugger, or abort
+ * immediately.
+ *
+ * @param aSeverity A NS_DEBUG_* value
+ * @param aStr A readable error message (ASCII, may be null)
+ * @param aExpr The expression evaluated (may be null)
+ * @param aFile The source file containing the assertion (may be null)
+ * @param aLine The source file line number (-1 indicates no line number)
+ */
+XPCOM_API(void) NS_DebugBreak(uint32_t aSeverity,
+ const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine);
+
+/**
+ * Perform a stack-walk to a debugging log under various
+ * circumstances. Used to aid debugging of leaked object graphs.
+ *
+ * The NS_Log* functions are available in both debug and release
+ * builds of XPCOM, but the output will be useless unless binary
+ * debugging symbols for all modules in the stacktrace are available.
+ */
+
+/**
+ * By default, refcount logging is enabled at NS_InitXPCOM and
+ * refcount statistics are printed at NS_ShutdownXPCOM. NS_LogInit and
+ * NS_LogTerm allow applications to enable logging earlier and delay
+ * printing of logging statistics. They should always be used as a
+ * matched pair.
+ */
+XPCOM_API(void) NS_LogInit();
+
+XPCOM_API(void) NS_LogTerm();
+
+#ifdef __cplusplus
+/**
+ * A helper class that calls NS_LogInit in its constructor and
+ * NS_LogTerm in its destructor.
+ */
+
+class ScopedLogging
+{
+public:
+ ScopedLogging()
+ {
+ NS_LogInit();
+ }
+
+ ~ScopedLogging()
+ {
+ NS_LogTerm();
+ }
+};
+#endif
+
+/**
+ * Log construction and destruction of objects. Processing tools can use the
+ * stacktraces printed by these functions to identify objects that are being
+ * leaked.
+ *
+ * @param aPtr A pointer to the concrete object.
+ * @param aTypeName The class name of the type
+ * @param aInstanceSize The size of the type
+ */
+
+XPCOM_API(void) NS_LogCtor(void* aPtr, const char* aTypeName,
+ uint32_t aInstanceSize);
+
+XPCOM_API(void) NS_LogDtor(void* aPtr, const char* aTypeName,
+ uint32_t aInstanceSize);
+
+/**
+ * Log a stacktrace when an XPCOM object's refcount is incremented or
+ * decremented. Processing tools can use the stacktraces printed by these
+ * functions to identify objects that were leaked due to XPCOM references.
+ *
+ * @param aPtr A pointer to the concrete object
+ * @param aNewRefCnt The new reference count.
+ * @param aTypeName The class name of the type
+ * @param aInstanceSize The size of the type
+ */
+XPCOM_API(void) NS_LogAddRef(void* aPtr, nsrefcnt aNewRefCnt,
+ const char* aTypeName, uint32_t aInstanceSize);
+
+XPCOM_API(void) NS_LogRelease(void* aPtr, nsrefcnt aNewRefCnt,
+ const char* aTypeName);
+
+/**
+ * Log reference counting performed by COMPtrs. Processing tools can
+ * use the stacktraces printed by these functions to simplify reports
+ * about leaked objects generated from the data printed by
+ * NS_LogAddRef/NS_LogRelease.
+ *
+ * @param aCOMPtr the address of the COMPtr holding a strong reference
+ * @param aObject the object being referenced by the COMPtr
+ */
+
+XPCOM_API(void) NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject);
+
+XPCOM_API(void) NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject);
+
+/**
+ * The XPCOM cycle collector analyzes and breaks reference cycles between
+ * participating XPCOM objects. All objects in the cycle must implement
+ * nsCycleCollectionParticipant to break cycles correctly.
+ */
+
+#ifdef __cplusplus
+
+class nsCycleCollectionParticipant;
+class nsCycleCollectingAutoRefCnt;
+
+XPCOM_API(void) NS_CycleCollectorSuspect3(void* aPtr,
+ nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete);
+
+#endif
+
+/**
+ * Categories (in the category manager service) used by XPCOM:
+ */
+
+/**
+ * A category which is read after component registration but before
+ * the "xpcom-startup" notifications. Each category entry is treated
+ * as the contract ID of a service which implements
+ * nsIDirectoryServiceProvider. Each directory service provider is
+ * installed in the global directory service.
+ */
+#define XPCOM_DIRECTORY_PROVIDER_CATEGORY "xpcom-directory-providers"
+
+/**
+ * A category which is read after component registration but before
+ * NS_InitXPCOM returns. Each category entry is treated as the contractID of
+ * a service: each service is instantiated, and if it implements nsIObserver
+ * the nsIObserver.observe method is called with the "xpcom-startup" topic.
+ */
+#define NS_XPCOM_STARTUP_CATEGORY "xpcom-startup"
+
+
+/**
+ * Observer topics (in the observer service) used by XPCOM:
+ */
+
+/**
+ * At XPCOM startup after component registration is complete, the
+ * following topic is notified. In order to receive this notification,
+ * component must register their contract ID in the category manager,
+ *
+ * @see NS_XPCOM_STARTUP_CATEGORY
+ */
+#define NS_XPCOM_STARTUP_OBSERVER_ID "xpcom-startup"
+
+/**
+ * At XPCOM shutdown, this topic is notified just before "xpcom-shutdown".
+ * Components should only use this to mark themselves as 'being destroyed'.
+ * Nothing should be dispatched to any event loop.
+ */
+#define NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID "xpcom-will-shutdown"
+
+/**
+ * At XPCOM shutdown, this topic is notified. All components must
+ * release any interface references to objects in other modules when
+ * this topic is notified.
+ */
+#define NS_XPCOM_SHUTDOWN_OBSERVER_ID "xpcom-shutdown"
+
+/**
+ * This topic is notified when an entry was added to a category in the
+ * category manager. The subject of the notification will be the name of
+ * the added entry as an nsISupportsCString, and the data will be the
+ * name of the category. The notification will occur on the main thread.
+ */
+#define NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID \
+ "xpcom-category-entry-added"
+
+/**
+ * This topic is notified when an entry was removed from a category in the
+ * category manager. The subject of the notification will be the name of
+ * the removed entry as an nsISupportsCString, and the data will be the
+ * name of the category. The notification will occur on the main thread.
+ */
+#define NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID \
+ "xpcom-category-entry-removed"
+
+/**
+ * This topic is notified when an a category was cleared in the category
+ * manager. The subject of the notification will be the category manager,
+ * and the data will be the name of the cleared category.
+ * The notification will occur on the main thread.
+ */
+#define NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID "xpcom-category-cleared"
+
+XPCOM_API(nsresult) NS_GetDebug(nsIDebug2** aResult);
+
+#endif
diff --git a/xpcom/build/nsXPCOMCID.h b/xpcom/build/nsXPCOMCID.h
new file mode 100644
index 000000000..7219babe4
--- /dev/null
+++ b/xpcom/build/nsXPCOMCID.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOMCID_h__
+#define nsXPCOMCID_h__
+
+/**
+ * XPCOM Directory Service Contract ID
+ * The directory service provides ways to obtain file system locations. The
+ * directory service is a singleton.
+ *
+ * This contract supports the nsIDirectoryService and the nsIProperties
+ * interfaces.
+ *
+ */
+#define NS_DIRECTORY_SERVICE_CONTRACTID "@mozilla.org/file/directory_service;1"
+
+/**
+ * XPCOM File
+ * The file abstraction provides ways to obtain and access files and
+ * directories located on the local system.
+ *
+ * This contract supports the nsIFile interface.
+ * This contract may also support platform specific interfaces such as
+ * nsILocalFileMac on platforms where additional interfaces are required.
+ *
+ */
+#define NS_LOCAL_FILE_CONTRACTID "@mozilla.org/file/local;1"
+
+/**
+ * XPCOM Category Manager Contract ID
+ * The contract supports the nsICategoryManager interface. The
+ * category manager is a singleton.
+ * The "enumerateCategory" method of nsICategoryManager will return an object
+ * that implements nsIUTF8StringEnumerator. In addition, the enumerator will
+ * return the entries in sorted order (sorted by byte comparison).
+ */
+#define NS_CATEGORYMANAGER_CONTRACTID "@mozilla.org/categorymanager;1"
+
+/**
+ * XPCOM Properties Object Contract ID
+ * Simple mapping object which supports the nsIProperties interface.
+ */
+#define NS_PROPERTIES_CONTRACTID "@mozilla.org/properties;1"
+
+/**
+ * XPCOM Array Object ContractID
+ * Simple array implementation which supports the nsIArray and
+ * nsIMutableArray interfaces.
+ */
+#define NS_ARRAY_CONTRACTID "@mozilla.org/array;1"
+
+/**
+ * Observer Service ContractID
+ * The observer service implements the global nsIObserverService object.
+ * It should be used from the main thread only.
+ */
+#define NS_OBSERVERSERVICE_CONTRACTID "@mozilla.org/observer-service;1"
+
+/**
+ * IO utilities service contract id.
+ * This guarantees implementation of nsIIOUtil. Usable from any thread.
+ */
+#define NS_IOUTIL_CONTRACTID "@mozilla.org/io-util;1"
+
+/**
+ * Memory reporter service CID
+ */
+#define NS_MEMORY_REPORTER_MANAGER_CONTRACTID "@mozilla.org/memory-reporter-manager;1"
+
+/**
+ * Memory info dumper service CID
+ */
+#define NS_MEMORY_INFO_DUMPER_CONTRACTID "@mozilla.org/memory-info-dumper;1"
+
+/**
+ * Status reporter service CID
+ */
+#define NS_STATUS_REPORTER_MANAGER_CONTRACTID "@mozilla.org/status-reporter-manager;1"
+
+/**
+ * Cycle collector logger contract id
+ */
+#define NS_CYCLE_COLLECTOR_LOGGER_CONTRACTID "@mozilla.org/cycle-collector-logger;1"
+
+/**
+ * nsMessageLoop contract id
+ */
+#define NS_MESSAGE_LOOP_CONTRACTID "@mozilla.org/message-loop;1"
+
+#define NS_COMPARTMENT_INFO_CONTRACTID "@mozilla.org/compartment-info;1"
+
+/**
+ * The following are the CIDs and Contract IDs of the nsISupports wrappers for
+ * primative types.
+ */
+#define NS_SUPPORTS_ID_CID \
+{ 0xacf8dc40, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_ID_CONTRACTID "@mozilla.org/supports-id;1"
+
+#define NS_SUPPORTS_CSTRING_CID \
+{ 0xacf8dc41, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_CSTRING_CONTRACTID "@mozilla.org/supports-cstring;1"
+
+#define NS_SUPPORTS_STRING_CID \
+{ 0xacf8dc42, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_STRING_CONTRACTID "@mozilla.org/supports-string;1"
+
+#define NS_SUPPORTS_PRBOOL_CID \
+{ 0xacf8dc43, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRBOOL_CONTRACTID "@mozilla.org/supports-PRBool;1"
+
+#define NS_SUPPORTS_PRUINT8_CID \
+{ 0xacf8dc44, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT8_CONTRACTID "@mozilla.org/supports-PRUint8;1"
+
+#define NS_SUPPORTS_PRUINT16_CID \
+{ 0xacf8dc46, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT16_CONTRACTID "@mozilla.org/supports-PRUint16;1"
+
+#define NS_SUPPORTS_PRUINT32_CID \
+{ 0xacf8dc47, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT32_CONTRACTID "@mozilla.org/supports-PRUint32;1"
+
+#define NS_SUPPORTS_PRUINT64_CID \
+{ 0xacf8dc48, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT64_CONTRACTID "@mozilla.org/supports-PRUint64;1"
+
+#define NS_SUPPORTS_PRTIME_CID \
+{ 0xacf8dc49, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRTIME_CONTRACTID "@mozilla.org/supports-PRTime;1"
+
+#define NS_SUPPORTS_CHAR_CID \
+{ 0xacf8dc4a, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_CHAR_CONTRACTID "@mozilla.org/supports-char;1"
+
+#define NS_SUPPORTS_PRINT16_CID \
+{ 0xacf8dc4b, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRINT16_CONTRACTID "@mozilla.org/supports-PRInt16;1"
+
+#define NS_SUPPORTS_PRINT32_CID \
+{ 0xacf8dc4c, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRINT32_CONTRACTID "@mozilla.org/supports-PRInt32;1"
+
+#define NS_SUPPORTS_PRINT64_CID \
+{ 0xacf8dc4d, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRINT64_CONTRACTID "@mozilla.org/supports-PRInt64;1"
+
+#define NS_SUPPORTS_FLOAT_CID \
+{ 0xcbf86870, 0x4ac0, 0x11d3, \
+{ 0xba, 0xea, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+#define NS_SUPPORTS_FLOAT_CONTRACTID "@mozilla.org/supports-float;1"
+
+#define NS_SUPPORTS_DOUBLE_CID \
+{ 0xcbf86871, 0x4ac0, 0x11d3, \
+{ 0xba, 0xea, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+#define NS_SUPPORTS_DOUBLE_CONTRACTID "@mozilla.org/supports-double;1"
+
+#define NS_SUPPORTS_VOID_CID \
+{ 0xaf10f3e0, 0x568d, 0x11d3, \
+{ 0xba, 0xf8, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+#define NS_SUPPORTS_VOID_CONTRACTID "@mozilla.org/supports-void;1"
+
+#define NS_SUPPORTS_INTERFACE_POINTER_CID \
+{ 0xA99FEBBA, 0x1DD1, 0x11B2, \
+{ 0xA9, 0x43, 0xB0, 0x23, 0x34, 0xA6, 0xD0, 0x83 } }
+#define NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID "@mozilla.org/supports-interface-pointer;1"
+
+#endif
diff --git a/xpcom/build/nsXPCOMCIDInternal.h b/xpcom/build/nsXPCOMCIDInternal.h
new file mode 100644
index 000000000..c9f7fb6f3
--- /dev/null
+++ b/xpcom/build/nsXPCOMCIDInternal.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOMCIDInternal_h__
+#define nsXPCOMCIDInternal_h__
+
+#include "nsXPCOMCID.h"
+
+/**
+ * A hashtable-based property bag component.
+ * @implements nsIWritablePropertyBag, nsIWritablePropertyBag2
+ */
+#define NS_HASH_PROPERTY_BAG_CID \
+{ 0x678c50b8, 0x6bcb, 0x4ad0, \
+{ 0xb9, 0xb8, 0xc8, 0x11, 0x75, 0x95, 0x51, 0x99 } }
+#define NS_HASH_PROPERTY_BAG_CONTRACTID "@mozilla.org/hash-property-bag;1"
+
+/**
+ * Factory for creating nsIUnicharInputStream
+ * @implements nsIUnicharInputStreamFactory
+ * @note nsIUnicharInputStream instances cannot be created via
+ * createInstance. Code must use one of the custom factory methods.
+ */
+#define NS_SIMPLE_UNICHAR_STREAM_FACTORY_CONTRACTID \
+ "@mozilla.org/xpcom/simple-unichar-stream-factory;1"
+
+/**
+ * The global thread manager service. This component is a singleton.
+ * @implements nsIThreadManager
+ */
+#define NS_THREADMANAGER_CONTRACTID "@mozilla.org/thread-manager;1"
+
+/**
+ * A thread pool component.
+ * @implements nsIThreadPool
+ */
+#define NS_THREADPOOL_CONTRACTID "@mozilla.org/thread-pool;1"
+
+/**
+ * The contract id for the nsIXULAppInfo service.
+ */
+#define XULAPPINFO_SERVICE_CONTRACTID \
+ "@mozilla.org/xre/app-info;1"
+
+/**
+ * The contract id for the nsIXULRuntime service.
+ */
+#define XULRUNTIME_SERVICE_CONTRACTID \
+ "@mozilla.org/xre/runtime;1"
+
+#endif // nsXPCOMCIDInternal_h__
diff --git a/xpcom/build/nsXPCOMPrivate.h b/xpcom/build/nsXPCOMPrivate.h
new file mode 100644
index 000000000..99a994013
--- /dev/null
+++ b/xpcom/build/nsXPCOMPrivate.h
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOMPrivate_h__
+#define nsXPCOMPrivate_h__
+
+#include "nscore.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMStrings.h"
+#include "xptcall.h"
+
+class nsStringContainer;
+class nsCStringContainer;
+class nsPurpleBufferEntry;
+
+/**
+ * During this shutdown notification all threads which run XPCOM code must
+ * be joined.
+ */
+#define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID "xpcom-shutdown-threads"
+
+/**
+ * During this shutdown notification all module loaders must unload XPCOM
+ * modules.
+ */
+#define NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID "xpcom-shutdown-loaders"
+
+// PUBLIC
+typedef nsresult (*InitFunc)(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider);
+typedef nsresult (*ShutdownFunc)(nsIServiceManager* aServMgr);
+typedef nsresult (*GetServiceManagerFunc)(nsIServiceManager** aResult);
+typedef nsresult (*GetComponentManagerFunc)(nsIComponentManager** aResult);
+typedef nsresult (*GetComponentRegistrarFunc)(nsIComponentRegistrar** aResult);
+typedef nsresult (*GetMemoryManagerFunc)(nsIMemory** aResult);
+typedef nsresult (*NewLocalFileFunc)(const nsAString& aPath,
+ bool aFollowLinks, nsIFile** aResult);
+typedef nsresult (*NewNativeLocalFileFunc)(const nsACString& aPath,
+ bool aFollowLinks,
+ nsIFile** aResult);
+
+typedef nsresult (*GetDebugFunc)(nsIDebug2** aResult);
+
+typedef nsresult (*StringContainerInitFunc)(nsStringContainer&);
+typedef nsresult (*StringContainerInit2Func)(nsStringContainer&,
+ const char16_t*,
+ uint32_t, uint32_t);
+typedef void (*StringContainerFinishFunc)(nsStringContainer&);
+typedef uint32_t (*StringGetDataFunc)(const nsAString&, const char16_t**,
+ bool*);
+typedef uint32_t (*StringGetMutableDataFunc)(nsAString&, uint32_t,
+ char16_t**);
+typedef char16_t* (*StringCloneDataFunc)(const nsAString&);
+typedef nsresult (*StringSetDataFunc)(nsAString&, const char16_t*, uint32_t);
+typedef nsresult (*StringSetDataRangeFunc)(nsAString&, uint32_t, uint32_t,
+ const char16_t*, uint32_t);
+typedef nsresult (*StringCopyFunc)(nsAString&, const nsAString&);
+typedef void (*StringSetIsVoidFunc)(nsAString&, const bool);
+typedef bool (*StringGetIsVoidFunc)(const nsAString&);
+
+typedef nsresult (*CStringContainerInitFunc)(nsCStringContainer&);
+typedef nsresult (*CStringContainerInit2Func)(nsCStringContainer&,
+ const char*,
+ uint32_t, uint32_t);
+typedef void (*CStringContainerFinishFunc)(nsCStringContainer&);
+typedef uint32_t (*CStringGetDataFunc)(const nsACString&, const char**,
+ bool*);
+typedef uint32_t (*CStringGetMutableDataFunc)(nsACString&, uint32_t, char**);
+typedef char* (*CStringCloneDataFunc)(const nsACString&);
+typedef nsresult (*CStringSetDataFunc)(nsACString&, const char*, uint32_t);
+typedef nsresult (*CStringSetDataRangeFunc)(nsACString&, uint32_t, uint32_t,
+ const char*, uint32_t);
+typedef nsresult (*CStringCopyFunc)(nsACString&, const nsACString&);
+typedef void (*CStringSetIsVoidFunc)(nsACString&, const bool);
+typedef bool (*CStringGetIsVoidFunc)(const nsACString&);
+
+typedef nsresult (*CStringToUTF16)(const nsACString&, nsCStringEncoding,
+ nsAString&);
+typedef nsresult (*UTF16ToCString)(const nsAString&, nsCStringEncoding,
+ nsACString&);
+
+typedef void* (*AllocFunc)(size_t aSize);
+typedef void* (*ReallocFunc)(void* aPtr, size_t aSize);
+typedef void (*FreeFunc)(void* aPtr);
+
+typedef void (*DebugBreakFunc)(uint32_t aSeverity,
+ const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine);
+
+typedef void (*xpcomVoidFunc)();
+typedef void (*LogAddRefFunc)(void*, nsrefcnt, const char*, uint32_t);
+typedef void (*LogReleaseFunc)(void*, nsrefcnt, const char*);
+typedef void (*LogCtorFunc)(void*, const char*, uint32_t);
+typedef void (*LogCOMPtrFunc)(void*, nsISupports*);
+
+typedef nsresult (*GetXPTCallStubFunc)(REFNSIID, nsIXPTCProxy*,
+ nsISomeInterface**);
+typedef void (*DestroyXPTCallStubFunc)(nsISomeInterface*);
+typedef nsresult (*InvokeByIndexFunc)(nsISupports*, uint32_t, uint32_t,
+ nsXPTCVariant*);
+typedef bool (*CycleCollectorFunc)(nsISupports*);
+typedef nsPurpleBufferEntry*
+ (*CycleCollectorSuspect2Func)(void*,
+ nsCycleCollectionParticipant*);
+typedef bool (*CycleCollectorForget2Func)(nsPurpleBufferEntry*);
+typedef void (*CycleCollectorSuspect3Func)(void*,
+ nsCycleCollectionParticipant*,
+ nsCycleCollectingAutoRefCnt*,
+ bool*);
+// PRIVATE AND DEPRECATED
+typedef NS_CALLBACK(XPCOMExitRoutine)(void);
+
+typedef nsresult (*RegisterXPCOMExitRoutineFunc)(XPCOMExitRoutine aExitRoutine,
+ uint32_t aPriority);
+typedef nsresult (*UnregisterXPCOMExitRoutineFunc)(XPCOMExitRoutine aExitRoutine);
+
+typedef struct XPCOMFunctions
+{
+ uint32_t version;
+ uint32_t size;
+
+ InitFunc init;
+ ShutdownFunc shutdown;
+ GetServiceManagerFunc getServiceManager;
+ GetComponentManagerFunc getComponentManager;
+ GetComponentRegistrarFunc getComponentRegistrar;
+ GetMemoryManagerFunc getMemoryManager;
+ NewLocalFileFunc newLocalFile;
+ NewNativeLocalFileFunc newNativeLocalFile;
+
+ RegisterXPCOMExitRoutineFunc registerExitRoutine;
+ UnregisterXPCOMExitRoutineFunc unregisterExitRoutine;
+
+ // Added for Mozilla 1.5
+ GetDebugFunc getDebug;
+ void* getTraceRefcnt;
+
+ // Added for Mozilla 1.7
+ StringContainerInitFunc stringContainerInit;
+ StringContainerFinishFunc stringContainerFinish;
+ StringGetDataFunc stringGetData;
+ StringSetDataFunc stringSetData;
+ StringSetDataRangeFunc stringSetDataRange;
+ StringCopyFunc stringCopy;
+ CStringContainerInitFunc cstringContainerInit;
+ CStringContainerFinishFunc cstringContainerFinish;
+ CStringGetDataFunc cstringGetData;
+ CStringSetDataFunc cstringSetData;
+ CStringSetDataRangeFunc cstringSetDataRange;
+ CStringCopyFunc cstringCopy;
+ CStringToUTF16 cstringToUTF16;
+ UTF16ToCString utf16ToCString;
+ StringCloneDataFunc stringCloneData;
+ CStringCloneDataFunc cstringCloneData;
+
+ // Added for Mozilla 1.8
+ AllocFunc allocFunc;
+ ReallocFunc reallocFunc;
+ FreeFunc freeFunc;
+ StringContainerInit2Func stringContainerInit2;
+ CStringContainerInit2Func cstringContainerInit2;
+ StringGetMutableDataFunc stringGetMutableData;
+ CStringGetMutableDataFunc cstringGetMutableData;
+ void* init3; // obsolete
+
+ // Added for Mozilla 1.9
+ DebugBreakFunc debugBreakFunc;
+ xpcomVoidFunc logInitFunc;
+ xpcomVoidFunc logTermFunc;
+ LogAddRefFunc logAddRefFunc;
+ LogReleaseFunc logReleaseFunc;
+ LogCtorFunc logCtorFunc;
+ LogCtorFunc logDtorFunc;
+ LogCOMPtrFunc logCOMPtrAddRefFunc;
+ LogCOMPtrFunc logCOMPtrReleaseFunc;
+ GetXPTCallStubFunc getXPTCallStubFunc;
+ DestroyXPTCallStubFunc destroyXPTCallStubFunc;
+ InvokeByIndexFunc invokeByIndexFunc;
+ CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect3Func
+ CycleCollectorFunc cycleForgetFunc; // obsolete
+ StringSetIsVoidFunc stringSetIsVoid;
+ StringGetIsVoidFunc stringGetIsVoid;
+ CStringSetIsVoidFunc cstringSetIsVoid;
+ CStringGetIsVoidFunc cstringGetIsVoid;
+
+ // Added for Mozilla 1.9.1
+ CycleCollectorSuspect2Func cycleSuspect2Func; // obsolete: use cycleSuspect3Func
+ CycleCollectorForget2Func cycleForget2Func; // obsolete
+
+ CycleCollectorSuspect3Func cycleSuspect3Func;
+
+} XPCOMFunctions;
+
+typedef nsresult (*GetFrozenFunctionsFunc)(XPCOMFunctions* aEntryPoints,
+ const char* aLibraryPath);
+XPCOM_API(nsresult) NS_GetFrozenFunctions(XPCOMFunctions* aEntryPoints,
+ const char* aLibraryPath);
+
+
+namespace mozilla {
+
+/**
+ * Shutdown XPCOM. You must call this method after you are finished
+ * using xpcom.
+ *
+ * @param aServMgr The service manager which was returned by NS_InitXPCOM.
+ * This will release servMgr. You may pass null.
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during shutdown
+ *
+ */
+nsresult
+ShutdownXPCOM(nsIServiceManager* aServMgr);
+
+void SetICUMemoryFunctions();
+
+/**
+ * C++ namespaced version of NS_LogTerm.
+ */
+void LogTerm();
+
+} // namespace mozilla
+
+
+// think hard before changing this
+#define XPCOM_GLUE_VERSION 1
+
+
+/* XPCOM Specific Defines
+ *
+ * XPCOM_DLL - name of the loadable xpcom library on disk.
+ * XUL_DLL - name of the loadable XUL library on disk
+ * XPCOM_SEARCH_KEY - name of the environment variable that can be
+ * modified to include additional search paths.
+ * GRE_CONF_NAME - Name of the GRE Configuration file
+ */
+
+#if defined(XP_WIN32)
+
+#define XPCOM_SEARCH_KEY "PATH"
+#define GRE_CONF_NAME "gre.config"
+#define GRE_WIN_REG_LOC L"Software\\mozilla.org\\GRE"
+#define XPCOM_DLL XUL_DLL
+#define LXPCOM_DLL LXUL_DLL
+#define XUL_DLL "xul.dll"
+#define LXUL_DLL L"xul.dll"
+
+#else // Unix
+#include <limits.h> // for PATH_MAX
+
+#define XPCOM_DLL XUL_DLL
+
+// you have to love apple..
+#ifdef XP_MACOSX
+#define XPCOM_SEARCH_KEY "DYLD_LIBRARY_PATH"
+#define GRE_FRAMEWORK_NAME "XUL.framework"
+#define XUL_DLL "XUL"
+#else
+#define XPCOM_SEARCH_KEY "LD_LIBRARY_PATH"
+#define XUL_DLL "libxul" MOZ_DLL_SUFFIX
+#endif
+
+#define GRE_CONF_NAME ".gre.config"
+#define GRE_CONF_PATH "/etc/gre.conf"
+#define GRE_CONF_DIR "/etc/gre.d"
+#define GRE_USER_CONF_DIR ".gre.d"
+#endif
+
+#if defined(XP_WIN)
+ #define XPCOM_FILE_PATH_SEPARATOR "\\"
+ #define XPCOM_ENV_PATH_SEPARATOR ";"
+#elif defined(XP_UNIX)
+ #define XPCOM_FILE_PATH_SEPARATOR "/"
+ #define XPCOM_ENV_PATH_SEPARATOR ":"
+#else
+ #error need_to_define_your_file_path_separator_and_illegal_characters
+#endif
+
+#ifdef AIX
+#include <sys/param.h>
+#endif
+
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#elif defined(_MAX_PATH)
+#define MAXPATHLEN _MAX_PATH
+#elif defined(CCHMAXPATH)
+#define MAXPATHLEN CCHMAXPATH
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+extern bool gXPCOMShuttingDown;
+extern bool gXPCOMThreadsShutDown;
+
+// Needed by the IPC layer from off the main thread
+extern char16_t* gGREBinPath;
+
+namespace mozilla {
+namespace services {
+
+/**
+ * Clears service cache, sets gXPCOMShuttingDown
+ */
+void Shutdown();
+
+} // namespace services
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/build/nsXPCOMStrings.cpp b/xpcom/build/nsXPCOMStrings.cpp
new file mode 100644
index 000000000..617edbab2
--- /dev/null
+++ b/xpcom/build/nsXPCOMStrings.cpp
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+#include "nsCharTraits.h"
+
+#include "nsXPCOMStrings.h"
+#include "nsNativeCharsetUtils.h"
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(nsresult)
+NS_StringContainerInit(nsStringContainer& aContainer)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsString),
+ "nsStringContainer is not large enough");
+
+ // use placement new to avoid heap allocating nsString object
+ new (&aContainer) nsString();
+
+ return NS_OK;
+}
+
+XPCOM_API(nsresult)
+NS_StringContainerInit2(nsStringContainer& aContainer,
+ const char16_t* aData,
+ uint32_t aDataLength,
+ uint32_t aFlags)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsString),
+ "nsStringContainer is not large enough");
+
+ if (!aData) {
+ new (&aContainer) nsString();
+ } else {
+ if (aDataLength == UINT32_MAX) {
+ if (NS_WARN_IF(aFlags & NS_STRING_CONTAINER_INIT_SUBSTRING)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ aDataLength = nsCharTraits<char16_t>::length(aData);
+ }
+
+ if (aFlags & (NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_ADOPT)) {
+ uint32_t flags;
+ if (aFlags & NS_STRING_CONTAINER_INIT_SUBSTRING) {
+ flags = nsSubstring::F_NONE;
+ } else {
+ flags = nsSubstring::F_TERMINATED;
+ }
+
+ if (aFlags & NS_STRING_CONTAINER_INIT_ADOPT) {
+ flags |= nsSubstring::F_OWNED;
+ }
+
+ new (&aContainer) nsSubstring(const_cast<char16_t*>(aData),
+ aDataLength, flags);
+ } else {
+ new (&aContainer) nsString(aData, aDataLength);
+ }
+ }
+
+ return NS_OK;
+}
+
+XPCOM_API(void)
+NS_StringContainerFinish(nsStringContainer& aContainer)
+{
+ // call the nsString dtor
+ reinterpret_cast<nsString*>(&aContainer)->~nsString();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(uint32_t)
+NS_StringGetData(const nsAString& aStr, const char16_t** aData,
+ bool* aTerminated)
+{
+ if (aTerminated) {
+ *aTerminated = aStr.IsTerminated();
+ }
+
+ *aData = aStr.BeginReading();
+ return aStr.Length();
+}
+
+XPCOM_API(uint32_t)
+NS_StringGetMutableData(nsAString& aStr, uint32_t aDataLength,
+ char16_t** aData)
+{
+ if (aDataLength != UINT32_MAX) {
+ aStr.SetLength(aDataLength);
+ if (aStr.Length() != aDataLength) {
+ *aData = nullptr;
+ return 0;
+ }
+ }
+
+ *aData = aStr.BeginWriting();
+ return aStr.Length();
+}
+
+XPCOM_API(char16_t*)
+NS_StringCloneData(const nsAString& aStr)
+{
+ return ToNewUnicode(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_StringSetData(nsAString& aStr, const char16_t* aData, uint32_t aDataLength)
+{
+ aStr.Assign(aData, aDataLength);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_StringSetDataRange(nsAString& aStr,
+ uint32_t aCutOffset, uint32_t aCutLength,
+ const char16_t* aData, uint32_t aDataLength)
+{
+ if (aCutOffset == UINT32_MAX) {
+ // append case
+ if (aData) {
+ aStr.Append(aData, aDataLength);
+ }
+ return NS_OK; // XXX report errors
+ }
+
+ if (aCutLength == UINT32_MAX) {
+ aCutLength = aStr.Length() - aCutOffset;
+ }
+
+ if (aData) {
+ if (aDataLength == UINT32_MAX) {
+ aStr.Replace(aCutOffset, aCutLength, nsDependentString(aData));
+ } else {
+ aStr.Replace(aCutOffset, aCutLength, Substring(aData, aDataLength));
+ }
+ } else {
+ aStr.Cut(aCutOffset, aCutLength);
+ }
+
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_StringCopy(nsAString& aDest, const nsAString& aSrc)
+{
+ aDest.Assign(aSrc);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(void)
+NS_StringSetIsVoid(nsAString& aStr, const bool aIsVoid)
+{
+ aStr.SetIsVoid(aIsVoid);
+}
+
+XPCOM_API(bool)
+NS_StringGetIsVoid(const nsAString& aStr)
+{
+ return aStr.IsVoid();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit(nsCStringContainer& aContainer)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsCString),
+ "nsCStringContainer is not large enough");
+
+ // use placement new to avoid heap allocating nsCString object
+ new (&aContainer) nsCString();
+
+ return NS_OK;
+}
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit2(nsCStringContainer& aContainer,
+ const char* aData,
+ uint32_t aDataLength,
+ uint32_t aFlags)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsCString),
+ "nsStringContainer is not large enough");
+
+ if (!aData) {
+ new (&aContainer) nsCString();
+ } else {
+ if (aDataLength == UINT32_MAX) {
+ if (NS_WARN_IF(aFlags & NS_CSTRING_CONTAINER_INIT_SUBSTRING)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ aDataLength = nsCharTraits<char>::length(aData);
+ }
+
+ if (aFlags & (NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_ADOPT)) {
+ uint32_t flags;
+ if (aFlags & NS_CSTRING_CONTAINER_INIT_SUBSTRING) {
+ flags = nsCSubstring::F_NONE;
+ } else {
+ flags = nsCSubstring::F_TERMINATED;
+ }
+
+ if (aFlags & NS_CSTRING_CONTAINER_INIT_ADOPT) {
+ flags |= nsCSubstring::F_OWNED;
+ }
+
+ new (&aContainer) nsCSubstring(const_cast<char*>(aData),
+ aDataLength, flags);
+ } else {
+ new (&aContainer) nsCString(aData, aDataLength);
+ }
+ }
+
+ return NS_OK;
+}
+
+XPCOM_API(void)
+NS_CStringContainerFinish(nsCStringContainer& aContainer)
+{
+ // call the nsCString dtor
+ reinterpret_cast<nsCString*>(&aContainer)->~nsCString();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(uint32_t)
+NS_CStringGetData(const nsACString& aStr, const char** aData,
+ bool* aTerminated)
+{
+ if (aTerminated) {
+ *aTerminated = aStr.IsTerminated();
+ }
+
+ *aData = aStr.BeginReading();
+ return aStr.Length();
+}
+
+XPCOM_API(uint32_t)
+NS_CStringGetMutableData(nsACString& aStr, uint32_t aDataLength, char** aData)
+{
+ if (aDataLength != UINT32_MAX) {
+ aStr.SetLength(aDataLength);
+ if (aStr.Length() != aDataLength) {
+ *aData = nullptr;
+ return 0;
+ }
+ }
+
+ *aData = aStr.BeginWriting();
+ return aStr.Length();
+}
+
+XPCOM_API(char*)
+NS_CStringCloneData(const nsACString& aStr)
+{
+ return ToNewCString(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetData(nsACString& aStr, const char* aData, uint32_t aDataLength)
+{
+ aStr.Assign(aData, aDataLength);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetDataRange(nsACString& aStr,
+ uint32_t aCutOffset, uint32_t aCutLength,
+ const char* aData, uint32_t aDataLength)
+{
+ if (aCutOffset == UINT32_MAX) {
+ // append case
+ if (aData) {
+ aStr.Append(aData, aDataLength);
+ }
+ return NS_OK; // XXX report errors
+ }
+
+ if (aCutLength == UINT32_MAX) {
+ aCutLength = aStr.Length() - aCutOffset;
+ }
+
+ if (aData) {
+ if (aDataLength == UINT32_MAX) {
+ aStr.Replace(aCutOffset, aCutLength, nsDependentCString(aData));
+ } else {
+ aStr.Replace(aCutOffset, aCutLength, Substring(aData, aDataLength));
+ }
+ } else {
+ aStr.Cut(aCutOffset, aCutLength);
+ }
+
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_CStringCopy(nsACString& aDest, const nsACString& aSrc)
+{
+ aDest.Assign(aSrc);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(void)
+NS_CStringSetIsVoid(nsACString& aStr, const bool aIsVoid)
+{
+ aStr.SetIsVoid(aIsVoid);
+}
+
+XPCOM_API(bool)
+NS_CStringGetIsVoid(const nsACString& aStr)
+{
+ return aStr.IsVoid();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(nsresult)
+NS_CStringToUTF16(const nsACString& aSrc,
+ nsCStringEncoding aSrcEncoding,
+ nsAString& aDest)
+{
+ switch (aSrcEncoding) {
+ case NS_CSTRING_ENCODING_ASCII:
+ CopyASCIItoUTF16(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_UTF8:
+ CopyUTF8toUTF16(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_NATIVE_FILESYSTEM:
+ NS_CopyNativeToUnicode(aSrc, aDest);
+ break;
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_UTF16ToCString(const nsAString& aSrc,
+ nsCStringEncoding aDestEncoding,
+ nsACString& aDest)
+{
+ switch (aDestEncoding) {
+ case NS_CSTRING_ENCODING_ASCII:
+ LossyCopyUTF16toASCII(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_UTF8:
+ CopyUTF16toUTF8(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_NATIVE_FILESYSTEM:
+ NS_CopyUnicodeToNative(aSrc, aDest);
+ break;
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return NS_OK; // XXX report errors
+}
diff --git a/xpcom/build/nsXREAppData.h b/xpcom/build/nsXREAppData.h
new file mode 100644
index 000000000..fbc7adb8f
--- /dev/null
+++ b/xpcom/build/nsXREAppData.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXREAppData_h
+#define nsXREAppData_h
+
+#include <stdint.h>
+#include "mozilla/Attributes.h"
+
+class nsIFile;
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+namespace sandbox {
+class BrokerServices;
+}
+#endif
+
+/**
+ * Application-specific data needed to start the apprunner.
+ *
+ * @note When this structure is allocated and manipulated by XRE_CreateAppData,
+ * string fields will be allocated with moz_xmalloc, and interface pointers
+ * are strong references.
+ */
+struct nsXREAppData
+{
+ /**
+ * This should be set to sizeof(nsXREAppData). This structure may be
+ * extended in future releases, and this ensures that binary compatibility
+ * is maintained.
+ */
+ uint32_t size;
+
+ /**
+ * The directory of the application to be run. May be null if the
+ * xulrunner and the app are installed into the same directory.
+ */
+ nsIFile* MOZ_NON_OWNING_REF directory;
+
+ /**
+ * The name of the application vendor. This must be ASCII, and is normally
+ * mixed-case, e.g. "Mozilla". Optional (may be null), but highly
+ * recommended. Must not be the empty string.
+ */
+ const char* vendor;
+
+ /**
+ * The name of the application. This must be ASCII, and is normally
+ * mixed-case, e.g. "Firefox". Required (must not be null or an empty
+ * string).
+ */
+ const char* name;
+
+ /**
+ * The internal name of the application for remoting purposes. When left
+ * unspecified, "name" is used instead. This must be ASCII, and is normally
+ * lowercase, e.g. "firefox". Optional (may be null but not an empty string).
+ */
+ const char* remotingName;
+
+ /**
+ * The major version, e.g. "0.8.0+". Optional (may be null), but
+ * required for advanced application features such as the extension
+ * manager and update service. Must not be the empty string.
+ */
+ const char* version;
+
+ /**
+ * The application's build identifier, e.g. "2004051604"
+ */
+ const char* buildID;
+
+ /**
+ * The application's UUID. Used by the extension manager to determine
+ * compatible extensions. Optional, but required for advanced application
+ * features such as the extension manager and update service.
+ *
+ * This has traditionally been in the form
+ * "{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}" but for new applications
+ * a more readable form is encouraged: "appname@vendor.tld". Only
+ * the following characters are allowed: a-z A-Z 0-9 - . @ _ { } *
+ */
+ const char* ID;
+
+ /**
+ * The copyright information to print for the -h commandline flag,
+ * e.g. "Copyright (c) 2003 mozilla.org".
+ */
+ const char* copyright;
+
+ /**
+ * Combination of NS_XRE_ prefixed flags (defined below).
+ */
+ uint32_t flags;
+
+ /**
+ * The location of the XRE. XRE_main may not be able to figure this out
+ * programatically.
+ */
+ nsIFile* MOZ_NON_OWNING_REF xreDirectory;
+
+ /**
+ * The minimum/maximum compatible XRE version.
+ */
+ const char* minVersion;
+ const char* maxVersion;
+
+ /**
+ * The server URL to send crash reports to.
+ */
+ const char* crashReporterURL;
+
+ /**
+ * The profile directory that will be used. Optional (may be null). Must not
+ * be the empty string, must be ASCII. The path is split into components
+ * along the path separator characters '/' and '\'.
+ *
+ * The application data directory ("UAppData", see below) is normally
+ * composed as follows, where $HOME is platform-specific:
+ *
+ * UAppData = $HOME[/$vendor]/$name
+ *
+ * If present, the 'profile' string will be used instead of the combination of
+ * vendor and name as follows:
+ *
+ * UAppData = $HOME/$profile
+ */
+ const char* profile;
+
+ /**
+ * The application name to use in the User Agent string.
+ */
+ const char* UAName;
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox BrokerServices.
+ */
+ sandbox::BrokerServices* sandboxBrokerServices;
+#endif
+};
+
+/**
+ * Indicates whether or not the profile migrator service may be
+ * invoked at startup when creating a profile.
+ */
+#define NS_XRE_ENABLE_PROFILE_MIGRATOR (1 << 1)
+
+/**
+ * Indicates the Windows DLL Blocklist initialized properly. For testing
+ * purposes only. Set in nsBrowserApp on startup, automated tests then
+ * check the result.
+ */
+#define NS_XRE_DLL_BLOCKLIST_ENABLED (1 << 2)
+
+/**
+ * Indicates whether or not to use Breakpad crash reporting.
+ */
+#define NS_XRE_ENABLE_CRASH_REPORTER (1 << 3)
+
+#endif // nsXREAppData_h
diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h
new file mode 100644
index 000000000..426a58f06
--- /dev/null
+++ b/xpcom/build/nsXULAppAPI.h
@@ -0,0 +1,538 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsXULAppAPI_h__
+#define _nsXULAppAPI_h__
+
+#include "nsID.h"
+#include "xrecore.h"
+#include "nsXPCOM.h"
+#include "nsISupports.h"
+#include "mozilla/Logging.h"
+#include "nsXREAppData.h"
+#include "js/TypeDecls.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Vector.h"
+#include "mozilla/TimeStamp.h"
+#include "XREChildData.h"
+#include "XREShellData.h"
+
+/**
+ * A directory service key which provides the platform-correct "application
+ * data" directory as follows, where $name and $vendor are as defined above and
+ * $vendor is optional:
+ *
+ * Windows:
+ * HOME = Documents and Settings\$USER\Application Data
+ * UAppData = $HOME[\$vendor]\$name
+ *
+ * Unix:
+ * HOME = ~
+ * UAppData = $HOME/.[$vendor/]$name
+ *
+ * Mac:
+ * HOME = ~
+ * UAppData = $HOME/Library/Application Support/$name
+ *
+ * Note that the "profile" member above will change the value of UAppData as
+ * follows:
+ *
+ * Windows:
+ * UAppData = $HOME\$profile
+ *
+ * Unix:
+ * UAppData = $HOME/.$profile
+ *
+ * Mac:
+ * UAppData = $HOME/Library/Application Support/$profile
+ */
+#define XRE_USER_APP_DATA_DIR "UAppData"
+
+/**
+ * A directory service key which provides a list of all enabled extension
+ * directories and files (packed XPIs). The list includes compatible
+ * platform-specific extension subdirectories.
+ *
+ * @note The directory list will have no members when the application is
+ * launched in safe mode.
+ */
+#define XRE_EXTENSIONS_DIR_LIST "XREExtDL"
+
+/**
+ * A directory service key which provides the executable file used to
+ * launch the current process. This is the same value returned by the
+ * XRE_GetBinaryPath function defined below.
+ */
+#define XRE_EXECUTABLE_FILE "XREExeF"
+
+/**
+ * A directory service key which specifies the profile
+ * directory. Unlike the NS_APP_USER_PROFILE_50_DIR key, this key may
+ * be available when the profile hasn't been "started", or after is
+ * has been shut down. If the application is running without a
+ * profile, such as when showing the profile manager UI, this key will
+ * not be available. This key is provided by the XUL apprunner or by
+ * the aAppDirProvider object passed to XRE_InitEmbedding.
+ */
+#define NS_APP_PROFILE_DIR_STARTUP "ProfDS"
+
+/**
+ * A directory service key which specifies the profile
+ * directory. Unlike the NS_APP_USER_PROFILE_LOCAL_50_DIR key, this key may
+ * be available when the profile hasn't been "started", or after is
+ * has been shut down. If the application is running without a
+ * profile, such as when showing the profile manager UI, this key will
+ * not be available. This key is provided by the XUL apprunner or by
+ * the aAppDirProvider object passed to XRE_InitEmbedding.
+ */
+#define NS_APP_PROFILE_LOCAL_DIR_STARTUP "ProfLDS"
+
+/**
+ * A directory service key which specifies the system extension
+ * parent directory containing platform-specific extensions.
+ * This key may not be available on all platforms.
+ */
+#define XRE_SYS_LOCAL_EXTENSION_PARENT_DIR "XRESysLExtPD"
+
+/**
+ * A directory service key which specifies the system extension
+ * parent directory containing platform-independent extensions.
+ * This key may not be available on all platforms.
+ * Additionally, the directory may be equal to that returned by
+ * XRE_SYS_LOCAL_EXTENSION_PARENT_DIR on some platforms.
+ */
+#define XRE_SYS_SHARE_EXTENSION_PARENT_DIR "XRESysSExtPD"
+
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+/**
+ * Directory service keys for the system-wide and user-specific
+ * directories where host manifests used by the WebExtensions
+ * native messaging feature are found.
+ */
+#define XRE_SYS_NATIVE_MESSAGING_MANIFESTS "XRESysNativeMessaging"
+#define XRE_USER_NATIVE_MESSAGING_MANIFESTS "XREUserNativeMessaging"
+#endif
+
+/**
+ * A directory service key which specifies the user system extension
+ * parent directory.
+ */
+#define XRE_USER_SYS_EXTENSION_DIR "XREUSysExt"
+
+/**
+ * A directory service key which specifies the distribution specific files for
+ * the application.
+ */
+#define XRE_APP_DISTRIBUTION_DIR "XREAppDist"
+
+/**
+ * A directory service key which specifies the location for system add-ons.
+ */
+#define XRE_APP_FEATURES_DIR "XREAppFeat"
+
+/**
+ * A directory service key which specifies the location for app dir add-ons.
+ * Should be a synonym for XCurProcD everywhere except in tests.
+ */
+#define XRE_ADDON_APP_DIR "XREAddonAppDir"
+
+/**
+ * A directory service key which provides the update directory. Callers should
+ * fall back to appDir.
+ * Windows: If vendor name exists:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <vendor name>\updates\
+ * <hash of the path to XRE_EXECUTABLE_FILE’s parent directory>
+ *
+ * If vendor name doesn't exist, but product name exists:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <product name>\updates\
+ * <hash of the path to XRE_EXECUTABLE_FILE’s parent directory>
+ *
+ * If neither vendor nor product name exists:
+ * If app dir is under Program Files:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <relative path to app dir from Program Files>
+ *
+ * If app dir isn’t under Program Files:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <MOZ_APP_NAME>
+ *
+ * Mac: ~/Library/Caches/Mozilla/updates/<absolute path to app dir>
+ *
+ * Gonk: /data/local
+ *
+ * All others: Parent directory of XRE_EXECUTABLE_FILE.
+ */
+#define XRE_UPDATE_ROOT_DIR "UpdRootD"
+
+/**
+ * A directory service key which provides an alternate location
+ * to UpdRootD to to store large files. This key is currently
+ * only implemented in the Gonk directory service provider.
+ */
+
+#define XRE_UPDATE_ARCHIVE_DIR "UpdArchD"
+
+/**
+ * A directory service key which provides the directory where an OS update is
+* applied.
+ * At present this is supported only in Gonk.
+ */
+#define XRE_OS_UPDATE_APPLY_TO_DIR "OSUpdApplyToD"
+
+/**
+ * Begin an XUL application. Does not return until the user exits the
+ * application.
+ *
+ * @param argc/argv Command-line parameters to pass to the application. On
+ * Windows, these should be in UTF8. On unix-like platforms
+ * these are in the "native" character set.
+ *
+ * @param aAppData Information about the application to be run.
+ *
+ * @param aFlags Platform specific flags.
+ *
+ * @return A native result code suitable for returning from main().
+ *
+ * @note If the binary is linked against the standalone XPCOM glue,
+ * XPCOMGlueStartup() should be called before this method.
+ */
+XRE_API(int,
+ XRE_main, (int argc, char* argv[], const nsXREAppData* aAppData,
+ uint32_t aFlags))
+
+/**
+ * Given a path relative to the current working directory (or an absolute
+ * path), return an appropriate nsIFile object.
+ *
+ * @note Pass UTF8 strings on Windows... native charset on other platforms.
+ */
+XRE_API(nsresult,
+ XRE_GetFileFromPath, (const char* aPath, nsIFile** aResult))
+
+/**
+ * Get the path of the running application binary and store it in aResult.
+ * @param aArgv0 The value passed as argv[0] of main(). This value is only
+ * used on *nix, and only when other methods of determining
+ * the binary path have failed.
+ */
+XRE_API(nsresult,
+ XRE_GetBinaryPath, (const char* aArgv0, nsIFile** aResult))
+
+/**
+ * Get the static module built in to libxul.
+ */
+XRE_API(const mozilla::Module*,
+ XRE_GetStaticModule, ())
+
+/**
+ * Lock a profile directory using platform-specific semantics.
+ *
+ * @param aDirectory The profile directory to lock.
+ * @param aLockObject An opaque lock object. The directory will remain locked
+ * as long as the XPCOM reference is held.
+ */
+XRE_API(nsresult,
+ XRE_LockProfileDirectory, (nsIFile* aDirectory,
+ nsISupports** aLockObject))
+
+/**
+ * Initialize libXUL for embedding purposes.
+ *
+ * @param aLibXULDirectory The directory in which the libXUL shared library
+ * was found.
+ * @param aAppDirectory The directory in which the application components
+ * and resources can be found. This will map to
+ * the NS_OS_CURRENT_PROCESS_DIR directory service
+ * key.
+ * @param aAppDirProvider A directory provider for the application. This
+ * provider will be aggregated by a libxul provider
+ * which will provide the base required GRE keys.
+ *
+ * @note This function must be called from the "main" thread.
+ *
+ * @note At the present time, this function may only be called once in
+ * a given process. Use XRE_TermEmbedding to clean up and free
+ * resources allocated by XRE_InitEmbedding.
+ */
+
+XRE_API(nsresult,
+ XRE_InitEmbedding2, (nsIFile* aLibXULDirectory,
+ nsIFile* aAppDirectory,
+ nsIDirectoryServiceProvider* aAppDirProvider))
+
+/**
+ * Register static XPCOM component information.
+ * This method may be called at any time before or after XRE_main or
+ * XRE_InitEmbedding.
+ */
+XRE_API(nsresult,
+ XRE_AddStaticComponent, (const mozilla::Module* aComponent))
+
+/**
+ * Register XPCOM components found in an array of files/directories.
+ * This method may be called at any time before or after XRE_main or
+ * XRE_InitEmbedding.
+ *
+ * @param aFiles An array of files or directories.
+ * @param aFileCount the number of items in the aFiles array.
+ * @note appdir/components is registered automatically.
+ *
+ * NS_APP_LOCATION specifies a location to search for binary XPCOM
+ * components as well as component/chrome manifest files.
+ *
+ * NS_EXTENSION_LOCATION excludes binary XPCOM components but allows other
+ * manifest instructions.
+ *
+ * NS_SKIN_LOCATION specifies a location to search for chrome manifest files
+ * which are only allowed to register only skin packages and style overlays.
+ */
+enum NSLocationType
+{
+ NS_APP_LOCATION,
+ NS_EXTENSION_LOCATION,
+ NS_SKIN_LOCATION,
+ NS_BOOTSTRAPPED_LOCATION
+};
+
+XRE_API(nsresult,
+ XRE_AddManifestLocation, (NSLocationType aType,
+ nsIFile* aLocation))
+
+/**
+ * Register XPCOM components found in a JAR.
+ * This is similar to XRE_AddManifestLocation except the file specified
+ * must be a zip archive with a manifest named chrome.manifest
+ * This method may be called at any time before or after XRE_main or
+ * XRE_InitEmbedding.
+ *
+ * @param aFiles An array of files or directories.
+ * @param aFileCount the number of items in the aFiles array.
+ * @note appdir/components is registered automatically.
+ *
+ * NS_COMPONENT_LOCATION specifies a location to search for binary XPCOM
+ * components as well as component/chrome manifest files.
+ *
+ * NS_SKIN_LOCATION specifies a location to search for chrome manifest files
+ * which are only allowed to register only skin packages and style overlays.
+ */
+XRE_API(nsresult,
+ XRE_AddJarManifestLocation, (NSLocationType aType,
+ nsIFile* aLocation))
+
+/**
+ * Fire notifications to inform the toolkit about a new profile. This
+ * method should be called after XRE_InitEmbedding if the embedder
+ * wishes to run with a profile. Normally the embedder should call
+ * XRE_LockProfileDirectory to lock the directory before calling this
+ * method.
+ *
+ * @note There are two possibilities for selecting a profile:
+ *
+ * 1) Select the profile before calling XRE_InitEmbedding. The aAppDirProvider
+ * object passed to XRE_InitEmbedding should provide the
+ * NS_APP_USER_PROFILE_50_DIR key, and may also provide the following keys:
+ * - NS_APP_USER_PROFILE_LOCAL_50_DIR
+ * - NS_APP_PROFILE_DIR_STARTUP
+ * - NS_APP_PROFILE_LOCAL_DIR_STARTUP
+ * In this scenario XRE_NotifyProfile should be called immediately after
+ * XRE_InitEmbedding. Component registration information will be stored in
+ * the profile and JS components may be stored in the fastload cache.
+ *
+ * 2) Select a profile some time after calling XRE_InitEmbedding. In this case
+ * the embedder must install a directory service provider which provides
+ * NS_APP_USER_PROFILE_50_DIR and optionally
+ * NS_APP_USER_PROFILE_LOCAL_50_DIR. Component registration information
+ * will be stored in the application directory and JS components will not
+ * fastload.
+ */
+XRE_API(void,
+ XRE_NotifyProfile, ())
+
+/**
+ * Terminate embedding started with XRE_InitEmbedding or XRE_InitEmbedding2
+ */
+XRE_API(void,
+ XRE_TermEmbedding, ())
+
+/**
+ * Create a new nsXREAppData structure from an application.ini file.
+ *
+ * @param aINIFile The application.ini file to parse.
+ * @param aAppData A newly-allocated nsXREAppData structure. The caller is
+ * responsible for freeing this structure using
+ * XRE_FreeAppData.
+ */
+XRE_API(nsresult,
+ XRE_CreateAppData, (nsIFile* aINIFile,
+ nsXREAppData** aAppData))
+
+/**
+ * Parse an INI file (application.ini or override.ini) into an existing
+ * nsXREAppData structure.
+ *
+ * @param aINIFile The INI file to parse
+ * @param aAppData The nsXREAppData structure to fill.
+ */
+XRE_API(nsresult,
+ XRE_ParseAppData, (nsIFile* aINIFile,
+ nsXREAppData* aAppData))
+
+/**
+ * Free a nsXREAppData structure that was allocated with XRE_CreateAppData.
+ */
+XRE_API(void,
+ XRE_FreeAppData, (nsXREAppData* aAppData))
+
+enum GeckoProcessType
+{
+ GeckoProcessType_Default = 0,
+
+ GeckoProcessType_Plugin,
+ GeckoProcessType_Content,
+
+ GeckoProcessType_IPDLUnitTest,
+
+ GeckoProcessType_GMPlugin, // Gecko Media Plugin
+
+ GeckoProcessType_GPU, // GPU and compositor process
+
+ GeckoProcessType_End,
+ GeckoProcessType_Invalid = GeckoProcessType_End
+};
+
+static const char* const kGeckoProcessTypeString[] = {
+ "default",
+ "plugin",
+ "tab",
+ "ipdlunittest",
+ "geckomediaplugin",
+ "gpu"
+};
+
+static_assert(MOZ_ARRAY_LENGTH(kGeckoProcessTypeString) ==
+ GeckoProcessType_End,
+ "Array length mismatch");
+
+XRE_API(const char*,
+ XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
+
+XRE_API(void,
+ XRE_SetProcessType, (const char* aProcessTypeString))
+
+#if defined(MOZ_CRASHREPORTER)
+// Used in the "master" parent process hosting the crash server
+XRE_API(bool,
+ XRE_TakeMinidumpForChild, (uint32_t aChildPid, nsIFile** aDump,
+ uint32_t* aSequence))
+
+// Used in child processes.
+XRE_API(bool,
+ XRE_SetRemoteExceptionHandler, (const char* aPipe))
+#endif
+
+namespace mozilla {
+namespace gmp {
+class GMPLoader;
+} // namespace gmp
+} // namespace mozilla
+
+XRE_API(nsresult,
+ XRE_InitChildProcess, (int aArgc,
+ char* aArgv[],
+ const XREChildData* aChildData))
+
+XRE_API(GeckoProcessType,
+ XRE_GetProcessType, ())
+
+XRE_API(bool,
+ XRE_IsParentProcess, ())
+
+XRE_API(bool,
+ XRE_IsContentProcess, ())
+
+XRE_API(bool,
+ XRE_IsGPUProcess, ())
+
+typedef void (*MainFunction)(void* aData);
+
+XRE_API(nsresult,
+ XRE_InitParentProcess, (int aArgc,
+ char* aArgv[],
+ MainFunction aMainFunction,
+ void* aMainFunctionExtraData))
+
+XRE_API(int,
+ XRE_RunIPDLTest, (int aArgc,
+ char* aArgv[]))
+
+XRE_API(nsresult,
+ XRE_RunAppShell, ())
+
+XRE_API(nsresult,
+ XRE_InitCommandLine, (int aArgc, char* aArgv[]))
+
+XRE_API(nsresult,
+ XRE_DeinitCommandLine, ())
+
+class MessageLoop;
+
+XRE_API(void,
+ XRE_ShutdownChildProcess, ())
+
+XRE_API(MessageLoop*,
+ XRE_GetIOMessageLoop, ())
+
+XRE_API(bool,
+ XRE_SendTestShellCommand, (JSContext* aCx,
+ JSString* aCommand,
+ void* aCallback))
+XRE_API(bool,
+ XRE_ShutdownTestShell, ())
+
+XRE_API(void,
+ XRE_InstallX11ErrorHandler, ())
+
+XRE_API(void,
+ XRE_TelemetryAccumulate, (int aID, uint32_t aSample))
+
+XRE_API(void,
+ XRE_StartupTimelineRecord, (int aEvent, mozilla::TimeStamp aWhen))
+
+XRE_API(void,
+ XRE_InitOmnijar, (nsIFile* aGreOmni,
+ nsIFile* aAppOmni))
+XRE_API(void,
+ XRE_StopLateWriteChecks, (void))
+
+XRE_API(void,
+ XRE_EnableSameExecutableForContentProc, ())
+
+XRE_API(int,
+ XRE_XPCShellMain, (int argc, char** argv, char** envp,
+ const XREShellData* aShellData))
+
+#if MOZ_WIDGET_GTK == 2
+XRE_API(void,
+ XRE_GlibInit, ())
+#endif
+
+
+#ifdef LIBFUZZER
+#include "LibFuzzerRegistry.h"
+
+XRE_API(void,
+ XRE_LibFuzzerSetMain, (int, char**, LibFuzzerMain))
+
+XRE_API(void,
+ XRE_LibFuzzerGetFuncs, (const char*, LibFuzzerInitFunc*,
+ LibFuzzerTestingFunc*))
+#endif // LIBFUZZER
+
+#endif // _nsXULAppAPI_h__
diff --git a/xpcom/build/perfprobe.cpp b/xpcom/build/perfprobe.cpp
new file mode 100644
index 000000000..118e73fc8
--- /dev/null
+++ b/xpcom/build/perfprobe.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*****************************
+ Windows implementation of probes, using xperf
+ *****************************/
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+
+#include "perfprobe.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace probes {
+
+#if defined(MOZ_LOGGING)
+static LazyLogModule sProbeLog("SysProbe");
+#define LOG(x) MOZ_LOG(sProbeLog, mozilla::LogLevel::Debug, x)
+#else
+#define LOG(x)
+#endif
+
+// Utility function
+GUID
+CID_to_GUID(const nsCID& aCID)
+{
+ GUID result;
+ result.Data1 = aCID.m0;
+ result.Data2 = aCID.m1;
+ result.Data3 = aCID.m2;
+ for (int i = 0; i < 8; ++i) {
+ result.Data4[i] = aCID.m3[i];
+ }
+ return result;
+}
+
+
+// Implementation of Probe
+
+Probe::Probe(const nsCID& aGUID,
+ const nsACString& aName,
+ ProbeManager* aManager)
+ : mGUID(CID_to_GUID(aGUID))
+ , mName(aName)
+ , mManager(aManager)
+{
+}
+
+nsresult
+Probe::Trigger()
+{
+ if (!(mManager->mIsActive)) {
+ //Do not trigger if there is no session
+ return NS_OK;
+ }
+
+ _EVENT_TRACE_HEADER event;
+ ZeroMemory(&event, sizeof(event));
+ event.Size = sizeof(event);
+ event.Flags = WNODE_FLAG_TRACED_GUID ;
+ event.Guid = (const GUID)mGUID;
+ event.Class.Type = 1;
+ event.Class.Version = 0;
+ event.Class.Level = TRACE_LEVEL_INFORMATION;
+
+ ULONG result = TraceEvent(mManager->mSessionHandle, &event);
+
+ LOG(("Probes: Triggered %s, %s, %ld",
+ mName.Data(),
+ result == ERROR_SUCCESS ? "success" : "failure",
+ result));
+
+ nsresult rv;
+ switch (result) {
+ case ERROR_SUCCESS:
+ rv = NS_OK;
+ break;
+ case ERROR_INVALID_FLAG_NUMBER:
+ case ERROR_MORE_DATA:
+ case ERROR_INVALID_PARAMETER:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+ case ERROR_INVALID_HANDLE:
+ rv = NS_ERROR_FAILURE;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ return rv;
+}
+
+
+// Implementation of ProbeManager
+
+ProbeManager::~ProbeManager()
+{
+ //If the manager goes out of scope, stop the session.
+ if (mIsActive && mRegistrationHandle) {
+ StopSession();
+ }
+}
+
+ProbeManager::ProbeManager(const nsCID& aApplicationUID,
+ const nsACString& aApplicationName)
+ : mIsActive(false)
+ , mApplicationUID(aApplicationUID)
+ , mApplicationName(aApplicationName)
+ , mSessionHandle(0)
+ , mRegistrationHandle(0)
+ , mInitialized(false)
+{
+#if defined(MOZ_LOGGING)
+ char cidStr[NSID_LENGTH];
+ aApplicationUID.ToProvidedString(cidStr);
+ LOG(("ProbeManager::Init for application %s, %s",
+ aApplicationName.Data(), cidStr));
+#endif
+}
+
+//Note: The Windows API is just a little bit scary there.
+//The only way to obtain the session handle is to
+//- ignore the session handle obtained from RegisterTraceGuids
+//- pass a callback
+//- in that callback, request the session handle through
+// GetTraceLoggerHandle and some opaque value received by the callback
+
+ULONG WINAPI
+ControlCallback(WMIDPREQUESTCODE aRequestCode,
+ PVOID aContext,
+ ULONG* aReserved,
+ PVOID aBuffer)
+{
+ ProbeManager* context = (ProbeManager*)aContext;
+ switch (aRequestCode) {
+ case WMI_ENABLE_EVENTS: {
+ context->mIsActive = true;
+ TRACEHANDLE sessionHandle = GetTraceLoggerHandle(aBuffer);
+ //Note: We only accept one handle
+ if ((HANDLE)sessionHandle == INVALID_HANDLE_VALUE) {
+ ULONG result = GetLastError();
+ LOG(("Probes: ControlCallback failed, %ul", result));
+ return result;
+ } else if (context->mIsActive && context->mSessionHandle &&
+ context->mSessionHandle != sessionHandle) {
+ LOG(("Probes: Can only handle one context at a time, "
+ "ignoring activation"));
+ return ERROR_SUCCESS;
+ } else {
+ context->mSessionHandle = sessionHandle;
+ LOG(("Probes: ControlCallback activated"));
+ return ERROR_SUCCESS;
+ }
+ }
+
+ case WMI_DISABLE_EVENTS:
+ context->mIsActive = false;
+ context->mSessionHandle = 0;
+ LOG(("Probes: ControlCallback deactivated"));
+ return ERROR_SUCCESS;
+
+ default:
+ LOG(("Probes: ControlCallback does not know what to do with %d",
+ aRequestCode));
+ return ERROR_INVALID_PARAMETER;
+ }
+}
+
+already_AddRefed<Probe>
+ProbeManager::GetProbe(const nsCID& aEventUID, const nsACString& aEventName)
+{
+ RefPtr<Probe> result(new Probe(aEventUID, aEventName, this));
+ mAllProbes.AppendElement(result);
+ return result.forget();
+}
+
+nsresult
+ProbeManager::StartSession()
+{
+ return StartSession(mAllProbes);
+}
+
+nsresult
+ProbeManager::StartSession(nsTArray<RefPtr<Probe>>& aProbes)
+{
+ const size_t probesCount = aProbes.Length();
+ _TRACE_GUID_REGISTRATION* probes = new _TRACE_GUID_REGISTRATION[probesCount];
+ for (unsigned int i = 0; i < probesCount; ++i) {
+ const Probe* probe = aProbes[i];
+ const Probe* probeX = static_cast<const Probe*>(probe);
+ probes[i].Guid = (LPCGUID)&probeX->mGUID;
+ }
+ ULONG result =
+ RegisterTraceGuids(&ControlCallback
+ /*RequestAddress: Sets mSessions appropriately.*/,
+ this
+ /*RequestContext: Passed to ControlCallback*/,
+ (LPGUID)&mApplicationUID
+ /*ControlGuid: Tracing GUID
+ the cast comes from MSDN examples*/,
+ probesCount
+ /*GuidCount: Number of probes*/,
+ probes
+ /*TraceGuidReg: Probes registration*/,
+ nullptr
+ /*MofImagePath: Must be nullptr, says MSDN*/,
+ nullptr
+ /*MofResourceName:Must be nullptr, says MSDN*/,
+ &mRegistrationHandle
+ /*RegistrationHandle: Handler.
+ used only for unregistration*/
+ );
+ delete[] probes;
+ if (NS_WARN_IF(result != ERROR_SUCCESS)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+nsresult
+ProbeManager::StopSession()
+{
+ LOG(("Probes: Stopping measures"));
+ if (mSessionHandle != 0) {
+ ULONG result = UnregisterTraceGuids(mSessionHandle);
+ mSessionHandle = 0;
+ if (result != ERROR_SUCCESS) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+ return NS_OK;
+}
+
+} // namespace probes
+} // namespace mozilla
diff --git a/xpcom/build/perfprobe.h b/xpcom/build/perfprobe.h
new file mode 100644
index 000000000..bc3563654
--- /dev/null
+++ b/xpcom/build/perfprobe.h
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * A mechanism for interacting with operating system-provided
+ * debugging/profiling tools such as Microsoft EWT/Windows Performance Toolkit.
+ */
+
+#ifndef mozilla_perfprobe_h
+#define mozilla_perfprobe_h
+
+#if !defined(XP_WIN)
+#error "For the moment, perfprobe.h is defined only for Windows platforms"
+#endif
+
+#include "nsError.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include <windows.h>
+#undef GetStartupInfo //Prevent Windows from polluting global namespace
+#include <wmistr.h>
+#include <evntrace.h>
+
+namespace mozilla {
+namespace probes {
+
+class ProbeManager;
+
+/**
+ * A data structure supporting a trigger operation that can be used to
+ * send information to the operating system.
+ */
+
+class Probe
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(Probe)
+
+ /**
+ * Trigger the event.
+ *
+ * Note: Can be called from any thread.
+ */
+ nsresult Trigger();
+
+protected:
+ ~Probe() {};
+
+ Probe(const nsCID& aGUID, const nsACString& aName, ProbeManager* aManager);
+ friend class ProbeManager;
+
+protected:
+
+ /**
+ * The system GUID associated to this probe. See the documentation
+ * of |ProbeManager::Make| for more details.
+ */
+ const GUID mGUID;
+
+ /**
+ * The name of this probe. See the documentation
+ * of |ProbeManager::Make| for more details.
+ */
+ const nsCString mName;
+
+ /**
+ * The ProbeManager managing this probe.
+ *
+ * Note: This is a weak reference to avoid a useless cycle.
+ */
+ class ProbeManager* mManager;
+};
+
+
+/**
+ * A manager for a group of probes.
+ *
+ * You can have several managers in one application, provided that they all
+ * have distinct IDs and names. However, having more than 2 is considered a bad
+ * practice.
+ */
+class ProbeManager
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(ProbeManager)
+
+ /**
+ * Create a new probe manager.
+ *
+ * This constructor should be called from the main thread.
+ *
+ * @param aApplicationUID The unique ID of the probe. Under Windows, this
+ * unique ID must have been previously registered using an external tool.
+ * See MyCategory on http://msdn.microsoft.com/en-us/library/aa364100.aspx
+ * @param aApplicationName A name for the probe. Currently used only for
+ * logging purposes. In the future, may be attached to the data sent to the
+ * operating system.
+ *
+ * Note: If two ProbeManagers are constructed with the same uid and/or name,
+ * behavior is unspecified.
+ */
+ ProbeManager(const nsCID& aApplicationUID,
+ const nsACString& aApplicationName);
+
+ /**
+ * Acquire a probe.
+ *
+ * Note: Only probes acquired before the call to SetReady are taken into
+ * account
+ * Note: Can be called only from the main thread.
+ *
+ * @param aEventUID The unique ID of the probe. Under Windows, this unique
+ * ID must have been previously registered using an external tool.
+ * See MyCategory on http://msdn.microsoft.com/en-us/library/aa364100.aspx
+ * @param aEventName A name for the probe. Currently used only for logging
+ * purposes. In the
+ * future, may be attached to the data sent to the operating system.
+ * @return Either |null| in case of error or a valid |Probe*|.
+ *
+ * Note: If this method is called twice with the same uid and/or name,
+ * behavior is undefined.
+ */
+ already_AddRefed<Probe> GetProbe(const nsCID& aEventUID,
+ const nsACString& aEventName);
+
+ /**
+ * Start/stop the measuring session.
+ *
+ * This method should be called from the main thread.
+ *
+ * Note that starting an already started probe manager has no effect,
+ * nor does stopping an already stopped probe manager.
+ */
+ nsresult StartSession();
+ nsresult StopSession();
+
+ /**
+ * @return true If measures are currently on, i.e. if triggering probes is any
+ * is useful. You do not have to check this before triggering a probe, unless
+ * this can avoid complex computations.
+ */
+ bool IsActive();
+
+protected:
+ ~ProbeManager();
+
+ nsresult StartSession(nsTArray<RefPtr<Probe>>& aProbes);
+ nsresult Init(const nsCID& aApplicationUID,
+ const nsACString& aApplicationName);
+
+protected:
+ /**
+ * `true` if a session is in activity, `false` otherwise.
+ */
+ bool mIsActive;
+
+ /**
+ * The UID of this manager.
+ * See documentation above for registration steps that you
+ * may have to take.
+ */
+ nsCID mApplicationUID;
+
+ /**
+ * The name of the application.
+ */
+ nsCString mApplicationName;
+
+ /**
+ * All the probes that have been created for this manager.
+ */
+ nsTArray<RefPtr<Probe>> mAllProbes;
+
+ /**
+ * Handle used for triggering events
+ */
+ TRACEHANDLE mSessionHandle;
+
+ /**
+ * Handle used for registration/unregistration
+ */
+ TRACEHANDLE mRegistrationHandle;
+
+ /**
+ * `true` if initialization has been performed, `false` until then.
+ */
+ bool mInitialized;
+
+ friend class Probe; // Needs to access |mSessionHandle|
+ friend ULONG WINAPI ControlCallback(WMIDPREQUESTCODE aRequestCode,
+ PVOID aContext,
+ ULONG* aReserved,
+ PVOID aBuffer); // Sets |mSessionHandle|
+};
+
+} // namespace probes
+} // namespace mozilla
+
+#endif //mozilla_perfprobe_h
diff --git a/xpcom/build/xpcom_alpha.def b/xpcom/build/xpcom_alpha.def
new file mode 100644
index 000000000..38fedfa17
--- /dev/null
+++ b/xpcom/build/xpcom_alpha.def
@@ -0,0 +1,256 @@
+;+# 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/.
+
+LIBRARY xpcom
+DESCRIPTION "xpcom library"
+
+EXPORTS
+ ?Stub3@nsXPTCStubBase@@UAAIXZ
+ ?Stub4@nsXPTCStubBase@@UAAIXZ
+ ?Stub5@nsXPTCStubBase@@UAAIXZ
+ ?Stub6@nsXPTCStubBase@@UAAIXZ
+ ?Stub7@nsXPTCStubBase@@UAAIXZ
+ ?Stub8@nsXPTCStubBase@@UAAIXZ
+ ?Stub9@nsXPTCStubBase@@UAAIXZ
+ ?Stub10@nsXPTCStubBase@@UAAIXZ
+ ?Stub11@nsXPTCStubBase@@UAAIXZ
+ ?Stub12@nsXPTCStubBase@@UAAIXZ
+ ?Stub13@nsXPTCStubBase@@UAAIXZ
+ ?Stub14@nsXPTCStubBase@@UAAIXZ
+ ?Stub15@nsXPTCStubBase@@UAAIXZ
+ ?Stub16@nsXPTCStubBase@@UAAIXZ
+ ?Stub17@nsXPTCStubBase@@UAAIXZ
+ ?Stub18@nsXPTCStubBase@@UAAIXZ
+ ?Stub19@nsXPTCStubBase@@UAAIXZ
+ ?Stub20@nsXPTCStubBase@@UAAIXZ
+ ?Stub21@nsXPTCStubBase@@UAAIXZ
+ ?Stub22@nsXPTCStubBase@@UAAIXZ
+ ?Stub23@nsXPTCStubBase@@UAAIXZ
+ ?Stub24@nsXPTCStubBase@@UAAIXZ
+ ?Stub25@nsXPTCStubBase@@UAAIXZ
+ ?Stub26@nsXPTCStubBase@@UAAIXZ
+ ?Stub27@nsXPTCStubBase@@UAAIXZ
+ ?Stub28@nsXPTCStubBase@@UAAIXZ
+ ?Stub29@nsXPTCStubBase@@UAAIXZ
+ ?Stub30@nsXPTCStubBase@@UAAIXZ
+ ?Stub31@nsXPTCStubBase@@UAAIXZ
+ ?Stub32@nsXPTCStubBase@@UAAIXZ
+ ?Stub33@nsXPTCStubBase@@UAAIXZ
+ ?Stub34@nsXPTCStubBase@@UAAIXZ
+ ?Stub35@nsXPTCStubBase@@UAAIXZ
+ ?Stub36@nsXPTCStubBase@@UAAIXZ
+ ?Stub37@nsXPTCStubBase@@UAAIXZ
+ ?Stub38@nsXPTCStubBase@@UAAIXZ
+ ?Stub39@nsXPTCStubBase@@UAAIXZ
+ ?Stub40@nsXPTCStubBase@@UAAIXZ
+ ?Stub41@nsXPTCStubBase@@UAAIXZ
+ ?Stub42@nsXPTCStubBase@@UAAIXZ
+ ?Stub43@nsXPTCStubBase@@UAAIXZ
+ ?Stub44@nsXPTCStubBase@@UAAIXZ
+ ?Stub45@nsXPTCStubBase@@UAAIXZ
+ ?Stub46@nsXPTCStubBase@@UAAIXZ
+ ?Stub47@nsXPTCStubBase@@UAAIXZ
+ ?Stub48@nsXPTCStubBase@@UAAIXZ
+ ?Stub49@nsXPTCStubBase@@UAAIXZ
+ ?Stub50@nsXPTCStubBase@@UAAIXZ
+ ?Stub51@nsXPTCStubBase@@UAAIXZ
+ ?Stub52@nsXPTCStubBase@@UAAIXZ
+ ?Stub53@nsXPTCStubBase@@UAAIXZ
+ ?Stub54@nsXPTCStubBase@@UAAIXZ
+ ?Stub55@nsXPTCStubBase@@UAAIXZ
+ ?Stub56@nsXPTCStubBase@@UAAIXZ
+ ?Stub57@nsXPTCStubBase@@UAAIXZ
+ ?Stub58@nsXPTCStubBase@@UAAIXZ
+ ?Stub59@nsXPTCStubBase@@UAAIXZ
+ ?Stub60@nsXPTCStubBase@@UAAIXZ
+ ?Stub61@nsXPTCStubBase@@UAAIXZ
+ ?Stub62@nsXPTCStubBase@@UAAIXZ
+ ?Stub63@nsXPTCStubBase@@UAAIXZ
+ ?Stub64@nsXPTCStubBase@@UAAIXZ
+ ?Stub65@nsXPTCStubBase@@UAAIXZ
+ ?Stub66@nsXPTCStubBase@@UAAIXZ
+ ?Stub67@nsXPTCStubBase@@UAAIXZ
+ ?Stub68@nsXPTCStubBase@@UAAIXZ
+ ?Stub69@nsXPTCStubBase@@UAAIXZ
+ ?Stub70@nsXPTCStubBase@@UAAIXZ
+ ?Stub71@nsXPTCStubBase@@UAAIXZ
+ ?Stub72@nsXPTCStubBase@@UAAIXZ
+ ?Stub73@nsXPTCStubBase@@UAAIXZ
+ ?Stub74@nsXPTCStubBase@@UAAIXZ
+ ?Stub75@nsXPTCStubBase@@UAAIXZ
+ ?Stub76@nsXPTCStubBase@@UAAIXZ
+ ?Stub77@nsXPTCStubBase@@UAAIXZ
+ ?Stub78@nsXPTCStubBase@@UAAIXZ
+ ?Stub79@nsXPTCStubBase@@UAAIXZ
+ ?Stub80@nsXPTCStubBase@@UAAIXZ
+ ?Stub81@nsXPTCStubBase@@UAAIXZ
+ ?Stub82@nsXPTCStubBase@@UAAIXZ
+ ?Stub83@nsXPTCStubBase@@UAAIXZ
+ ?Stub84@nsXPTCStubBase@@UAAIXZ
+ ?Stub85@nsXPTCStubBase@@UAAIXZ
+ ?Stub86@nsXPTCStubBase@@UAAIXZ
+ ?Stub87@nsXPTCStubBase@@UAAIXZ
+ ?Stub88@nsXPTCStubBase@@UAAIXZ
+ ?Stub89@nsXPTCStubBase@@UAAIXZ
+ ?Stub90@nsXPTCStubBase@@UAAIXZ
+ ?Stub91@nsXPTCStubBase@@UAAIXZ
+ ?Stub92@nsXPTCStubBase@@UAAIXZ
+ ?Stub93@nsXPTCStubBase@@UAAIXZ
+ ?Stub94@nsXPTCStubBase@@UAAIXZ
+ ?Stub95@nsXPTCStubBase@@UAAIXZ
+ ?Stub96@nsXPTCStubBase@@UAAIXZ
+ ?Stub97@nsXPTCStubBase@@UAAIXZ
+ ?Stub98@nsXPTCStubBase@@UAAIXZ
+ ?Stub99@nsXPTCStubBase@@UAAIXZ
+ ?Stub100@nsXPTCStubBase@@UAAIXZ
+ ?Stub101@nsXPTCStubBase@@UAAIXZ
+ ?Stub102@nsXPTCStubBase@@UAAIXZ
+ ?Stub103@nsXPTCStubBase@@UAAIXZ
+ ?Stub104@nsXPTCStubBase@@UAAIXZ
+ ?Stub105@nsXPTCStubBase@@UAAIXZ
+ ?Stub106@nsXPTCStubBase@@UAAIXZ
+ ?Stub107@nsXPTCStubBase@@UAAIXZ
+ ?Stub108@nsXPTCStubBase@@UAAIXZ
+ ?Stub109@nsXPTCStubBase@@UAAIXZ
+ ?Stub110@nsXPTCStubBase@@UAAIXZ
+ ?Stub111@nsXPTCStubBase@@UAAIXZ
+ ?Stub112@nsXPTCStubBase@@UAAIXZ
+ ?Stub113@nsXPTCStubBase@@UAAIXZ
+ ?Stub114@nsXPTCStubBase@@UAAIXZ
+ ?Stub115@nsXPTCStubBase@@UAAIXZ
+ ?Stub116@nsXPTCStubBase@@UAAIXZ
+ ?Stub117@nsXPTCStubBase@@UAAIXZ
+ ?Stub118@nsXPTCStubBase@@UAAIXZ
+ ?Stub119@nsXPTCStubBase@@UAAIXZ
+ ?Stub120@nsXPTCStubBase@@UAAIXZ
+ ?Stub121@nsXPTCStubBase@@UAAIXZ
+ ?Stub122@nsXPTCStubBase@@UAAIXZ
+ ?Stub123@nsXPTCStubBase@@UAAIXZ
+ ?Stub124@nsXPTCStubBase@@UAAIXZ
+ ?Stub125@nsXPTCStubBase@@UAAIXZ
+ ?Stub126@nsXPTCStubBase@@UAAIXZ
+ ?Stub127@nsXPTCStubBase@@UAAIXZ
+ ?Stub128@nsXPTCStubBase@@UAAIXZ
+ ?Stub129@nsXPTCStubBase@@UAAIXZ
+ ?Stub130@nsXPTCStubBase@@UAAIXZ
+ ?Stub131@nsXPTCStubBase@@UAAIXZ
+ ?Stub132@nsXPTCStubBase@@UAAIXZ
+ ?Stub133@nsXPTCStubBase@@UAAIXZ
+ ?Stub134@nsXPTCStubBase@@UAAIXZ
+ ?Stub135@nsXPTCStubBase@@UAAIXZ
+ ?Stub136@nsXPTCStubBase@@UAAIXZ
+ ?Stub137@nsXPTCStubBase@@UAAIXZ
+ ?Stub138@nsXPTCStubBase@@UAAIXZ
+ ?Stub139@nsXPTCStubBase@@UAAIXZ
+ ?Stub140@nsXPTCStubBase@@UAAIXZ
+ ?Stub141@nsXPTCStubBase@@UAAIXZ
+ ?Stub142@nsXPTCStubBase@@UAAIXZ
+ ?Stub143@nsXPTCStubBase@@UAAIXZ
+ ?Stub144@nsXPTCStubBase@@UAAIXZ
+ ?Stub145@nsXPTCStubBase@@UAAIXZ
+ ?Stub146@nsXPTCStubBase@@UAAIXZ
+ ?Stub147@nsXPTCStubBase@@UAAIXZ
+ ?Stub148@nsXPTCStubBase@@UAAIXZ
+ ?Stub149@nsXPTCStubBase@@UAAIXZ
+ ?Stub150@nsXPTCStubBase@@UAAIXZ
+ ?Stub151@nsXPTCStubBase@@UAAIXZ
+ ?Stub152@nsXPTCStubBase@@UAAIXZ
+ ?Stub153@nsXPTCStubBase@@UAAIXZ
+ ?Stub154@nsXPTCStubBase@@UAAIXZ
+ ?Stub155@nsXPTCStubBase@@UAAIXZ
+ ?Stub156@nsXPTCStubBase@@UAAIXZ
+ ?Stub157@nsXPTCStubBase@@UAAIXZ
+ ?Stub158@nsXPTCStubBase@@UAAIXZ
+ ?Stub159@nsXPTCStubBase@@UAAIXZ
+ ?Stub160@nsXPTCStubBase@@UAAIXZ
+ ?Stub161@nsXPTCStubBase@@UAAIXZ
+ ?Stub162@nsXPTCStubBase@@UAAIXZ
+ ?Stub163@nsXPTCStubBase@@UAAIXZ
+ ?Stub164@nsXPTCStubBase@@UAAIXZ
+ ?Stub165@nsXPTCStubBase@@UAAIXZ
+ ?Stub166@nsXPTCStubBase@@UAAIXZ
+ ?Stub167@nsXPTCStubBase@@UAAIXZ
+ ?Stub168@nsXPTCStubBase@@UAAIXZ
+ ?Stub169@nsXPTCStubBase@@UAAIXZ
+ ?Stub170@nsXPTCStubBase@@UAAIXZ
+ ?Stub171@nsXPTCStubBase@@UAAIXZ
+ ?Stub172@nsXPTCStubBase@@UAAIXZ
+ ?Stub173@nsXPTCStubBase@@UAAIXZ
+ ?Stub174@nsXPTCStubBase@@UAAIXZ
+ ?Stub175@nsXPTCStubBase@@UAAIXZ
+ ?Stub176@nsXPTCStubBase@@UAAIXZ
+ ?Stub177@nsXPTCStubBase@@UAAIXZ
+ ?Stub178@nsXPTCStubBase@@UAAIXZ
+ ?Stub179@nsXPTCStubBase@@UAAIXZ
+ ?Stub180@nsXPTCStubBase@@UAAIXZ
+ ?Stub181@nsXPTCStubBase@@UAAIXZ
+ ?Stub182@nsXPTCStubBase@@UAAIXZ
+ ?Stub183@nsXPTCStubBase@@UAAIXZ
+ ?Stub184@nsXPTCStubBase@@UAAIXZ
+ ?Stub185@nsXPTCStubBase@@UAAIXZ
+ ?Stub186@nsXPTCStubBase@@UAAIXZ
+ ?Stub187@nsXPTCStubBase@@UAAIXZ
+ ?Stub188@nsXPTCStubBase@@UAAIXZ
+ ?Stub189@nsXPTCStubBase@@UAAIXZ
+ ?Stub190@nsXPTCStubBase@@UAAIXZ
+ ?Stub191@nsXPTCStubBase@@UAAIXZ
+ ?Stub192@nsXPTCStubBase@@UAAIXZ
+ ?Stub193@nsXPTCStubBase@@UAAIXZ
+ ?Stub194@nsXPTCStubBase@@UAAIXZ
+ ?Stub195@nsXPTCStubBase@@UAAIXZ
+ ?Stub196@nsXPTCStubBase@@UAAIXZ
+ ?Stub197@nsXPTCStubBase@@UAAIXZ
+ ?Stub198@nsXPTCStubBase@@UAAIXZ
+ ?Stub199@nsXPTCStubBase@@UAAIXZ
+ ?Stub200@nsXPTCStubBase@@UAAIXZ
+ ?Stub201@nsXPTCStubBase@@UAAIXZ
+ ?Stub202@nsXPTCStubBase@@UAAIXZ
+ ?Stub203@nsXPTCStubBase@@UAAIXZ
+ ?Stub204@nsXPTCStubBase@@UAAIXZ
+ ?Stub205@nsXPTCStubBase@@UAAIXZ
+ ?Stub206@nsXPTCStubBase@@UAAIXZ
+ ?Stub207@nsXPTCStubBase@@UAAIXZ
+ ?Stub208@nsXPTCStubBase@@UAAIXZ
+ ?Stub209@nsXPTCStubBase@@UAAIXZ
+ ?Stub210@nsXPTCStubBase@@UAAIXZ
+ ?Stub211@nsXPTCStubBase@@UAAIXZ
+ ?Stub212@nsXPTCStubBase@@UAAIXZ
+ ?Stub213@nsXPTCStubBase@@UAAIXZ
+ ?Stub214@nsXPTCStubBase@@UAAIXZ
+ ?Stub215@nsXPTCStubBase@@UAAIXZ
+ ?Stub216@nsXPTCStubBase@@UAAIXZ
+ ?Stub217@nsXPTCStubBase@@UAAIXZ
+ ?Stub218@nsXPTCStubBase@@UAAIXZ
+ ?Stub219@nsXPTCStubBase@@UAAIXZ
+ ?Stub220@nsXPTCStubBase@@UAAIXZ
+ ?Stub221@nsXPTCStubBase@@UAAIXZ
+ ?Stub222@nsXPTCStubBase@@UAAIXZ
+ ?Stub223@nsXPTCStubBase@@UAAIXZ
+ ?Stub224@nsXPTCStubBase@@UAAIXZ
+ ?Stub225@nsXPTCStubBase@@UAAIXZ
+ ?Stub226@nsXPTCStubBase@@UAAIXZ
+ ?Stub227@nsXPTCStubBase@@UAAIXZ
+ ?Stub228@nsXPTCStubBase@@UAAIXZ
+ ?Stub229@nsXPTCStubBase@@UAAIXZ
+ ?Stub230@nsXPTCStubBase@@UAAIXZ
+ ?Stub231@nsXPTCStubBase@@UAAIXZ
+ ?Stub232@nsXPTCStubBase@@UAAIXZ
+ ?Stub233@nsXPTCStubBase@@UAAIXZ
+ ?Stub234@nsXPTCStubBase@@UAAIXZ
+ ?Stub235@nsXPTCStubBase@@UAAIXZ
+ ?Stub236@nsXPTCStubBase@@UAAIXZ
+ ?Stub237@nsXPTCStubBase@@UAAIXZ
+ ?Stub238@nsXPTCStubBase@@UAAIXZ
+ ?Stub239@nsXPTCStubBase@@UAAIXZ
+ ?Stub240@nsXPTCStubBase@@UAAIXZ
+ ?Stub241@nsXPTCStubBase@@UAAIXZ
+ ?Stub242@nsXPTCStubBase@@UAAIXZ
+ ?Stub243@nsXPTCStubBase@@UAAIXZ
+ ?Stub244@nsXPTCStubBase@@UAAIXZ
+ ?Stub245@nsXPTCStubBase@@UAAIXZ
+ ?Stub246@nsXPTCStubBase@@UAAIXZ
+ ?Stub247@nsXPTCStubBase@@UAAIXZ
+ ?Stub248@nsXPTCStubBase@@UAAIXZ
+ ?Stub249@nsXPTCStubBase@@UAAIXZ
+
diff --git a/xpcom/build/xrecore.h b/xpcom/build/xrecore.h
new file mode 100644
index 000000000..9749a8e90
--- /dev/null
+++ b/xpcom/build/xrecore.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef xrecore_h__
+#define xrecore_h__
+
+#include "nscore.h"
+
+/**
+ * Import/export macros for libXUL APIs.
+ */
+#ifdef XPCOM_GLUE
+#define XRE_API(type, name, params) \
+ typedef type (NS_FROZENCALL * name##Type) params; \
+ extern name##Type name NS_HIDDEN;
+#elif defined(IMPL_LIBXUL)
+#define XRE_API(type, name, params) EXPORT_XPCOM_API(type) name params;
+#else
+#define XRE_API(type, name, params) IMPORT_XPCOM_API(type) name params;
+#endif
+
+#endif // xrecore_h__
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp
new file mode 100644
index 000000000..4dcfc8402
--- /dev/null
+++ b/xpcom/components/ManifestParser.cpp
@@ -0,0 +1,792 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "ManifestParser.h"
+
+#include <string.h>
+
+#include "prio.h"
+#include "prprf.h"
+#if defined(XP_WIN)
+#include <windows.h>
+#elif defined(MOZ_WIDGET_COCOA)
+#include <CoreServices/CoreServices.h>
+#include "nsCocoaFeatures.h"
+#elif defined(MOZ_WIDGET_GTK)
+#include <gtk/gtk.h>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+#include "mozilla/Services.h"
+
+#include "nsCRT.h"
+#include "nsConsoleMessage.h"
+#include "nsTextFormatter.h"
+#include "nsVersionComparator.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "nsIConsoleService.h"
+#include "nsIScriptError.h"
+#include "nsIXULAppInfo.h"
+#include "nsIXULRuntime.h"
+
+using namespace mozilla;
+
+struct ManifestDirective
+{
+ const char* directive;
+ int argc;
+
+ // Binary components are only allowed for APP locations.
+ bool apponly;
+
+ // Some directives should only be delivered for APP or EXTENSION locations.
+ bool componentonly;
+
+ bool ischrome;
+
+ bool allowbootstrap;
+
+ // The platform/contentaccessible flags only apply to content directives.
+ bool contentflags;
+
+ // Function to handle this directive. This isn't a union because C++ still
+ // hasn't learned how to initialize unions in a sane way.
+ void (nsComponentManagerImpl::*mgrfunc)(
+ nsComponentManagerImpl::ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv);
+ void (nsChromeRegistry::*regfunc)(
+ nsChromeRegistry::ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv, int aFlags);
+ void* xptonlyfunc;
+
+ bool isContract;
+};
+static const ManifestDirective kParsingTable[] = {
+ {
+ "manifest", 1, false, false, true, true, false,
+ &nsComponentManagerImpl::ManifestManifest, nullptr, nullptr
+ },
+ {
+ "binary-component", 1, true, true, false, false, false,
+ &nsComponentManagerImpl::ManifestBinaryComponent, nullptr, nullptr
+ },
+ {
+ "interfaces", 1, false, true, false, false, false,
+ &nsComponentManagerImpl::ManifestXPT, nullptr, nullptr
+ },
+ {
+ "component", 2, false, true, false, false, false,
+ &nsComponentManagerImpl::ManifestComponent, nullptr, nullptr
+ },
+ {
+ "contract", 2, false, true, false, false, false,
+ &nsComponentManagerImpl::ManifestContract, nullptr, nullptr, true
+ },
+ {
+ "category", 3, false, true, false, false, false,
+ &nsComponentManagerImpl::ManifestCategory, nullptr, nullptr
+ },
+ {
+ "content", 2, false, true, true, true, true,
+ nullptr, &nsChromeRegistry::ManifestContent, nullptr
+ },
+ {
+ "locale", 3, false, true, true, true, false,
+ nullptr, &nsChromeRegistry::ManifestLocale, nullptr
+ },
+ {
+ "skin", 3, false, false, true, true, false,
+ nullptr, &nsChromeRegistry::ManifestSkin, nullptr
+ },
+ {
+ "overlay", 2, false, true, true, false, false,
+ nullptr, &nsChromeRegistry::ManifestOverlay, nullptr
+ },
+ {
+ "style", 2, false, false, true, false, false,
+ nullptr, &nsChromeRegistry::ManifestStyle, nullptr
+ },
+ {
+ // NB: note that while skin manifests can use this, they are only allowed
+ // to use it for chrome://../skin/ URLs
+ "override", 2, false, false, true, true, false,
+ nullptr, &nsChromeRegistry::ManifestOverride, nullptr
+ },
+ {
+ "resource", 2, false, true, true, true, false,
+ nullptr, &nsChromeRegistry::ManifestResource, nullptr
+ }
+};
+
+static const char kWhitespace[] = "\t ";
+
+static bool
+IsNewline(char aChar)
+{
+ return aChar == '\n' || aChar == '\r';
+}
+
+namespace {
+struct AutoPR_smprintf_free
+{
+ explicit AutoPR_smprintf_free(char* aBuf) : mBuf(aBuf) {}
+
+ ~AutoPR_smprintf_free()
+ {
+ if (mBuf) {
+ PR_smprintf_free(mBuf);
+ }
+ }
+
+ operator char*() const { return mBuf; }
+
+ char* mBuf;
+};
+
+} // namespace
+
+/**
+ * If we are pre-loading XPTs, this method may do nothing because the
+ * console service is not initialized.
+ */
+void
+LogMessage(const char* aMsg, ...)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return;
+ }
+
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (!console) {
+ return;
+ }
+
+ va_list args;
+ va_start(args, aMsg);
+ AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args));
+ va_end(args);
+
+ nsCOMPtr<nsIConsoleMessage> error =
+ new nsConsoleMessage(NS_ConvertUTF8toUTF16(formatted).get());
+ console->LogMessage(error);
+}
+
+/**
+ * If we are pre-loading XPTs, this method may do nothing because the
+ * console service is not initialized.
+ */
+void
+LogMessageWithContext(FileLocation& aFile,
+ uint32_t aLineNumber, const char* aMsg, ...)
+{
+ va_list args;
+ va_start(args, aMsg);
+ AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args));
+ va_end(args);
+ if (!formatted) {
+ return;
+ }
+
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return;
+ }
+
+ nsCString file;
+ aFile.GetURIString(file);
+
+ nsCOMPtr<nsIScriptError> error =
+ do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+ if (!error) {
+ // This can happen early in component registration. Fall back to a
+ // generic console message.
+ LogMessage("Warning: in '%s', line %i: %s", file.get(),
+ aLineNumber, (char*)formatted);
+ return;
+ }
+
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (!console) {
+ return;
+ }
+
+ nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
+ NS_ConvertUTF8toUTF16(file), EmptyString(),
+ aLineNumber, 0, nsIScriptError::warningFlag,
+ "chrome registration");
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ console->LogMessage(error);
+}
+
+/**
+ * Check for a modifier flag of the following forms:
+ * "flag" (same as "true")
+ * "flag=yes|true|1"
+ * "flag="no|false|0"
+ * @param aFlag The flag to compare.
+ * @param aData The tokenized data to check; this is lowercased
+ * before being passed in.
+ * @param aResult If the flag is found, the value is assigned here.
+ * @return Whether the flag was handled.
+ */
+static bool
+CheckFlag(const nsSubstring& aFlag, const nsSubstring& aData, bool& aResult)
+{
+ if (!StringBeginsWith(aData, aFlag)) {
+ return false;
+ }
+
+ if (aFlag.Length() == aData.Length()) {
+ // the data is simply "flag", which is the same as "flag=yes"
+ aResult = true;
+ return true;
+ }
+
+ if (aData.CharAt(aFlag.Length()) != '=') {
+ // the data is "flag2=", which is not anything we care about
+ return false;
+ }
+
+ if (aData.Length() == aFlag.Length() + 1) {
+ aResult = false;
+ return true;
+ }
+
+ switch (aData.CharAt(aFlag.Length() + 1)) {
+ case '1':
+ case 't': //true
+ case 'y': //yes
+ aResult = true;
+ return true;
+
+ case '0':
+ case 'f': //false
+ case 'n': //no
+ aResult = false;
+ return true;
+ }
+
+ return false;
+}
+
+enum TriState
+{
+ eUnspecified,
+ eBad,
+ eOK
+};
+
+/**
+ * Check for a modifier flag of the following form:
+ * "flag=string"
+ * "flag!=string"
+ * @param aFlag The flag to compare.
+ * @param aData The tokenized data to check; this is lowercased
+ * before being passed in.
+ * @param aValue The value that is expected.
+ * @param aResult If this is "ok" when passed in, this is left alone.
+ * Otherwise if the flag is found it is set to eBad or eOK.
+ * @return Whether the flag was handled.
+ */
+static bool
+CheckStringFlag(const nsSubstring& aFlag, const nsSubstring& aData,
+ const nsSubstring& aValue, TriState& aResult)
+{
+ if (aData.Length() < aFlag.Length() + 1) {
+ return false;
+ }
+
+ if (!StringBeginsWith(aData, aFlag)) {
+ return false;
+ }
+
+ bool comparison = true;
+ if (aData[aFlag.Length()] != '=') {
+ if (aData[aFlag.Length()] == '!' &&
+ aData.Length() >= aFlag.Length() + 2 &&
+ aData[aFlag.Length() + 1] == '=') {
+ comparison = false;
+ } else {
+ return false;
+ }
+ }
+
+ if (aResult != eOK) {
+ nsDependentSubstring testdata =
+ Substring(aData, aFlag.Length() + (comparison ? 1 : 2));
+ if (testdata.Equals(aValue)) {
+ aResult = comparison ? eOK : eBad;
+ } else {
+ aResult = comparison ? eBad : eOK;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Check for a modifier flag of the following form:
+ * "flag=version"
+ * "flag<=version"
+ * "flag<version"
+ * "flag>=version"
+ * "flag>version"
+ * @param aFlag The flag to compare.
+ * @param aData The tokenized data to check; this is lowercased
+ * before being passed in.
+ * @param aValue The value that is expected. If this is empty then no
+ * comparison will match.
+ * @param aResult If this is eOK when passed in, this is left alone.
+ * Otherwise if the flag is found it is set to eBad or eOK.
+ * @return Whether the flag was handled.
+ */
+
+#define COMPARE_EQ 1 << 0
+#define COMPARE_LT 1 << 1
+#define COMPARE_GT 1 << 2
+
+static bool
+CheckVersionFlag(const nsString& aFlag, const nsString& aData,
+ const nsString& aValue, TriState& aResult)
+{
+ if (aData.Length() < aFlag.Length() + 2) {
+ return false;
+ }
+
+ if (!StringBeginsWith(aData, aFlag)) {
+ return false;
+ }
+
+ if (aValue.Length() == 0) {
+ if (aResult != eOK) {
+ aResult = eBad;
+ }
+ return true;
+ }
+
+ uint32_t comparison;
+ nsAutoString testdata;
+
+ switch (aData[aFlag.Length()]) {
+ case '=':
+ comparison = COMPARE_EQ;
+ testdata = Substring(aData, aFlag.Length() + 1);
+ break;
+
+ case '<':
+ if (aData[aFlag.Length() + 1] == '=') {
+ comparison = COMPARE_EQ | COMPARE_LT;
+ testdata = Substring(aData, aFlag.Length() + 2);
+ } else {
+ comparison = COMPARE_LT;
+ testdata = Substring(aData, aFlag.Length() + 1);
+ }
+ break;
+
+ case '>':
+ if (aData[aFlag.Length() + 1] == '=') {
+ comparison = COMPARE_EQ | COMPARE_GT;
+ testdata = Substring(aData, aFlag.Length() + 2);
+ } else {
+ comparison = COMPARE_GT;
+ testdata = Substring(aData, aFlag.Length() + 1);
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ if (testdata.Length() == 0) {
+ return false;
+ }
+
+ if (aResult != eOK) {
+ int32_t c = mozilla::CompareVersions(NS_ConvertUTF16toUTF8(aValue).get(),
+ NS_ConvertUTF16toUTF8(testdata).get());
+ if ((c == 0 && comparison & COMPARE_EQ) ||
+ (c < 0 && comparison & COMPARE_LT) ||
+ (c > 0 && comparison & COMPARE_GT)) {
+ aResult = eOK;
+ } else {
+ aResult = eBad;
+ }
+ }
+
+ return true;
+}
+
+// In-place conversion of ascii characters to lower case
+static void
+ToLowerCase(char* aToken)
+{
+ for (; *aToken; ++aToken) {
+ *aToken = NS_ToLower(*aToken);
+ }
+}
+
+namespace {
+
+struct CachedDirective
+{
+ int lineno;
+ char* argv[4];
+};
+
+} // namespace
+
+
+/**
+ * For XPT-Only mode, the parser handles only directives of "manifest"
+ * and "interfaces", and always call the function given by |xptonlyfunc|
+ * variable of struct |ManifestDirective|.
+ *
+ * This function is safe to be called before the component manager is
+ * ready if aXPTOnly is true for it don't invoke any component during
+ * parsing.
+ */
+void
+ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+ bool aChromeOnly, bool aXPTOnly)
+{
+ nsComponentManagerImpl::ManifestProcessingContext mgrcx(aType, aFile,
+ aChromeOnly);
+ nsChromeRegistry::ManifestProcessingContext chromecx(aType, aFile);
+ nsresult rv;
+
+ NS_NAMED_LITERAL_STRING(kPlatform, "platform");
+ NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
+ NS_NAMED_LITERAL_STRING(kRemoteEnabled, "remoteenabled");
+ NS_NAMED_LITERAL_STRING(kRemoteRequired, "remoterequired");
+ NS_NAMED_LITERAL_STRING(kApplication, "application");
+ NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
+ NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion");
+ NS_NAMED_LITERAL_STRING(kOs, "os");
+ NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
+ NS_NAMED_LITERAL_STRING(kABI, "abi");
+ NS_NAMED_LITERAL_STRING(kProcess, "process");
+#if defined(MOZ_WIDGET_ANDROID)
+ NS_NAMED_LITERAL_STRING(kTablet, "tablet");
+#endif
+
+ NS_NAMED_LITERAL_STRING(kMain, "main");
+ NS_NAMED_LITERAL_STRING(kContent, "content");
+
+ // Obsolete
+ NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers");
+
+ nsAutoString appID;
+ nsAutoString appVersion;
+ nsAutoString geckoVersion;
+ nsAutoString osTarget;
+ nsAutoString abi;
+ nsAutoString process;
+
+ nsCOMPtr<nsIXULAppInfo> xapp;
+ if (!aXPTOnly) {
+ // Avoid to create any component for XPT only mode.
+ // No xapp means no ID, version, ..., modifiers checking.
+ xapp = do_GetService(XULAPPINFO_SERVICE_CONTRACTID);
+ }
+ if (xapp) {
+ nsAutoCString s;
+ rv = xapp->GetID(s);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(s, appID);
+ }
+
+ rv = xapp->GetVersion(s);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(s, appVersion);
+ }
+
+ rv = xapp->GetPlatformVersion(s);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(s, geckoVersion);
+ }
+
+ nsCOMPtr<nsIXULRuntime> xruntime(do_QueryInterface(xapp));
+ if (xruntime) {
+ rv = xruntime->GetOS(s);
+ if (NS_SUCCEEDED(rv)) {
+ ToLowerCase(s);
+ CopyUTF8toUTF16(s, osTarget);
+ }
+
+ rv = xruntime->GetXPCOMABI(s);
+ if (NS_SUCCEEDED(rv) && osTarget.Length()) {
+ ToLowerCase(s);
+ CopyUTF8toUTF16(s, abi);
+ abi.Insert(char16_t('_'), 0);
+ abi.Insert(osTarget, 0);
+ }
+ }
+ }
+
+ nsAutoString osVersion;
+#if defined(XP_WIN)
+#pragma warning(push)
+#pragma warning(disable:4996) // VC12+ deprecates GetVersionEx
+ OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
+ if (GetVersionEx(&info)) {
+ nsTextFormatter::ssprintf(osVersion, u"%ld.%ld",
+ info.dwMajorVersion,
+ info.dwMinorVersion);
+ }
+#pragma warning(pop)
+#elif defined(MOZ_WIDGET_COCOA)
+ SInt32 majorVersion = nsCocoaFeatures::OSXVersionMajor();
+ SInt32 minorVersion = nsCocoaFeatures::OSXVersionMinor();
+ nsTextFormatter::ssprintf(osVersion, u"%ld.%ld",
+ majorVersion,
+ minorVersion);
+#elif defined(MOZ_WIDGET_GTK)
+ nsTextFormatter::ssprintf(osVersion, u"%ld.%ld",
+ gtk_major_version,
+ gtk_minor_version);
+#elif defined(MOZ_WIDGET_ANDROID)
+ bool isTablet = false;
+ if (mozilla::AndroidBridge::Bridge()) {
+ mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION",
+ "RELEASE",
+ osVersion);
+ isTablet = java::GeckoAppShell::IsTablet();
+ }
+#endif
+
+ if (XRE_IsContentProcess()) {
+ process = kContent;
+ } else {
+ process = kMain;
+ }
+
+ // Because contracts must be registered after CIDs, we save and process them
+ // at the end.
+ nsTArray<CachedDirective> contracts;
+
+ char* token;
+ char* newline = aBuf;
+ uint32_t line = 0;
+
+ // outer loop tokenizes by newline
+ while (*newline) {
+ while (*newline && IsNewline(*newline)) {
+ ++newline;
+ ++line;
+ }
+ if (!*newline) {
+ break;
+ }
+
+ token = newline;
+ while (*newline && !IsNewline(*newline)) {
+ ++newline;
+ }
+
+ if (*newline) {
+ *newline = '\0';
+ ++newline;
+ }
+ ++line;
+
+ if (*token == '#') { // ignore lines that begin with # as comments
+ continue;
+ }
+
+ char* whitespace = token;
+ token = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
+ if (!token) {
+ continue;
+ }
+
+ const ManifestDirective* directive = nullptr;
+ for (const ManifestDirective* d = kParsingTable;
+ d < ArrayEnd(kParsingTable);
+ ++d) {
+ if (!strcmp(d->directive, token) &&
+ (!aXPTOnly || d->xptonlyfunc)) {
+ directive = d;
+ break;
+ }
+ }
+
+ if (!directive) {
+ LogMessageWithContext(aFile, line,
+ "Ignoring unrecognized chrome manifest directive '%s'.",
+ token);
+ continue;
+ }
+
+ if (!directive->allowbootstrap && NS_BOOTSTRAPPED_LOCATION == aType) {
+ LogMessageWithContext(aFile, line,
+ "Bootstrapped manifest not allowed to use '%s' directive.",
+ token);
+ continue;
+ }
+
+#ifndef MOZ_BINARY_EXTENSIONS
+ if (directive->apponly && NS_APP_LOCATION != aType) {
+ LogMessageWithContext(aFile, line,
+ "Only application manifests may use the '%s' directive.", token);
+ continue;
+ }
+#endif
+
+ if (directive->componentonly && NS_SKIN_LOCATION == aType) {
+ LogMessageWithContext(aFile, line,
+ "Skin manifest not allowed to use '%s' directive.",
+ token);
+ continue;
+ }
+
+ NS_ASSERTION(directive->argc < 4, "Need to reset argv array length");
+ char* argv[4];
+ for (int i = 0; i < directive->argc; ++i) {
+ argv[i] = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
+ }
+
+ if (!argv[directive->argc - 1]) {
+ LogMessageWithContext(aFile, line,
+ "Not enough arguments for chrome manifest directive '%s', expected %i.",
+ token, directive->argc);
+ continue;
+ }
+
+ bool ok = true;
+ TriState stAppVersion = eUnspecified;
+ TriState stGeckoVersion = eUnspecified;
+ TriState stApp = eUnspecified;
+ TriState stOsVersion = eUnspecified;
+ TriState stOs = eUnspecified;
+ TriState stABI = eUnspecified;
+ TriState stProcess = eUnspecified;
+#if defined(MOZ_WIDGET_ANDROID)
+ TriState stTablet = eUnspecified;
+#endif
+ int flags = 0;
+
+ while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
+ ok) {
+ ToLowerCase(token);
+ NS_ConvertASCIItoUTF16 wtoken(token);
+
+ if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
+ CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
+ CheckStringFlag(kABI, wtoken, abi, stABI) ||
+ CheckStringFlag(kProcess, wtoken, process, stProcess) ||
+ CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
+ CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
+ CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion)) {
+ continue;
+ }
+
+#if defined(MOZ_WIDGET_ANDROID)
+ bool tablet = false;
+ if (CheckFlag(kTablet, wtoken, tablet)) {
+ stTablet = (tablet == isTablet) ? eOK : eBad;
+ continue;
+ }
+#endif
+
+ if (directive->contentflags) {
+ bool flag;
+ if (CheckFlag(kPlatform, wtoken, flag)) {
+ if (flag)
+ flags |= nsChromeRegistry::PLATFORM_PACKAGE;
+ continue;
+ }
+ if (CheckFlag(kContentAccessible, wtoken, flag)) {
+ if (flag)
+ flags |= nsChromeRegistry::CONTENT_ACCESSIBLE;
+ continue;
+ }
+ if (CheckFlag(kRemoteEnabled, wtoken, flag)) {
+ if (flag)
+ flags |= nsChromeRegistry::REMOTE_ALLOWED;
+ continue;
+ }
+ if (CheckFlag(kRemoteRequired, wtoken, flag)) {
+ if (flag)
+ flags |= nsChromeRegistry::REMOTE_REQUIRED;
+ continue;
+ }
+ }
+
+ bool xpcNativeWrappers = true; // Dummy for CheckFlag.
+ if (CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers)) {
+ LogMessageWithContext(aFile, line,
+ "Ignoring obsolete chrome registration modifier '%s'.",
+ token);
+ continue;
+ }
+
+ LogMessageWithContext(aFile, line,
+ "Unrecognized chrome manifest modifier '%s'.",
+ token);
+ ok = false;
+ }
+
+ if (!ok ||
+ stApp == eBad ||
+ stAppVersion == eBad ||
+ stGeckoVersion == eBad ||
+ stOs == eBad ||
+ stOsVersion == eBad ||
+#ifdef MOZ_WIDGET_ANDROID
+ stTablet == eBad ||
+#endif
+ stABI == eBad ||
+ stProcess == eBad) {
+ continue;
+ }
+
+ if (directive->regfunc) {
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ continue;
+ }
+
+ if (!nsChromeRegistry::gChromeRegistry) {
+ nsCOMPtr<nsIChromeRegistry> cr =
+ mozilla::services::GetChromeRegistryService();
+ if (!nsChromeRegistry::gChromeRegistry) {
+ LogMessageWithContext(aFile, line,
+ "Chrome registry isn't available yet.");
+ continue;
+ }
+ }
+
+ (nsChromeRegistry::gChromeRegistry->*(directive->regfunc))(
+ chromecx, line, argv, flags);
+ } else if (directive->ischrome || !aChromeOnly) {
+ if (directive->isContract) {
+ CachedDirective* cd = contracts.AppendElement();
+ cd->lineno = line;
+ cd->argv[0] = argv[0];
+ cd->argv[1] = argv[1];
+ } else {
+ (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(
+ mgrcx, line, argv);
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < contracts.Length(); ++i) {
+ CachedDirective& d = contracts[i];
+ nsComponentManagerImpl::gComponentManager->ManifestContract(mgrcx,
+ d.lineno,
+ d.argv);
+ }
+}
diff --git a/xpcom/components/ManifestParser.h b/xpcom/components/ManifestParser.h
new file mode 100644
index 000000000..019ec326c
--- /dev/null
+++ b/xpcom/components/ManifestParser.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ManifestParser_h
+#define ManifestParser_h
+
+#include "nsComponentManager.h"
+#include "nsChromeRegistry.h"
+#include "mozilla/FileLocation.h"
+
+void ParseManifest(NSLocationType aType, mozilla::FileLocation& aFile,
+ char* aBuf, bool aChromeOnly, bool aXPTOnly = false);
+
+void LogMessage(const char* aMsg, ...);
+
+void LogMessageWithContext(mozilla::FileLocation& aFile,
+ uint32_t aLineNumber, const char* aMsg, ...);
+
+#endif // ManifestParser_h
diff --git a/xpcom/components/Module.h b/xpcom/components/Module.h
new file mode 100644
index 000000000..21d07e82e
--- /dev/null
+++ b/xpcom/components/Module.h
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Module_h
+#define mozilla_Module_h
+
+#include "nscore.h"
+#include "nsID.h"
+#include "nsIFactory.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+
+namespace mozilla {
+
+/**
+ * A module implements one or more XPCOM components. This structure is used
+ * for both binary and script modules, but the registration members
+ * (cids/contractids/categoryentries) are unused for modules which are loaded
+ * via a module loader.
+ */
+struct Module
+{
+ static const unsigned int kVersion = 52;
+
+ struct CIDEntry;
+
+ typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
+ const Module& module, const CIDEntry& entry);
+
+ typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aResult);
+
+ typedef nsresult (*LoadFuncPtr)();
+ typedef void (*UnloadFuncPtr)();
+
+ /**
+ * This selector allows CIDEntrys to be marked so that they're only loaded
+ * into certain kinds of processes. Selectors can be combined.
+ */
+ enum ProcessSelector
+ {
+ ANY_PROCESS = 0x0,
+ MAIN_PROCESS_ONLY = 0x1,
+ CONTENT_PROCESS_ONLY = 0x2,
+
+ /**
+ * By default, modules are not loaded in the GPU process, even if
+ * ANY_PROCESS is specified. This flag enables a module in the
+ * GPU process.
+ */
+ ALLOW_IN_GPU_PROCESS = 0x4
+ };
+
+ /**
+ * The constructor callback is an implementation detail of the default binary
+ * loader and may be null.
+ */
+ struct CIDEntry
+ {
+ const nsCID* cid;
+ bool service;
+ GetFactoryProcPtr getFactoryProc;
+ ConstructorProcPtr constructorProc;
+ ProcessSelector processSelector;
+ };
+
+ struct ContractIDEntry
+ {
+ const char* contractid;
+ nsID const* cid;
+ ProcessSelector processSelector;
+ };
+
+ struct CategoryEntry
+ {
+ const char* category;
+ const char* entry;
+ const char* value;
+ };
+
+ /**
+ * Binary compatibility check, should be kModuleVersion.
+ */
+ unsigned int mVersion;
+
+ /**
+ * An array of CIDs (class IDs) implemented by this module. The final entry
+ * should be { nullptr }.
+ */
+ const CIDEntry* mCIDs;
+
+ /**
+ * An array of mappings from contractid to CID. The final entry should
+ * be { nullptr }.
+ */
+ const ContractIDEntry* mContractIDs;
+
+ /**
+ * An array of category manager entries. The final entry should be
+ * { nullptr }.
+ */
+ const CategoryEntry* mCategoryEntries;
+
+ /**
+ * When the component manager tries to get the factory for a CID, it first
+ * checks for this module-level getfactory callback. If this function is
+ * not implemented, it checks the CIDEntry getfactory callback. If that is
+ * also nullptr, a generic factory is generated using the CIDEntry
+ * constructor callback which must be non-nullptr.
+ */
+ GetFactoryProcPtr getFactoryProc;
+
+ /**
+ * Optional Function which are called when this module is loaded and
+ * at shutdown. These are not C++ constructor/destructors to avoid
+ * calling them too early in startup or too late in shutdown.
+ */
+ LoadFuncPtr loadProc;
+ UnloadFuncPtr unloadProc;
+
+ /**
+ * Optional flags which control whether the module loads on a process-type
+ * basis.
+ */
+ ProcessSelector selector;
+};
+
+} // namespace mozilla
+
+#if defined(MOZILLA_INTERNAL_API)
+# define NSMODULE_NAME(_name) _name##_NSModule
+# if defined(_MSC_VER)
+# pragma section(".kPStaticModules$M", read)
+# pragma comment(linker, "/merge:.kPStaticModules=.rdata")
+# define NSMODULE_SECTION __declspec(allocate(".kPStaticModules$M"), dllexport)
+# elif defined(__GNUC__)
+# if defined(__ELF__)
+# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), visibility("default")))
+# elif defined(__MACH__)
+# define NSMODULE_SECTION __attribute__((section("__DATA, .kPStaticModules"), visibility("default")))
+# elif defined (_WIN32)
+# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), dllexport))
+# endif
+# endif
+# if !defined(NSMODULE_SECTION)
+# error Do not know how to define sections.
+# endif
+# define NSMODULE_DEFN(_name) extern NSMODULE_SECTION mozilla::Module const *const NSMODULE_NAME(_name)
+#else
+# define NSMODULE_NAME(_name) NSModule
+# define NSMODULE_DEFN(_name) extern "C" NS_EXPORT mozilla::Module const *const NSModule
+#endif
+
+#endif // mozilla_Module_h
diff --git a/xpcom/components/ModuleLoader.h b/xpcom/components/ModuleLoader.h
new file mode 100644
index 000000000..c2c920d4d
--- /dev/null
+++ b/xpcom/components/ModuleLoader.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ModuleLoader_h
+#define mozilla_ModuleLoader_h
+
+#include "nsISupports.h"
+#include "mozilla/Module.h"
+#include "mozilla/FileLocation.h"
+
+#define MOZILLA_MODULELOADER_PSEUDO_IID \
+{ 0xD951A8CE, 0x6E9F, 0x464F, \
+ { 0x8A, 0xC8, 0x14, 0x61, 0xC0, 0xD3, 0x63, 0xC8 } }
+
+namespace mozilla {
+
+/**
+ * Module loaders are responsible for loading a component file. The static
+ * component loader is special and does not use this abstract interface.
+ *
+ * @note Implementations of this interface should be threadsafe,
+ * methods may be called from any thread.
+ */
+class ModuleLoader : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_MODULELOADER_PSEUDO_IID)
+
+ /**
+ * Return the module for a specified file. The caller should cache
+ * the module: the implementer should not expect for the same file
+ * to be loaded multiple times. The Module object should either be
+ * statically or permanently allocated; it will not be freed.
+ */
+ virtual const Module* LoadModule(mozilla::FileLocation& aFile) = 0;
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(ModuleLoader, MOZILLA_MODULELOADER_PSEUDO_IID)
+
+} // namespace mozilla
+
+#endif // mozilla_ModuleLoader_h
diff --git a/xpcom/components/ModuleUtils.h b/xpcom/components/ModuleUtils.h
new file mode 100644
index 000000000..9c31ed76b
--- /dev/null
+++ b/xpcom/components/ModuleUtils.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_GenericModule_h
+#define mozilla_GenericModule_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Module.h"
+
+#define NS_GENERIC_FACTORY_CONSTRUCTOR(_InstanceClass) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ RefPtr<_InstanceClass> inst; \
+ \
+ *aResult = nullptr; \
+ if (nullptr != aOuter) { \
+ return NS_ERROR_NO_AGGREGATION; \
+ } \
+ \
+ inst = new _InstanceClass(); \
+ return inst->QueryInterface(aIID, aResult); \
+}
+
+#define NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ nsresult rv; \
+ \
+ RefPtr<_InstanceClass> inst; \
+ \
+ *aResult = nullptr; \
+ if (nullptr != aOuter) { \
+ return NS_ERROR_NO_AGGREGATION; \
+ } \
+ \
+ inst = new _InstanceClass(); \
+ rv = inst->_InitMethod(); \
+ if (NS_SUCCEEDED(rv)) { \
+ rv = inst->QueryInterface(aIID, aResult); \
+ } \
+ \
+ return rv; \
+}
+
+// 'Constructor' that uses an existing getter function that gets a singleton.
+// NOTE: assumes that getter does an AddRef - so additional AddRef is not done.
+#define NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(_InstanceClass, _GetterProc) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ RefPtr<_InstanceClass> inst; \
+ \
+ *aResult = nullptr; \
+ if (nullptr != aOuter) { \
+ return NS_ERROR_NO_AGGREGATION; \
+ } \
+ \
+ inst = already_AddRefed<_InstanceClass>(_GetterProc()); \
+ if (nullptr == inst) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ return inst->QueryInterface(aIID, aResult); \
+}
+
+#ifndef MOZILLA_INTERNAL_API
+
+#include "nsIModule.h"
+#include "nsISupportsUtils.h"
+
+namespace mozilla {
+
+class GenericModule final : public nsIModule
+{
+ ~GenericModule() {}
+
+public:
+ explicit GenericModule(const mozilla::Module* aData) : mData(aData) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMODULE
+
+private:
+ const mozilla::Module* mData;
+};
+
+} // namespace mozilla
+
+#endif
+
+#endif // mozilla_GenericModule_h
diff --git a/xpcom/components/moz.build b/xpcom/components/moz.build
new file mode 100644
index 000000000..73a85cb3b
--- /dev/null
+++ b/xpcom/components/moz.build
@@ -0,0 +1,55 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsICategoryManager.idl',
+ 'nsIClassInfo.idl',
+ 'nsIComponentManager.idl',
+ 'nsIComponentRegistrar.idl',
+ 'nsIFactory.idl',
+ 'nsIModule.idl',
+ 'nsIServiceManager.idl',
+]
+
+XPIDL_MODULE = 'xpcom_components'
+
+EXPORTS += [
+ 'nsCategoryManagerUtils.h',
+]
+
+EXPORTS.mozilla += [
+ 'Module.h',
+ 'ModuleLoader.h',
+ 'ModuleUtils.h',
+]
+
+# nsCategoryManager.cpp and nsComponentManager.cpp cannot be built in
+# unified mode because they use thea PL_ARENA_CONST_ALIGN_MASK macro
+# with plarena.h.
+SOURCES += [
+ 'nsCategoryManager.cpp',
+ 'nsComponentManager.cpp',
+]
+
+UNIFIED_SOURCES += [
+ 'ManifestParser.cpp',
+ 'nsNativeModuleLoader.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '!..',
+ '../base',
+ '../build',
+ '../ds',
+ '../reflect/xptinfo',
+ '/chrome',
+ '/modules/libjar',
+]
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/xpcom/components/nsCategoryManager.cpp b/xpcom/components/nsCategoryManager.cpp
new file mode 100644
index 000000000..527c78719
--- /dev/null
+++ b/xpcom/components/nsCategoryManager.cpp
@@ -0,0 +1,832 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define PL_ARENA_CONST_ALIGN_MASK 7
+
+#include "nsICategoryManager.h"
+#include "nsCategoryManager.h"
+
+#include "plarena.h"
+#include "prio.h"
+#include "prprf.h"
+#include "prlock.h"
+#include "nsCOMPtr.h"
+#include "nsTHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsIFactory.h"
+#include "nsIStringEnumerator.h"
+#include "nsSupportsPrimitives.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsQuickSort.h"
+#include "nsEnumeratorUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Services.h"
+
+#include "ManifestParser.h"
+#include "nsISimpleEnumerator.h"
+
+using namespace mozilla;
+class nsIComponentLoaderManager;
+
+/*
+ CategoryDatabase
+ contains 0 or more 1-1 mappings of string to Category
+ each Category contains 0 or more 1-1 mappings of string keys to string values
+
+ In other words, the CategoryDatabase is a tree, whose root is a hashtable.
+ Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
+
+ The leaf strings are allocated in an arena, because we assume they're not
+ going to change much ;)
+*/
+
+#define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
+
+// pulled in from nsComponentManager.cpp
+char* ArenaStrdup(const char* aStr, PLArenaPool* aArena);
+
+//
+// BaseStringEnumerator is subclassed by EntryEnumerator and
+// CategoryEnumerator
+//
+class BaseStringEnumerator
+ : public nsISimpleEnumerator
+ , private nsIUTF8StringEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+
+protected:
+ // Callback function for NS_QuickSort to sort mArray
+ static int SortCallback(const void*, const void*, void*);
+
+ BaseStringEnumerator()
+ : mArray(nullptr)
+ , mCount(0)
+ , mSimpleCurItem(0)
+ , mStringCurItem(0)
+ {
+ }
+
+ // A virtual destructor is needed here because subclasses of
+ // BaseStringEnumerator do not implement their own Release() method.
+
+ virtual ~BaseStringEnumerator()
+ {
+ delete [] mArray;
+ }
+
+ void Sort();
+
+ const char** mArray;
+ uint32_t mCount;
+ uint32_t mSimpleCurItem;
+ uint32_t mStringCurItem;
+};
+
+NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator,
+ nsIUTF8StringEnumerator)
+
+NS_IMETHODIMP
+BaseStringEnumerator::HasMoreElements(bool* aResult)
+{
+ *aResult = (mSimpleCurItem < mCount);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BaseStringEnumerator::GetNext(nsISupports** aResult)
+{
+ if (mSimpleCurItem >= mCount) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsSupportsDependentCString* str =
+ new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
+ if (!str) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ *aResult = str;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BaseStringEnumerator::HasMore(bool* aResult)
+{
+ *aResult = (mStringCurItem < mCount);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BaseStringEnumerator::GetNext(nsACString& aResult)
+{
+ if (mStringCurItem >= mCount) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aResult = nsDependentCString(mArray[mStringCurItem++]);
+ return NS_OK;
+}
+
+int
+BaseStringEnumerator::SortCallback(const void* aE1, const void* aE2,
+ void* /*unused*/)
+{
+ char const* const* s1 = reinterpret_cast<char const* const*>(aE1);
+ char const* const* s2 = reinterpret_cast<char const* const*>(aE2);
+
+ return strcmp(*s1, *s2);
+}
+
+void
+BaseStringEnumerator::Sort()
+{
+ NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr);
+}
+
+//
+// EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
+//
+class EntryEnumerator
+ : public BaseStringEnumerator
+{
+public:
+ static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
+};
+
+
+EntryEnumerator*
+EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
+{
+ EntryEnumerator* enumObj = new EntryEnumerator();
+ if (!enumObj) {
+ return nullptr;
+ }
+
+ enumObj->mArray = new char const* [aTable.Count()];
+ if (!enumObj->mArray) {
+ delete enumObj;
+ return nullptr;
+ }
+
+ for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
+ CategoryLeaf* leaf = iter.Get();
+ if (leaf->value) {
+ enumObj->mArray[enumObj->mCount++] = leaf->GetKey();
+ }
+ }
+
+ enumObj->Sort();
+
+ return enumObj;
+}
+
+
+//
+// CategoryNode implementations
+//
+
+CategoryNode*
+CategoryNode::Create(PLArenaPool* aArena)
+{
+ return new (aArena) CategoryNode();
+}
+
+CategoryNode::~CategoryNode()
+{
+}
+
+void*
+CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
+{
+ void* p;
+ PL_ARENA_ALLOCATE(p, aArena, aSize);
+ return p;
+}
+
+nsresult
+CategoryNode::GetLeaf(const char* aEntryName,
+ char** aResult)
+{
+ MutexAutoLock lock(mLock);
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+ CategoryLeaf* ent = mTable.GetEntry(aEntryName);
+
+ if (ent && ent->value) {
+ *aResult = NS_strdup(ent->value);
+ if (*aResult) {
+ rv = NS_OK;
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+CategoryNode::AddLeaf(const char* aEntryName,
+ const char* aValue,
+ bool aReplace,
+ char** aResult,
+ PLArenaPool* aArena)
+{
+ if (aResult) {
+ *aResult = nullptr;
+ }
+
+ MutexAutoLock lock(mLock);
+ CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
+
+ if (!leaf) {
+ const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
+ if (!arenaEntryName) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ leaf = mTable.PutEntry(arenaEntryName);
+ if (!leaf) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (leaf->value && !aReplace) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const char* arenaValue = ArenaStrdup(aValue, aArena);
+ if (!arenaValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (aResult && leaf->value) {
+ *aResult = ToNewCString(nsDependentCString(leaf->value));
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ leaf->value = arenaValue;
+ return NS_OK;
+}
+
+void
+CategoryNode::DeleteLeaf(const char* aEntryName)
+{
+ // we don't throw any errors, because it normally doesn't matter
+ // and it makes JS a lot cleaner
+ MutexAutoLock lock(mLock);
+
+ // we can just remove the entire hash entry without introspection
+ mTable.RemoveEntry(aEntryName);
+}
+
+nsresult
+CategoryNode::Enumerate(nsISimpleEnumerator** aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ MutexAutoLock lock(mLock);
+ EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
+
+ if (!enumObj) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ *aResult = enumObj;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+size_t
+CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
+{
+ // We don't measure the strings pointed to by the entries because the
+ // pointers are non-owning.
+ return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+}
+
+//
+// CategoryEnumerator class
+//
+
+class CategoryEnumerator
+ : public BaseStringEnumerator
+{
+public:
+ static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey,
+ CategoryNode>& aTable);
+};
+
+CategoryEnumerator*
+CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>&
+ aTable)
+{
+ CategoryEnumerator* enumObj = new CategoryEnumerator();
+ if (!enumObj) {
+ return nullptr;
+ }
+
+ enumObj->mArray = new const char* [aTable.Count()];
+ if (!enumObj->mArray) {
+ delete enumObj;
+ return nullptr;
+ }
+
+ for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
+ // if a category has no entries, we pretend it doesn't exist
+ CategoryNode* aNode = iter.UserData();
+ if (aNode->Count()) {
+ const char* str = iter.Key();
+ enumObj->mArray[enumObj->mCount++] = str;
+ }
+ }
+
+ return enumObj;
+}
+
+
+//
+// nsCategoryManager implementations
+//
+
+NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsCategoryManager::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsCategoryManager::Release()
+{
+ return 1;
+}
+
+nsCategoryManager* nsCategoryManager::gCategoryManager;
+
+/* static */ nsCategoryManager*
+nsCategoryManager::GetSingleton()
+{
+ if (!gCategoryManager) {
+ gCategoryManager = new nsCategoryManager();
+ }
+ return gCategoryManager;
+}
+
+/* static */ void
+nsCategoryManager::Destroy()
+{
+ // The nsMemoryReporterManager gets destroyed before the nsCategoryManager,
+ // so we don't need to unregister the nsCategoryManager as a memory reporter.
+ // In debug builds we assert that unregistering fails, as a way (imperfect
+ // but better than nothing) of testing the "destroyed before" part.
+ MOZ_ASSERT(NS_FAILED(UnregisterWeakMemoryReporter(gCategoryManager)));
+
+ delete gCategoryManager;
+ gCategoryManager = nullptr;
+}
+
+nsresult
+nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ return GetSingleton()->QueryInterface(aIID, aResult);
+}
+
+nsCategoryManager::nsCategoryManager()
+ : mLock("nsCategoryManager")
+ , mSuppressNotifications(false)
+{
+ PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena",
+ NS_CATEGORYMANAGER_ARENA_SIZE);
+}
+
+void
+nsCategoryManager::InitMemoryReporter()
+{
+ RegisterWeakMemoryReporter(this);
+}
+
+nsCategoryManager::~nsCategoryManager()
+{
+ // the hashtable contains entries that must be deleted before the arena is
+ // destroyed, or else you will have PRLocks undestroyed and other Really
+ // Bad Stuff (TM)
+ mTable.Clear();
+
+ PL_FinishArenaPool(&mArena);
+}
+
+inline CategoryNode*
+nsCategoryManager::get_category(const char* aName)
+{
+ CategoryNode* node;
+ if (!mTable.Get(aName, &node)) {
+ return nullptr;
+ }
+ return node;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
+
+NS_IMETHODIMP
+nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ MOZ_COLLECT_REPORT(
+ "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES,
+ SizeOfIncludingThis(CategoryManagerMallocSizeOf),
+ "Memory used for the XPCOM category manager.");
+
+ return NS_OK;
+}
+
+size_t
+nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+
+ n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf);
+
+ n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
+ // We don't measure the key string because it's a non-owning pointer.
+ n += iter.Data()->SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ return n;
+}
+
+namespace {
+
+class CategoryNotificationRunnable : public Runnable
+{
+public:
+ CategoryNotificationRunnable(nsISupports* aSubject,
+ const char* aTopic,
+ const char* aData)
+ : mSubject(aSubject)
+ , mTopic(aTopic)
+ , mData(aData)
+ {
+ }
+
+ NS_DECL_NSIRUNNABLE
+
+private:
+ nsCOMPtr<nsISupports> mSubject;
+ const char* mTopic;
+ NS_ConvertUTF8toUTF16 mData;
+};
+
+NS_IMETHODIMP
+CategoryNotificationRunnable::Run()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(mSubject, mTopic, mData.get());
+ }
+
+ return NS_OK;
+}
+
+} // namespace
+
+
+void
+nsCategoryManager::NotifyObservers(const char* aTopic,
+ const char* aCategoryName,
+ const char* aEntryName)
+{
+ if (mSuppressNotifications) {
+ return;
+ }
+
+ RefPtr<CategoryNotificationRunnable> r;
+
+ if (aEntryName) {
+ nsCOMPtr<nsISupportsCString> entry =
+ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
+ if (!entry) {
+ return;
+ }
+
+ nsresult rv = entry->SetData(nsDependentCString(aEntryName));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
+ } else {
+ r = new CategoryNotificationRunnable(NS_ISUPPORTS_CAST(nsICategoryManager*,
+ this),
+ aTopic, aCategoryName);
+ }
+
+ NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+nsCategoryManager::GetCategoryEntry(const char* aCategoryName,
+ const char* aEntryName,
+ char** aResult)
+{
+ if (NS_WARN_IF(!aCategoryName) ||
+ NS_WARN_IF(!aEntryName) ||
+ NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult status = NS_ERROR_NOT_AVAILABLE;
+
+ CategoryNode* category;
+ {
+ MutexAutoLock lock(mLock);
+ category = get_category(aCategoryName);
+ }
+
+ if (category) {
+ status = category->GetLeaf(aEntryName, aResult);
+ }
+
+ return status;
+}
+
+NS_IMETHODIMP
+nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
+ const char* aEntryName,
+ const char* aValue,
+ bool aPersist,
+ bool aReplace,
+ char** aResult)
+{
+ if (aPersist) {
+ NS_ERROR("Category manager doesn't support persistence.");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, aResult);
+ return NS_OK;
+}
+
+void
+nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
+ const char* aEntryName,
+ const char* aValue,
+ bool aReplace,
+ char** aOldValue)
+{
+ if (aOldValue) {
+ *aOldValue = nullptr;
+ }
+
+ // Before we can insert a new entry, we'll need to
+ // find the |CategoryNode| to put it in...
+ CategoryNode* category;
+ {
+ MutexAutoLock lock(mLock);
+ category = get_category(aCategoryName);
+
+ if (!category) {
+ // That category doesn't exist yet; let's make it.
+ category = CategoryNode::Create(&mArena);
+
+ char* categoryName = ArenaStrdup(aCategoryName, &mArena);
+ mTable.Put(categoryName, category);
+ }
+ }
+
+ if (!category) {
+ return;
+ }
+
+ // We will need the return value of AddLeaf even if the called doesn't want it
+ char* oldEntry = nullptr;
+
+ nsresult rv = category->AddLeaf(aEntryName,
+ aValue,
+ aReplace,
+ &oldEntry,
+ &mArena);
+
+ if (NS_SUCCEEDED(rv)) {
+ if (oldEntry) {
+ NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
+ aCategoryName, aEntryName);
+ }
+ NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
+ aCategoryName, aEntryName);
+
+ if (aOldValue) {
+ *aOldValue = oldEntry;
+ } else {
+ free(oldEntry);
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsCategoryManager::DeleteCategoryEntry(const char* aCategoryName,
+ const char* aEntryName,
+ bool aDontPersist)
+{
+ if (NS_WARN_IF(!aCategoryName) ||
+ NS_WARN_IF(!aEntryName)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ /*
+ Note: no errors are reported since failure to delete
+ probably won't hurt you, and returning errors seriously
+ inconveniences JS clients
+ */
+
+ CategoryNode* category;
+ {
+ MutexAutoLock lock(mLock);
+ category = get_category(aCategoryName);
+ }
+
+ if (category) {
+ category->DeleteLeaf(aEntryName);
+
+ NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
+ aCategoryName, aEntryName);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCategoryManager::DeleteCategory(const char* aCategoryName)
+{
+ if (NS_WARN_IF(!aCategoryName)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // the categories are arena-allocated, so we don't
+ // actually delete them. We just remove all of the
+ // leaf nodes.
+
+ CategoryNode* category;
+ {
+ MutexAutoLock lock(mLock);
+ category = get_category(aCategoryName);
+ }
+
+ if (category) {
+ category->Clear();
+ NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
+ aCategoryName, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCategoryManager::EnumerateCategory(const char* aCategoryName,
+ nsISimpleEnumerator** aResult)
+{
+ if (NS_WARN_IF(!aCategoryName) ||
+ NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ CategoryNode* category;
+ {
+ MutexAutoLock lock(mLock);
+ category = get_category(aCategoryName);
+ }
+
+ if (!category) {
+ return NS_NewEmptyEnumerator(aResult);
+ }
+
+ return category->Enumerate(aResult);
+}
+
+NS_IMETHODIMP
+nsCategoryManager::EnumerateCategories(nsISimpleEnumerator** aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ MutexAutoLock lock(mLock);
+ CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
+
+ if (!enumObj) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ *aResult = enumObj;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+struct writecat_struct
+{
+ PRFileDesc* fd;
+ bool success;
+};
+
+nsresult
+nsCategoryManager::SuppressNotifications(bool aSuppress)
+{
+ mSuppressNotifications = aSuppress;
+ return NS_OK;
+}
+
+/*
+ * CreateServicesFromCategory()
+ *
+ * Given a category, this convenience functions enumerates the category and
+ * creates a service of every CID or ContractID registered under the category.
+ * If observerTopic is non null and the service implements nsIObserver,
+ * this will attempt to notify the observer with the origin, observerTopic string
+ * as parameter.
+ */
+void
+NS_CreateServicesFromCategory(const char* aCategory,
+ nsISupports* aOrigin,
+ const char* aObserverTopic,
+ const char16_t* aObserverData)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsICategoryManager> categoryManager =
+ do_GetService("@mozilla.org/categorymanager;1");
+ if (!categoryManager) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = categoryManager->EnumerateCategory(aCategory,
+ getter_AddRefs(enumerator));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
+ do_QueryInterface(enumerator);
+ if (!senumerator) {
+ NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
+ return;
+ }
+
+ bool hasMore;
+ while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
+ // From here on just skip any error we get.
+ nsAutoCString entryString;
+ if (NS_FAILED(senumerator->GetNext(entryString))) {
+ continue;
+ }
+
+ nsXPIDLCString contractID;
+ rv = categoryManager->GetCategoryEntry(aCategory, entryString.get(),
+ getter_Copies(contractID));
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ nsCOMPtr<nsISupports> instance = do_GetService(contractID);
+ if (!instance) {
+ LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
+ aCategory, entryString.get(), contractID.get());
+ continue;
+ }
+
+ if (aObserverTopic) {
+ // try an observer, if it implements it.
+ nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance);
+ if (observer) {
+ observer->Observe(aOrigin, aObserverTopic,
+ aObserverData ? aObserverData : u"");
+ } else {
+ LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
+ aCategory, entryString.get(), contractID.get());
+ }
+ }
+ }
+}
diff --git a/xpcom/components/nsCategoryManager.h b/xpcom/components/nsCategoryManager.h
new file mode 100644
index 000000000..3a4faed72
--- /dev/null
+++ b/xpcom/components/nsCategoryManager.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef NSCATEGORYMANAGER_H
+#define NSCATEGORYMANAGER_H
+
+#include "prio.h"
+#include "plarena.h"
+#include "nsClassHashtable.h"
+#include "nsICategoryManager.h"
+#include "nsIMemoryReporter.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+
+class nsIMemoryReporter;
+
+/* 16d222a6-1dd2-11b2-b693-f38b02c021b2 */
+#define NS_CATEGORYMANAGER_CID \
+{ 0x16d222a6, 0x1dd2, 0x11b2, \
+ {0xb6, 0x93, 0xf3, 0x8b, 0x02, 0xc0, 0x21, 0xb2} }
+
+/**
+ * a "leaf-node", managed by the nsCategoryNode hashtable.
+ *
+ * we need to keep a "persistent value" (which will be written to the registry)
+ * and a non-persistent value (for the current runtime): these are usually
+ * the same, except when aPersist==false. The strings are permanently arena-
+ * allocated, and will never go away.
+ */
+class CategoryLeaf : public nsDepCharHashKey
+{
+public:
+ explicit CategoryLeaf(const char* aKey) : nsDepCharHashKey(aKey), value(nullptr) {}
+ const char* value;
+};
+
+
+/**
+ * CategoryNode keeps a hashtable of its entries.
+ * the CategoryNode itself is permanently allocated in
+ * the arena.
+ */
+class CategoryNode
+{
+public:
+ nsresult GetLeaf(const char* aEntryName,
+ char** aResult);
+
+ nsresult AddLeaf(const char* aEntryName,
+ const char* aValue,
+ bool aReplace,
+ char** aResult,
+ PLArenaPool* aArena);
+
+ void DeleteLeaf(const char* aEntryName);
+
+ void Clear()
+ {
+ mozilla::MutexAutoLock lock(mLock);
+ mTable.Clear();
+ }
+
+ uint32_t Count()
+ {
+ mozilla::MutexAutoLock lock(mLock);
+ uint32_t tCount = mTable.Count();
+ return tCount;
+ }
+
+ nsresult Enumerate(nsISimpleEnumerator** aResult);
+
+ // CategoryNode is arena-allocated, with the strings
+ static CategoryNode* Create(PLArenaPool* aArena);
+ ~CategoryNode();
+ void operator delete(void*) {}
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+private:
+ CategoryNode() : mLock("CategoryLeaf") {}
+
+ void* operator new(size_t aSize, PLArenaPool* aArena);
+
+ nsTHashtable<CategoryLeaf> mTable;
+ mozilla::Mutex mLock;
+};
+
+
+/**
+ * The main implementation of nsICategoryManager.
+ *
+ * This implementation is thread-safe.
+ */
+class nsCategoryManager final
+ : public nsICategoryManager
+ , public nsIMemoryReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICATEGORYMANAGER
+ NS_DECL_NSIMEMORYREPORTER
+
+ /**
+ * Suppress or unsuppress notifications of category changes to the
+ * observer service. This is to be used by nsComponentManagerImpl
+ * on startup while reading the stored category list.
+ */
+ nsresult SuppressNotifications(bool aSuppress);
+
+ void AddCategoryEntry(const char* aCategory,
+ const char* aKey,
+ const char* aValue,
+ bool aReplace = true,
+ char** aOldValue = nullptr);
+
+ static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+ void InitMemoryReporter();
+
+ static nsCategoryManager* GetSingleton();
+ static void Destroy();
+
+private:
+ static nsCategoryManager* gCategoryManager;
+
+ nsCategoryManager();
+ ~nsCategoryManager();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ CategoryNode* get_category(const char* aName);
+ void NotifyObservers(const char* aTopic,
+ const char* aCategoryName, // must be a static string
+ const char* aEntryName);
+
+ PLArenaPool mArena;
+ nsClassHashtable<nsDepCharHashKey, CategoryNode> mTable;
+ mozilla::Mutex mLock;
+ bool mSuppressNotifications;
+};
+
+#endif
diff --git a/xpcom/components/nsCategoryManagerUtils.h b/xpcom/components/nsCategoryManagerUtils.h
new file mode 100644
index 000000000..a83d097c2
--- /dev/null
+++ b/xpcom/components/nsCategoryManagerUtils.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCategoryManagerUtils_h__
+#define nsCategoryManagerUtils_h__
+
+#include "nsICategoryManager.h"
+
+void
+NS_CreateServicesFromCategory(const char* aCategory,
+ nsISupports* aOrigin,
+ const char* aObserverTopic,
+ const char16_t* aObserverData = nullptr);
+
+#endif
diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp
new file mode 100644
index 000000000..b9eb8e275
--- /dev/null
+++ b/xpcom/components/nsComponentManager.cpp
@@ -0,0 +1,2083 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
+ */
+
+#include <stdlib.h>
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nspr.h"
+#include "nsCRT.h" // for atoll
+
+// Arena used by component manager for storing contractid string, dll
+// location strings and small objects
+// CAUTION: Arena align mask needs to be defined before including plarena.h
+// currently from nsComponentManager.h
+#define PL_ARENA_CONST_ALIGN_MASK 7
+#define NS_CM_BLOCK_SIZE (1024 * 8)
+
+#include "nsCategoryManager.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManager.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "xptiprivate.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+#include "nsIConsoleService.h"
+#include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMPrivate.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIClassInfo.h"
+#include "nsLocalFile.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "prcmon.h"
+#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself...
+#include "nsThreadUtils.h"
+#include "prthread.h"
+#include "private/pprthred.h"
+#include "nsTArray.h"
+#include "prio.h"
+#include "ManifestParser.h"
+#include "nsNetUtil.h"
+#include "mozilla/Services.h"
+
+#include "mozilla/GenericFactory.h"
+#include "nsSupportsPrimitives.h"
+#include "nsArray.h"
+#include "nsIMutableArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsStringEnumerator.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "nsDataHashtable.h"
+
+#include <new> // for placement new
+
+#include "mozilla/Omnijar.h"
+
+#include "mozilla/Logging.h"
+#include "LogModulePrefWatcher.h"
+
+using namespace mozilla;
+
+static LazyLogModule nsComponentManagerLog("nsComponentManager");
+
+#if 0 || defined (DEBUG_timeless)
+ #define SHOW_DENIED_ON_SHUTDOWN
+ #define SHOW_CI_ON_EXISTING_SERVICE
+#endif
+
+// Bloated registry buffer size to improve startup performance -- needs to
+// be big enough to fit the entire file into memory or it'll thrash.
+// 512K is big enough to allow for some future growth in the registry.
+#define BIG_REGISTRY_BUFLEN (512*1024)
+
+// Common Key Names
+const char xpcomComponentsKeyName[] = "software/mozilla/XPCOM/components";
+const char xpcomKeyName[] = "software/mozilla/XPCOM";
+
+// Common Value Names
+const char fileSizeValueName[] = "FileSize";
+const char lastModValueName[] = "LastModTimeStamp";
+const char nativeComponentType[] = "application/x-mozilla-native";
+const char staticComponentType[] = "application/x-mozilla-static";
+
+NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID);
+
+#define UID_STRING_LENGTH 39
+
+nsresult
+nsGetServiceFromCategory::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult rv;
+ nsXPIDLCString value;
+ nsCOMPtr<nsICategoryManager> catman;
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (!compMgr) {
+ rv = NS_ERROR_NOT_INITIALIZED;
+ goto error;
+ }
+
+ if (!mCategory || !mEntry) {
+ // when categories have defaults, use that for null mEntry
+ rv = NS_ERROR_NULL_POINTER;
+ goto error;
+ }
+
+ rv = compMgr->nsComponentManagerImpl::GetService(kCategoryManagerCID,
+ NS_GET_IID(nsICategoryManager),
+ getter_AddRefs(catman));
+ if (NS_FAILED(rv)) {
+ goto error;
+ }
+
+ /* find the contractID for category.entry */
+ rv = catman->GetCategoryEntry(mCategory, mEntry,
+ getter_Copies(value));
+ if (NS_FAILED(rv)) {
+ goto error;
+ }
+ if (!value) {
+ rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ goto error;
+ }
+
+ rv = compMgr->nsComponentManagerImpl::GetServiceByContractID(value,
+ aIID,
+ aInstancePtr);
+ if (NS_FAILED(rv)) {
+error:
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = rv;
+ }
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Arena helper functions
+////////////////////////////////////////////////////////////////////////////////
+char*
+ArenaStrndup(const char* aStr, uint32_t aLen, PLArenaPool* aArena)
+{
+ void* mem;
+ // Include trailing null in the aLen
+ PL_ARENA_ALLOCATE(mem, aArena, aLen + 1);
+ if (mem) {
+ memcpy(mem, aStr, aLen + 1);
+ }
+ return static_cast<char*>(mem);
+}
+
+char*
+ArenaStrdup(const char* aStr, PLArenaPool* aArena)
+{
+ return ArenaStrndup(aStr, strlen(aStr), aArena);
+}
+
+// GetService and a few other functions need to exit their mutex mid-function
+// without reentering it later in the block. This class supports that
+// style of early-exit that MutexAutoUnlock doesn't.
+
+namespace {
+
+class MOZ_STACK_CLASS MutexLock
+{
+public:
+ explicit MutexLock(SafeMutex& aMutex)
+ : mMutex(aMutex)
+ , mLocked(false)
+ {
+ Lock();
+ }
+
+ ~MutexLock()
+ {
+ if (mLocked) {
+ Unlock();
+ }
+ }
+
+ void Lock()
+ {
+ NS_ASSERTION(!mLocked, "Re-entering a mutex");
+ mMutex.Lock();
+ mLocked = true;
+ }
+
+ void Unlock()
+ {
+ NS_ASSERTION(mLocked, "Exiting a mutex that isn't held!");
+ mMutex.Unlock();
+ mLocked = false;
+ }
+
+private:
+ SafeMutex& mMutex;
+ bool mLocked;
+};
+
+} // namespace
+
+// this is safe to call during InitXPCOM
+static already_AddRefed<nsIFile>
+GetLocationFromDirectoryService(const char* aProp)
+{
+ nsCOMPtr<nsIProperties> directoryService;
+ nsDirectoryService::Create(nullptr,
+ NS_GET_IID(nsIProperties),
+ getter_AddRefs(directoryService));
+
+ if (!directoryService) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = directoryService->Get(aProp,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ return file.forget();
+}
+
+static already_AddRefed<nsIFile>
+CloneAndAppend(nsIFile* aBase, const nsACString& aAppend)
+{
+ nsCOMPtr<nsIFile> f;
+ aBase->Clone(getter_AddRefs(f));
+ if (!f) {
+ return nullptr;
+ }
+
+ f->AppendNative(aAppend);
+ return f.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsComponentManagerImpl
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ if (!gComponentManager) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return gComponentManager->QueryInterface(aIID, aResult);
+}
+
+static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 1024;
+
+nsComponentManagerImpl::nsComponentManagerImpl()
+ : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH)
+ , mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH)
+ , mLock("nsComponentManagerImpl.mLock")
+ , mStatus(NOT_INITIALIZED)
+{
+}
+
+nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules;
+
+NSMODULE_DEFN(start_kPStaticModules);
+NSMODULE_DEFN(end_kPStaticModules);
+
+/* The content between start_kPStaticModules and end_kPStaticModules is gathered
+ * by the linker from various objects containing symbols in a specific section.
+ * ASAN considers (rightfully) the use of this content as a global buffer
+ * overflow. But this is a deliberate and well-considered choice, with no proper
+ * way to make ASAN happy. */
+MOZ_ASAN_BLACKLIST
+/* static */ void
+nsComponentManagerImpl::InitializeStaticModules()
+{
+ if (sStaticModules) {
+ return;
+ }
+
+ sStaticModules = new nsTArray<const mozilla::Module*>;
+ for (const mozilla::Module * const* staticModules =
+ &NSMODULE_NAME(start_kPStaticModules) + 1;
+ staticModules < &NSMODULE_NAME(end_kPStaticModules); ++staticModules)
+ if (*staticModules) { // ASAN adds padding
+ sStaticModules->AppendElement(*staticModules);
+ }
+}
+
+nsTArray<nsComponentManagerImpl::ComponentLocation>*
+nsComponentManagerImpl::sModuleLocations;
+
+/* static */ void
+nsComponentManagerImpl::InitializeModuleLocations()
+{
+ if (sModuleLocations) {
+ return;
+ }
+
+ sModuleLocations = new nsTArray<ComponentLocation>;
+}
+
+nsresult
+nsComponentManagerImpl::Init()
+{
+ MOZ_ASSERT(NOT_INITIALIZED == mStatus);
+
+ // Initialize our arena
+ PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE);
+
+ nsCOMPtr<nsIFile> greDir =
+ GetLocationFromDirectoryService(NS_GRE_DIR);
+ nsCOMPtr<nsIFile> appDir =
+ GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
+
+ InitializeStaticModules();
+
+ nsresult rv = mNativeModuleLoader.Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(true);
+
+ RegisterModule(&kXPCOMModule, nullptr);
+
+ for (uint32_t i = 0; i < sStaticModules->Length(); ++i) {
+ RegisterModule((*sStaticModules)[i], nullptr);
+ }
+
+ bool loadChromeManifests = (XRE_GetProcessType() != GeckoProcessType_GPU);
+ if (loadChromeManifests) {
+ // The overall order in which chrome.manifests are expected to be treated
+ // is the following:
+ // - greDir
+ // - greDir's omni.ja
+ // - appDir
+ // - appDir's omni.ja
+
+ InitializeModuleLocations();
+ ComponentLocation* cl = sModuleLocations->AppendElement();
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir,
+ NS_LITERAL_CSTRING("chrome.manifest"));
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(lf);
+
+ RefPtr<nsZipArchive> greOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
+ if (greOmnijar) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(greOmnijar, "chrome.manifest");
+ }
+
+ bool equals = false;
+ appDir->Equals(greDir, &equals);
+ if (!equals) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ lf = CloneAndAppend(appDir, NS_LITERAL_CSTRING("chrome.manifest"));
+ cl->location.Init(lf);
+ }
+
+ RefPtr<nsZipArchive> appOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
+ if (appOmnijar) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(appOmnijar, "chrome.manifest");
+ }
+
+ RereadChromeManifests(false);
+ }
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(false);
+
+ RegisterWeakMemoryReporter(this);
+
+ // NB: The logging preference watcher needs to be registered late enough in
+ // startup that it's okay to use the preference system, but also as soon as
+ // possible so that log modules enabled via preferences are turned on as
+ // early as possible.
+ //
+ // We can't initialize the preference watcher when the log module manager is
+ // initialized, as a number of things attempt to start logging before the
+ // preference system is initialized.
+ //
+ // The preference system is registered as a component so at this point during
+ // component manager initialization we know it is setup and we can register
+ // for notifications.
+ LogModulePrefWatcher::RegisterPrefWatcher();
+
+ // Unfortunately, we can't register the nsCategoryManager memory reporter
+ // in its constructor (which is triggered by the GetSingleton() call
+ // above) because the memory reporter manager isn't initialized at that
+ // point. So we wait until now.
+ nsCategoryManager::GetSingleton()->InitMemoryReporter();
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Initialized."));
+
+ mStatus = NORMAL;
+
+ return NS_OK;
+}
+
+static bool
+ProcessSelectorMatches(Module::ProcessSelector aSelector)
+{
+ GeckoProcessType type = XRE_GetProcessType();
+ if (type == GeckoProcessType_GPU) {
+ return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS);
+ }
+
+ if (aSelector & Module::MAIN_PROCESS_ONLY) {
+ return type == GeckoProcessType_Default;
+ }
+ if (aSelector & Module::CONTENT_PROCESS_ONLY) {
+ return type == GeckoProcessType_Content;
+ }
+ return true;
+}
+
+static const int kModuleVersionWithSelector = 51;
+
+void
+nsComponentManagerImpl::RegisterModule(const mozilla::Module* aModule,
+ FileLocation* aFile)
+{
+ mLock.AssertNotCurrentThreadOwns();
+
+ if (aModule->mVersion >= kModuleVersionWithSelector &&
+ !ProcessSelectorMatches(aModule->selector))
+ {
+ return;
+ }
+
+ {
+ // Scope the monitor so that we don't hold it while calling into the
+ // category manager.
+ MutexLock lock(mLock);
+
+ KnownModule* m;
+ if (aFile) {
+ nsCString uri;
+ aFile->GetURIString(uri);
+ NS_ASSERTION(!mKnownModules.Get(uri),
+ "Must not register a binary module twice.");
+
+ m = new KnownModule(aModule, *aFile);
+ mKnownModules.Put(uri, m);
+ } else {
+ m = new KnownModule(aModule);
+ mKnownStaticModules.AppendElement(m);
+ }
+
+ if (aModule->mCIDs) {
+ const mozilla::Module::CIDEntry* entry;
+ for (entry = aModule->mCIDs; entry->cid; ++entry) {
+ RegisterCIDEntryLocked(entry, m);
+ }
+ }
+
+ if (aModule->mContractIDs) {
+ const mozilla::Module::ContractIDEntry* entry;
+ for (entry = aModule->mContractIDs; entry->contractid; ++entry) {
+ RegisterContractIDLocked(entry);
+ }
+ MOZ_ASSERT(!entry->cid, "Incorrectly terminated contract list");
+ }
+ }
+
+ if (aModule->mCategoryEntries) {
+ const mozilla::Module::CategoryEntry* entry;
+ for (entry = aModule->mCategoryEntries; entry->category; ++entry)
+ nsCategoryManager::GetSingleton()->AddCategoryEntry(entry->category,
+ entry->entry,
+ entry->value);
+ }
+}
+
+void
+nsComponentManagerImpl::RegisterCIDEntryLocked(
+ const mozilla::Module::CIDEntry* aEntry,
+ KnownModule* aModule)
+{
+ mLock.AssertCurrentThreadOwns();
+
+ if (!ProcessSelectorMatches(aEntry->processSelector)) {
+ return;
+ }
+
+ nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
+ if (f) {
+ NS_WARNING("Re-registering a CID?");
+
+ char idstr[NSID_LENGTH];
+ aEntry->cid->ToProvidedString(idstr);
+
+ nsCString existing;
+ if (f->mModule) {
+ existing = f->mModule->Description();
+ } else {
+ existing = "<unknown module>";
+ }
+ SafeMutexAutoUnlock unlock(mLock);
+ LogMessage("While registering XPCOM module %s, trying to re-register CID '%s' already registered by %s.",
+ aModule->Description().get(),
+ idstr,
+ existing.get());
+ return;
+ }
+
+ f = new nsFactoryEntry(aEntry, aModule);
+ mFactories.Put(*aEntry->cid, f);
+}
+
+void
+nsComponentManagerImpl::RegisterContractIDLocked(
+ const mozilla::Module::ContractIDEntry* aEntry)
+{
+ mLock.AssertCurrentThreadOwns();
+
+ if (!ProcessSelectorMatches(aEntry->processSelector)) {
+ return;
+ }
+
+ nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
+ if (!f) {
+ NS_WARNING("No CID found when attempting to map contract ID");
+
+ char idstr[NSID_LENGTH];
+ aEntry->cid->ToProvidedString(idstr);
+
+ SafeMutexAutoUnlock unlock(mLock);
+ LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
+ aEntry->contractid,
+ idstr);
+
+ return;
+ }
+
+ mContractIDs.Put(nsDependentCString(aEntry->contractid), f);
+}
+
+static void
+CutExtension(nsCString& aPath)
+{
+ int32_t dotPos = aPath.RFindChar('.');
+ if (kNotFound == dotPos) {
+ aPath.Truncate();
+ } else {
+ aPath.Cut(0, dotPos + 1);
+ }
+}
+
+static void
+DoRegisterManifest(NSLocationType aType,
+ FileLocation& aFile,
+ bool aChromeOnly,
+ bool aXPTOnly)
+{
+ MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager);
+ uint32_t len;
+ FileLocation::Data data;
+ UniquePtr<char[]> buf;
+ nsresult rv = aFile.GetData(data);
+ if (NS_SUCCEEDED(rv)) {
+ rv = data.GetSize(&len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ buf = MakeUnique<char[]>(len + 1);
+ rv = data.Copy(buf.get(), len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ buf[len] = '\0';
+ ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly);
+ } else if (NS_BOOTSTRAPPED_LOCATION != aType) {
+ nsCString uri;
+ aFile.GetURIString(uri);
+ LogMessage("Could not read chrome manifest '%s'.", uri.get());
+ }
+}
+
+void
+nsComponentManagerImpl::RegisterManifest(NSLocationType aType,
+ FileLocation& aFile,
+ bool aChromeOnly)
+{
+ DoRegisterManifest(aType, aFile, aChromeOnly, false);
+}
+
+void
+nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ char* file = aArgv[0];
+ FileLocation f(aCx.mFile, file);
+ RegisterManifest(aCx.mType, f, aCx.mChromeOnly);
+}
+
+void
+nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& aCx,
+ int aLineNo,
+ char* const* aArgv)
+{
+ if (aCx.mFile.IsZip()) {
+ NS_WARNING("Cannot load binary components from a jar.");
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Cannot load binary components from a jar.");
+ return;
+ }
+
+ FileLocation f(aCx.mFile, aArgv[0]);
+ nsCString uri;
+ f.GetURIString(uri);
+
+ if (mKnownModules.Get(uri)) {
+ NS_WARNING("Attempting to register a binary component twice.");
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Attempting to register a binary component twice.");
+ return;
+ }
+
+ const mozilla::Module* m = mNativeModuleLoader.LoadModule(f);
+ // The native module loader should report an error here, we don't have to
+ if (!m) {
+ return;
+ }
+
+ RegisterModule(m, &f);
+}
+
+static void
+DoRegisterXPT(FileLocation& aFile)
+{
+ uint32_t len;
+ FileLocation::Data data;
+ UniquePtr<char[]> buf;
+ nsresult rv = aFile.GetData(data);
+ if (NS_SUCCEEDED(rv)) {
+ rv = data.GetSize(&len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ buf = MakeUnique<char[]>(len);
+ rv = data.Copy(buf.get(), len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf.get(), len);
+ } else {
+ nsCString uri;
+ aFile.GetURIString(uri);
+ LogMessage("Could not read '%s'.", uri.get());
+ }
+}
+
+void
+nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ FileLocation f(aCx.mFile, aArgv[0]);
+ DoRegisterXPT(f);
+}
+
+void
+nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ mLock.AssertNotCurrentThreadOwns();
+
+ char* id = aArgv[0];
+ char* file = aArgv[1];
+
+ nsID cid;
+ if (!cid.Parse(id)) {
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Malformed CID: '%s'.", id);
+ return;
+ }
+
+ // Precompute the hash/file data outside of the lock
+ FileLocation fl(aCx.mFile, file);
+ nsCString hash;
+ fl.GetURIString(hash);
+
+ MutexLock lock(mLock);
+ nsFactoryEntry* f = mFactories.Get(cid);
+ if (f) {
+ char idstr[NSID_LENGTH];
+ cid.ToProvidedString(idstr);
+
+ nsCString existing;
+ if (f->mModule) {
+ existing = f->mModule->Description();
+ } else {
+ existing = "<unknown module>";
+ }
+
+ lock.Unlock();
+
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Trying to re-register CID '%s' already registered by %s.",
+ idstr,
+ existing.get());
+ return;
+ }
+
+ KnownModule* km;
+
+ km = mKnownModules.Get(hash);
+ if (!km) {
+ km = new KnownModule(fl);
+ mKnownModules.Put(hash, km);
+ }
+
+ void* place;
+
+ PL_ARENA_ALLOCATE(place, &mArena, sizeof(nsCID));
+ nsID* permanentCID = static_cast<nsID*>(place);
+ *permanentCID = cid;
+
+ PL_ARENA_ALLOCATE(place, &mArena, sizeof(mozilla::Module::CIDEntry));
+ mozilla::Module::CIDEntry* e = new (place) mozilla::Module::CIDEntry();
+ e->cid = permanentCID;
+
+ f = new nsFactoryEntry(e, km);
+ mFactories.Put(cid, f);
+}
+
+void
+nsComponentManagerImpl::ManifestContract(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ mLock.AssertNotCurrentThreadOwns();
+
+ char* contract = aArgv[0];
+ char* id = aArgv[1];
+
+ nsID cid;
+ if (!cid.Parse(id)) {
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Malformed CID: '%s'.", id);
+ return;
+ }
+
+ MutexLock lock(mLock);
+ nsFactoryEntry* f = mFactories.Get(cid);
+ if (!f) {
+ lock.Unlock();
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
+ contract, id);
+ return;
+ }
+
+ mContractIDs.Put(nsDependentCString(contract), f);
+}
+
+void
+nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ char* category = aArgv[0];
+ char* key = aArgv[1];
+ char* value = aArgv[2];
+
+ nsCategoryManager::GetSingleton()->
+ AddCategoryEntry(category, key, value);
+}
+
+void
+nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly)
+{
+ for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
+ ComponentLocation& l = sModuleLocations->ElementAt(i);
+ RegisterManifest(l.type, l.location, aChromeOnly);
+ }
+}
+
+bool
+nsComponentManagerImpl::KnownModule::EnsureLoader()
+{
+ if (!mLoader) {
+ nsCString extension;
+ mFile.GetURIString(extension);
+ CutExtension(extension);
+ mLoader =
+ nsComponentManagerImpl::gComponentManager->LoaderForExtension(extension);
+ }
+ return !!mLoader;
+}
+
+bool
+nsComponentManagerImpl::KnownModule::Load()
+{
+ if (mFailed) {
+ return false;
+ }
+ if (!mModule) {
+ if (!EnsureLoader()) {
+ return false;
+ }
+
+ mModule = mLoader->LoadModule(mFile);
+
+ if (!mModule) {
+ mFailed = true;
+ return false;
+ }
+ }
+ if (!mLoaded) {
+ if (mModule->loadProc) {
+ nsresult rv = mModule->loadProc();
+ if (NS_FAILED(rv)) {
+ mFailed = true;
+ return false;
+ }
+ }
+ mLoaded = true;
+ }
+ return true;
+}
+
+nsCString
+nsComponentManagerImpl::KnownModule::Description() const
+{
+ nsCString s;
+ if (mFile) {
+ mFile.GetURIString(s);
+ } else {
+ s = "<static module>";
+ }
+ return s;
+}
+
+nsresult nsComponentManagerImpl::Shutdown(void)
+{
+ MOZ_ASSERT(NORMAL == mStatus);
+
+ mStatus = SHUTDOWN_IN_PROGRESS;
+
+ // Shutdown the component manager
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Beginning Shutdown."));
+
+ UnregisterWeakMemoryReporter(this);
+
+ // Release all cached factories
+ mContractIDs.Clear();
+ mFactories.Clear(); // XXX release the objects, don't just clear
+ mLoaderMap.Clear();
+ mKnownModules.Clear();
+ mKnownStaticModules.Clear();
+
+ delete sStaticModules;
+ delete sModuleLocations;
+
+ // Unload libraries
+ mNativeModuleLoader.UnloadLibraries();
+
+ // delete arena for strings and small objects
+ PL_FinishArenaPool(&mArena);
+
+ mStatus = SHUTDOWN_COMPLETE;
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Shutdown complete."));
+
+ return NS_OK;
+}
+
+nsComponentManagerImpl::~nsComponentManagerImpl()
+{
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Beginning destruction."));
+
+ if (SHUTDOWN_COMPLETE != mStatus) {
+ Shutdown();
+ }
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Destroyed."));
+}
+
+NS_IMPL_ISUPPORTS(nsComponentManagerImpl,
+ nsIComponentManager,
+ nsIServiceManager,
+ nsIComponentRegistrar,
+ nsISupportsWeakReference,
+ nsIInterfaceRequestor,
+ nsIMemoryReporter)
+
+nsresult
+nsComponentManagerImpl::GetInterface(const nsIID& aUuid, void** aResult)
+{
+ NS_WARNING("This isn't supported");
+ // fall through to QI as anything QIable is a superset of what can be
+ // got via the GetInterface()
+ return QueryInterface(aUuid, aResult);
+}
+
+nsFactoryEntry*
+nsComponentManagerImpl::GetFactoryEntry(const char* aContractID,
+ uint32_t aContractIDLen)
+{
+ SafeMutexAutoLock lock(mLock);
+ return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen));
+}
+
+
+nsFactoryEntry*
+nsComponentManagerImpl::GetFactoryEntry(const nsCID& aClass)
+{
+ SafeMutexAutoLock lock(mLock);
+ return mFactories.Get(aClass);
+}
+
+already_AddRefed<nsIFactory>
+nsComponentManagerImpl::FindFactory(const nsCID& aClass)
+{
+ nsFactoryEntry* e = GetFactoryEntry(aClass);
+ if (!e) {
+ return nullptr;
+ }
+
+ return e->GetFactory();
+}
+
+already_AddRefed<nsIFactory>
+nsComponentManagerImpl::FindFactory(const char* aContractID,
+ uint32_t aContractIDLen)
+{
+ nsFactoryEntry* entry = GetFactoryEntry(aContractID, aContractIDLen);
+ if (!entry) {
+ return nullptr;
+ }
+
+ return entry->GetFactory();
+}
+
+/**
+ * GetClassObject()
+ *
+ * Given a classID, this finds the singleton ClassObject that implements the CID.
+ * Returns an interface of type aIID off the singleton classobject.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID,
+ void** aResult)
+{
+ nsresult rv;
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Debug)) {
+ char* buf = aClass.ToString();
+ PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf);
+ if (buf) {
+ free(buf);
+ }
+ }
+
+ MOZ_ASSERT(aResult != nullptr);
+
+ nsCOMPtr<nsIFactory> factory = FindFactory(aClass);
+ if (!factory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ rv = factory->QueryInterface(aIID, aResult);
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetClassObjectByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult)
+{
+ if (NS_WARN_IF(!aResult) ||
+ NS_WARN_IF(!aContractID)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: GetClassObject(%s)", aContractID));
+
+ nsCOMPtr<nsIFactory> factory = FindFactory(aContractID, strlen(aContractID));
+ if (!factory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ rv = factory->QueryInterface(aIID, aResult);
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+/**
+ * CreateInstance()
+ *
+ * Create an instance of an object that implements an interface and belongs
+ * to the implementation aClass using the factory. The factory is immediately
+ * released and not held onto for any longer.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::CreateInstance(const nsCID& aClass,
+ nsISupports* aDelegate,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // test this first, since there's no point in creating a component during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString cid, iid;
+ cid.Adopt(aClass.ToString());
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Creating new instance on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n", cid.get(), iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ nsFactoryEntry* entry = GetFactoryEntry(aClass);
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->mServiceObject) {
+ nsXPIDLCString cid;
+ cid.Adopt(aClass.ToString());
+ nsAutoCString message;
+ message = NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
+ cid +
+ NS_LITERAL_CSTRING("\" when a service for this CID already exists!");
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+ rv = factory->CreateInstance(aDelegate, aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_FOUND;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Warning)) {
+ char* buf = aClass.ToString();
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstance(%s) %s", buf,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+ if (buf) {
+ free(buf);
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * CreateInstanceByContractID()
+ *
+ * A variant of CreateInstance() that creates an instance of the object that
+ * implements the interface aIID and whose implementation has a contractID aContractID.
+ *
+ * This is only a convenience routine that turns around can calls the
+ * CreateInstance() with classid and iid.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID,
+ nsISupports* aDelegate,
+ const nsIID& aIID,
+ void** aResult)
+{
+ if (NS_WARN_IF(!aContractID)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // test this first, since there's no point in creating a component during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString iid;
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Creating new instance on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n", aContractID, iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ nsFactoryEntry* entry = GetFactoryEntry(aContractID, strlen(aContractID));
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->mServiceObject) {
+ nsAutoCString message;
+ message =
+ NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
+ nsDependentCString(aContractID) +
+ NS_LITERAL_CSTRING("\" when a service for this CID already exists! "
+ "Add it to abusedContracts to track down the service consumer.");
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+
+ rv = factory->CreateInstance(aDelegate, aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_FOUND;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+nsresult
+nsComponentManagerImpl::FreeServices()
+{
+ NS_ASSERTION(gXPCOMShuttingDown,
+ "Must be shutting down in order to free all services");
+
+ if (!gXPCOMShuttingDown) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
+ nsFactoryEntry* entry = iter.UserData();
+ entry->mFactory = nullptr;
+ entry->mServiceObject = nullptr;
+ }
+
+ return NS_OK;
+}
+
+// This should only ever be called within the monitor!
+nsComponentManagerImpl::PendingServiceInfo*
+nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID,
+ PRThread* aThread)
+{
+ PendingServiceInfo* newInfo = mPendingServices.AppendElement();
+ if (newInfo) {
+ newInfo->cid = &aServiceCID;
+ newInfo->thread = aThread;
+ }
+ return newInfo;
+}
+
+// This should only ever be called within the monitor!
+void
+nsComponentManagerImpl::RemovePendingService(const nsCID& aServiceCID)
+{
+ uint32_t pendingCount = mPendingServices.Length();
+ for (uint32_t index = 0; index < pendingCount; ++index) {
+ const PendingServiceInfo& info = mPendingServices.ElementAt(index);
+ if (info.cid->Equals(aServiceCID)) {
+ mPendingServices.RemoveElementAt(index);
+ return;
+ }
+ }
+}
+
+// This should only ever be called within the monitor!
+PRThread*
+nsComponentManagerImpl::GetPendingServiceThread(const nsCID& aServiceCID) const
+{
+ uint32_t pendingCount = mPendingServices.Length();
+ for (uint32_t index = 0; index < pendingCount; ++index) {
+ const PendingServiceInfo& info = mPendingServices.ElementAt(index);
+ if (info.cid->Equals(aServiceCID)) {
+ return info.thread;
+ }
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetService(const nsCID& aClass,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString cid, iid;
+ cid.Adopt(aClass.ToString());
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Getting service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n", cid.get(), iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // `service` must be released after the lock is released, so it must be
+ // declared before the lock in this C++ block.
+ nsCOMPtr<nsISupports> service;
+ MutexLock lock(mLock);
+
+ nsFactoryEntry* entry = mFactories.Get(aClass);
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (entry->mServiceObject) {
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+ PRThread* currentPRThread = PR_GetCurrentThread();
+ MOZ_ASSERT(currentPRThread, "This should never be null!");
+
+ // Needed to optimize the event loop below.
+ nsIThread* currentThread = nullptr;
+
+ PRThread* pendingPRThread;
+ while ((pendingPRThread = GetPendingServiceThread(aClass))) {
+ if (pendingPRThread == currentPRThread) {
+ NS_ERROR("Recursive GetService!");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+
+ SafeMutexAutoUnlock unlockPending(mLock);
+
+ if (!currentThread) {
+ currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread, "This should never be null!");
+ }
+
+ // This will process a single event or yield the thread if no event is
+ // pending.
+ if (!NS_ProcessNextEvent(currentThread, false)) {
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+ }
+ }
+
+ // It's still possible that the other thread failed to create the
+ // service so we're not guaranteed to have an entry or service yet.
+ if (entry->mServiceObject) {
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+#ifdef DEBUG
+ PendingServiceInfo* newInfo =
+#endif
+ AddPendingService(aClass, currentPRThread);
+ NS_ASSERTION(newInfo, "Failed to add info to the array!");
+
+ // We need to not be holding the service manager's lock while calling
+ // CreateInstance, because it invokes user code which could try to re-enter
+ // the service manager:
+
+ nsresult rv;
+ {
+ SafeMutexAutoUnlock unlock(mLock);
+ rv = CreateInstance(aClass, nullptr, aIID, getter_AddRefs(service));
+ }
+ if (NS_SUCCEEDED(rv) && !service) {
+ NS_ERROR("Factory did not return an object but returned success");
+ return NS_ERROR_SERVICE_NOT_FOUND;
+ }
+
+#ifdef DEBUG
+ pendingPRThread = GetPendingServiceThread(aClass);
+ MOZ_ASSERT(pendingPRThread == currentPRThread,
+ "Pending service array has been changed!");
+#endif
+ RemovePendingService(aClass);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!");
+
+ entry->mServiceObject = service.forget();
+
+ lock.Unlock();
+ nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult);
+ *sresult = entry->mServiceObject;
+ (*sresult)->AddRef();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass,
+ const nsIID& aIID,
+ bool* aResult)
+{
+ // Now we want to get the service if we already got it. If not, we don't want
+ // to create an instance of it. mmh!
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString cid, iid;
+ cid.Adopt(aClass.ToString());
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Checking for service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n", cid.get(), iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ nsFactoryEntry* entry;
+
+ {
+ SafeMutexAutoLock lock(mLock);
+ entry = mFactories.Get(aClass);
+ }
+
+ if (entry && entry->mServiceObject) {
+ nsCOMPtr<nsISupports> service;
+ rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
+ *aResult = (service != nullptr);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsServiceInstantiatedByContractID(
+ const char* aContractID,
+ const nsIID& aIID,
+ bool* aResult)
+{
+ // Now we want to get the service if we already got it. If not, we don't want
+ // to create an instance of it. mmh!
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString iid;
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Checking for service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n", aContractID, iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ nsFactoryEntry* entry;
+ {
+ SafeMutexAutoLock lock(mLock);
+ entry = mContractIDs.Get(nsDependentCString(aContractID));
+ }
+
+ if (entry && entry->mServiceObject) {
+ nsCOMPtr<nsISupports> service;
+ rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
+ *aResult = (service != nullptr);
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString iid;
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Getting service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n", aContractID, iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // `service` must be released after the lock is released, so it must be
+ // declared before the lock in this C++ block.
+ nsCOMPtr<nsISupports> service;
+ MutexLock lock(mLock);
+
+ nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (entry->mServiceObject) {
+ // We need to not be holding the service manager's monitor while calling
+ // QueryInterface, because it invokes user code which could try to re-enter
+ // the service manager, or try to grab some other lock/monitor/condvar
+ // and deadlock, e.g. bug 282743.
+ // `entry` is valid until XPCOM shutdown, so we can safely use it after
+ // exiting the lock.
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+ PRThread* currentPRThread = PR_GetCurrentThread();
+ MOZ_ASSERT(currentPRThread, "This should never be null!");
+
+ // Needed to optimize the event loop below.
+ nsIThread* currentThread = nullptr;
+
+ PRThread* pendingPRThread;
+ while ((pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid))) {
+ if (pendingPRThread == currentPRThread) {
+ NS_ERROR("Recursive GetService!");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ SafeMutexAutoUnlock unlockPending(mLock);
+
+ if (!currentThread) {
+ currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread, "This should never be null!");
+ }
+
+ // This will process a single event or yield the thread if no event is
+ // pending.
+ if (!NS_ProcessNextEvent(currentThread, false)) {
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+ }
+ }
+
+ if (currentThread && entry->mServiceObject) {
+ // If we have a currentThread then we must have waited on another thread
+ // to create the service. Grab it now if that succeeded.
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+#ifdef DEBUG
+ PendingServiceInfo* newInfo =
+#endif
+ AddPendingService(*entry->mCIDEntry->cid, currentPRThread);
+ NS_ASSERTION(newInfo, "Failed to add info to the array!");
+
+ // We need to not be holding the service manager's lock while calling
+ // CreateInstance, because it invokes user code which could try to re-enter
+ // the service manager:
+
+ nsresult rv;
+ {
+ SafeMutexAutoUnlock unlock(mLock);
+ rv = CreateInstanceByContractID(aContractID, nullptr, aIID,
+ getter_AddRefs(service));
+ }
+ if (NS_SUCCEEDED(rv) && !service) {
+ NS_ERROR("Factory did not return an object but returned success");
+ return NS_ERROR_SERVICE_NOT_FOUND;
+ }
+
+#ifdef DEBUG
+ pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid);
+ MOZ_ASSERT(pendingPRThread == currentPRThread,
+ "Pending service array has been changed!");
+#endif
+ RemovePendingService(*entry->mCIDEntry->cid);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!");
+
+ entry->mServiceObject = service.forget();
+
+ lock.Unlock();
+
+ nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult);
+ *sresult = entry->mServiceObject;
+ (*sresult)->AddRef();
+
+ return NS_OK;
+}
+
+already_AddRefed<mozilla::ModuleLoader>
+nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt)
+{
+ nsCOMPtr<mozilla::ModuleLoader> loader = mLoaderMap.Get(aExt);
+ if (!loader) {
+ loader = do_GetServiceFromCategory("module-loader",
+ PromiseFlatCString(aExt).get());
+ if (!loader) {
+ return nullptr;
+ }
+
+ mLoaderMap.Put(aExt, loader);
+ }
+
+ return loader.forget();
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RegisterFactory(const nsCID& aClass,
+ const char* aName,
+ const char* aContractID,
+ nsIFactory* aFactory)
+{
+ if (!aFactory) {
+ // If a null factory is passed in, this call just wants to reset
+ // the contract ID to point to an existing CID entry.
+ if (!aContractID) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* oldf = mFactories.Get(aClass);
+ if (!oldf) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ mContractIDs.Put(nsDependentCString(aContractID), oldf);
+ return NS_OK;
+ }
+
+ nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory));
+
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* oldf = mFactories.Get(aClass);
+ if (oldf) {
+ return NS_ERROR_FACTORY_EXISTS;
+ }
+
+ if (aContractID) {
+ mContractIDs.Put(nsDependentCString(aContractID), f);
+ }
+
+ mFactories.Put(aClass, f.forget());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
+ nsIFactory* aFactory)
+{
+ // Don't release the dying factory or service object until releasing
+ // the component manager monitor.
+ nsCOMPtr<nsIFactory> dyingFactory;
+ nsCOMPtr<nsISupports> dyingServiceObject;
+
+ {
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* f = mFactories.Get(aClass);
+ if (!f || f->mFactory != aFactory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ mFactories.Remove(aClass);
+
+ // This might leave a stale contractid -> factory mapping in
+ // place, so null out the factory entry (see
+ // nsFactoryEntry::GetFactory)
+ f->mFactory.swap(dyingFactory);
+ f->mServiceObject.swap(dyingServiceObject);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AutoRegister(nsIFile* aLocation)
+{
+ XRE_AddManifestLocation(NS_EXTENSION_LOCATION, aLocation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AutoUnregister(nsIFile* aLocation)
+{
+ NS_ERROR("AutoUnregister not implemented.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RegisterFactoryLocation(const nsCID& aCID,
+ const char* aClassName,
+ const char* aContractID,
+ nsIFile* aFile,
+ const char* aLoaderStr,
+ const char* aType)
+{
+ NS_ERROR("RegisterFactoryLocation not implemented.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID,
+ nsIFile* aFile)
+{
+ NS_ERROR("UnregisterFactoryLocation not implemented.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass,
+ bool* aResult)
+{
+ *aResult = (nullptr != GetFactoryEntry(aClass));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsContractIDRegistered(const char* aClass,
+ bool* aResult)
+{
+ if (NS_WARN_IF(!aClass)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsFactoryEntry* entry = GetFactoryEntry(aClass, strlen(aClass));
+
+ if (entry) {
+ // UnregisterFactory might have left a stale nsFactoryEntry in
+ // mContractIDs, so we should check to see whether this entry has
+ // anything useful.
+ *aResult = (bool(entry->mModule) ||
+ bool(entry->mFactory) ||
+ bool(entry->mServiceObject));
+ } else {
+ *aResult = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator** aEnumerator)
+{
+ nsCOMArray<nsISupports> array;
+ for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
+ const nsID& id = iter.Key();
+ nsCOMPtr<nsISupportsID> wrapper = new nsSupportsID();
+ wrapper->SetData(&id);
+ array.AppendObject(wrapper);
+ }
+ return NS_NewArrayEnumerator(aEnumerator, array);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::EnumerateContractIDs(nsISimpleEnumerator** aEnumerator)
+{
+ nsTArray<nsCString>* array = new nsTArray<nsCString>;
+ for (auto iter = mContractIDs.Iter(); !iter.Done(); iter.Next()) {
+ const nsACString& contract = iter.Key();
+ array->AppendElement(contract);
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> e;
+ nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return CallQueryInterface(e, aEnumerator);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::CIDToContractID(const nsCID& aClass,
+ char** aResult)
+{
+ NS_ERROR("CIDTOContractID not implemented");
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::ContractIDToCID(const char* aContractID,
+ nsCID** aResult)
+{
+ {
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
+ if (entry) {
+ *aResult = (nsCID*)moz_xmalloc(sizeof(nsCID));
+ **aResult = *entry->mCIDEntry->cid;
+ return NS_OK;
+ }
+ }
+ *aResult = nullptr;
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ComponentManagerMallocSizeOf)
+
+NS_IMETHODIMP
+nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ MOZ_COLLECT_REPORT(
+ "explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES,
+ SizeOfIncludingThis(ComponentManagerMallocSizeOf),
+ "Memory used for the XPCOM component manager.");
+
+ return NS_OK;
+}
+
+size_t
+nsComponentManagerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+ const
+{
+ size_t n = aMallocSizeOf(this);
+
+ n += mLoaderMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mFactories.ConstIter(); !iter.Done(); iter.Next()) {
+ n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mContractIDs.ConstIter(); !iter.Done(); iter.Next()) {
+ // We don't measure the nsFactoryEntry data because it's owned by
+ // mFactories (which is measured above).
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ n += sStaticModules->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf);
+
+ n += mKnownStaticModules.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ n += mKnownModules.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf);
+
+ n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mLoaderMap's keys and values
+ // - mMon
+ // - sStaticModules' entries
+ // - sModuleLocations' entries
+ // - mNativeModuleLoader
+ // - mKnownStaticModules' entries?
+ // - mKnownModules' keys and values?
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsFactoryEntry
+////////////////////////////////////////////////////////////////////////////////
+
+nsFactoryEntry::nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry,
+ nsComponentManagerImpl::KnownModule* aModule)
+ : mCIDEntry(aEntry)
+ , mModule(aModule)
+{
+}
+
+nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory)
+ : mCIDEntry(nullptr)
+ , mModule(nullptr)
+ , mFactory(aFactory)
+{
+ mozilla::Module::CIDEntry* e = new mozilla::Module::CIDEntry();
+ nsCID* cid = new nsCID;
+ *cid = aCID;
+ e->cid = cid;
+ mCIDEntry = e;
+}
+
+nsFactoryEntry::~nsFactoryEntry()
+{
+ // If this was a RegisterFactory entry, we own the CIDEntry/CID
+ if (!mModule) {
+ delete mCIDEntry->cid;
+ delete mCIDEntry;
+ }
+}
+
+already_AddRefed<nsIFactory>
+nsFactoryEntry::GetFactory()
+{
+ nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns();
+
+ if (!mFactory) {
+ // RegisterFactory then UnregisterFactory can leave an entry in mContractIDs
+ // pointing to an unusable nsFactoryEntry.
+ if (!mModule) {
+ return nullptr;
+ }
+
+ if (!mModule->Load()) {
+ return nullptr;
+ }
+
+ // Don't set mFactory directly, it needs to be locked
+ nsCOMPtr<nsIFactory> factory;
+
+ if (mModule->Module()->getFactoryProc) {
+ factory = mModule->Module()->getFactoryProc(*mModule->Module(),
+ *mCIDEntry);
+ } else if (mCIDEntry->getFactoryProc) {
+ factory = mCIDEntry->getFactoryProc(*mModule->Module(), *mCIDEntry);
+ } else {
+ NS_ASSERTION(mCIDEntry->constructorProc, "no getfactory or constructor");
+ factory = new mozilla::GenericFactory(mCIDEntry->constructorProc);
+ }
+ if (!factory) {
+ return nullptr;
+ }
+
+ SafeMutexAutoLock lock(nsComponentManagerImpl::gComponentManager->mLock);
+ // Threads can race to set mFactory
+ if (!mFactory) {
+ factory.swap(mFactory);
+ }
+ }
+ nsCOMPtr<nsIFactory> factory = mFactory;
+ return factory.forget();
+}
+
+size_t
+nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mCIDEntry;
+ // - mModule;
+ // - mFactory;
+ // - mServiceObject;
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Static Access Functions
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_GetComponentManager(nsIComponentManager** aResult)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+nsresult
+NS_GetServiceManager(nsIServiceManager** aResult)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+
+nsresult
+NS_GetComponentRegistrar(nsIComponentRegistrar** aResult)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddStaticComponent(const mozilla::Module* aComponent)
+{
+ nsComponentManagerImpl::InitializeStaticModules();
+ nsComponentManagerImpl::sStaticModules->AppendElement(aComponent);
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterModule(aComponent,
+ nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation)
+{
+ nsString path;
+ nsresult rv = aLocation->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ return XRE_AddJarManifestLocation(NS_BOOTSTRAPPED_LOCATION, aLocation);
+ }
+
+ nsCOMPtr<nsIFile> manifest =
+ CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest"));
+ return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation)
+{
+ nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistryService();
+ if (!cr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString path;
+ nsresult rv = aLocation->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsComponentManagerImpl::ComponentLocation elem;
+ elem.type = NS_BOOTSTRAPPED_LOCATION;
+
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ elem.location.Init(aLocation, "chrome.manifest");
+ } else {
+ nsCOMPtr<nsIFile> lf =
+ CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest"));
+ elem.location.Init(lf);
+ }
+
+ // Remove reference.
+ nsComponentManagerImpl::sModuleLocations->RemoveElement(elem,
+ ComponentLocationComparator());
+
+ rv = cr->CheckForNewChrome();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations)
+{
+ NS_ENSURE_ARG_POINTER(aLocations);
+ *aLocations = nullptr;
+
+ if (!sModuleLocations) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIMutableArray> locations = nsArray::Create();
+ nsresult rv;
+ for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
+ ComponentLocation& l = sModuleLocations->ElementAt(i);
+ FileLocation loc = l.location;
+ nsCString uriString;
+ loc.GetURIString(uriString);
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), uriString);
+ if (NS_SUCCEEDED(rv)) {
+ locations->AppendElement(uri, false);
+ }
+ }
+
+ locations.forget(aLocations);
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation)
+{
+ nsComponentManagerImpl::InitializeModuleLocations();
+ nsComponentManagerImpl::ComponentLocation* c =
+ nsComponentManagerImpl::sModuleLocations->AppendElement();
+ c->type = aType;
+ c->location.Init(aLocation);
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterManifest(aType,
+ c->location,
+ false);
+ }
+
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation)
+{
+ nsComponentManagerImpl::InitializeModuleLocations();
+ nsComponentManagerImpl::ComponentLocation* c =
+ nsComponentManagerImpl::sModuleLocations->AppendElement();
+
+ c->type = aType;
+ c->location.Init(aLocation, "chrome.manifest");
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterManifest(aType,
+ c->location,
+ false);
+ }
+
+ return NS_OK;
+}
+
diff --git a/xpcom/components/nsComponentManager.h b/xpcom/components/nsComponentManager.h
new file mode 100644
index 000000000..f0e0f684a
--- /dev/null
+++ b/xpcom/components/nsComponentManager.h
@@ -0,0 +1,363 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsComponentManager_h__
+#define nsComponentManager_h__
+
+#include "nsXPCOM.h"
+
+#include "xpcom-private.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIMemoryReporter.h"
+#include "nsIServiceManager.h"
+#include "nsIFile.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Module.h"
+#include "mozilla/ModuleLoader.h"
+#include "mozilla/Mutex.h"
+#include "nsXULAppAPI.h"
+#include "nsNativeModuleLoader.h"
+#include "nsIFactory.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "PLDHashTable.h"
+#include "prtime.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsWeakReference.h"
+#include "plarena.h"
+#include "nsCOMArray.h"
+#include "nsDataHashtable.h"
+#include "nsInterfaceHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsTArray.h"
+
+#include "mozilla/Omnijar.h"
+#include "mozilla/Attributes.h"
+
+struct nsFactoryEntry;
+class nsIServiceManager;
+struct PRThread;
+
+#define NS_COMPONENTMANAGER_CID \
+{ /* 91775d60-d5dc-11d2-92fb-00e09805570f */ \
+ 0x91775d60, \
+ 0xd5dc, \
+ 0x11d2, \
+ {0x92, 0xfb, 0x00, 0xe0, 0x98, 0x05, 0x57, 0x0f} \
+}
+
+/* keys for registry use */
+extern const char xpcomKeyName[];
+extern const char xpcomComponentsKeyName[];
+extern const char lastModValueName[];
+extern const char fileSizeValueName[];
+extern const char nativeComponentType[];
+extern const char staticComponentType[];
+
+#ifdef DEBUG
+#define XPCOM_CHECK_PENDING_CIDS
+#endif
+////////////////////////////////////////////////////////////////////////////////
+
+extern const mozilla::Module kXPCOMModule;
+
+/**
+ * This is a wrapper around mozilla::Mutex which provides runtime
+ * checking for a deadlock where the same thread tries to lock a mutex while
+ * it is already locked. This checking is present in both debug and release
+ * builds.
+ */
+class SafeMutex
+{
+public:
+ explicit SafeMutex(const char* aName)
+ : mMutex(aName)
+ , mOwnerThread(nullptr)
+ {
+ }
+
+ ~SafeMutex() {}
+
+ void Lock()
+ {
+ AssertNotCurrentThreadOwns();
+ mMutex.Lock();
+ MOZ_ASSERT(mOwnerThread == nullptr);
+ mOwnerThread = PR_GetCurrentThread();
+ }
+
+ void Unlock()
+ {
+ MOZ_ASSERT(mOwnerThread == PR_GetCurrentThread());
+ mOwnerThread = nullptr;
+ mMutex.Unlock();
+ }
+
+ void AssertCurrentThreadOwns() const
+ {
+ // This method is a debug-only check
+ MOZ_ASSERT(mOwnerThread == PR_GetCurrentThread());
+ }
+
+ MOZ_NEVER_INLINE void AssertNotCurrentThreadOwns() const
+ {
+ // This method is a release-mode check
+ if (PR_GetCurrentThread() == mOwnerThread) {
+ MOZ_CRASH();
+ }
+ }
+
+private:
+ mozilla::Mutex mMutex;
+ mozilla::Atomic<PRThread*, mozilla::Relaxed> mOwnerThread;
+};
+
+typedef mozilla::BaseAutoLock<SafeMutex> SafeMutexAutoLock;
+typedef mozilla::BaseAutoUnlock<SafeMutex> SafeMutexAutoUnlock;
+
+class nsComponentManagerImpl final
+ : public nsIComponentManager
+ , public nsIServiceManager
+ , public nsSupportsWeakReference
+ , public nsIComponentRegistrar
+ , public nsIInterfaceRequestor
+ , public nsIMemoryReporter
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSICOMPONENTMANAGER
+ NS_DECL_NSICOMPONENTREGISTRAR
+ NS_DECL_NSIMEMORYREPORTER
+
+ static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+ nsresult RegistryLocationForFile(nsIFile* aFile,
+ nsCString& aResult);
+ nsresult FileForRegistryLocation(const nsCString& aLocation,
+ nsIFile** aSpec);
+
+ NS_DECL_NSISERVICEMANAGER
+
+ // nsComponentManagerImpl methods:
+ nsComponentManagerImpl();
+
+ static nsComponentManagerImpl* gComponentManager;
+ nsresult Init();
+
+ nsresult Shutdown(void);
+
+ nsresult FreeServices();
+
+ already_AddRefed<mozilla::ModuleLoader> LoaderForExtension(const nsACString& aExt);
+ nsInterfaceHashtable<nsCStringHashKey, mozilla::ModuleLoader> mLoaderMap;
+
+ already_AddRefed<nsIFactory> FindFactory(const nsCID& aClass);
+ already_AddRefed<nsIFactory> FindFactory(const char* aContractID,
+ uint32_t aContractIDLen);
+
+ already_AddRefed<nsIFactory> LoadFactory(nsFactoryEntry* aEntry);
+
+ nsFactoryEntry* GetFactoryEntry(const char* aContractID,
+ uint32_t aContractIDLen);
+ nsFactoryEntry* GetFactoryEntry(const nsCID& aClass);
+
+ nsDataHashtable<nsIDHashKey, nsFactoryEntry*> mFactories;
+ nsDataHashtable<nsCStringHashKey, nsFactoryEntry*> mContractIDs;
+
+ SafeMutex mLock;
+
+ static void InitializeStaticModules();
+ static void InitializeModuleLocations();
+
+ struct ComponentLocation
+ {
+ NSLocationType type;
+ mozilla::FileLocation location;
+ };
+
+ class ComponentLocationComparator
+ {
+ public:
+ bool Equals(const ComponentLocation& aA, const ComponentLocation& aB) const
+ {
+ return (aA.type == aB.type && aA.location.Equals(aB.location));
+ }
+ };
+
+ static nsTArray<const mozilla::Module*>* sStaticModules;
+ static nsTArray<ComponentLocation>* sModuleLocations;
+
+ nsNativeModuleLoader mNativeModuleLoader;
+
+ class KnownModule
+ {
+ public:
+ /**
+ * Static or binary module.
+ */
+ KnownModule(const mozilla::Module* aModule, mozilla::FileLocation& aFile)
+ : mModule(aModule)
+ , mFile(aFile)
+ , mLoaded(false)
+ , mFailed(false)
+ {
+ }
+
+ explicit KnownModule(const mozilla::Module* aModule)
+ : mModule(aModule)
+ , mLoaded(false)
+ , mFailed(false)
+ {
+ }
+
+ explicit KnownModule(mozilla::FileLocation& aFile)
+ : mModule(nullptr)
+ , mFile(aFile)
+ , mLoader(nullptr)
+ , mLoaded(false)
+ , mFailed(false)
+ {
+ }
+
+ ~KnownModule()
+ {
+ if (mLoaded && mModule->unloadProc) {
+ mModule->unloadProc();
+ }
+ }
+
+ bool EnsureLoader();
+ bool Load();
+
+ const mozilla::Module* Module() const { return mModule; }
+
+ /**
+ * For error logging, get a description of this module, either the
+ * file path, or <static module>.
+ */
+ nsCString Description() const;
+
+ private:
+ const mozilla::Module* mModule;
+ mozilla::FileLocation mFile;
+ nsCOMPtr<mozilla::ModuleLoader> mLoader;
+ bool mLoaded;
+ bool mFailed;
+ };
+
+ // The KnownModule is kept alive by these members, it is
+ // referenced by pointer from the factory entries.
+ nsTArray<nsAutoPtr<KnownModule>> mKnownStaticModules;
+ // The key is the URI string of the module
+ nsClassHashtable<nsCStringHashKey, KnownModule> mKnownModules;
+
+ // Mutex not held
+ void RegisterModule(const mozilla::Module* aModule,
+ mozilla::FileLocation* aFile);
+
+
+ // Mutex held
+ void RegisterCIDEntryLocked(const mozilla::Module::CIDEntry* aEntry,
+ KnownModule* aModule);
+ void RegisterContractIDLocked(const mozilla::Module::ContractIDEntry* aEntry);
+
+ // Mutex not held
+ void RegisterManifest(NSLocationType aType, mozilla::FileLocation& aFile,
+ bool aChromeOnly);
+
+ struct ManifestProcessingContext
+ {
+ ManifestProcessingContext(NSLocationType aType,
+ mozilla::FileLocation& aFile, bool aChromeOnly)
+ : mType(aType)
+ , mFile(aFile)
+ , mChromeOnly(aChromeOnly)
+ {
+ }
+
+ ~ManifestProcessingContext() {}
+
+ NSLocationType mType;
+ mozilla::FileLocation mFile;
+ bool mChromeOnly;
+ };
+
+ void ManifestManifest(ManifestProcessingContext& aCx, int aLineNo,
+ char* const* aArgv);
+ void ManifestBinaryComponent(ManifestProcessingContext& aCx, int aLineNo,
+ char* const* aArgv);
+ void ManifestXPT(ManifestProcessingContext& aCx, int aLineNo,
+ char* const* aArgv);
+ void ManifestComponent(ManifestProcessingContext& aCx, int aLineNo,
+ char* const* aArgv);
+ void ManifestContract(ManifestProcessingContext& aCx, int aLineNo,
+ char* const* aArgv);
+ void ManifestCategory(ManifestProcessingContext& aCx, int aLineNo,
+ char* const* aArgv);
+
+ void RereadChromeManifests(bool aChromeOnly = true);
+
+ // Shutdown
+ enum
+ {
+ NOT_INITIALIZED,
+ NORMAL,
+ SHUTDOWN_IN_PROGRESS,
+ SHUTDOWN_COMPLETE
+ } mStatus;
+
+ PLArenaPool mArena;
+
+ struct PendingServiceInfo
+ {
+ const nsCID* cid;
+ PRThread* thread;
+ };
+
+ inline PendingServiceInfo* AddPendingService(const nsCID& aServiceCID,
+ PRThread* aThread);
+ inline void RemovePendingService(const nsCID& aServiceCID);
+ inline PRThread* GetPendingServiceThread(const nsCID& aServiceCID) const;
+
+ nsTArray<PendingServiceInfo> mPendingServices;
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+private:
+ ~nsComponentManagerImpl();
+};
+
+
+#define NS_MAX_FILENAME_LEN 1024
+
+#define NS_ERROR_IS_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, 24)
+
+struct nsFactoryEntry
+{
+ nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry,
+ nsComponentManagerImpl::KnownModule* aModule);
+
+ // nsIComponentRegistrar.registerFactory support
+ nsFactoryEntry(const nsCID& aClass, nsIFactory* aFactory);
+
+ ~nsFactoryEntry();
+
+ already_AddRefed<nsIFactory> GetFactory();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ const mozilla::Module::CIDEntry* mCIDEntry;
+ nsComponentManagerImpl::KnownModule* mModule;
+
+ nsCOMPtr<nsIFactory> mFactory;
+ nsCOMPtr<nsISupports> mServiceObject;
+};
+
+#endif // nsComponentManager_h__
diff --git a/xpcom/components/nsICategoryManager.idl b/xpcom/components/nsICategoryManager.idl
new file mode 100644
index 000000000..e33d3ed8b
--- /dev/null
+++ b/xpcom/components/nsICategoryManager.idl
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsISimpleEnumerator;
+
+/*
+ * nsICategoryManager
+ */
+
+[scriptable, uuid(3275b2cd-af6d-429a-80d7-f0c5120342ac)]
+interface nsICategoryManager : nsISupports
+{
+ /**
+ * Get the value for the given category's entry.
+ * @param aCategory The name of the category ("protocol")
+ * @param aEntry The entry you're looking for ("http")
+ * @return The value.
+ */
+ string getCategoryEntry(in string aCategory, in string aEntry);
+
+ /**
+ * Add an entry to a category.
+ * @param aCategory The name of the category ("protocol")
+ * @param aEntry The entry to be added ("http")
+ * @param aValue The value for the entry ("moz.httprulez.1")
+ * @param aPersist Should this data persist between invocations?
+ * @param aReplace Should we replace an existing entry?
+ * @return Previous entry, if any
+ */
+ string addCategoryEntry(in string aCategory, in string aEntry,
+ in string aValue, in boolean aPersist,
+ in boolean aReplace);
+
+ /**
+ * Delete an entry from the category.
+ * @param aCategory The name of the category ("protocol")
+ * @param aEntry The entry to be added ("http")
+ * @param aPersist Delete persistent data from registry, if present?
+ */
+ void deleteCategoryEntry(in string aCategory, in string aEntry,
+ in boolean aPersist);
+
+ /**
+ * Delete a category and all entries.
+ * @param aCategory The category to be deleted.
+ */
+ void deleteCategory(in string aCategory);
+
+ /**
+ * Enumerate the entries in a category.
+ * @param aCategory The category to be enumerated.
+ * @return a simple enumerator, each result QIs to
+ * nsISupportsCString.
+ */
+ nsISimpleEnumerator enumerateCategory(in string aCategory);
+
+ /**
+ * Enumerate all existing categories
+ * @param aCategory The category to be enumerated.
+ * @return a simple enumerator, each result QIs to
+ * nsISupportsCString.
+ */
+ nsISimpleEnumerator enumerateCategories();
+};
+
diff --git a/xpcom/components/nsIClassInfo.idl b/xpcom/components/nsIClassInfo.idl
new file mode 100644
index 000000000..639d12aa8
--- /dev/null
+++ b/xpcom/components/nsIClassInfo.idl
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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"
+
+interface nsIXPCScriptable;
+
+/**
+ * Provides information about a specific implementation class. If you want
+ * your class to implement nsIClassInfo, see nsIClassInfoImpl.h for
+ * instructions--you most likely do not want to inherit from nsIClassInfo.
+ */
+
+[scriptable, uuid(a60569d7-d401-4677-ba63-2aa5971af25d)]
+interface nsIClassInfo : nsISupports
+{
+ /**
+ * Get an ordered list of the interface ids that instances of the class
+ * promise to implement. Note that nsISupports is an implicit member
+ * of any such list and need not be included.
+ *
+ * Should set *count = 0 and *array = null and return NS_OK if getting the
+ * list is not supported.
+ */
+ void getInterfaces(out uint32_t count,
+ [array, size_is(count), retval] out nsIIDPtr array);
+
+ /**
+ * Return an object to assist XPConnect in supplying JavaScript-specific
+ * behavior to callers of the instance object, or null if not needed.
+ */
+ nsIXPCScriptable getScriptableHelper();
+
+ /**
+ * A contract ID through which an instance of this class can be created
+ * (or accessed as a service, if |flags & SINGLETON|), or null.
+ */
+ readonly attribute string contractID;
+
+ /**
+ * A human readable string naming the class, or null.
+ */
+ readonly attribute string classDescription;
+
+ /**
+ * A class ID through which an instance of this class can be created
+ * (or accessed as a service, if |flags & SINGLETON|), or null.
+ */
+ readonly attribute nsCIDPtr classID;
+
+ /**
+ * Bitflags for 'flags' attribute.
+ */
+ const uint32_t SINGLETON = 1 << 0;
+ const uint32_t THREADSAFE = 1 << 1;
+ const uint32_t MAIN_THREAD_ONLY = 1 << 2;
+ const uint32_t DOM_OBJECT = 1 << 3;
+ const uint32_t PLUGIN_OBJECT = 1 << 4;
+ const uint32_t SINGLETON_CLASSINFO = 1 << 5;
+
+ /**
+ * 'flags' attribute bitflag: whether objects of this type implement
+ * nsIContent.
+ */
+ const uint32_t CONTENT_NODE = 1 << 6;
+
+ // The high order bit is RESERVED for consumers of these flags.
+ // No implementor of this interface should ever return flags
+ // with this bit set.
+ const uint32_t RESERVED = 1 << 31;
+
+
+ readonly attribute uint32_t flags;
+
+ /**
+ * Also a class ID through which an instance of this class can be created
+ * (or accessed as a service, if |flags & SINGLETON|). If the class does
+ * not have a CID, it should return NS_ERROR_NOT_AVAILABLE. This attribute
+ * exists so C++ callers can avoid allocating and freeing a CID, as would
+ * happen if they used classID.
+ */
+ [noscript] readonly attribute nsCID classIDNoAlloc;
+
+};
diff --git a/xpcom/components/nsIComponentManager.idl b/xpcom/components/nsIComponentManager.idl
new file mode 100644
index 000000000..1e5693a28
--- /dev/null
+++ b/xpcom/components/nsIComponentManager.idl
@@ -0,0 +1,106 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsIComponentManager interface.
+ */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIFactory;
+interface nsIArray;
+
+[scriptable, uuid(d604ffc3-1ba3-4f6c-b65f-1ed4199364c3)]
+interface nsIComponentManager : nsISupports
+{
+ /**
+ * getClassObject
+ *
+ * Returns the factory object that can be used to create instances of
+ * CID aClass
+ *
+ * @param aClass The classid of the factory that is being requested
+ */
+ void getClassObject(in nsCIDRef aClass,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result);
+
+ /**
+ * getClassObjectByContractID
+ *
+ * Returns the factory object that can be used to create instances of
+ * CID aClass
+ *
+ * @param aClass The classid of the factory that is being requested
+ */
+ void getClassObjectByContractID(in string aContractID,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result);
+
+
+ /**
+ * createInstance
+ *
+ * Create an instance of the CID aClass and return the interface aIID.
+ *
+ * @param aClass : ClassID of object instance requested
+ * @param aDelegate : Used for aggregation
+ * @param aIID : IID of interface requested
+ */
+ void createInstance(in nsCIDRef aClass,
+ in nsISupports aDelegate,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result);
+
+ /**
+ * createInstanceByContractID
+ *
+ * Create an instance of the CID that implements aContractID and return the
+ * interface aIID.
+ *
+ * @param aContractID : aContractID of object instance requested
+ * @param aDelegate : Used for aggregation
+ * @param aIID : IID of interface requested
+ */
+ void createInstanceByContractID(in string aContractID,
+ in nsISupports aDelegate,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result);
+
+ /**
+ * addBootstrappedManifestLocation
+ *
+ * Adds a bootstrapped manifest location on runtime.
+ *
+ * @param aLocation : A directory where chrome.manifest resides,
+ * or an XPI with it on the root.
+ */
+ void addBootstrappedManifestLocation(in nsIFile aLocation);
+
+ /**
+ * removeBootstrappedManifestLocation
+ *
+ * Removes a bootstrapped manifest location on runtime.
+ *
+ * @param aLocation : A directory where chrome.manifest resides,
+ * or an XPI with it on the root.
+ */
+ void removeBootstrappedManifestLocation(in nsIFile aLocation);
+
+ /**
+ * getManifestLocations
+ *
+ * Get an array of nsIURIs of all registered and builtin manifest locations.
+ */
+ nsIArray getManifestLocations();
+};
+
+
+%{ C++
+#ifdef MOZILLA_INTERNAL_API
+#include "nsComponentManagerUtils.h"
+#endif
+%} C++
diff --git a/xpcom/components/nsIComponentRegistrar.idl b/xpcom/components/nsIComponentRegistrar.idl
new file mode 100644
index 000000000..e12acdf25
--- /dev/null
+++ b/xpcom/components/nsIComponentRegistrar.idl
@@ -0,0 +1,163 @@
+/* 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 nsIComponentRegistrar interface.
+ */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsIFactory;
+interface nsISimpleEnumerator;
+
+[scriptable, uuid(2417cbfe-65ad-48a6-b4b6-eb84db174392)]
+interface nsIComponentRegistrar : nsISupports
+{
+ /**
+ * autoRegister
+ *
+ * Register a .manifest file, or an entire directory containing
+ * these files. Registration lasts for this run only, and is not cached.
+ *
+ * @note Formerly this method would register component files directly. This
+ * is no longer supported.
+ */
+ void autoRegister(in nsIFile aSpec);
+
+ /**
+ * autoUnregister
+ * @status OBSOLETE: This method is no longer implemented, but preserved
+ * in this interface for binary compatibility with
+ * Mozilla 1.9.2.
+ */
+ void autoUnregister(in nsIFile aSpec);
+
+
+ /**
+ * registerFactory
+ *
+ * Register a factory with a given ContractID, CID and Class Name.
+ *
+ * @param aClass : CID of object
+ * @param aClassName : Class Name of CID (unused)
+ * @param aContractID : ContractID associated with CID aClass. May be null
+ * if no contract ID is needed.
+ * @param aFactory : Factory that will be registered for CID aClass.
+ * If aFactory is null, the contract will be associated
+ * with a previously registered CID.
+ */
+ void registerFactory(in nsCIDRef aClass,
+ in string aClassName,
+ in string aContractID,
+ in nsIFactory aFactory);
+
+ /**
+ * unregisterFactory
+ *
+ * Unregister a factory associated with CID aClass.
+ *
+ * @param aClass : CID being unregistered
+ * @param aFactory : Factory previously registered to create instances of
+ * CID aClass.
+ *
+ * @throws NS_ERROR* Method failure.
+ */
+ void unregisterFactory(in nsCIDRef aClass,
+ in nsIFactory aFactory);
+
+ /**
+ * registerFactoryLocation
+ * @status OBSOLETE: This method is no longer implemented, but preserved
+ * in this interface for binary compatibility with
+ * Mozilla 1.9.2.
+ */
+ void registerFactoryLocation(in nsCIDRef aClass,
+ in string aClassName,
+ in string aContractID,
+ in nsIFile aFile,
+ in string aLoaderStr,
+ in string aType);
+
+ /**
+ * unregisterFactoryLocation
+ * @status OBSOLETE: This method is no longer implemented, but preserved
+ * in this interface for binary compatibility with
+ * Mozilla 1.9.2.
+ */
+ void unregisterFactoryLocation(in nsCIDRef aClass,
+ in nsIFile aFile);
+
+ /**
+ * isCIDRegistered
+ *
+ * Returns true if a factory is registered for the CID.
+ *
+ * @param aClass : CID queried for registeration
+ * @return : true if a factory is registered for CID
+ * false otherwise.
+ */
+ boolean isCIDRegistered(in nsCIDRef aClass);
+
+ /**
+ * isContractIDRegistered
+ *
+ * Returns true if a factory is registered for the contract id.
+ *
+ * @param aClass : contract id queried for registeration
+ * @return : true if a factory is registered for contract id
+ * false otherwise.
+ */
+ boolean isContractIDRegistered(in string aContractID);
+
+ /**
+ * enumerateCIDs
+ *
+ * Enumerate the list of all registered CIDs.
+ *
+ * @return : enumerator for CIDs. Elements of the enumeration can be QI'ed
+ * for the nsISupportsID interface. From the nsISupportsID, you
+ * can obtain the actual CID.
+ */
+ nsISimpleEnumerator enumerateCIDs();
+
+ /**
+ * enumerateContractIDs
+ *
+ * Enumerate the list of all registered ContractIDs.
+ *
+ * @return : enumerator for ContractIDs. Elements of the enumeration can be
+ * QI'ed for the nsISupportsCString interface. From the
+ * nsISupportsCString interface, you can obtain the actual
+ * Contract ID string.
+ */
+ nsISimpleEnumerator enumerateContractIDs();
+
+ /**
+ * CIDToContractID
+ * @status OBSOLETE: This method is no longer implemented, but preserved
+ * in this interface for binary compatibility with
+ * Mozilla 1.9.2.
+ */
+ string CIDToContractID(in nsCIDRef aClass);
+
+ /**
+ * contractIDToCID
+ *
+ * Returns the CID for a given Contract ID, if one exists and is registered.
+ *
+ * @return : Contract ID.
+ */
+ nsCIDPtr contractIDToCID(in string aContractID);
+};
+
+
+
+
+
+
+
+
+
+
diff --git a/xpcom/components/nsIFactory.idl b/xpcom/components/nsIFactory.idl
new file mode 100644
index 000000000..54152976f
--- /dev/null
+++ b/xpcom/components/nsIFactory.idl
@@ -0,0 +1,42 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * A class factory allows the creation of nsISupports derived
+ * components without specifying a concrete base class.
+ */
+
+[scriptable, object, uuid(00000001-0000-0000-c000-000000000046)]
+interface nsIFactory : nsISupports {
+ /**
+ * Creates an instance of a component.
+ *
+ * @param aOuter Pointer to a component that wishes to be aggregated
+ * in the resulting instance. This will be nullptr if no
+ * aggregation is requested.
+ * @param iid The IID of the interface being requested in
+ * the component which is being currently created.
+ * @param result [out] Pointer to the newly created instance, if successful.
+ * @throws NS_NOINTERFACE - Interface not accessible.
+ * @throws NS_ERROR_NO_AGGREGATION - if an 'outer' object is supplied, but the
+ * component is not aggregatable.
+ * NS_ERROR* - Method failure.
+ */
+ void createInstance(in nsISupports aOuter, in nsIIDRef iid,
+ [retval, iid_is(iid)] out nsQIResult result);
+
+ /**
+ * LockFactory provides the client a way to keep the component
+ * in memory until it is finished with it. The client can call
+ * LockFactory(PR_TRUE) to lock the factory and LockFactory(PR_FALSE)
+ * to release the factory.
+ *
+ * @param lock - Must be PR_TRUE or PR_FALSE
+ * @throws NS_ERROR* - Method failure.
+ */
+ void lockFactory(in boolean lock);
+};
diff --git a/xpcom/components/nsIModule.idl b/xpcom/components/nsIModule.idl
new file mode 100644
index 000000000..4570a0199
--- /dev/null
+++ b/xpcom/components/nsIModule.idl
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIFile;
+interface nsIComponentManager;
+
+/**
+ * The nsIModule interface.
+ */
+
+[scriptable, uuid(7392D032-5371-11d3-994E-00805FD26FEE)]
+interface nsIModule : nsISupports
+{
+ /**
+ * Object Instance Creation
+ *
+ * Obtains a Class Object from a nsIModule for a given CID and IID pair.
+ * This class object can either be query to a nsIFactory or a may be
+ * query to a nsIClassInfo.
+ *
+ * @param aCompMgr : The global component manager
+ * @param aClass : ClassID of object instance requested
+ * @param aIID : IID of interface requested
+ *
+ */
+ void getClassObject(in nsIComponentManager aCompMgr,
+ in nsCIDRef aClass,
+ in nsIIDRef aIID,
+ [retval, iid_is(aIID)] out nsQIResult aResult);
+
+
+ /**
+ * One time registration callback
+ *
+ * When the nsIModule is discovered, this method will be
+ * called so that any setup registration can be preformed.
+ *
+ * @param aCompMgr : The global component manager
+ * @param aLocation : The location of the nsIModule on disk
+ * @param aLoaderStr: Opaque loader specific string
+ * @param aType : Loader Type being used to load this module
+ */
+ void registerSelf(in nsIComponentManager aCompMgr,
+ in nsIFile aLocation,
+ in string aLoaderStr,
+ in string aType);
+ /**
+ * One time unregistration callback
+ *
+ * When the nsIModule is being unregistered, this method will be
+ * called so that any unregistration can be preformed
+ *
+ * @param aCompMgr : The global component manager
+ * @param aLocation : The location of the nsIModule on disk
+ * @param aLoaderStr : Opaque loader specific string
+ *
+ */
+ void unregisterSelf(in nsIComponentManager aCompMgr,
+ in nsIFile aLocation,
+ in string aLoaderStr);
+
+ /**
+ * Module load management
+ *
+ * @param aCompMgr : The global component manager
+ *
+ * @return indicates to the caller if the module can be unloaded.
+ * Returning PR_TRUE isn't a guarantee that the module will be
+ * unloaded. It constitues only willingness of the module to be
+ * unloaded. It is very important to ensure that no outstanding
+ * references to the module's code/data exist before returning
+ * PR_TRUE.
+ * Returning PR_FALSE guaratees that the module won't be unloaded.
+ */
+ boolean canUnload(in nsIComponentManager aCompMgr);
+};
+
+
diff --git a/xpcom/components/nsIServiceManager.idl b/xpcom/components/nsIServiceManager.idl
new file mode 100644
index 000000000..45c1c3cc2
--- /dev/null
+++ b/xpcom/components/nsIServiceManager.idl
@@ -0,0 +1,72 @@
+/* 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"
+
+/**
+ * The nsIServiceManager manager interface provides a means to obtain
+ * global services in an application. The service manager depends on the
+ * repository to find and instantiate factories to obtain services.
+ *
+ * Users of the service manager must first obtain a pointer to the global
+ * service manager by calling NS_GetServiceManager. After that,
+ * they can request specific services by calling GetService. When they are
+ * finished they can NS_RELEASE() the service as usual.
+ *
+ * A user of a service may keep references to particular services indefinitely
+ * and only must call Release when it shuts down.
+ */
+
+[scriptable, uuid(8bb35ed9-e332-462d-9155-4a002ab5c958)]
+interface nsIServiceManager : nsISupports
+{
+ /**
+ * getServiceByContractID
+ *
+ * Returns the instance that implements aClass or aContractID and the
+ * interface aIID. This may result in the instance being created.
+ *
+ * @param aClass or aContractID : aClass or aContractID of object
+ * instance requested
+ * @param aIID : IID of interface requested
+ * @param result : resulting service
+ */
+ void getService(in nsCIDRef aClass,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result);
+
+ void getServiceByContractID(in string aContractID,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result);
+
+ /**
+ * isServiceInstantiated
+ *
+ * isServiceInstantiated will return a true if the service has already
+ * been created, or throw otherwise
+ *
+ * @param aClass or aContractID : aClass or aContractID of object
+ * instance requested
+ * @param aIID : IID of interface requested
+ * @throws NS_ERROR_SERVICE_NOT_AVAILABLE if the service hasn't been
+ * instantiated
+ * @throws NS_NOINTERFACE if the IID given isn't supported by the object
+ */
+ boolean isServiceInstantiated(in nsCIDRef aClass, in nsIIDRef aIID);
+ boolean isServiceInstantiatedByContractID(in string aContractID, in nsIIDRef aIID);
+};
+
+
+%{C++
+// Observing xpcom autoregistration. Topics will be 'start' and 'stop'.
+#define NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID "xpcom-autoregistration"
+
+#ifdef MOZILLA_INTERNAL_API
+#include "nsXPCOM.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#endif
+%}
+
diff --git a/xpcom/components/nsNativeModuleLoader.cpp b/xpcom/components/nsNativeModuleLoader.cpp
new file mode 100644
index 000000000..6452f5d1b
--- /dev/null
+++ b/xpcom/components/nsNativeModuleLoader.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
+ */
+
+#include "nsNativeModuleLoader.h"
+
+#include "mozilla/Logging.h"
+#include "prinit.h"
+#include "prerror.h"
+
+#include "nsComponentManager.h"
+#include "ManifestParser.h" // for LogMessage
+#include "nsCRTGlue.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+
+#include "nsIFile.h"
+#include "mozilla/WindowsDllBlocklist.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#ifdef XP_MACOSX
+#include <signal.h>
+#endif
+
+#ifdef DEBUG
+#define IMPLEMENT_BREAK_AFTER_LOAD
+#endif
+
+using namespace mozilla;
+
+static LazyLogModule sNativeModuleLoaderLog("nsNativeModuleLoader");
+#define LOG(level, args) MOZ_LOG(sNativeModuleLoaderLog, level, args)
+
+nsresult
+nsNativeModuleLoader::Init()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Startup not on main thread?");
+ LOG(LogLevel::Debug, ("nsNativeModuleLoader::Init()"));
+ return NS_OK;
+}
+
+class LoadModuleMainThreadRunnable : public Runnable
+{
+public:
+ LoadModuleMainThreadRunnable(nsNativeModuleLoader* aLoader,
+ FileLocation& aFile)
+ : mManager(nsComponentManagerImpl::gComponentManager)
+ , mLoader(aLoader)
+ , mFile(aFile)
+ , mResult(nullptr)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mResult = mLoader->LoadModule(mFile);
+ return NS_OK;
+ }
+
+ RefPtr<nsComponentManagerImpl> mManager;
+ nsNativeModuleLoader* mLoader;
+ FileLocation mFile;
+ const mozilla::Module* mResult;
+};
+
+const mozilla::Module*
+nsNativeModuleLoader::LoadModule(FileLocation& aFile)
+{
+ if (aFile.IsZip()) {
+ NS_ERROR("Binary components cannot be loaded from JARs");
+ return nullptr;
+ }
+ nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
+ nsresult rv;
+
+ if (!NS_IsMainThread()) {
+ // If this call is off the main thread, synchronously proxy it
+ // to the main thread.
+ RefPtr<LoadModuleMainThreadRunnable> r =
+ new LoadModuleMainThreadRunnable(this, aFile);
+ NS_DispatchToMainThread(r, NS_DISPATCH_SYNC);
+ return r->mResult;
+ }
+
+ nsCOMPtr<nsIHashable> hashedFile(do_QueryInterface(file));
+ if (!hashedFile) {
+ NS_ERROR("nsIFile is not nsIHashable");
+ return nullptr;
+ }
+
+ nsAutoCString filePath;
+ file->GetNativePath(filePath);
+
+ NativeLoadData data;
+
+ if (mLibraries.Get(hashedFile, &data)) {
+ NS_ASSERTION(data.mModule, "Corrupt mLibraries hash");
+ LOG(LogLevel::Debug,
+ ("nsNativeModuleLoader::LoadModule(\"%s\") - found in cache",
+ filePath.get()));
+ return data.mModule;
+ }
+
+ // We haven't loaded this module before
+ {
+#ifdef HAS_DLL_BLOCKLIST
+ AutoSetXPCOMLoadOnMainThread guard;
+#endif
+ rv = file->Load(&data.mLibrary);
+ }
+
+ if (NS_FAILED(rv)) {
+ char errorMsg[1024] = "<unknown; can't get error from NSPR>";
+
+ if (PR_GetErrorTextLength() < (int)sizeof(errorMsg)) {
+ PR_GetErrorText(errorMsg);
+ }
+
+ LogMessage("Failed to load native module at path '%s': (%lx) %s",
+ filePath.get(), rv, errorMsg);
+
+ return nullptr;
+ }
+
+#ifdef IMPLEMENT_BREAK_AFTER_LOAD
+ nsAutoCString leafName;
+ file->GetNativeLeafName(leafName);
+
+ char* env = getenv("XPCOM_BREAK_ON_LOAD");
+ char* blist;
+ if (env && *env && (blist = strdup(env))) {
+ char* nextTok = blist;
+ while (char* token = NS_strtok(":", &nextTok)) {
+ if (leafName.Find(token, true) != kNotFound) {
+ NS_BREAK();
+ }
+ }
+
+ free(blist);
+ }
+#endif
+
+ void* module = PR_FindSymbol(data.mLibrary, "NSModule");
+ if (!module) {
+ LogMessage("Native module at path '%s' doesn't export symbol `NSModule`.",
+ filePath.get());
+ PR_UnloadLibrary(data.mLibrary);
+ return nullptr;
+ }
+
+ data.mModule = *(mozilla::Module const* const*)module;
+ if (mozilla::Module::kVersion != data.mModule->mVersion) {
+ LogMessage("Native module at path '%s' is incompatible with this version of Firefox, has version %i, expected %i.",
+ filePath.get(), data.mModule->mVersion,
+ mozilla::Module::kVersion);
+ PR_UnloadLibrary(data.mLibrary);
+ return nullptr;
+ }
+
+ mLibraries.Put(hashedFile, data); // infallible
+ return data.mModule;
+}
+
+void
+nsNativeModuleLoader::UnloadLibraries()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Shutdown not on main thread?");
+
+ for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
+ NativeLoadData& loadData = iter.Data();
+ loadData.mModule = nullptr;
+ }
+
+ for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
+ if (MOZ_LOG_TEST(sNativeModuleLoaderLog, LogLevel::Debug)) {
+ nsIHashable* hashedFile = iter.Key();
+ nsCOMPtr<nsIFile> file(do_QueryInterface(hashedFile));
+
+ nsAutoCString filePath;
+ file->GetNativePath(filePath);
+
+ LOG(LogLevel::Debug,
+ ("nsNativeModuleLoader::UnloaderFunc(\"%s\")", filePath.get()));
+ }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcnt::SetActivityIsLegal(false);
+#endif
+
+#if 0
+ // XXXbsmedberg: do this as soon as the static-destructor crash(es)
+ // are fixed
+ NativeLoadData& loadData = iter.Data();
+ PRStatus ret = PR_UnloadLibrary(loadData.mLibrary);
+ NS_ASSERTION(ret == PR_SUCCESS, "Failed to unload library");
+#endif
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcnt::SetActivityIsLegal(true);
+#endif
+
+ iter.Remove();
+ }
+}
diff --git a/xpcom/components/nsNativeModuleLoader.h b/xpcom/components/nsNativeModuleLoader.h
new file mode 100644
index 000000000..b05a4a7a2
--- /dev/null
+++ b/xpcom/components/nsNativeModuleLoader.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNativeModuleLoader_h__
+#define nsNativeModuleLoader_h__
+
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "prlink.h"
+
+namespace mozilla {
+class FileLocation;
+} // namespace mozilla
+
+class nsNativeModuleLoader final
+{
+public:
+ const mozilla::Module* LoadModule(mozilla::FileLocation& aFile);
+
+ nsresult Init();
+
+ void UnloadLibraries();
+
+private:
+ struct NativeLoadData
+ {
+ NativeLoadData() : mModule(nullptr), mLibrary(nullptr) {}
+
+ const mozilla::Module* mModule;
+ PRLibrary* mLibrary;
+ };
+
+ nsDataHashtable<nsHashableHashKey, NativeLoadData> mLibraries;
+};
+
+#endif /* nsNativeModuleLoader_h__ */
diff --git a/xpcom/doc/README b/xpcom/doc/README
new file mode 100644
index 000000000..6817f789f
--- /dev/null
+++ b/xpcom/doc/README
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>READ ME</title>
+ </head>
+ <body>
+ <h4>
+ XPCOM documentation can be found at <a href="https://developer.mozilla.org/en-US/docs/XPCOM">https://developer.mozilla.org/en-US/docs/XPCOM</a>
+ </h4>
+ </body>
+</html>
diff --git a/xpcom/ds/IncrementalTokenizer.cpp b/xpcom/ds/IncrementalTokenizer.cpp
new file mode 100644
index 000000000..429428516
--- /dev/null
+++ b/xpcom/ds/IncrementalTokenizer.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/IncrementalTokenizer.h"
+
+#include "mozilla/AutoRestore.h"
+
+#include "nsIInputStream.h"
+#include "IncrementalTokenizer.h"
+#include <algorithm>
+
+namespace mozilla {
+
+IncrementalTokenizer::IncrementalTokenizer(Consumer aConsumer,
+ const char * aWhitespaces,
+ const char * aAdditionalWordChars,
+ uint32_t aRawMinBuffered)
+ : TokenizerBase(aWhitespaces, aAdditionalWordChars)
+#ifdef DEBUG
+ , mConsuming(false)
+#endif
+ , mNeedMoreInput(false)
+ , mRollback(false)
+ , mInputCursor(0)
+ , mConsumer(aConsumer)
+{
+ mInputFinished = false;
+ mMinRawDelivery = aRawMinBuffered;
+}
+
+nsresult IncrementalTokenizer::FeedInput(const nsACString & aInput)
+{
+ NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
+ MOZ_ASSERT(!mInputFinished);
+
+ mInput.Cut(0, mInputCursor);
+ mInputCursor = 0;
+
+ mInput.Append(aInput);
+
+ return Process();
+}
+
+nsresult IncrementalTokenizer::FeedInput(nsIInputStream * aInput, uint32_t aCount)
+{
+ NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
+ MOZ_ASSERT(!mInputFinished);
+ MOZ_ASSERT(!mConsuming);
+
+ mInput.Cut(0, mInputCursor);
+ mInputCursor = 0;
+
+ nsresult rv = NS_OK;
+ while (NS_SUCCEEDED(rv) && aCount) {
+ nsCString::index_type remainder = mInput.Length();
+ nsCString::index_type load =
+ std::min<nsCString::index_type>(aCount, PR_UINT32_MAX - remainder);
+
+ if (!load) {
+ // To keep the API simple, we fail if the input data buffer if filled.
+ // It's highly unlikely there will ever be such amout of data cumulated
+ // unless a logic fault in the consumer code.
+ NS_ERROR("IncrementalTokenizer consumer not reading data?");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!mInput.SetLength(remainder + load, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCString::char_iterator buffer = mInput.BeginWriting() + remainder;
+
+ uint32_t read;
+ rv = aInput->Read(buffer, load, &read);
+ if (NS_SUCCEEDED(rv)) {
+ // remainder + load fits the uint32_t size, so must remainder + read.
+ mInput.SetLength(remainder + read);
+ aCount -= read;
+
+ rv = Process();
+ }
+ }
+
+ return rv;
+}
+
+nsresult IncrementalTokenizer::FinishInput()
+{
+ NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
+ MOZ_ASSERT(!mInputFinished);
+ MOZ_ASSERT(!mConsuming);
+
+ mInput.Cut(0, mInputCursor);
+ mInputCursor = 0;
+
+ mInputFinished = true;
+ nsresult rv = Process();
+ mConsumer = nullptr;
+ return rv;
+}
+
+bool IncrementalTokenizer::Next(Token & aToken)
+{
+ // Assert we are called only from the consumer callback
+ MOZ_ASSERT(mConsuming);
+
+ if (mPastEof) {
+ return false;
+ }
+
+ nsACString::const_char_iterator next = Parse(aToken);
+ mPastEof = aToken.Type() == TOKEN_EOF;
+ if (next == mCursor && !mPastEof) {
+ // Not enough input to make a deterministic decision.
+ return false;
+ }
+
+ AssignFragment(aToken, mCursor, next);
+ mCursor = next;
+ return true;
+}
+
+void IncrementalTokenizer::NeedMoreInput()
+{
+ // Assert we are called only from the consumer callback
+ MOZ_ASSERT(mConsuming);
+
+ // When the input has been finished, we can't set the flag to prevent
+ // indefinite wait for more input (that will never come)
+ mNeedMoreInput = !mInputFinished;
+}
+
+void IncrementalTokenizer::Rollback()
+{
+ // Assert we are called only from the consumer callback
+ MOZ_ASSERT(mConsuming);
+
+ mRollback = true;
+}
+
+nsresult IncrementalTokenizer::Process()
+{
+#ifdef DEBUG
+ // Assert we are not re-entered
+ MOZ_ASSERT(!mConsuming);
+
+ AutoRestore<bool> consuming(mConsuming);
+ mConsuming = true;
+#endif
+
+ MOZ_ASSERT(!mPastEof);
+
+ nsresult rv = NS_OK;
+
+ mInput.BeginReading(mCursor);
+ mCursor += mInputCursor;
+ mInput.EndReading(mEnd);
+
+ while (NS_SUCCEEDED(rv) && !mPastEof) {
+ Token token;
+ nsACString::const_char_iterator next = Parse(token);
+ mPastEof = token.Type() == TOKEN_EOF;
+ if (next == mCursor && !mPastEof) {
+ // Not enough input to make a deterministic decision.
+ break;
+ }
+
+ AssignFragment(token, mCursor, next);
+
+ nsACString::const_char_iterator rollback = mCursor;
+ mCursor = next;
+
+ mNeedMoreInput = mRollback = false;
+
+ rv = mConsumer(token, *this);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ if (mNeedMoreInput || mRollback) {
+ mCursor = rollback;
+ mPastEof = false;
+ if (mNeedMoreInput) {
+ break;
+ }
+ }
+ }
+
+ mInputCursor = mCursor - mInput.BeginReading();
+ return rv;
+}
+
+} // mozilla
diff --git a/xpcom/ds/IncrementalTokenizer.h b/xpcom/ds/IncrementalTokenizer.h
new file mode 100644
index 000000000..f93668e63
--- /dev/null
+++ b/xpcom/ds/IncrementalTokenizer.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef INCREMENTAL_TOKENIZER_H__
+#define INCREMENTAL_TOKENIZER_H__
+
+#include "mozilla/Tokenizer.h"
+
+#include "nsError.h"
+#include <functional>
+
+class nsIInputStream;
+
+namespace mozilla {
+
+class IncrementalTokenizer : public TokenizerBase
+{
+public:
+ /**
+ * The consumer callback. The function is called for every single token
+ * as found in the input. Failure result returned by this callback stops
+ * the tokenization immediately and bubbles to result of Feed/FinishInput.
+ *
+ * Fragment()s of consumed tokens are ensured to remain valid until next call to
+ * Feed/FinishInput and are pointing to a single linear buffer. Hence, those can
+ * be safely used to accumulate the data for processing after Feed/FinishInput
+ * returned.
+ */
+ typedef std::function<nsresult(Token const&, IncrementalTokenizer& i)> Consumer;
+
+ /**
+ * For aWhitespaces and aAdditionalWordChars arguments see TokenizerBase.
+ *
+ * @param aConsumer
+ * A mandatory non-null argument, a function that consumes the tokens as they
+ * come when the tokenizer is fed.
+ * @param aRawMinBuffered
+ * When we have buffered at least aRawMinBuffered data, but there was no custom
+ * token found so far because of too small incremental feed chunks, deliver
+ * the raw data to preserve streaming and to save memory. This only has effect
+ * in OnlyCustomTokenizing mode.
+ */
+ explicit IncrementalTokenizer(Consumer aConsumer,
+ const char* aWhitespaces = nullptr,
+ const char* aAdditionalWordChars = nullptr,
+ uint32_t aRawMinBuffered = 1024);
+
+ /**
+ * Pushes the input to be tokenized. These directly call the Consumer callback
+ * on every found token. Result of the Consumer callback is returned here.
+ *
+ * The tokenizer must be initialized with a valid consumer prior call to these
+ * methods. It's not allowed to call Feed/FinishInput from inside the Consumer
+ * callback.
+ */
+ nsresult FeedInput(const nsACString& aInput);
+ nsresult FeedInput(nsIInputStream* aInput, uint32_t aCount);
+ nsresult FinishInput();
+
+ /**
+ * Can only be called from inside the consumer callback.
+ *
+ * When there is still anything to read from the input, tokenize it, store
+ * the token type and value to aToken result and shift the cursor past this
+ * just parsed token. Each call to Next() reads another token from
+ * the input and shifts the cursor.
+ *
+ * Returns false if there is not enough data to deterministically recognize
+ * tokens or when the last returned token was EOF.
+ */
+ MOZ_MUST_USE
+ bool Next(Token& aToken);
+
+ /**
+ * Can only be called from inside the consumer callback.
+ *
+ * Tells the tokenizer to revert the cursor and stop the async parsing until
+ * next feed of the input. This is useful when more than one token is needed
+ * to decide on the syntax but there is not enough input to get a next token
+ * (Next() returned false.)
+ */
+ void NeedMoreInput();
+
+ /**
+ * Can only be called from inside the consumer callback.
+ *
+ * This makes the consumer callback be called again while parsing
+ * the input at the previous cursor position again. This is useful when
+ * the tokenizer state (custom tokens, tokenization mode) has changed and
+ * we want to re-parse the input again.
+ */
+ void Rollback();
+
+private:
+ // Loops over the input with TokenizerBase::Parse and calls the Consumer callback.
+ nsresult Process();
+
+#ifdef DEBUG
+ // True when inside the consumer callback, used only for assertions.
+ bool mConsuming;
+#endif // DEBUG
+ // Modifyable only from the Consumer callback, tells the parser to break, rollback
+ // and wait for more input.
+ bool mNeedMoreInput;
+ // Modifyable only from the Consumer callback, tells the parser to rollback and
+ // parse the input again, with (if modified) new settings of the tokenizer.
+ bool mRollback;
+ // The input buffer. Updated with each call to Feed/FinishInput.
+ nsCString mInput;
+ // Numerical index pointing at the current cursor position. We don't keep direct
+ // reference to the string buffer since the buffer gets often reallocated.
+ nsCString::index_type mInputCursor;
+ // Refernce to the consumer function.
+ Consumer mConsumer;
+};
+
+} // mozilla
+
+#endif
diff --git a/xpcom/ds/StickyTimeDuration.h b/xpcom/ds/StickyTimeDuration.h
new file mode 100644
index 000000000..39a887dbc
--- /dev/null
+++ b/xpcom/ds/StickyTimeDuration.h
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_StickyTimeDuration_h
+#define mozilla_StickyTimeDuration_h
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/FloatingPoint.h"
+
+namespace mozilla {
+
+/**
+ * A ValueCalculator class that performs additional checks before performing
+ * arithmetic operations such that if either operand is Forever (or the
+ * negative equivalent) the result remains Forever (or the negative equivalent
+ * as appropriate).
+ *
+ * Currently this only checks if either argument to each operation is
+ * Forever/-Forever. However, it is possible that, for example,
+ * aA + aB > INT64_MAX (or < INT64_MIN).
+ *
+ * We currently don't check for that case since we don't expect that to
+ * happen often except under test conditions in which case the wrapping
+ * behavior is probably acceptable.
+ */
+class StickyTimeDurationValueCalculator
+{
+public:
+ static int64_t
+ Add(int64_t aA, int64_t aB)
+ {
+ MOZ_ASSERT((aA != INT64_MAX || aB != INT64_MIN) &&
+ (aA != INT64_MIN || aB != INT64_MAX),
+ "'Infinity + -Infinity' and '-Infinity + Infinity'"
+ " are undefined");
+
+ // Forever + x = Forever
+ // x + Forever = Forever
+ if (aA == INT64_MAX || aB == INT64_MAX) {
+ return INT64_MAX;
+ }
+ // -Forever + x = -Forever
+ // x + -Forever = -Forever
+ if (aA == INT64_MIN || aB == INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return aA + aB;
+ }
+
+ // Note that we can't just define Add and have BaseTimeDuration call Add with
+ // negative arguments since INT64_MAX != -INT64_MIN so the saturating logic
+ // won't work.
+ static int64_t
+ Subtract(int64_t aA, int64_t aB)
+ {
+ MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) || aA != aB,
+ "'Infinity - Infinity' and '-Infinity - -Infinity'"
+ " are undefined");
+
+ // Forever - x = Forever
+ // x - -Forever = Forever
+ if (aA == INT64_MAX || aB == INT64_MIN) {
+ return INT64_MAX;
+ }
+ // -Forever - x = -Forever
+ // x - Forever = -Forever
+ if (aA == INT64_MIN || aB == INT64_MAX) {
+ return INT64_MIN;
+ }
+
+ return aA - aB;
+ }
+
+ template <typename T>
+ static int64_t
+ Multiply(int64_t aA, T aB) {
+ // Specializations for double, float, and int64_t are provided following.
+ return Multiply(aA, static_cast<int64_t>(aB));
+ }
+
+ static int64_t
+ Divide(int64_t aA, int64_t aB) {
+ MOZ_ASSERT(aB != 0, "Division by zero");
+ MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) ||
+ (aB != INT64_MAX && aB != INT64_MIN),
+ "Dividing +/-Infinity by +/-Infinity is undefined");
+
+ // Forever / +x = Forever
+ // Forever / -x = -Forever
+ // -Forever / +x = -Forever
+ // -Forever / -x = Forever
+ if (aA == INT64_MAX || aA == INT64_MIN) {
+ return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX;
+ }
+ // x / Forever = 0
+ // x / -Forever = 0
+ if (aB == INT64_MAX || aB == INT64_MIN) {
+ return 0;
+ }
+
+ return aA / aB;
+ }
+
+ static double
+ DivideDouble(int64_t aA, int64_t aB)
+ {
+ MOZ_ASSERT(aB != 0, "Division by zero");
+ MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) ||
+ (aB != INT64_MAX && aB != INT64_MIN),
+ "Dividing +/-Infinity by +/-Infinity is undefined");
+
+ // Forever / +x = Forever
+ // Forever / -x = -Forever
+ // -Forever / +x = -Forever
+ // -Forever / -x = Forever
+ if (aA == INT64_MAX || aA == INT64_MIN) {
+ return (aA >= 0) ^ (aB >= 0)
+ ? NegativeInfinity<double>()
+ : PositiveInfinity<double>();
+ }
+ // x / Forever = 0
+ // x / -Forever = 0
+ if (aB == INT64_MAX || aB == INT64_MIN) {
+ return 0.0;
+ }
+
+ return static_cast<double>(aA) / aB;
+ }
+
+ static int64_t
+ Modulo(int64_t aA, int64_t aB)
+ {
+ MOZ_ASSERT(aA != INT64_MAX && aA != INT64_MIN,
+ "Infinity modulo x is undefined");
+
+ return aA % aB;
+ }
+};
+
+template <>
+inline int64_t
+StickyTimeDurationValueCalculator::Multiply<int64_t>(int64_t aA,
+ int64_t aB)
+{
+ MOZ_ASSERT((aA != 0 || (aB != INT64_MIN && aB != INT64_MAX)) &&
+ ((aA != INT64_MIN && aA != INT64_MAX) || aB != 0),
+ "Multiplication of infinity by zero");
+
+ // Forever * +x = Forever
+ // Forever * -x = -Forever
+ // -Forever * +x = -Forever
+ // -Forever * -x = Forever
+ //
+ // i.e. If one or more of the arguments is +/-Forever, then
+ // return -Forever if the signs differ, or +Forever otherwise.
+ if (aA == INT64_MAX || aA == INT64_MIN ||
+ aB == INT64_MAX || aB == INT64_MIN) {
+ return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX;
+ }
+
+ return aA * aB;
+}
+
+template <>
+inline int64_t
+StickyTimeDurationValueCalculator::Multiply<double>(int64_t aA, double aB)
+{
+ MOZ_ASSERT((aA != 0 || (!IsInfinite(aB))) &&
+ ((aA != INT64_MIN && aA != INT64_MAX) || aB != 0.0),
+ "Multiplication of infinity by zero");
+
+ // As with Multiply<int64_t>, if one or more of the arguments is
+ // +/-Forever or +/-Infinity, then return -Forever if the signs differ,
+ // or +Forever otherwise.
+ if (aA == INT64_MAX || aA == INT64_MIN || IsInfinite(aB)) {
+ return (aA >= 0) ^ (aB >= 0.0) ? INT64_MIN : INT64_MAX;
+ }
+
+ return aA * aB;
+}
+
+template <>
+inline int64_t
+StickyTimeDurationValueCalculator::Multiply<float>(int64_t aA, float aB)
+{
+ MOZ_ASSERT(IsInfinite(aB) == IsInfinite(static_cast<double>(aB)),
+ "Casting to float loses infinite-ness");
+
+ return Multiply(aA, static_cast<double>(aB));
+}
+
+/**
+ * Specialization of BaseTimeDuration that uses
+ * StickyTimeDurationValueCalculator for arithmetic on the mValue member.
+ *
+ * Use this class when you need a time duration that is expected to hold values
+ * of Forever (or the negative equivalent) *and* when you expect that
+ * time duration to be used in arithmetic operations (and not just value
+ * comparisons).
+ */
+typedef BaseTimeDuration<StickyTimeDurationValueCalculator>
+ StickyTimeDuration;
+
+// Template specializations to allow arithmetic between StickyTimeDuration
+// and TimeDuration objects by falling back to the safe behavior.
+inline StickyTimeDuration
+operator+(const TimeDuration& aA, const StickyTimeDuration& aB)
+{
+ return StickyTimeDuration(aA) + aB;
+}
+inline StickyTimeDuration
+operator+(const StickyTimeDuration& aA, const TimeDuration& aB)
+{
+ return aA + StickyTimeDuration(aB);
+}
+
+inline StickyTimeDuration
+operator-(const TimeDuration& aA, const StickyTimeDuration& aB)
+{
+ return StickyTimeDuration(aA) - aB;
+}
+inline StickyTimeDuration
+operator-(const StickyTimeDuration& aA, const TimeDuration& aB)
+{
+ return aA - StickyTimeDuration(aB);
+}
+
+inline StickyTimeDuration&
+operator+=(StickyTimeDuration &aA, const TimeDuration& aB)
+{
+ return aA += StickyTimeDuration(aB);
+}
+inline StickyTimeDuration&
+operator-=(StickyTimeDuration &aA, const TimeDuration& aB)
+{
+ return aA -= StickyTimeDuration(aB);
+}
+
+inline double
+operator/(const TimeDuration& aA, const StickyTimeDuration& aB)
+{
+ return StickyTimeDuration(aA) / aB;
+}
+inline double
+operator/(const StickyTimeDuration& aA, const TimeDuration& aB)
+{
+ return aA / StickyTimeDuration(aB);
+}
+
+inline StickyTimeDuration
+operator%(const TimeDuration& aA, const StickyTimeDuration& aB)
+{
+ return StickyTimeDuration(aA) % aB;
+}
+inline StickyTimeDuration
+operator%(const StickyTimeDuration& aA, const TimeDuration& aB)
+{
+ return aA % StickyTimeDuration(aB);
+}
+
+} // namespace mozilla
+
+#endif /* mozilla_StickyTimeDuration_h */
diff --git a/xpcom/ds/Tokenizer.cpp b/xpcom/ds/Tokenizer.cpp
new file mode 100644
index 000000000..66cc1ebb7
--- /dev/null
+++ b/xpcom/ds/Tokenizer.cpp
@@ -0,0 +1,738 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Tokenizer.h"
+
+#include "nsUnicharUtils.h"
+#include <algorithm>
+
+namespace mozilla {
+
+static const char sWhitespaces[] = " \t";
+
+Tokenizer::Tokenizer(const nsACString& aSource,
+ const char* aWhitespaces,
+ const char* aAdditionalWordChars)
+ : TokenizerBase(aWhitespaces, aAdditionalWordChars)
+{
+ mInputFinished = true;
+ aSource.BeginReading(mCursor);
+ mRecord = mRollback = mCursor;
+ aSource.EndReading(mEnd);
+}
+
+Tokenizer::Tokenizer(const char* aSource,
+ const char* aWhitespaces,
+ const char* aAdditionalWordChars)
+ : Tokenizer(nsDependentCString(aSource), aWhitespaces, aAdditionalWordChars)
+{
+}
+
+bool
+Tokenizer::Next(Token& aToken)
+{
+ if (!HasInput()) {
+ mHasFailed = true;
+ return false;
+ }
+
+ mRollback = mCursor;
+ mCursor = Parse(aToken);
+
+ AssignFragment(aToken, mRollback, mCursor);
+
+ mPastEof = aToken.Type() == TOKEN_EOF;
+ mHasFailed = false;
+ return true;
+}
+
+bool
+Tokenizer::Check(const TokenType aTokenType, Token& aResult)
+{
+ if (!HasInput()) {
+ mHasFailed = true;
+ return false;
+ }
+
+ nsACString::const_char_iterator next = Parse(aResult);
+ if (aTokenType != aResult.Type()) {
+ mHasFailed = true;
+ return false;
+ }
+
+ mRollback = mCursor;
+ mCursor = next;
+
+ AssignFragment(aResult, mRollback, mCursor);
+
+ mPastEof = aResult.Type() == TOKEN_EOF;
+ mHasFailed = false;
+ return true;
+}
+
+bool
+Tokenizer::Check(const Token& aToken)
+{
+ if (!HasInput()) {
+ mHasFailed = true;
+ return false;
+ }
+
+ Token parsed;
+ nsACString::const_char_iterator next = Parse(parsed);
+ if (!aToken.Equals(parsed)) {
+ mHasFailed = true;
+ return false;
+ }
+
+ mRollback = mCursor;
+ mCursor = next;
+ mPastEof = parsed.Type() == TOKEN_EOF;
+ mHasFailed = false;
+ return true;
+}
+
+void
+Tokenizer::SkipWhites(WhiteSkipping aIncludeNewLines)
+{
+ if (!CheckWhite() && (aIncludeNewLines == DONT_INCLUDE_NEW_LINE || !CheckEOL())) {
+ return;
+ }
+
+ nsACString::const_char_iterator rollback = mRollback;
+ while (CheckWhite() || (aIncludeNewLines == INCLUDE_NEW_LINE && CheckEOL())) {
+ }
+
+ mHasFailed = false;
+ mRollback = rollback;
+}
+
+void
+Tokenizer::SkipUntil(Token const& aToken)
+{
+ nsACString::const_char_iterator rollback = mCursor;
+ const Token eof = Token::EndOfFile();
+
+ Token t;
+ while (Next(t)) {
+ if (aToken.Equals(t) || eof.Equals(t)) {
+ Rollback();
+ break;
+ }
+ }
+
+ mRollback = rollback;
+}
+
+bool
+Tokenizer::CheckChar(bool (*aClassifier)(const char aChar))
+{
+ if (!aClassifier) {
+ MOZ_ASSERT(false);
+ return false;
+ }
+
+ if (!HasInput() || mCursor == mEnd) {
+ mHasFailed = true;
+ return false;
+ }
+
+ if (!aClassifier(*mCursor)) {
+ mHasFailed = true;
+ return false;
+ }
+
+ mRollback = mCursor;
+ ++mCursor;
+ mHasFailed = false;
+ return true;
+}
+
+bool
+Tokenizer::ReadChar(char* aValue)
+{
+ MOZ_RELEASE_ASSERT(aValue);
+
+ Token t;
+ if (!Check(TOKEN_CHAR, t)) {
+ return false;
+ }
+
+ *aValue = t.AsChar();
+ return true;
+}
+
+bool
+Tokenizer::ReadChar(bool (*aClassifier)(const char aChar), char* aValue)
+{
+ MOZ_RELEASE_ASSERT(aValue);
+
+ if (!CheckChar(aClassifier)) {
+ return false;
+ }
+
+ *aValue = *mRollback;
+ return true;
+}
+
+bool
+Tokenizer::ReadWord(nsACString& aValue)
+{
+ Token t;
+ if (!Check(TOKEN_WORD, t)) {
+ return false;
+ }
+
+ aValue.Assign(t.AsString());
+ return true;
+}
+
+bool
+Tokenizer::ReadWord(nsDependentCSubstring& aValue)
+{
+ Token t;
+ if (!Check(TOKEN_WORD, t)) {
+ return false;
+ }
+
+ aValue.Rebind(t.AsString().BeginReading(), t.AsString().Length());
+ return true;
+}
+
+bool
+Tokenizer::ReadUntil(Token const& aToken, nsACString& aResult, ClaimInclusion aInclude)
+{
+ nsDependentCSubstring substring;
+ bool rv = ReadUntil(aToken, substring, aInclude);
+ aResult.Assign(substring);
+ return rv;
+}
+
+bool
+Tokenizer::ReadUntil(Token const& aToken, nsDependentCSubstring& aResult, ClaimInclusion aInclude)
+{
+ Record();
+ nsACString::const_char_iterator rollback = mCursor;
+
+ bool found = false;
+ Token t;
+ while (Next(t)) {
+ if (aToken.Equals(t)) {
+ found = true;
+ break;
+ }
+ }
+
+ Claim(aResult, aInclude);
+ mRollback = rollback;
+ return found;
+}
+
+void
+Tokenizer::Rollback()
+{
+ MOZ_ASSERT(mCursor > mRollback || mPastEof,
+ "Tokenizer::Rollback() cannot use twice or before any parsing");
+
+ mPastEof = false;
+ mHasFailed = false;
+ mCursor = mRollback;
+}
+
+void
+Tokenizer::Record(ClaimInclusion aInclude)
+{
+ mRecord = aInclude == INCLUDE_LAST
+ ? mRollback
+ : mCursor;
+}
+
+void
+Tokenizer::Claim(nsACString& aResult, ClaimInclusion aInclusion)
+{
+ nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
+ ? mRollback
+ : mCursor;
+ aResult.Assign(Substring(mRecord, close));
+}
+
+void
+Tokenizer::Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclusion)
+{
+ nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
+ ? mRollback
+ : mCursor;
+ aResult.Rebind(mRecord, close - mRecord);
+}
+
+// TokenizerBase
+
+TokenizerBase::TokenizerBase(const char* aWhitespaces,
+ const char* aAdditionalWordChars)
+ : mPastEof(false)
+ , mHasFailed(false)
+ , mInputFinished(true)
+ , mMode(Mode::FULL)
+ , mMinRawDelivery(1024)
+ , mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces)
+ , mAdditionalWordChars(aAdditionalWordChars)
+ , mCursor(nullptr)
+ , mEnd(nullptr)
+ , mNextCustomTokenID(TOKEN_CUSTOM0)
+{
+}
+
+TokenizerBase::Token
+TokenizerBase::AddCustomToken(const nsACString & aValue,
+ ECaseSensitivity aCaseInsensitivity, bool aEnabled)
+{
+ MOZ_ASSERT(!aValue.IsEmpty());
+
+ UniquePtr<Token>& t = *mCustomTokens.AppendElement();
+ t = MakeUnique<Token>();
+
+ t->mType = static_cast<TokenType>(++mNextCustomTokenID);
+ t->mCustomCaseInsensitivity = aCaseInsensitivity;
+ t->mCustomEnabled = aEnabled;
+ t->mCustom.Assign(aValue);
+ return *t;
+}
+
+void
+TokenizerBase::RemoveCustomToken(Token& aToken)
+{
+ if (aToken.mType == TOKEN_UNKNOWN) {
+ // Already removed
+ return;
+ }
+
+ for (UniquePtr<Token> const& custom : mCustomTokens) {
+ if (custom->mType == aToken.mType) {
+ mCustomTokens.RemoveElement(custom);
+ aToken.mType = TOKEN_UNKNOWN;
+ return;
+ }
+ }
+
+ MOZ_ASSERT(false, "Token to remove not found");
+}
+
+void
+TokenizerBase::EnableCustomToken(Token const& aToken, bool aEnabled)
+{
+ if (aToken.mType == TOKEN_UNKNOWN) {
+ // Already removed
+ return;
+ }
+
+ for (UniquePtr<Token> const& custom : mCustomTokens) {
+ if (custom->Type() == aToken.Type()) {
+ // This effectively destroys the token instance.
+ custom->mCustomEnabled = aEnabled;
+ return;
+ }
+ }
+
+ MOZ_ASSERT(false, "Token to change not found");
+}
+
+void
+TokenizerBase::SetTokenizingMode(Mode aMode)
+{
+ mMode = aMode;
+}
+
+bool
+TokenizerBase::HasFailed() const
+{
+ return mHasFailed;
+}
+
+bool
+TokenizerBase::HasInput() const
+{
+ return !mPastEof;
+}
+
+nsACString::const_char_iterator
+TokenizerBase::Parse(Token& aToken) const
+{
+ if (mCursor == mEnd) {
+ if (!mInputFinished) {
+ return mCursor;
+ }
+
+ aToken = Token::EndOfFile();
+ return mEnd;
+ }
+
+ nsACString::size_type available = mEnd - mCursor;
+
+ uint32_t longestCustom = 0;
+ for (UniquePtr<Token> const& custom : mCustomTokens) {
+ if (IsCustom(mCursor, *custom, &longestCustom)) {
+ aToken = *custom;
+ return mCursor + custom->mCustom.Length();
+ }
+ }
+
+ if (!mInputFinished && available < longestCustom) {
+ // Not enough data to deterministically decide.
+ return mCursor;
+ }
+
+ nsACString::const_char_iterator next = mCursor;
+
+ if (mMode == Mode::CUSTOM_ONLY) {
+ // We have to do a brute-force search for all of the enabled custom
+ // tokens.
+ while (next < mEnd) {
+ ++next;
+ for (UniquePtr<Token> const& custom : mCustomTokens) {
+ if (IsCustom(next, *custom)) {
+ aToken = Token::Raw();
+ return next;
+ }
+ }
+ }
+
+ if (mInputFinished) {
+ // End of the data reached.
+ aToken = Token::Raw();
+ return next;
+ }
+
+ if (longestCustom < available && available > mMinRawDelivery) {
+ // We can return some data w/o waiting for either a custom token
+ // or call to FinishData() when we leave the tail where all the
+ // custom tokens potentially fit, so we can't lose only partially
+ // delivered tokens. This preserves reasonable granularity.
+ aToken = Token::Raw();
+ return mEnd - longestCustom + 1;
+ }
+
+ // Not enough data to deterministically decide.
+ return mCursor;
+ }
+
+ enum State {
+ PARSE_INTEGER,
+ PARSE_WORD,
+ PARSE_CRLF,
+ PARSE_LF,
+ PARSE_WS,
+ PARSE_CHAR,
+ } state;
+
+ if (IsWordFirst(*next)) {
+ state = PARSE_WORD;
+ } else if (IsNumber(*next)) {
+ state = PARSE_INTEGER;
+ } else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly?
+ state = PARSE_WS;
+ } else if (*next == '\r') {
+ state = PARSE_CRLF;
+ } else if (*next == '\n') {
+ state = PARSE_LF;
+ } else {
+ state = PARSE_CHAR;
+ }
+
+ mozilla::CheckedUint64 resultingNumber = 0;
+
+ while (next < mEnd) {
+ switch (state) {
+ case PARSE_INTEGER:
+ // Keep it simple for now
+ resultingNumber *= 10;
+ resultingNumber += static_cast<uint64_t>(*next - '0');
+
+ ++next;
+ if (IsPending(next)) {
+ break;
+ }
+ if (IsEnd(next) || !IsNumber(*next)) {
+ if (!resultingNumber.isValid()) {
+ aToken = Token::Error();
+ } else {
+ aToken = Token::Number(resultingNumber.value());
+ }
+ return next;
+ }
+ break;
+
+ case PARSE_WORD:
+ ++next;
+ if (IsPending(next)) {
+ break;
+ }
+ if (IsEnd(next) || !IsWord(*next)) {
+ aToken = Token::Word(Substring(mCursor, next));
+ return next;
+ }
+ break;
+
+ case PARSE_CRLF:
+ ++next;
+ if (IsPending(next)) {
+ break;
+ }
+ if (!IsEnd(next) && *next == '\n') { // LF is optional
+ ++next;
+ }
+ aToken = Token::NewLine();
+ return next;
+
+ case PARSE_LF:
+ ++next;
+ aToken = Token::NewLine();
+ return next;
+
+ case PARSE_WS:
+ ++next;
+ aToken = Token::Whitespace();
+ return next;
+
+ case PARSE_CHAR:
+ ++next;
+ aToken = Token::Char(*mCursor);
+ return next;
+ } // switch (state)
+ } // while (next < end)
+
+ MOZ_ASSERT(!mInputFinished);
+ return mCursor;
+}
+
+bool
+TokenizerBase::IsEnd(const nsACString::const_char_iterator& caret) const
+{
+ return caret == mEnd;
+}
+
+bool
+TokenizerBase::IsPending(const nsACString::const_char_iterator& caret) const
+{
+ return IsEnd(caret) && !mInputFinished;
+}
+
+bool
+TokenizerBase::IsWordFirst(const char aInput) const
+{
+ // TODO: make this fully work with unicode
+ return (ToLowerCase(static_cast<uint32_t>(aInput)) !=
+ ToUpperCase(static_cast<uint32_t>(aInput))) ||
+ '_' == aInput ||
+ (mAdditionalWordChars ? !!strchr(mAdditionalWordChars, aInput) : false);
+}
+
+bool
+TokenizerBase::IsWord(const char aInput) const
+{
+ return IsWordFirst(aInput) || IsNumber(aInput);
+}
+
+bool
+TokenizerBase::IsNumber(const char aInput) const
+{
+ // TODO: are there unicode numbers?
+ return aInput >= '0' && aInput <= '9';
+}
+
+bool
+TokenizerBase::IsCustom(const nsACString::const_char_iterator & caret,
+ const Token & aCustomToken,
+ uint32_t * aLongest) const
+{
+ MOZ_ASSERT(aCustomToken.mType > TOKEN_CUSTOM0);
+ if (!aCustomToken.mCustomEnabled) {
+ return false;
+ }
+
+ if (aLongest) {
+ *aLongest = std::max(*aLongest, aCustomToken.mCustom.Length());
+ }
+
+ uint32_t inputLength = mEnd - caret;
+ if (aCustomToken.mCustom.Length() > inputLength) {
+ return false;
+ }
+
+ nsDependentCSubstring inputFragment(caret, aCustomToken.mCustom.Length());
+ if (aCustomToken.mCustomCaseInsensitivity == CASE_INSENSITIVE) {
+ return inputFragment.Equals(aCustomToken.mCustom, nsCaseInsensitiveUTF8StringComparator());
+ }
+ return inputFragment.Equals(aCustomToken.mCustom);
+}
+
+void TokenizerBase::AssignFragment(Token& aToken,
+ nsACString::const_char_iterator begin,
+ nsACString::const_char_iterator end)
+{
+ aToken.AssignFragment(begin, end);
+}
+
+// TokenizerBase::Token
+
+TokenizerBase::Token::Token()
+ : mType(TOKEN_UNKNOWN)
+ , mChar(0)
+ , mInteger(0)
+ , mCustomCaseInsensitivity(CASE_SENSITIVE)
+ , mCustomEnabled(false)
+{
+}
+
+TokenizerBase::Token::Token(const Token& aOther)
+ : mType(aOther.mType)
+ , mCustom(aOther.mCustom)
+ , mChar(aOther.mChar)
+ , mInteger(aOther.mInteger)
+ , mCustomCaseInsensitivity(aOther.mCustomCaseInsensitivity)
+ , mCustomEnabled(aOther.mCustomEnabled)
+{
+ if (mType == TOKEN_WORD || mType > TOKEN_CUSTOM0) {
+ mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
+ }
+}
+
+TokenizerBase::Token&
+TokenizerBase::Token::operator=(const Token& aOther)
+{
+ mType = aOther.mType;
+ mCustom = aOther.mCustom;
+ mChar = aOther.mChar;
+ mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
+ mInteger = aOther.mInteger;
+ mCustomCaseInsensitivity = aOther.mCustomCaseInsensitivity;
+ mCustomEnabled = aOther.mCustomEnabled;
+ return *this;
+}
+
+void
+TokenizerBase::Token::AssignFragment(nsACString::const_char_iterator begin,
+ nsACString::const_char_iterator end)
+{
+ mFragment.Rebind(begin, end - begin);
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::Raw()
+{
+ Token t;
+ t.mType = TOKEN_RAW;
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::Word(const nsACString& aValue)
+{
+ Token t;
+ t.mType = TOKEN_WORD;
+ t.mWord.Rebind(aValue.BeginReading(), aValue.Length());
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::Char(const char aValue)
+{
+ Token t;
+ t.mType = TOKEN_CHAR;
+ t.mChar = aValue;
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::Number(const uint64_t aValue)
+{
+ Token t;
+ t.mType = TOKEN_INTEGER;
+ t.mInteger = aValue;
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::Whitespace()
+{
+ Token t;
+ t.mType = TOKEN_WS;
+ t.mChar = '\0';
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::NewLine()
+{
+ Token t;
+ t.mType = TOKEN_EOL;
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::EndOfFile()
+{
+ Token t;
+ t.mType = TOKEN_EOF;
+ return t;
+}
+
+// static
+TokenizerBase::Token
+TokenizerBase::Token::Error()
+{
+ Token t;
+ t.mType = TOKEN_ERROR;
+ return t;
+}
+
+bool
+TokenizerBase::Token::Equals(const Token& aOther) const
+{
+ if (mType != aOther.mType) {
+ return false;
+ }
+
+ switch (mType) {
+ case TOKEN_INTEGER:
+ return AsInteger() == aOther.AsInteger();
+ case TOKEN_WORD:
+ return AsString() == aOther.AsString();
+ case TOKEN_CHAR:
+ return AsChar() == aOther.AsChar();
+ default:
+ return true;
+ }
+}
+
+char
+TokenizerBase::Token::AsChar() const
+{
+ MOZ_ASSERT(mType == TOKEN_CHAR || mType == TOKEN_WS);
+ return mChar;
+}
+
+nsDependentCSubstring
+TokenizerBase::Token::AsString() const
+{
+ MOZ_ASSERT(mType == TOKEN_WORD);
+ return mWord;
+}
+
+uint64_t
+TokenizerBase::Token::AsInteger() const
+{
+ MOZ_ASSERT(mType == TOKEN_INTEGER);
+ return mInteger;
+}
+
+} // mozilla
diff --git a/xpcom/ds/Tokenizer.h b/xpcom/ds/Tokenizer.h
new file mode 100644
index 000000000..b4aad9ed9
--- /dev/null
+++ b/xpcom/ds/Tokenizer.h
@@ -0,0 +1,446 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef Tokenizer_h__
+#define Tokenizer_h__
+
+#include "nsString.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+class TokenizerBase
+{
+public:
+ /**
+ * The analyzer works with elements in the input cut to a sequence of token
+ * where each token has an elementary type
+ */
+ enum TokenType : uint32_t
+ {
+ TOKEN_UNKNOWN,
+ TOKEN_RAW,
+ TOKEN_ERROR,
+ TOKEN_INTEGER,
+ TOKEN_WORD,
+ TOKEN_CHAR,
+ TOKEN_WS,
+ TOKEN_EOL,
+ TOKEN_EOF,
+ TOKEN_CUSTOM0 = 1000
+ };
+
+ enum ECaseSensitivity
+ {
+ CASE_SENSITIVE,
+ CASE_INSENSITIVE
+ };
+
+ /**
+ * Class holding the type and the value of a token. It can be manually created
+ * to allow checks against it via methods of Tokenizer or are results of some of
+ * the Tokenizer's methods.
+ */
+ class Token
+ {
+ TokenType mType;
+ nsDependentCSubstring mWord;
+ nsCString mCustom;
+ char mChar;
+ uint64_t mInteger;
+ ECaseSensitivity mCustomCaseInsensitivity;
+ bool mCustomEnabled;
+
+ // If this token is a result of the parsing process, this member is referencing
+ // a sub-string in the input buffer. If this is externally created Token this
+ // member is left an empty string.
+ nsDependentCSubstring mFragment;
+
+ friend class TokenizerBase;
+ void AssignFragment(nsACString::const_char_iterator begin,
+ nsACString::const_char_iterator end);
+
+ static Token Raw();
+
+ public:
+ Token();
+ Token(const Token& aOther);
+ Token& operator=(const Token& aOther);
+
+ // Static constructors of tokens by type and value
+ static Token Word(const nsACString& aWord);
+ static Token Char(const char aChar);
+ static Token Number(const uint64_t aNumber);
+ static Token Whitespace();
+ static Token NewLine();
+ static Token EndOfFile();
+ static Token Error();
+
+ // Compares the two tokens, type must be identical and value
+ // of one of the tokens must be 'any' or equal.
+ bool Equals(const Token& aOther) const;
+
+ TokenType Type() const { return mType; }
+ char AsChar() const;
+ nsDependentCSubstring AsString() const;
+ uint64_t AsInteger() const;
+
+ nsDependentCSubstring Fragment() const { return mFragment; }
+ };
+
+ /**
+ * Consumers may register a custom string that, when found in the input, is considered
+ * a token and returned by Next*() and accepted by Check*() methods.
+ * AddCustomToken() returns a reference to a token that can then be comapred using
+ * Token::Equals() againts the output from Next*() or be passed to Check*().
+ */
+ Token AddCustomToken(const nsACString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true);
+ template <uint32_t N>
+ Token AddCustomToken(const char(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true)
+ {
+ return AddCustomToken(nsDependentCSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled);
+ }
+ void RemoveCustomToken(Token& aToken);
+ /**
+ * Only applies to a custom type of a Token (see AddCustomToken above.)
+ * This turns on and off token recognition. When a custom token is disabled,
+ * it's ignored as never added as a custom token.
+ */
+ void EnableCustomToken(Token const& aToken, bool aEnable);
+
+ /**
+ * Mode of tokenization.
+ * FULL tokenization, the default, recognizes built-in tokens and any custom tokens,
+ * if added.
+ * CUSTOM_ONLY will only recognize custom tokens, the rest is seen as 'raw'.
+ * This mode can be understood as a 'binary' mode.
+ */
+ enum class Mode
+ {
+ FULL,
+ CUSTOM_ONLY
+ };
+ void SetTokenizingMode(Mode aMode);
+
+ /**
+ * Return false iff the last Check*() call has returned false or when we've read past
+ * the end of the input string.
+ */
+ MOZ_MUST_USE bool HasFailed() const;
+
+protected:
+ explicit TokenizerBase(const char* aWhitespaces = nullptr,
+ const char* aAdditionalWordChars = nullptr);
+
+ // false if we have already read the EOF token.
+ bool HasInput() const;
+ // Main parsing function, it doesn't shift the read cursor, just returns the next
+ // token position.
+ nsACString::const_char_iterator Parse(Token& aToken) const;
+ // Is read cursor at the end?
+ bool IsEnd(const nsACString::const_char_iterator& caret) const;
+ // True, when we are at the end of the input data, but it has not been marked
+ // as complete yet. In that case we cannot proceed with providing a multi-char token.
+ bool IsPending(const nsACString::const_char_iterator & caret) const;
+ // Is read cursor on a character that is a word start?
+ bool IsWordFirst(const char aInput) const;
+ // Is read cursor on a character that is an in-word letter?
+ bool IsWord(const char aInput) const;
+ // Is read cursor on a character that is a valid number?
+ // TODO - support multiple radix
+ bool IsNumber(const char aInput) const;
+ // Is equal to the given custom token?
+ bool IsCustom(const nsACString::const_char_iterator& caret,
+ const Token& aCustomToken, uint32_t* aLongest = nullptr) const;
+
+ // Friendly helper to assign a fragment on a Token
+ static void AssignFragment(Token& aToken,
+ nsACString::const_char_iterator begin,
+ nsACString::const_char_iterator end);
+
+ // true iff we have already read the EOF token
+ bool mPastEof;
+ // true iff the last Check*() call has returned false, reverts to true on Rollback() call
+ bool mHasFailed;
+ // true if the input string is final (finished), false when we expect more data
+ // yet to be fed to the tokenizer (see IncrementalTokenizer derived class).
+ bool mInputFinished;
+ // custom only vs full tokenizing mode, see the Parse() method
+ Mode mMode;
+ // minimal raw data chunked delivery during incremental feed
+ uint32_t mMinRawDelivery;
+
+ // Customizable list of whitespaces
+ const char* mWhitespaces;
+ // Additinal custom word characters
+ const char* mAdditionalWordChars;
+
+ // All these point to the original buffer passed to the constructor or to the incremental
+ // buffer after FeedInput.
+ nsACString::const_char_iterator mCursor; // Position of the current (actually next to read) token start
+ nsACString::const_char_iterator mEnd; // End of the input position
+
+ // This is the list of tokens user has registered with AddCustomToken()
+ nsTArray<UniquePtr<Token>> mCustomTokens;
+ uint32_t mNextCustomTokenID;
+
+private:
+ TokenizerBase() = delete;
+ TokenizerBase(const TokenizerBase&) = delete;
+ TokenizerBase(TokenizerBase&&) = delete;
+ TokenizerBase(const TokenizerBase&&) = delete;
+ TokenizerBase &operator=(const TokenizerBase&) = delete;
+};
+
+/**
+ * This is a simple implementation of a lexical analyzer or maybe better
+ * called a tokenizer. It doesn't allow any user dictionaries or
+ * user define token types.
+ *
+ * It is limited only to ASCII input for now. UTF-8 or any other input
+ * encoding must yet be implemented.
+ */
+class Tokenizer : public TokenizerBase
+{
+public:
+ /**
+ * @param aSource
+ * The string to parse.
+ * IMPORTANT NOTE: Tokenizer doesn't ensure the input string buffer lifetime.
+ * It's up to the consumer to make sure the string's buffer outlives the Tokenizer!
+ * @param aWhitespaces
+ * If non-null Tokenizer will use this custom set of whitespaces for CheckWhite()
+ * and SkipWhites() calls.
+ * By default the list consists of space and tab.
+ * @param aAdditionalWordChars
+ * If non-null it will be added to the list of characters that consist a word.
+ * This is useful when you want to accept e.g. '-' in HTTP headers.
+ * By default a word character is consider any character for which upper case
+ * is different from lower case.
+ *
+ * If there is an overlap between aWhitespaces and aAdditionalWordChars, the check for
+ * word characters is made first.
+ */
+ explicit Tokenizer(const nsACString& aSource,
+ const char* aWhitespaces = nullptr,
+ const char* aAdditionalWordChars = nullptr);
+ explicit Tokenizer(const char* aSource,
+ const char* aWhitespaces = nullptr,
+ const char* aAdditionalWordChars = nullptr);
+
+ /**
+ * When there is still anything to read from the input, tokenize it, store the token type
+ * and value to aToken result and shift the cursor past this just parsed token. Each call
+ * to Next() reads another token from the input and shifts the cursor.
+ * Returns false if we have passed the end of the input.
+ */
+ MOZ_MUST_USE
+ bool Next(Token& aToken);
+
+ /**
+ * Parse the token on the input read cursor position, check its type is equal to aTokenType
+ * and if so, put it into aResult, shift the cursor and return true. Otherwise, leave
+ * the input read cursor position intact and return false.
+ */
+ MOZ_MUST_USE
+ bool Check(const TokenType aTokenType, Token& aResult);
+ /**
+ * Same as above method, just compares both token type and token value passed in aToken.
+ * When both the type and the value equals, shift the cursor and return true. Otherwise
+ * return false.
+ */
+ MOZ_MUST_USE
+ bool Check(const Token& aToken);
+
+ /**
+ * SkipWhites method (below) may also skip new line characters automatically.
+ */
+ enum WhiteSkipping {
+ /**
+ * SkipWhites will only skip what is defined as a white space (default).
+ */
+ DONT_INCLUDE_NEW_LINE = 0,
+ /**
+ * SkipWhites will skip definited white spaces as well as new lines
+ * automatically.
+ */
+ INCLUDE_NEW_LINE = 1
+ };
+
+ /**
+ * Skips any occurence of whitespaces specified in mWhitespaces member,
+ * optionally skip also new lines.
+ */
+ void SkipWhites(WhiteSkipping aIncludeNewLines = DONT_INCLUDE_NEW_LINE);
+
+ /**
+ * Skips all tokens until the given one is found or EOF is hit. The token
+ * or EOF are next to read.
+ */
+ void SkipUntil(Token const& aToken);
+
+ // These are mostly shortcuts for the Check() methods above.
+
+ /**
+ * Check whitespace character is present.
+ */
+ MOZ_MUST_USE
+ bool CheckWhite() { return Check(Token::Whitespace()); }
+ /**
+ * Check there is a single character on the read cursor position. If so, shift the read
+ * cursor position and return true. Otherwise false.
+ */
+ MOZ_MUST_USE
+ bool CheckChar(const char aChar) { return Check(Token::Char(aChar)); }
+ /**
+ * This is a customizable version of CheckChar. aClassifier is a function called with
+ * value of the character on the current input read position. If this user function
+ * returns true, read cursor is shifted and true returned. Otherwise false.
+ * The user classifiction function is not called when we are at or past the end and
+ * false is immediately returned.
+ */
+ MOZ_MUST_USE
+ bool CheckChar(bool (*aClassifier)(const char aChar));
+ /**
+ * Check for a whole expected word.
+ */
+ MOZ_MUST_USE
+ bool CheckWord(const nsACString& aWord) { return Check(Token::Word(aWord)); }
+ /**
+ * Shortcut for literal const word check with compile time length calculation.
+ */
+ template <uint32_t N>
+ MOZ_MUST_USE
+ bool CheckWord(const char (&aWord)[N]) { return Check(Token::Word(nsDependentCString(aWord, N - 1))); }
+ /**
+ * Checks \r, \n or \r\n.
+ */
+ MOZ_MUST_USE
+ bool CheckEOL() { return Check(Token::NewLine()); }
+ /**
+ * Checks we are at the end of the input string reading. If so, shift past the end
+ * and returns true. Otherwise does nothing and returns false.
+ */
+ MOZ_MUST_USE
+ bool CheckEOF() { return Check(Token::EndOfFile()); }
+
+ /**
+ * These are shortcuts to obtain the value immediately when the token type matches.
+ */
+ MOZ_MUST_USE bool ReadChar(char* aValue);
+ MOZ_MUST_USE bool ReadChar(bool (*aClassifier)(const char aChar),
+ char* aValue);
+ MOZ_MUST_USE bool ReadWord(nsACString& aValue);
+ MOZ_MUST_USE bool ReadWord(nsDependentCSubstring& aValue);
+
+ /**
+ * This is an integer read helper. It returns false and doesn't move the read
+ * cursor when any of the following happens:
+ * - the token at the read cursor is not an integer
+ * - the final number doesn't fit the T type
+ * Otherwise true is returned, aValue is filled with the integral number
+ * and the cursor is moved forward.
+ */
+ template <typename T>
+ MOZ_MUST_USE bool ReadInteger(T* aValue)
+ {
+ MOZ_RELEASE_ASSERT(aValue);
+
+ nsACString::const_char_iterator rollback = mRollback;
+ nsACString::const_char_iterator cursor = mCursor;
+ Token t;
+ if (!Check(TOKEN_INTEGER, t)) {
+ return false;
+ }
+
+ mozilla::CheckedInt<T> checked(t.AsInteger());
+ if (!checked.isValid()) {
+ // Move to a state as if Check() call has failed
+ mRollback = rollback;
+ mCursor = cursor;
+ mHasFailed = true;
+ return false;
+ }
+
+ *aValue = checked.value();
+ return true;
+ }
+
+ /**
+ * Returns the read cursor position back as it was before the last call of any parsing
+ * method of Tokenizer (Next, Check*, Skip*, Read*) so that the last operation
+ * can be repeated.
+ * Rollback cannot be used multiple times, it only reverts the last successfull parse
+ * operation. It also cannot be used before any parsing operation has been called
+ * on the Tokenizer.
+ */
+ void Rollback();
+
+ /**
+ * Record() and Claim() are collecting the input as it is being parsed to obtain
+ * a substring between particular syntax bounderies defined by any recursive
+ * descent parser or simple parser the Tokenizer is used to read the input for.
+ * Inlucsion of a token that has just been parsed can be controlled using an arguemnt.
+ */
+ enum ClaimInclusion {
+ /**
+ * Include resulting (or passed) token of the last lexical analyzer operation in the result.
+ */
+ INCLUDE_LAST,
+ /**
+ * Do not include it.
+ */
+ EXCLUDE_LAST
+ };
+
+ /**
+ * Start the process of recording. Based on aInclude value the begining of the recorded
+ * sub-string is at the current position (EXCLUDE_LAST) or at the position before the last
+ * parsed token (INCLUDE_LAST).
+ */
+ void Record(ClaimInclusion aInclude = EXCLUDE_LAST);
+ /**
+ * Claim result of the record started with Record() call before. Depending on aInclude
+ * the ending of the sub-string result includes or excludes the last parsed or checked
+ * token.
+ */
+ void Claim(nsACString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
+ void Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
+
+ /**
+ * If aToken is found, aResult is set to the substring between the current
+ * position and the position of aToken, potentially including aToken depending
+ * on aInclude.
+ * If aToken isn't found aResult is set to the substring between the current
+ * position and the end of the string.
+ * If aToken is found, the method returns true. Otherwise it returns false.
+ *
+ * Calling Rollback() after ReadUntil() will return the read cursor to the
+ * position it had before ReadUntil was called.
+ */
+ MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsDependentCSubstring& aResult,
+ ClaimInclusion aInclude = EXCLUDE_LAST);
+ MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsACString& aResult,
+ ClaimInclusion aInclude = EXCLUDE_LAST);
+
+protected:
+ // All these point to the original buffer passed to the Tokenizer's constructor
+ nsACString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is
+ nsACString::const_char_iterator mRollback; // Position of the previous token start
+
+private:
+ Tokenizer() = delete;
+ Tokenizer(const Tokenizer&) = delete;
+ Tokenizer(Tokenizer&&) = delete;
+ Tokenizer(const Tokenizer&&) = delete;
+ Tokenizer &operator=(const Tokenizer&) = delete;
+};
+
+} // mozilla
+
+#endif // Tokenizer_h__
diff --git a/xpcom/ds/moz.build b/xpcom/ds/moz.build
new file mode 100644
index 000000000..e12f1c3dd
--- /dev/null
+++ b/xpcom/ds/moz.build
@@ -0,0 +1,105 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIArray.idl',
+ 'nsIArrayExtensions.idl',
+ 'nsIAtom.idl',
+ 'nsIAtomService.idl',
+ 'nsICollection.idl',
+ 'nsIEnumerator.idl',
+ 'nsIHashable.idl',
+ 'nsIINIParser.idl',
+ 'nsIMutableArray.idl',
+ 'nsIObserver.idl',
+ 'nsIObserverService.idl',
+ 'nsIPersistentProperties2.idl',
+ 'nsIProperties.idl',
+ 'nsIProperty.idl',
+ 'nsIPropertyBag.idl',
+ 'nsIPropertyBag2.idl',
+ 'nsISerializable.idl',
+ 'nsISimpleEnumerator.idl',
+ 'nsIStringEnumerator.idl',
+ 'nsISupportsArray.idl',
+ 'nsISupportsIterators.idl',
+ 'nsISupportsPrimitives.idl',
+ 'nsIVariant.idl',
+ 'nsIWritablePropertyBag.idl',
+ 'nsIWritablePropertyBag2.idl',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ XPIDL_SOURCES += [
+ 'nsIWindowsRegKey.idl',
+ ]
+ EXPORTS += ['nsWindowsRegKey.h']
+ SOURCES += [
+ 'nsWindowsRegKey.cpp'
+ ]
+
+XPIDL_MODULE = 'xpcom_ds'
+
+EXPORTS += [
+ 'nsArray.h',
+ 'nsAtomService.h',
+ 'nsCharSeparatedTokenizer.h',
+ 'nsCheapSets.h',
+ 'nsCRT.h',
+ 'nsExpirationTracker.h',
+ 'nsHashPropertyBag.h',
+ 'nsMathUtils.h',
+ 'nsStaticAtom.h',
+ 'nsStaticNameTable.h',
+ 'nsStringEnumerator.h',
+ 'nsSupportsArray.h',
+ 'nsSupportsPrimitives.h',
+ 'nsVariant.h',
+ 'nsWhitespaceTokenizer.h',
+]
+
+EXPORTS.mozilla += [
+ 'IncrementalTokenizer.h',
+ 'StickyTimeDuration.h',
+ 'Tokenizer.h',
+]
+
+UNIFIED_SOURCES += [
+ 'IncrementalTokenizer.cpp',
+ 'nsArray.cpp',
+ 'nsAtomService.cpp',
+ 'nsAtomTable.cpp',
+ 'nsCRT.cpp',
+ 'nsHashPropertyBag.cpp',
+ 'nsINIParserImpl.cpp',
+ 'nsObserverList.cpp',
+ 'nsObserverService.cpp',
+ 'nsProperties.cpp',
+ 'nsStringEnumerator.cpp',
+ 'nsSupportsArray.cpp',
+ 'nsSupportsArrayEnumerator.cpp',
+ 'nsSupportsPrimitives.cpp',
+ 'nsVariant.cpp',
+ 'Tokenizer.cpp',
+]
+
+# These two files cannot be built in unified mode because they use the
+# PL_ARENA_CONST_ALIGN_MASK macro with plarena.h.
+SOURCES += [
+ 'nsPersistentProperties.cpp',
+ 'nsStaticNameTable.cpp',
+]
+
+EXTRA_COMPONENTS += [
+ 'nsINIProcessor.js',
+ 'nsINIProcessor.manifest',
+]
+
+LOCAL_INCLUDES += [
+ '../io',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/xpcom/ds/nsArray.cpp b/xpcom/ds/nsArray.cpp
new file mode 100644
index 000000000..d137a14a5
--- /dev/null
+++ b/xpcom/ds/nsArray.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsIWeakReference.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsThreadUtils.h"
+
+// used by IndexOf()
+struct MOZ_STACK_CLASS findIndexOfClosure
+{
+ // This is only used for pointer comparison, so we can just use a void*.
+ void* targetElement;
+ uint32_t startIndex;
+ uint32_t resultIndex;
+};
+
+static bool FindElementCallback(void* aElement, void* aClosure);
+
+NS_INTERFACE_MAP_BEGIN(nsArray)
+ NS_INTERFACE_MAP_ENTRY(nsIArray)
+ NS_INTERFACE_MAP_ENTRY(nsIArrayExtensions)
+ NS_INTERFACE_MAP_ENTRY(nsIMutableArray)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMutableArray)
+NS_INTERFACE_MAP_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsArrayCC)
+ NS_INTERFACE_MAP_ENTRY(nsIArray)
+ NS_INTERFACE_MAP_ENTRY(nsIArrayExtensions)
+ NS_INTERFACE_MAP_ENTRY(nsIMutableArray)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMutableArray)
+NS_INTERFACE_MAP_END
+
+nsArrayBase::~nsArrayBase()
+{
+ Clear();
+}
+
+
+NS_IMPL_ADDREF(nsArray)
+NS_IMPL_RELEASE(nsArray)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsArrayCC)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsArrayCC)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsArrayCC)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsArrayCC)
+ tmp->Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsArrayCC)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArray)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMETHODIMP
+nsArrayBase::GetLength(uint32_t* aLength)
+{
+ *aLength = mArray.Count();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsArrayBase::QueryElementAt(uint32_t aIndex,
+ const nsIID& aIID,
+ void** aResult)
+{
+ nsISupports* obj = mArray.SafeObjectAt(aIndex);
+ if (!obj) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // no need to worry about a leak here, because SafeObjectAt()
+ // doesn't addref its result
+ return obj->QueryInterface(aIID, aResult);
+}
+
+NS_IMETHODIMP
+nsArrayBase::IndexOf(uint32_t aStartIndex, nsISupports* aElement,
+ uint32_t* aResult)
+{
+ // optimize for the common case by forwarding to mArray
+ if (aStartIndex == 0) {
+ uint32_t idx = mArray.IndexOf(aElement);
+ if (idx == UINT32_MAX) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = idx;
+ return NS_OK;
+ }
+
+ findIndexOfClosure closure = { aElement, aStartIndex, 0 };
+ bool notFound = mArray.EnumerateForwards(FindElementCallback, &closure);
+ if (notFound) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = closure.resultIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsArrayBase::Enumerate(nsISimpleEnumerator** aResult)
+{
+ return NS_NewArrayEnumerator(aResult, static_cast<nsIArray*>(this));
+}
+
+// nsIMutableArray implementation
+
+NS_IMETHODIMP
+nsArrayBase::AppendElement(nsISupports* aElement, bool aWeak)
+{
+ bool result;
+ if (aWeak) {
+ nsCOMPtr<nsIWeakReference> elementRef = do_GetWeakReference(aElement);
+ NS_ASSERTION(elementRef,
+ "AppendElement: Trying to use weak references on an object that doesn't support it");
+ if (!elementRef) {
+ return NS_ERROR_FAILURE;
+ }
+ result = mArray.AppendObject(elementRef);
+ }
+
+ else {
+ // add the object directly
+ result = mArray.AppendObject(aElement);
+ }
+ return result ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsArrayBase::RemoveElementAt(uint32_t aIndex)
+{
+ bool result = mArray.RemoveObjectAt(aIndex);
+ return result ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsArrayBase::InsertElementAt(nsISupports* aElement, uint32_t aIndex, bool aWeak)
+{
+ nsCOMPtr<nsISupports> elementRef;
+ if (aWeak) {
+ elementRef = do_GetWeakReference(aElement);
+ NS_ASSERTION(elementRef,
+ "InsertElementAt: Trying to use weak references on an object that doesn't support it");
+ if (!elementRef) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ elementRef = aElement;
+ }
+ bool result = mArray.InsertObjectAt(elementRef, aIndex);
+ return result ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsArrayBase::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex, bool aWeak)
+{
+ nsCOMPtr<nsISupports> elementRef;
+ if (aWeak) {
+ elementRef = do_GetWeakReference(aElement);
+ NS_ASSERTION(elementRef,
+ "ReplaceElementAt: Trying to use weak references on an object that doesn't support it");
+ if (!elementRef) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ elementRef = aElement;
+ }
+ mArray.ReplaceObjectAt(elementRef, aIndex);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsArrayBase::Clear()
+{
+ mArray.Clear();
+ return NS_OK;
+}
+
+// nsIArrayExtensions implementation.
+
+NS_IMETHODIMP
+nsArrayBase::Count(uint32_t* aResult)
+{
+ return GetLength(aResult);
+}
+
+NS_IMETHODIMP
+nsArrayBase::GetElementAt(uint32_t aIndex, nsISupports** aResult)
+{
+ nsCOMPtr<nsISupports> obj = mArray.SafeObjectAt(aIndex);
+ obj.forget(aResult);
+ return NS_OK;
+}
+
+//
+// static helper routines
+//
+bool
+FindElementCallback(void* aElement, void* aClosure)
+{
+ findIndexOfClosure* closure = static_cast<findIndexOfClosure*>(aClosure);
+ nsISupports* element = static_cast<nsISupports*>(aElement);
+
+ // don't start searching until we're past the startIndex
+ if (closure->resultIndex >= closure->startIndex &&
+ element == closure->targetElement) {
+ return false; // stop! We found it
+ }
+ closure->resultIndex++;
+
+ return true;
+}
+
+nsresult
+nsArrayBase::XPCOMConstructor(nsISupports* aOuter, const nsIID& aIID,
+ void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsIMutableArray> inst = Create();
+ return inst->QueryInterface(aIID, aResult);
+}
+
+already_AddRefed<nsIMutableArray>
+nsArrayBase::Create()
+{
+ nsCOMPtr<nsIMutableArray> inst;
+ if (NS_IsMainThread()) {
+ inst = new nsArrayCC;
+ } else {
+ inst = new nsArray;
+ }
+ return inst.forget();
+}
diff --git a/xpcom/ds/nsArray.h b/xpcom/ds/nsArray.h
new file mode 100644
index 000000000..0cda23487
--- /dev/null
+++ b/xpcom/ds/nsArray.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsArray_h__
+#define nsArray_h__
+
+#include "nsIArrayExtensions.h"
+#include "nsIMutableArray.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+
+// {35C66FD1-95E9-4e0a-80C5-C3BD2B375481}
+#define NS_ARRAY_CID \
+{ 0x35c66fd1, 0x95e9, 0x4e0a, \
+ { 0x80, 0xc5, 0xc3, 0xbd, 0x2b, 0x37, 0x54, 0x81 } }
+
+// nsArray without any refcounting declarations
+class nsArrayBase : public nsIMutableArray
+{
+public:
+ NS_DECL_NSIARRAY
+ NS_DECL_NSIARRAYEXTENSIONS
+ NS_DECL_NSIMUTABLEARRAY
+
+ /* Both of these factory functions create a cycle-collectable array
+ on the main thread and a non-cycle-collectable array on other
+ threads. */
+ static already_AddRefed<nsIMutableArray> Create();
+ /* Only for the benefit of the XPCOM module system, use Create()
+ instead. */
+ static nsresult XPCOMConstructor(nsISupports* aOuter, const nsIID& aIID,
+ void** aResult);
+protected:
+ nsArrayBase() {}
+ nsArrayBase(const nsArrayBase& aOther);
+ explicit nsArrayBase(const nsCOMArray_base& aBaseArray) : mArray(aBaseArray) {}
+ virtual ~nsArrayBase();
+
+ nsCOMArray_base mArray;
+};
+
+class nsArray final : public nsArrayBase
+{
+ friend class nsArrayBase;
+
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ nsArray() : nsArrayBase() {}
+ nsArray(const nsArray& aOther);
+ explicit nsArray(const nsCOMArray_base& aBaseArray) : nsArrayBase(aBaseArray) {}
+ ~nsArray() {}
+};
+
+class nsArrayCC final : public nsArrayBase
+{
+ friend class nsArrayBase;
+
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsArrayCC, nsIMutableArray)
+
+private:
+ nsArrayCC() : nsArrayBase() {}
+ nsArrayCC(const nsArrayCC& aOther);
+ explicit nsArrayCC(const nsCOMArray_base& aBaseArray) : nsArrayBase(aBaseArray) {}
+ ~nsArrayCC() {}
+};
+
+#endif
diff --git a/xpcom/ds/nsAtomService.cpp b/xpcom/ds/nsAtomService.cpp
new file mode 100644
index 000000000..7785ca2e4
--- /dev/null
+++ b/xpcom/ds/nsAtomService.cpp
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAtomService.h"
+#include "nsIAtom.h"
+
+NS_IMPL_ISUPPORTS(nsAtomService, nsIAtomService)
+
+nsAtomService::nsAtomService()
+{
+}
+
+nsresult
+nsAtomService::GetAtom(const nsAString& aString, nsIAtom** aResult)
+{
+ *aResult = NS_Atomize(aString).take();
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
diff --git a/xpcom/ds/nsAtomService.h b/xpcom/ds/nsAtomService.h
new file mode 100644
index 000000000..7a1bcf2cd
--- /dev/null
+++ b/xpcom/ds/nsAtomService.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsAtomService_h
+#define __nsAtomService_h
+
+#include "nsIAtomService.h"
+#include "mozilla/Attributes.h"
+
+class nsAtomService final : public nsIAtomService
+{
+public:
+ nsAtomService();
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIATOMSERVICE
+
+private:
+ ~nsAtomService() {}
+};
+
+#endif
diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp
new file mode 100644
index 000000000..3dd3bd36c
--- /dev/null
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -0,0 +1,736 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
+
+#include "nsAtomTable.h"
+#include "nsStaticAtom.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "PLDHashTable.h"
+#include "prenv.h"
+#include "nsThreadUtils.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
+#include "nsUnicharUtils.h"
+#include "nsPrintfCString.h"
+
+// There are two kinds of atoms handled by this module.
+//
+// - DynamicAtom: the atom itself is heap allocated, as is the nsStringBuffer it
+// points to. |gAtomTable| holds weak references to them DynamicAtoms. When
+// the refcount of a DynamicAtom drops to zero, we increment a static counter.
+// When that counter reaches a certain threshold, we iterate over the atom
+// table, removing and deleting DynamicAtoms with refcount zero. This allows
+// us to avoid acquiring the atom table lock during normal refcounting.
+//
+// - StaticAtom: the atom itself is heap allocated, but it points to a static
+// nsStringBuffer. |gAtomTable| effectively owns StaticAtoms, because such
+// atoms ignore all AddRef/Release calls, which ensures they stay alive until
+// |gAtomTable| itself is destroyed whereupon they are explicitly deleted.
+//
+// Note that gAtomTable is used on multiple threads, and callers must
+// acquire gAtomTableLock before touching it.
+
+using namespace mozilla;
+
+//----------------------------------------------------------------------
+
+class CheckStaticAtomSizes
+{
+ CheckStaticAtomSizes()
+ {
+ static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
+ sizeof(nsStringBuffer().mRefCount)) &&
+ (sizeof(nsFakeStringBuffer<1>().mSize) ==
+ sizeof(nsStringBuffer().mStorageSize)) &&
+ (offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
+ offsetof(nsStringBuffer, mRefCount)) &&
+ (offsetof(nsFakeStringBuffer<1>, mSize) ==
+ offsetof(nsStringBuffer, mStorageSize)) &&
+ (offsetof(nsFakeStringBuffer<1>, mStringData) ==
+ sizeof(nsStringBuffer)),
+ "mocked-up strings' representations should be compatible");
+ }
+};
+
+//----------------------------------------------------------------------
+
+static Atomic<uint32_t, ReleaseAcquire> gUnusedAtomCount(0);
+
+class DynamicAtom final : public nsIAtom
+{
+public:
+ static already_AddRefed<DynamicAtom> Create(const nsAString& aString, uint32_t aHash)
+ {
+ // The refcount is appropriately initialized in the constructor.
+ return dont_AddRef(new DynamicAtom(aString, aHash));
+ }
+
+ static void GCAtomTable();
+
+ enum class GCKind {
+ RegularOperation,
+ Shutdown,
+ };
+
+ static void GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
+ GCKind aKind);
+
+private:
+ DynamicAtom(const nsAString& aString, uint32_t aHash)
+ : mRefCnt(1)
+ {
+ mLength = aString.Length();
+ mIsStatic = false;
+ RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
+ if (buf) {
+ mString = static_cast<char16_t*>(buf->Data());
+ } else {
+ const size_t size = (mLength + 1) * sizeof(char16_t);
+ buf = nsStringBuffer::Alloc(size);
+ if (MOZ_UNLIKELY(!buf)) {
+ // We OOM because atom allocations should be small and it's hard to
+ // handle them more gracefully in a constructor.
+ NS_ABORT_OOM(size);
+ }
+ mString = static_cast<char16_t*>(buf->Data());
+ CopyUnicodeTo(aString, 0, mString, mLength);
+ mString[mLength] = char16_t(0);
+ }
+
+ mHash = aHash;
+ MOZ_ASSERT(mHash == HashString(mString, mLength));
+
+ NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
+ NS_ASSERTION(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
+ "enough storage");
+ NS_ASSERTION(Equals(aString), "correct data");
+
+ // Take ownership of buffer
+ mozilla::Unused << buf.forget();
+ }
+
+private:
+ // We don't need a virtual destructor because we always delete via a
+ // DynamicAtom* pointer (in GCAtomTable()), not an nsIAtom* pointer.
+ ~DynamicAtom();
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIATOM
+};
+
+class StaticAtom final : public nsIAtom
+{
+public:
+ StaticAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash)
+ {
+ mLength = aLength;
+ mIsStatic = true;
+ mString = static_cast<char16_t*>(aStringBuffer->Data());
+ // Technically we could currently avoid doing this addref by instead making
+ // the static atom buffers have an initial refcount of 2.
+ aStringBuffer->AddRef();
+
+ mHash = aHash;
+ MOZ_ASSERT(mHash == HashString(mString, mLength));
+
+ MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
+ MOZ_ASSERT(aStringBuffer &&
+ aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
+ "correct storage");
+ }
+
+ // We don't need a virtual destructor because we always delete via a
+ // StaticAtom* pointer (in AtomTableClearEntry()), not an nsIAtom* pointer.
+ ~StaticAtom() {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIATOM
+};
+
+NS_IMPL_QUERY_INTERFACE(StaticAtom, nsIAtom)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+StaticAtom::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+StaticAtom::Release()
+{
+ return 1;
+}
+
+NS_IMETHODIMP
+DynamicAtom::ScriptableToString(nsAString& aBuf)
+{
+ nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StaticAtom::ScriptableToString(nsAString& aBuf)
+{
+ nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DynamicAtom::ToUTF8String(nsACString& aBuf)
+{
+ CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StaticAtom::ToUTF8String(nsACString& aBuf)
+{
+ CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DynamicAtom::ScriptableEquals(const nsAString& aString, bool* aResult)
+{
+ *aResult = aString.Equals(nsDependentString(mString, mLength));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StaticAtom::ScriptableEquals(const nsAString& aString, bool* aResult)
+{
+ *aResult = aString.Equals(nsDependentString(mString, mLength));
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(size_t)
+DynamicAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ n += nsStringBuffer::FromData(mString)->SizeOfIncludingThisIfUnshared(
+ aMallocSizeOf);
+ return n;
+}
+
+NS_IMETHODIMP_(size_t)
+StaticAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ // Don't measure the string buffer pointed to by the StaticAtom because it's
+ // in static memory.
+ return n;
+}
+
+//----------------------------------------------------------------------
+
+/**
+ * The shared hash table for atom lookups.
+ *
+ * Callers must hold gAtomTableLock before manipulating the table.
+ */
+static PLDHashTable* gAtomTable;
+static Mutex* gAtomTableLock;
+
+struct AtomTableKey
+{
+ AtomTableKey(const char16_t* aUTF16String, uint32_t aLength, uint32_t aHash)
+ : mUTF16String(aUTF16String)
+ , mUTF8String(nullptr)
+ , mLength(aLength)
+ , mHash(aHash)
+ {
+ MOZ_ASSERT(mHash == HashString(mUTF16String, mLength));
+ }
+
+ AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t aHash)
+ : mUTF16String(nullptr)
+ , mUTF8String(aUTF8String)
+ , mLength(aLength)
+ , mHash(aHash)
+ {
+ mozilla::DebugOnly<bool> err;
+ MOZ_ASSERT(aHash == HashUTF8AsUTF16(mUTF8String, mLength, &err));
+ }
+
+ AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
+ uint32_t* aHashOut)
+ : mUTF16String(aUTF16String)
+ , mUTF8String(nullptr)
+ , mLength(aLength)
+ {
+ mHash = HashString(mUTF16String, mLength);
+ *aHashOut = mHash;
+ }
+
+ AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t* aHashOut)
+ : mUTF16String(nullptr)
+ , mUTF8String(aUTF8String)
+ , mLength(aLength)
+ {
+ bool err;
+ mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err);
+ if (err) {
+ mUTF8String = nullptr;
+ mLength = 0;
+ mHash = 0;
+ }
+ *aHashOut = mHash;
+ }
+
+ const char16_t* mUTF16String;
+ const char* mUTF8String;
+ uint32_t mLength;
+ uint32_t mHash;
+};
+
+struct AtomTableEntry : public PLDHashEntryHdr
+{
+ // These references are either to DynamicAtoms, in which case they are
+ // non-owning, or they are to StaticAtoms, which aren't really refcounted.
+ // See the comment at the top of this file for more details.
+ nsIAtom* MOZ_NON_OWNING_REF mAtom;
+};
+
+static PLDHashNumber
+AtomTableGetHash(const void* aKey)
+{
+ const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
+ return k->mHash;
+}
+
+static bool
+AtomTableMatchKey(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const AtomTableEntry* he = static_cast<const AtomTableEntry*>(aEntry);
+ const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
+
+ if (k->mUTF8String) {
+ return
+ CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
+ k->mUTF8String + k->mLength),
+ nsDependentAtomString(he->mAtom)) == 0;
+ }
+
+ uint32_t length = he->mAtom->GetLength();
+ if (length != k->mLength) {
+ return false;
+ }
+
+ return memcmp(he->mAtom->GetUTF16String(),
+ k->mUTF16String, length * sizeof(char16_t)) == 0;
+}
+
+static void
+AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
+{
+ auto entry = static_cast<AtomTableEntry*>(aEntry);
+ nsIAtom* atom = entry->mAtom;
+ if (atom->IsStaticAtom()) {
+ // This case -- when the entry being cleared holds a StaticAtom -- only
+ // occurs when gAtomTable is destroyed, whereupon all StaticAtoms within it
+ // must be explicitly deleted. The cast is required because StaticAtom
+ // doesn't have a virtual destructor.
+ delete static_cast<StaticAtom*>(atom);
+ }
+}
+
+static void
+AtomTableInitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ static_cast<AtomTableEntry*>(aEntry)->mAtom = nullptr;
+}
+
+static const PLDHashTableOps AtomTableOps = {
+ AtomTableGetHash,
+ AtomTableMatchKey,
+ PLDHashTable::MoveEntryStub,
+ AtomTableClearEntry,
+ AtomTableInitEntry
+};
+
+//----------------------------------------------------------------------
+
+void
+DynamicAtom::GCAtomTable()
+{
+ MutexAutoLock lock(*gAtomTableLock);
+ GCAtomTableLocked(lock, GCKind::RegularOperation);
+}
+
+void
+DynamicAtom::GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
+ GCKind aKind)
+{
+ uint32_t removedCount = 0; // Use a non-atomic temporary for cheaper increments.
+ nsAutoCString nonZeroRefcountAtoms;
+ uint32_t nonZeroRefcountAtomsCount = 0;
+ for (auto i = gAtomTable->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<AtomTableEntry*>(i.Get());
+ if (entry->mAtom->IsStaticAtom()) {
+ continue;
+ }
+
+ auto atom = static_cast<DynamicAtom*>(entry->mAtom);
+ if (atom->mRefCnt == 0) {
+ i.Remove();
+ delete atom;
+ ++removedCount;
+ }
+#ifdef NS_FREE_PERMANENT_DATA
+ else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
+ // Only report leaking atoms in leak-checking builds in a run
+ // where we are checking for leaks, during shutdown. If
+ // something is anomalous, then we'll assert later in this
+ // function.
+ nsAutoCString name;
+ atom->ToUTF8String(name);
+ if (nonZeroRefcountAtomsCount == 0) {
+ nonZeroRefcountAtoms = name;
+ } else if (nonZeroRefcountAtomsCount < 20) {
+ nonZeroRefcountAtoms += NS_LITERAL_CSTRING(",") + name;
+ } else if (nonZeroRefcountAtomsCount == 20) {
+ nonZeroRefcountAtoms += NS_LITERAL_CSTRING(",...");
+ }
+ nonZeroRefcountAtomsCount++;
+ }
+#endif
+
+ }
+ if (nonZeroRefcountAtomsCount) {
+ nsPrintfCString msg("%d dynamic atom(s) with non-zero refcount: %s",
+ nonZeroRefcountAtomsCount, nonZeroRefcountAtoms.get());
+ NS_ASSERTION(nonZeroRefcountAtomsCount == 0, msg.get());
+ }
+
+ // During the course of this function, the atom table is locked. This means
+ // that, barring refcounting bugs in consumers, an atom can never go from
+ // refcount == 0 to refcount != 0 during a GC. However, an atom _can_ go from
+ // refcount != 0 to refcount == 0 if a Release() occurs in parallel with GC.
+ // This means that we cannot assert that gUnusedAtomCount == removedCount, and
+ // thus that there are no unused atoms at the end of a GC. We can and do,
+ // however, assert this after the last GC at shutdown.
+ if (aKind == GCKind::RegularOperation) {
+ MOZ_ASSERT(removedCount <= gUnusedAtomCount);
+ } else {
+ // Complain if somebody adds new GCKind enums.
+ MOZ_ASSERT(aKind == GCKind::Shutdown);
+ // Our unused atom count should be accurate.
+ MOZ_ASSERT(removedCount == gUnusedAtomCount);
+ }
+
+ gUnusedAtomCount -= removedCount;
+}
+
+NS_IMPL_QUERY_INTERFACE(DynamicAtom, nsIAtom)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+DynamicAtom::AddRef(void)
+{
+ nsrefcnt count = ++mRefCnt;
+ if (count == 1) {
+ MOZ_ASSERT(gUnusedAtomCount > 0);
+ gUnusedAtomCount--;
+ }
+ return count;
+}
+
+#ifdef DEBUG
+// We set a lower GC threshold for atoms in debug builds so that we exercise
+// the GC machinery more often.
+static const uint32_t kAtomGCThreshold = 20;
+#else
+static const uint32_t kAtomGCThreshold = 10000;
+#endif
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+DynamicAtom::Release(void)
+{
+ MOZ_ASSERT(mRefCnt > 0);
+ nsrefcnt count = --mRefCnt;
+ if (count == 0) {
+ if (++gUnusedAtomCount >= kAtomGCThreshold) {
+ GCAtomTable();
+ }
+ }
+
+ return count;
+}
+
+DynamicAtom::~DynamicAtom()
+{
+ nsStringBuffer::FromData(mString)->Release();
+}
+
+//----------------------------------------------------------------------
+
+class StaticAtomEntry : public PLDHashEntryHdr
+{
+public:
+ typedef const nsAString& KeyType;
+ typedef const nsAString* KeyTypePointer;
+
+ explicit StaticAtomEntry(KeyTypePointer aKey) {}
+ StaticAtomEntry(const StaticAtomEntry& aOther) : mAtom(aOther.mAtom) {}
+
+ // We do not delete the atom because that's done when gAtomTable is
+ // destroyed -- which happens immediately after gStaticAtomTable is destroyed
+ // -- in NS_PurgeAtomTable().
+ ~StaticAtomEntry() {}
+
+ bool KeyEquals(KeyTypePointer aKey) const
+ {
+ return mAtom->Equals(*aKey);
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return HashString(*aKey);
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+ // StaticAtoms aren't really refcounted. Because these entries live in a
+ // global hashtable, this reference is essentially owning.
+ StaticAtom* MOZ_OWNING_REF mAtom;
+};
+
+/**
+ * A hashtable of static atoms that existed at app startup. This hashtable
+ * helps nsHtml5AtomTable.
+ */
+typedef nsTHashtable<StaticAtomEntry> StaticAtomTable;
+static StaticAtomTable* gStaticAtomTable = nullptr;
+
+/**
+ * Whether it is still OK to add atoms to gStaticAtomTable.
+ */
+static bool gStaticAtomTableSealed = false;
+
+// The atom table very quickly gets 10,000+ entries in it (or even 100,000+).
+// But choosing the best initial length has some subtleties: we add ~2700
+// static atoms to the table at start-up, and then we start adding and removing
+// dynamic atoms. If we make the table too big to start with, when the first
+// dynamic atom gets removed the load factor will be < 25% and so we will
+// shrink it to 4096 entries.
+//
+// By choosing an initial length of 4096, we get an initial capacity of 8192.
+// That's the biggest initial capacity that will let us be > 25% full when the
+// first dynamic atom is removed (when the count is ~2700), thus avoiding any
+// shrinking.
+#define ATOM_HASHTABLE_INITIAL_LENGTH 4096
+
+void
+NS_InitAtomTable()
+{
+ MOZ_ASSERT(!gAtomTable);
+ gAtomTable = new PLDHashTable(&AtomTableOps, sizeof(AtomTableEntry),
+ ATOM_HASHTABLE_INITIAL_LENGTH);
+ gAtomTableLock = new Mutex("Atom Table Lock");
+}
+
+void
+NS_ShutdownAtomTable()
+{
+ delete gStaticAtomTable;
+ gStaticAtomTable = nullptr;
+
+#ifdef NS_FREE_PERMANENT_DATA
+ // Do a final GC to satisfy leak checking. We skip this step in release
+ // builds.
+ {
+ MutexAutoLock lock(*gAtomTableLock);
+ DynamicAtom::GCAtomTableLocked(lock, DynamicAtom::GCKind::Shutdown);
+ }
+#endif
+
+ delete gAtomTable;
+ gAtomTable = nullptr;
+ delete gAtomTableLock;
+ gAtomTableLock = nullptr;
+}
+
+void
+NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf,
+ size_t* aMain, size_t* aStatic)
+{
+ MutexAutoLock lock(*gAtomTableLock);
+ *aMain = gAtomTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ for (auto iter = gAtomTable->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<AtomTableEntry*>(iter.Get());
+ *aMain += entry->mAtom->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ // The atoms pointed to by gStaticAtomTable are also pointed to by gAtomTable,
+ // and they're measured by the loop above. So no need to measure them here.
+ *aStatic = gStaticAtomTable
+ ? gStaticAtomTable->ShallowSizeOfIncludingThis(aMallocSizeOf)
+ : 0;
+}
+
+static inline AtomTableEntry*
+GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t* aHashOut)
+{
+ gAtomTableLock->AssertCurrentThreadOwns();
+ AtomTableKey key(aString, aLength, aHashOut);
+ // This is an infallible add.
+ return static_cast<AtomTableEntry*>(gAtomTable->Add(&key));
+}
+
+static inline AtomTableEntry*
+GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t* aHashOut)
+{
+ gAtomTableLock->AssertCurrentThreadOwns();
+ AtomTableKey key(aString, aLength, aHashOut);
+ // This is an infallible add.
+ return static_cast<AtomTableEntry*>(gAtomTable->Add(&key));
+}
+
+void
+RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
+{
+ MutexAutoLock lock(*gAtomTableLock);
+
+ MOZ_RELEASE_ASSERT(!gStaticAtomTableSealed,
+ "Atom table has already been sealed!");
+
+ if (!gStaticAtomTable) {
+ gStaticAtomTable = new StaticAtomTable();
+ }
+
+ for (uint32_t i = 0; i < aAtomCount; ++i) {
+ nsStringBuffer* stringBuffer = aAtoms[i].mStringBuffer;
+ nsIAtom** atomp = aAtoms[i].mAtom;
+
+ MOZ_ASSERT(nsCRT::IsAscii(static_cast<char16_t*>(stringBuffer->Data())));
+
+ uint32_t stringLen = stringBuffer->StorageSize() / sizeof(char16_t) - 1;
+
+ uint32_t hash;
+ AtomTableEntry* he =
+ GetAtomHashEntry(static_cast<char16_t*>(stringBuffer->Data()),
+ stringLen, &hash);
+
+ nsIAtom* atom = he->mAtom;
+ if (atom) {
+ // Disallow creating a dynamic atom, and then later, while the
+ // dynamic atom is still alive, registering that same atom as a
+ // static atom. It causes subtle bugs, and we're programming in
+ // C++ here, not Smalltalk.
+ if (!atom->IsStaticAtom()) {
+ nsAutoCString name;
+ atom->ToUTF8String(name);
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "Static atom registration for %s should be pushed back", name.get());
+ }
+ } else {
+ atom = new StaticAtom(stringBuffer, stringLen, hash);
+ he->mAtom = atom;
+ }
+ *atomp = atom;
+
+ if (!gStaticAtomTableSealed) {
+ StaticAtomEntry* entry =
+ gStaticAtomTable->PutEntry(nsDependentAtomString(atom));
+ MOZ_ASSERT(atom->IsStaticAtom());
+ entry->mAtom = static_cast<StaticAtom*>(atom);
+ }
+ }
+}
+
+already_AddRefed<nsIAtom>
+NS_Atomize(const char* aUTF8String)
+{
+ return NS_Atomize(nsDependentCString(aUTF8String));
+}
+
+already_AddRefed<nsIAtom>
+NS_Atomize(const nsACString& aUTF8String)
+{
+ MutexAutoLock lock(*gAtomTableLock);
+ uint32_t hash;
+ AtomTableEntry* he = GetAtomHashEntry(aUTF8String.Data(),
+ aUTF8String.Length(),
+ &hash);
+
+ if (he->mAtom) {
+ nsCOMPtr<nsIAtom> atom = he->mAtom;
+
+ return atom.forget();
+ }
+
+ // This results in an extra addref/release of the nsStringBuffer.
+ // Unfortunately there doesn't seem to be any APIs to avoid that.
+ // Actually, now there is, sort of: ForgetSharedBuffer.
+ nsString str;
+ CopyUTF8toUTF16(aUTF8String, str);
+ RefPtr<DynamicAtom> atom = DynamicAtom::Create(str, hash);
+
+ he->mAtom = atom;
+
+ return atom.forget();
+}
+
+already_AddRefed<nsIAtom>
+NS_Atomize(const char16_t* aUTF16String)
+{
+ return NS_Atomize(nsDependentString(aUTF16String));
+}
+
+already_AddRefed<nsIAtom>
+NS_Atomize(const nsAString& aUTF16String)
+{
+ MutexAutoLock lock(*gAtomTableLock);
+ uint32_t hash;
+ AtomTableEntry* he = GetAtomHashEntry(aUTF16String.Data(),
+ aUTF16String.Length(),
+ &hash);
+
+ if (he->mAtom) {
+ nsCOMPtr<nsIAtom> atom = he->mAtom;
+
+ return atom.forget();
+ }
+
+ RefPtr<DynamicAtom> atom = DynamicAtom::Create(aUTF16String, hash);
+ he->mAtom = atom;
+
+ return atom.forget();
+}
+
+nsrefcnt
+NS_GetNumberOfAtoms(void)
+{
+ DynamicAtom::GCAtomTable(); // Trigger a GC so that we return a deterministic result.
+ MutexAutoLock lock(*gAtomTableLock);
+ return gAtomTable->EntryCount();
+}
+
+nsIAtom*
+NS_GetStaticAtom(const nsAString& aUTF16String)
+{
+ NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
+ NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
+ StaticAtomEntry* entry = gStaticAtomTable->GetEntry(aUTF16String);
+ return entry ? entry->mAtom : nullptr;
+}
+
+void
+NS_SealStaticAtomTable()
+{
+ gStaticAtomTableSealed = true;
+}
diff --git a/xpcom/ds/nsAtomTable.h b/xpcom/ds/nsAtomTable.h
new file mode 100644
index 000000000..89e5792f7
--- /dev/null
+++ b/xpcom/ds/nsAtomTable.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAtomTable_h__
+#define nsAtomTable_h__
+
+#include "mozilla/MemoryReporting.h"
+#include <stddef.h>
+
+void NS_InitAtomTable();
+void NS_ShutdownAtomTable();
+
+void NS_SizeOfAtomTablesIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ size_t* aMain, size_t* aStatic);
+
+#endif // nsAtomTable_h__
diff --git a/xpcom/ds/nsCRT.cpp b/xpcom/ds/nsCRT.cpp
new file mode 100644
index 000000000..0d11a8c26
--- /dev/null
+++ b/xpcom/ds/nsCRT.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/**
+ * MODULE NOTES:
+ * @update gess7/30/98
+ *
+ * Much as I hate to do it, we were using string compares wrong.
+ * Often, programmers call functions like strcmp(s1,s2), and pass
+ * one or more null strings. Rather than blow up on these, I've
+ * added quick checks to ensure that cases like this don't cause
+ * us to fail.
+ *
+ * In general, if you pass a null into any of these string compare
+ * routines, we simply return 0.
+ */
+
+
+#include "nsCRT.h"
+#include "nsDebug.h"
+
+//----------------------------------------------------------------------
+
+
+////////////////////////////////////////////////////////////////////////////////
+// My lovely strtok routine
+
+#define IS_DELIM(m, c) ((m)[(c) >> 3] & (1 << ((c) & 7)))
+#define SET_DELIM(m, c) ((m)[(c) >> 3] |= (1 << ((c) & 7)))
+#define DELIM_TABLE_SIZE 32
+
+char*
+nsCRT::strtok(char* aString, const char* aDelims, char** aNewStr)
+{
+ NS_ASSERTION(aString,
+ "Unlike regular strtok, the first argument cannot be null.");
+
+ char delimTable[DELIM_TABLE_SIZE];
+ uint32_t i;
+ char* result;
+ char* str = aString;
+
+ for (i = 0; i < DELIM_TABLE_SIZE; ++i) {
+ delimTable[i] = '\0';
+ }
+
+ for (i = 0; aDelims[i]; i++) {
+ SET_DELIM(delimTable, static_cast<uint8_t>(aDelims[i]));
+ }
+ NS_ASSERTION(aDelims[i] == '\0', "too many delimiters");
+
+ // skip to beginning
+ while (*str && IS_DELIM(delimTable, static_cast<uint8_t>(*str))) {
+ str++;
+ }
+ result = str;
+
+ // fix up the end of the token
+ while (*str) {
+ if (IS_DELIM(delimTable, static_cast<uint8_t>(*str))) {
+ *str++ = '\0';
+ break;
+ }
+ str++;
+ }
+ *aNewStr = str;
+
+ return str == result ? nullptr : result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Compare unichar string ptrs, stopping at the 1st null
+ * NOTE: If both are null, we return 0.
+ * NOTE: We terminate the search upon encountering a nullptr
+ *
+ * @update gess 11/10/99
+ * @param s1 and s2 both point to unichar strings
+ * @return 0 if they match, -1 if s1<s2; 1 if s1>s2
+ */
+int32_t
+nsCRT::strcmp(const char16_t* aStr1, const char16_t* aStr2)
+{
+ if (aStr1 && aStr2) {
+ for (;;) {
+ char16_t c1 = *aStr1++;
+ char16_t c2 = *aStr2++;
+ if (c1 != c2) {
+ if (c1 < c2) {
+ return -1;
+ }
+ return 1;
+ }
+ if (c1 == 0 || c2 == 0) {
+ break;
+ }
+ }
+ } else {
+ if (aStr1) { // aStr2 must have been null
+ return -1;
+ }
+ if (aStr2) { // aStr1 must have been null
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Compare unichar string ptrs, stopping at the 1st null or nth char.
+ * NOTE: If either is null, we return 0.
+ * NOTE: We DO NOT terminate the search upon encountering nullptr's before N
+ *
+ * @update gess 11/10/99
+ * @param s1 and s2 both point to unichar strings
+ * @return 0 if they match, -1 if s1<s2; 1 if s1>s2
+ */
+int32_t
+nsCRT::strncmp(const char16_t* aStr1, const char16_t* aStr2, uint32_t aNum)
+{
+ if (aStr1 && aStr2) {
+ if (aNum != 0) {
+ do {
+ char16_t c1 = *aStr1++;
+ char16_t c2 = *aStr2++;
+ if (c1 != c2) {
+ if (c1 < c2) {
+ return -1;
+ }
+ return 1;
+ }
+ } while (--aNum != 0);
+ }
+ }
+ return 0;
+}
+
+const char*
+nsCRT::memmem(const char* aHaystack, uint32_t aHaystackLen,
+ const char* aNeedle, uint32_t aNeedleLen)
+{
+ // Sanity checking
+ if (!(aHaystack && aNeedle && aHaystackLen && aNeedleLen &&
+ aNeedleLen <= aHaystackLen)) {
+ return nullptr;
+ }
+
+#ifdef HAVE_MEMMEM
+ return (const char*)::memmem(aHaystack, aHaystackLen, aNeedle, aNeedleLen);
+#else
+ // No memmem means we need to roll our own. This isn't really optimized
+ // for performance ... if that becomes an issue we can take some inspiration
+ // from the js string compare code in jsstr.cpp
+ for (uint32_t i = 0; i < aHaystackLen - aNeedleLen; i++) {
+ if (!memcmp(aHaystack + i, aNeedle, aNeedleLen)) {
+ return aHaystack + i;
+ }
+ }
+#endif
+ return nullptr;
+}
+
+// This should use NSPR but NSPR isn't exporting its PR_strtoll function
+// Until then...
+int64_t
+nsCRT::atoll(const char* aStr)
+{
+ if (!aStr) {
+ return 0;
+ }
+
+ int64_t ll = 0;
+
+ while (*aStr && *aStr >= '0' && *aStr <= '9') {
+ ll *= 10;
+ ll += *aStr - '0';
+ aStr++;
+ }
+
+ return ll;
+}
+
diff --git a/xpcom/ds/nsCRT.h b/xpcom/ds/nsCRT.h
new file mode 100644
index 000000000..6fad83d6d
--- /dev/null
+++ b/xpcom/ds/nsCRT.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsCRT_h___
+#define nsCRT_h___
+
+#include <stdlib.h>
+#include <ctype.h>
+#include "plstr.h"
+#include "nscore.h"
+#include "nsCRTGlue.h"
+
+#if defined(XP_WIN)
+# define NS_LINEBREAK "\015\012"
+# define NS_LINEBREAK_LEN 2
+#else
+# ifdef XP_UNIX
+# define NS_LINEBREAK "\012"
+# define NS_LINEBREAK_LEN 1
+# endif /* XP_UNIX */
+#endif /* XP_WIN */
+
+extern const char16_t kIsoLatin1ToUCS2[256];
+
+// This macro can be used in a class declaration for classes that want
+// to ensure that their instance memory is zeroed.
+#define NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW \
+ void* operator new(size_t sz) CPP_THROW_NEW { \
+ void* rv = ::operator new(sz); \
+ if (rv) { \
+ memset(rv, 0, sz); \
+ } \
+ return rv; \
+ } \
+ void operator delete(void* ptr) { \
+ ::operator delete(ptr); \
+ }
+
+// This macro works with the next macro to declare a non-inlined
+// version of the above.
+#define NS_DECL_ZEROING_OPERATOR_NEW \
+ void* operator new(size_t sz) CPP_THROW_NEW; \
+ void operator delete(void* ptr);
+
+#define NS_IMPL_ZEROING_OPERATOR_NEW(_class) \
+ void* _class::operator new(size_t sz) CPP_THROW_NEW { \
+ void* rv = ::operator new(sz); \
+ if (rv) { \
+ memset(rv, 0, sz); \
+ } \
+ return rv; \
+ } \
+ void _class::operator delete(void* ptr) { \
+ ::operator delete(ptr); \
+ }
+
+// Freeing helper
+#define CRTFREEIF(x) if (x) { nsCRT::free(x); x = 0; }
+
+/// This is a wrapper class around all the C runtime functions.
+
+class nsCRT
+{
+public:
+ enum
+ {
+ LF = '\n' /* Line Feed */,
+ VTAB = '\v' /* Vertical Tab */,
+ CR = '\r' /* Carriage Return */
+ };
+
+ /// String comparison.
+ static int32_t strcmp(const char* aStr1, const char* aStr2)
+ {
+ return int32_t(PL_strcmp(aStr1, aStr2));
+ }
+
+ static int32_t strncmp(const char* aStr1, const char* aStr2,
+ uint32_t aMaxLen)
+ {
+ return int32_t(PL_strncmp(aStr1, aStr2, aMaxLen));
+ }
+
+ /// Case-insensitive string comparison.
+ static int32_t strcasecmp(const char* aStr1, const char* aStr2)
+ {
+ return int32_t(PL_strcasecmp(aStr1, aStr2));
+ }
+
+ /// Case-insensitive string comparison with length
+ static int32_t strncasecmp(const char* aStr1, const char* aStr2,
+ uint32_t aMaxLen)
+ {
+ int32_t result = int32_t(PL_strncasecmp(aStr1, aStr2, aMaxLen));
+ //Egads. PL_strncasecmp is returning *very* negative numbers.
+ //Some folks expect -1,0,1, so let's temper its enthusiasm.
+ if (result < 0) {
+ result = -1;
+ }
+ return result;
+ }
+
+ static int32_t strncmp(const char* aStr1, const char* aStr2, int32_t aMaxLen)
+ {
+ // inline the first test (assumes strings are not null):
+ int32_t diff =
+ ((const unsigned char*)aStr1)[0] - ((const unsigned char*)aStr2)[0];
+ if (diff != 0) {
+ return diff;
+ }
+ return int32_t(PL_strncmp(aStr1, aStr2, unsigned(aMaxLen)));
+ }
+
+ /**
+
+ How to use this fancy (thread-safe) version of strtok:
+
+ void main(void) {
+ printf("%s\n\nTokens:\n", string);
+ // Establish string and get the first token:
+ char* newStr;
+ token = nsCRT::strtok(string, seps, &newStr);
+ while (token != nullptr) {
+ // While there are tokens in "string"
+ printf(" %s\n", token);
+ // Get next token:
+ token = nsCRT::strtok(newStr, seps, &newStr);
+ }
+ }
+ * WARNING - STRTOK WHACKS str THE FIRST TIME IT IS CALLED *
+ * MAKE A COPY OF str IF YOU NEED TO USE IT AFTER strtok() *
+ */
+ static char* strtok(char* aStr, const char* aDelims, char** aNewStr);
+
+ /// Like strcmp except for ucs2 strings
+ static int32_t strcmp(const char16_t* aStr1, const char16_t* aStr2);
+ /// Like strcmp except for ucs2 strings
+ static int32_t strncmp(const char16_t* aStr1, const char16_t* aStr2,
+ uint32_t aMaxLen);
+
+ // The GNU libc has memmem, which is strstr except for binary data
+ // This is our own implementation that uses memmem on platforms
+ // where it's available.
+ static const char* memmem(const char* aHaystack, uint32_t aHaystackLen,
+ const char* aNeedle, uint32_t aNeedleLen);
+
+ // String to longlong
+ static int64_t atoll(const char* aStr);
+
+ static char ToUpper(char aChar) { return NS_ToUpper(aChar); }
+ static char ToLower(char aChar) { return NS_ToLower(aChar); }
+
+ static bool IsUpper(char aChar) { return NS_IsUpper(aChar); }
+ static bool IsLower(char aChar) { return NS_IsLower(aChar); }
+
+ static bool IsAscii(char16_t aChar) { return NS_IsAscii(aChar); }
+ static bool IsAscii(const char16_t* aString) { return NS_IsAscii(aString); }
+ static bool IsAsciiAlpha(char16_t aChar) { return NS_IsAsciiAlpha(aChar); }
+ static bool IsAsciiDigit(char16_t aChar) { return NS_IsAsciiDigit(aChar); }
+ static bool IsAsciiSpace(char16_t aChar) { return NS_IsAsciiWhitespace(aChar); }
+ static bool IsAscii(const char* aString) { return NS_IsAscii(aString); }
+ static bool IsAscii(const char* aString, uint32_t aLength)
+ {
+ return NS_IsAscii(aString, aLength);
+ }
+};
+
+
+inline bool
+NS_IS_SPACE(char16_t aChar)
+{
+ return ((int(aChar) & 0x7f) == int(aChar)) && isspace(int(aChar));
+}
+
+#define NS_IS_CNTRL(i) ((((unsigned int) (i)) > 0x7f) ? (int) 0 : iscntrl(i))
+#define NS_IS_DIGIT(i) ((((unsigned int) (i)) > 0x7f) ? (int) 0 : isdigit(i))
+#if defined(XP_WIN)
+#define NS_IS_ALPHA(VAL) (isascii((int)(VAL)) && isalpha((int)(VAL)))
+#else
+#define NS_IS_ALPHA(VAL) ((((unsigned int) (VAL)) > 0x7f) ? (int) 0 : isalpha((int)(VAL)))
+#endif
+
+
+#endif /* nsCRT_h___ */
diff --git a/xpcom/ds/nsCharSeparatedTokenizer.h b/xpcom/ds/nsCharSeparatedTokenizer.h
new file mode 100644
index 000000000..0e24d9d3e
--- /dev/null
+++ b/xpcom/ds/nsCharSeparatedTokenizer.h
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsCharSeparatedTokenizer_h
+#define __nsCharSeparatedTokenizer_h
+
+#include "mozilla/RangedPtr.h"
+
+#include "nsDependentSubstring.h"
+#include "nsCRT.h"
+
+/**
+ * This parses a SeparatorChar-separated string into tokens.
+ * Whitespace surrounding tokens is not treated as part of tokens, however
+ * whitespace inside a token is. If the final token is the empty string, it is
+ * not returned.
+ *
+ * Some examples, with SeparatorChar = ',':
+ *
+ * "foo, bar, baz" -> "foo" "bar" "baz"
+ * "foo,bar,baz" -> "foo" "bar" "baz"
+ * "foo , bar hi , baz" -> "foo" "bar hi" "baz"
+ * "foo, ,bar,baz" -> "foo" "" "bar" "baz"
+ * "foo,,bar,baz" -> "foo" "" "bar" "baz"
+ * "foo,bar,baz," -> "foo" "bar" "baz"
+ *
+ * The function used for whitespace detection is a template argument.
+ * By default, it is NS_IsAsciiWhitespace.
+ */
+template<typename DependentSubstringType, bool IsWhitespace(char16_t)>
+class nsTCharSeparatedTokenizer
+{
+ typedef typename DependentSubstringType::char_type CharType;
+ typedef typename DependentSubstringType::substring_type SubstringType;
+
+public:
+ // Flags -- only one for now. If we need more, they should be defined to
+ // be 1 << 1, 1 << 2, etc. (They're masks, and aFlags is a bitfield.)
+ enum
+ {
+ SEPARATOR_OPTIONAL = 1
+ };
+
+ nsTCharSeparatedTokenizer(const SubstringType& aSource,
+ CharType aSeparatorChar,
+ uint32_t aFlags = 0)
+ : mIter(aSource.Data(), aSource.Length())
+ , mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
+ aSource.Length())
+ , mSeparatorChar(aSeparatorChar)
+ , mWhitespaceBeforeFirstToken(false)
+ , mWhitespaceAfterCurrentToken(false)
+ , mSeparatorAfterCurrentToken(false)
+ , mSeparatorOptional(aFlags & SEPARATOR_OPTIONAL)
+ {
+ // Skip initial whitespace
+ while (mIter < mEnd && IsWhitespace(*mIter)) {
+ mWhitespaceBeforeFirstToken = true;
+ ++mIter;
+ }
+ }
+
+ /**
+ * Checks if any more tokens are available.
+ */
+ bool hasMoreTokens() const
+ {
+ MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
+ "Should be at beginning of token if there is one");
+
+ return mIter < mEnd;
+ }
+
+ /*
+ * Returns true if there is whitespace prior to the first token.
+ */
+ bool whitespaceBeforeFirstToken() const
+ {
+ return mWhitespaceBeforeFirstToken;
+ }
+
+ /*
+ * Returns true if there is a separator after the current token.
+ * Useful if you want to check whether the last token has a separator
+ * after it which may not be valid.
+ */
+ bool separatorAfterCurrentToken() const
+ {
+ return mSeparatorAfterCurrentToken;
+ }
+
+ /*
+ * Returns true if there is any whitespace after the current token.
+ */
+ bool whitespaceAfterCurrentToken() const
+ {
+ return mWhitespaceAfterCurrentToken;
+ }
+
+ /**
+ * Returns the next token.
+ */
+ const DependentSubstringType nextToken()
+ {
+ mozilla::RangedPtr<const CharType> tokenStart = mIter;
+ mozilla::RangedPtr<const CharType> tokenEnd = mIter;
+
+ MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
+ "Should be at beginning of token if there is one");
+
+ // Search until we hit separator or end (or whitespace, if a separator
+ // isn't required -- see clause with 'break' below).
+ while (mIter < mEnd && *mIter != mSeparatorChar) {
+ // Skip to end of the current word.
+ while (mIter < mEnd &&
+ !IsWhitespace(*mIter) && *mIter != mSeparatorChar) {
+ ++mIter;
+ }
+ tokenEnd = mIter;
+
+ // Skip whitespace after the current word.
+ mWhitespaceAfterCurrentToken = false;
+ while (mIter < mEnd && IsWhitespace(*mIter)) {
+ mWhitespaceAfterCurrentToken = true;
+ ++mIter;
+ }
+ if (mSeparatorOptional) {
+ // We've hit (and skipped) whitespace, and that's sufficient to end
+ // our token, regardless of whether we've reached a SeparatorChar.
+ break;
+ } // (else, we'll keep looping until we hit mEnd or SeparatorChar)
+ }
+
+ mSeparatorAfterCurrentToken = (mIter != mEnd &&
+ *mIter == mSeparatorChar);
+ MOZ_ASSERT(mSeparatorOptional ||
+ (mSeparatorAfterCurrentToken == (mIter < mEnd)),
+ "If we require a separator and haven't hit the end of "
+ "our string, then we shouldn't have left the loop "
+ "unless we hit a separator");
+
+ // Skip separator (and any whitespace after it), if we're at one.
+ if (mSeparatorAfterCurrentToken) {
+ ++mIter;
+
+ while (mIter < mEnd && IsWhitespace(*mIter)) {
+ mWhitespaceAfterCurrentToken = true;
+ ++mIter;
+ }
+ }
+
+ return Substring(tokenStart.get(), tokenEnd.get());
+ }
+
+private:
+ mozilla::RangedPtr<const CharType> mIter;
+ const mozilla::RangedPtr<const CharType> mEnd;
+ CharType mSeparatorChar;
+ bool mWhitespaceBeforeFirstToken;
+ bool mWhitespaceAfterCurrentToken;
+ bool mSeparatorAfterCurrentToken;
+ bool mSeparatorOptional;
+};
+
+template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
+class nsCharSeparatedTokenizerTemplate
+ : public nsTCharSeparatedTokenizer<nsDependentSubstring, IsWhitespace>
+{
+public:
+ nsCharSeparatedTokenizerTemplate(const nsSubstring& aSource,
+ char16_t aSeparatorChar,
+ uint32_t aFlags = 0)
+ : nsTCharSeparatedTokenizer<nsDependentSubstring,
+ IsWhitespace>(aSource, aSeparatorChar, aFlags)
+ {
+ }
+};
+
+typedef nsCharSeparatedTokenizerTemplate<> nsCharSeparatedTokenizer;
+
+template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
+class nsCCharSeparatedTokenizerTemplate
+ : public nsTCharSeparatedTokenizer<nsDependentCSubstring, IsWhitespace>
+{
+public:
+ nsCCharSeparatedTokenizerTemplate(const nsCSubstring& aSource,
+ char aSeparatorChar,
+ uint32_t aFlags = 0)
+ : nsTCharSeparatedTokenizer<nsDependentCSubstring,
+ IsWhitespace>(aSource, aSeparatorChar, aFlags)
+ {
+ }
+};
+
+typedef nsCCharSeparatedTokenizerTemplate<> nsCCharSeparatedTokenizer;
+
+#endif /* __nsCharSeparatedTokenizer_h */
diff --git a/xpcom/ds/nsCheapSets.h b/xpcom/ds/nsCheapSets.h
new file mode 100644
index 000000000..d75e60d20
--- /dev/null
+++ b/xpcom/ds/nsCheapSets.h
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsCheapSets_h__
+#define __nsCheapSets_h__
+
+#include "nsTHashtable.h"
+#include <stdint.h>
+
+enum nsCheapSetOperator
+{
+ OpNext = 0, // enumerator says continue
+ OpRemove = 1, // enumerator says remove and continue
+};
+
+/**
+ * A set that takes up minimal size when there are 0 or 1 entries in the set.
+ * Use for cases where sizes of 0 and 1 are even slightly common.
+ */
+template<typename EntryType>
+class nsCheapSet
+{
+public:
+ typedef typename EntryType::KeyType KeyType;
+ typedef nsCheapSetOperator (*Enumerator)(EntryType* aEntry, void* userArg);
+
+ nsCheapSet() : mState(ZERO) {}
+ ~nsCheapSet() { Clear(); }
+
+ /**
+ * Remove all entries.
+ */
+ void Clear()
+ {
+ switch (mState) {
+ case ZERO:
+ break;
+ case ONE:
+ GetSingleEntry()->~EntryType();
+ break;
+ case MANY:
+ delete mUnion.table;
+ break;
+ default:
+ NS_NOTREACHED("bogus state");
+ break;
+ }
+ mState = ZERO;
+ }
+
+ void Put(const KeyType aVal);
+
+ void Remove(const KeyType aVal);
+
+ bool Contains(const KeyType aVal)
+ {
+ switch (mState) {
+ case ZERO:
+ return false;
+ case ONE:
+ return GetSingleEntry()->KeyEquals(EntryType::KeyToPointer(aVal));
+ case MANY:
+ return !!mUnion.table->GetEntry(aVal);
+ default:
+ NS_NOTREACHED("bogus state");
+ return false;
+ }
+ }
+
+ uint32_t EnumerateEntries(Enumerator aEnumFunc, void* aUserArg)
+ {
+ switch (mState) {
+ case ZERO:
+ return 0;
+ case ONE:
+ if (aEnumFunc(GetSingleEntry(), aUserArg) == OpRemove) {
+ GetSingleEntry()->~EntryType();
+ mState = ZERO;
+ }
+ return 1;
+ case MANY: {
+ uint32_t n = mUnion.table->Count();
+ for (auto iter = mUnion.table->Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<EntryType*>(iter.Get());
+ if (aEnumFunc(entry, aUserArg) == OpRemove) {
+ iter.Remove();
+ }
+ }
+ return n;
+ }
+ default:
+ NS_NOTREACHED("bogus state");
+ return 0;
+ }
+ }
+
+private:
+ EntryType* GetSingleEntry()
+ {
+ return reinterpret_cast<EntryType*>(&mUnion.singleEntry[0]);
+ }
+
+ enum SetState
+ {
+ ZERO,
+ ONE,
+ MANY
+ };
+
+ union
+ {
+ nsTHashtable<EntryType>* table;
+ char singleEntry[sizeof(EntryType)];
+ } mUnion;
+ enum SetState mState;
+};
+
+template<typename EntryType>
+void
+nsCheapSet<EntryType>::Put(const KeyType aVal)
+{
+ switch (mState) {
+ case ZERO:
+ new (GetSingleEntry()) EntryType(EntryType::KeyToPointer(aVal));
+ mState = ONE;
+ return;
+ case ONE: {
+ nsTHashtable<EntryType>* table = new nsTHashtable<EntryType>();
+ EntryType* entry = GetSingleEntry();
+ table->PutEntry(entry->GetKey());
+ entry->~EntryType();
+ mUnion.table = table;
+ mState = MANY;
+ }
+ MOZ_FALLTHROUGH;
+
+ case MANY:
+ mUnion.table->PutEntry(aVal);
+ return;
+ default:
+ NS_NOTREACHED("bogus state");
+ return;
+ }
+}
+
+template<typename EntryType>
+void
+nsCheapSet<EntryType>::Remove(const KeyType aVal)
+{
+ switch (mState) {
+ case ZERO:
+ break;
+ case ONE:
+ if (Contains(aVal)) {
+ GetSingleEntry()->~EntryType();
+ mState = ZERO;
+ }
+ break;
+ case MANY:
+ mUnion.table->RemoveEntry(aVal);
+ break;
+ default:
+ NS_NOTREACHED("bogus state");
+ break;
+ }
+}
+
+#endif
diff --git a/xpcom/ds/nsExpirationTracker.h b/xpcom/ds/nsExpirationTracker.h
new file mode 100644
index 000000000..2e77895db
--- /dev/null
+++ b/xpcom/ds/nsExpirationTracker.h
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSEXPIRATIONTRACKER_H_
+#define NSEXPIRATIONTRACKER_H_
+
+#include "mozilla/Logging.h"
+#include "nsTArray.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Services.h"
+
+/**
+ * Data used to track the expiration state of an object. We promise that this
+ * is 32 bits so that objects that includes this as a field can pad and align
+ * efficiently.
+ */
+struct nsExpirationState
+{
+ enum
+ {
+ NOT_TRACKED = (1U << 4) - 1,
+ MAX_INDEX_IN_GENERATION = (1U << 28) - 1
+ };
+
+ nsExpirationState() : mGeneration(NOT_TRACKED) {}
+ bool IsTracked() { return mGeneration != NOT_TRACKED; }
+
+ /**
+ * The generation that this object belongs to, or NOT_TRACKED.
+ */
+ uint32_t mGeneration:4;
+ uint32_t mIndexInGeneration:28;
+};
+
+/**
+ * ExpirationTracker classes:
+ * - ExpirationTrackerImpl (Thread-safe class)
+ * - nsExpirationTracker (Main-thread only class)
+ *
+ * These classes can track the lifetimes and usage of a large number of
+ * objects, and send a notification some window of time after a live object was
+ * last used. This is very useful when you manage a large number of objects
+ * and want to flush some after they haven't been used for a while.
+ * nsExpirationTracker is designed to be very space and time efficient.
+ *
+ * The type parameter T is the object type that we will track pointers to. T
+ * must include an accessible method GetExpirationState() that returns a
+ * pointer to an nsExpirationState associated with the object (preferably,
+ * stored in a field of the object).
+ *
+ * The parameter K is the number of generations that will be used. Increasing
+ * the number of generations narrows the window within which we promise
+ * to fire notifications, at a slight increase in space cost for the tracker.
+ * We require 2 <= K <= nsExpirationState::NOT_TRACKED (currently 15).
+ *
+ * To use this class, you need to inherit from it and override the
+ * NotifyExpired() method.
+ *
+ * The approach is to track objects in K generations. When an object is accessed
+ * it moves from its current generation to the newest generation. Generations
+ * are stored in a cyclic array; when a timer interrupt fires, we advance
+ * the current generation pointer to effectively age all objects very efficiently.
+ * By storing information in each object about its generation and index within its
+ * generation array, we make removal of objects from a generation very cheap.
+ *
+ * Future work:
+ * -- Add a method to change the timer period?
+ */
+
+/**
+ * Base class for ExiprationTracker implementations.
+ *
+ * nsExpirationTracker class below is a specialized class to be inherited by the
+ * instances to be accessed only on main-thread.
+ *
+ * For creating a thread-safe tracker, you can define a subclass inheriting this
+ * base class and specialize the Mutex and AutoLock to be used.
+ *
+ */
+template<typename T, uint32_t K, typename Mutex, typename AutoLock>
+class ExpirationTrackerImpl
+{
+public:
+ /**
+ * Initialize the tracker.
+ * @param aTimerPeriod the timer period in milliseconds. The guarantees
+ * provided by the tracker are defined in terms of this period. If the
+ * period is zero, then we don't use a timer and rely on someone calling
+ * AgeOneGenerationLocked explicitly.
+ */
+ ExpirationTrackerImpl(uint32_t aTimerPeriod, const char* aName)
+ : mTimerPeriod(aTimerPeriod)
+ , mNewestGeneration(0)
+ , mInAgeOneGeneration(false)
+ , mName(aName)
+ {
+ static_assert(K >= 2 && K <= nsExpirationState::NOT_TRACKED,
+ "Unsupported number of generations (must be 2 <= K <= 15)");
+ MOZ_ASSERT(NS_IsMainThread());
+ mObserver = new ExpirationTrackerObserver();
+ mObserver->Init(this);
+ }
+
+ virtual ~ExpirationTrackerImpl()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mTimer) {
+ mTimer->Cancel();
+ }
+ mObserver->Destroy();
+ }
+
+ /**
+ * Add an object to be tracked. It must not already be tracked. It will
+ * be added to the newest generation, i.e., as if it was just used.
+ * @return an error on out-of-memory
+ */
+ nsresult AddObjectLocked(T* aObj, const AutoLock& aAutoLock)
+ {
+ nsExpirationState* state = aObj->GetExpirationState();
+ NS_ASSERTION(!state->IsTracked(),
+ "Tried to add an object that's already tracked");
+ nsTArray<T*>& generation = mGenerations[mNewestGeneration];
+ uint32_t index = generation.Length();
+ if (index > nsExpirationState::MAX_INDEX_IN_GENERATION) {
+ NS_WARNING("More than 256M elements tracked, this is probably a problem");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (index == 0) {
+ // We might need to start the timer
+ nsresult rv = CheckStartTimerLocked(aAutoLock);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ if (!generation.AppendElement(aObj)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ state->mGeneration = mNewestGeneration;
+ state->mIndexInGeneration = index;
+ return NS_OK;
+ }
+
+ /**
+ * Remove an object from the tracker. It must currently be tracked.
+ */
+ void RemoveObjectLocked(T* aObj, const AutoLock& aAutoLock)
+ {
+ nsExpirationState* state = aObj->GetExpirationState();
+ NS_ASSERTION(state->IsTracked(), "Tried to remove an object that's not tracked");
+ nsTArray<T*>& generation = mGenerations[state->mGeneration];
+ uint32_t index = state->mIndexInGeneration;
+ NS_ASSERTION(generation.Length() > index &&
+ generation[index] == aObj, "Object is lying about its index");
+ // Move the last object to fill the hole created by removing aObj
+ uint32_t last = generation.Length() - 1;
+ T* lastObj = generation[last];
+ generation[index] = lastObj;
+ lastObj->GetExpirationState()->mIndexInGeneration = index;
+ generation.RemoveElementAt(last);
+ state->mGeneration = nsExpirationState::NOT_TRACKED;
+ // We do not check whether we need to stop the timer here. The timer
+ // will check that itself next time it fires. Checking here would not
+ // be efficient since we'd need to track all generations. Also we could
+ // thrash by incessantly creating and destroying timers if someone
+ // kept adding and removing an object from the tracker.
+ }
+
+ /**
+ * Notify that an object has been used.
+ * @return an error if we lost the object from the tracker...
+ */
+ nsresult MarkUsedLocked(T* aObj, const AutoLock& aAutoLock)
+ {
+ nsExpirationState* state = aObj->GetExpirationState();
+ if (mNewestGeneration == state->mGeneration) {
+ return NS_OK;
+ }
+ RemoveObjectLocked(aObj, aAutoLock);
+ return AddObjectLocked(aObj, aAutoLock);
+ }
+
+ /**
+ * The timer calls this, but it can also be manually called if you want
+ * to age objects "artifically". This can result in calls to NotifyExpiredLocked.
+ */
+ void AgeOneGenerationLocked(const AutoLock& aAutoLock)
+ {
+ if (mInAgeOneGeneration) {
+ NS_WARNING("Can't reenter AgeOneGeneration from NotifyExpired");
+ return;
+ }
+
+ mInAgeOneGeneration = true;
+ uint32_t reapGeneration =
+ mNewestGeneration > 0 ? mNewestGeneration - 1 : K - 1;
+ nsTArray<T*>& generation = mGenerations[reapGeneration];
+ // The following is rather tricky. We have to cope with objects being
+ // removed from this generation either because of a call to RemoveObject
+ // (or indirectly via MarkUsedLocked) inside NotifyExpiredLocked. Fortunately
+ // no objects can be added to this generation because it's not the newest
+ // generation. We depend on the fact that RemoveObject can only cause
+ // the indexes of objects in this generation to *decrease*, not increase.
+ // So if we start from the end and work our way backwards we are guaranteed
+ // to see each object at least once.
+ size_t index = generation.Length();
+ for (;;) {
+ // Objects could have been removed so index could be outside
+ // the array
+ index = XPCOM_MIN(index, generation.Length());
+ if (index == 0) {
+ break;
+ }
+ --index;
+ NotifyExpiredLocked(generation[index], aAutoLock);
+ }
+ // Any leftover objects from reapGeneration just end up in the new
+ // newest-generation. This is bad form, though, so warn if there are any.
+ if (!generation.IsEmpty()) {
+ NS_WARNING("Expired objects were not removed or marked used");
+ }
+ // Free excess memory used by the generation array, since we probably
+ // just removed most or all of its elements.
+ generation.Compact();
+ mNewestGeneration = reapGeneration;
+ mInAgeOneGeneration = false;
+ }
+
+ /**
+ * This just calls AgeOneGenerationLocked K times. Under normal circumstances
+ * this will result in all objects getting NotifyExpiredLocked called on them,
+ * but if NotifyExpiredLocked itself marks some objects as used, then those
+ * objects might not expire. This would be a good thing to call if we get into
+ * a critically-low memory situation.
+ */
+ void AgeAllGenerationsLocked(const AutoLock& aAutoLock)
+ {
+ uint32_t i;
+ for (i = 0; i < K; ++i) {
+ AgeOneGenerationLocked(aAutoLock);
+ }
+ }
+
+ class Iterator
+ {
+ private:
+ ExpirationTrackerImpl<T, K, Mutex, AutoLock>* mTracker;
+ uint32_t mGeneration;
+ uint32_t mIndex;
+ public:
+ Iterator(ExpirationTrackerImpl<T, K, Mutex, AutoLock>* aTracker,
+ AutoLock& aAutoLock)
+ : mTracker(aTracker)
+ , mGeneration(0)
+ , mIndex(0)
+ {
+ }
+
+ T* Next()
+ {
+ while (mGeneration < K) {
+ nsTArray<T*>* generation = &mTracker->mGenerations[mGeneration];
+ if (mIndex < generation->Length()) {
+ ++mIndex;
+ return (*generation)[mIndex - 1];
+ }
+ ++mGeneration;
+ mIndex = 0;
+ }
+ return nullptr;
+ }
+ };
+
+ friend class Iterator;
+
+ bool IsEmptyLocked(const AutoLock& aAutoLock)
+ {
+ for (uint32_t i = 0; i < K; ++i) {
+ if (!mGenerations[i].IsEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+protected:
+ /**
+ * This must be overridden to catch notifications. It is called whenever
+ * we detect that an object has not been used for at least (K-1)*mTimerPeriod
+ * milliseconds. If timer events are not delayed, it will be called within
+ * roughly K*mTimerPeriod milliseconds after the last use.
+ * (Unless AgeOneGenerationLocked or AgeAllGenerationsLocked have been called
+ * to accelerate the aging process.)
+ *
+ * NOTE: These bounds ignore delays in timer firings due to actual work being
+ * performed by the browser. We use a slack timer so there is always at least
+ * mTimerPeriod milliseconds between firings, which gives us (K-1)*mTimerPeriod
+ * as a pretty solid lower bound. The upper bound is rather loose, however.
+ * If the maximum amount by which any given timer firing is delayed is D, then
+ * the upper bound before NotifyExpiredLocked is called is K*(mTimerPeriod + D).
+ *
+ * The NotifyExpiredLocked call is expected to remove the object from the tracker,
+ * but it need not. The object (or other objects) could be "resurrected"
+ * by calling MarkUsedLocked() on them, or they might just not be removed.
+ * Any objects left over that have not been resurrected or removed
+ * are placed in the new newest-generation, but this is considered "bad form"
+ * and should be avoided (we'll issue a warning). (This recycling counts
+ * as "a use" for the purposes of the expiry guarantee above...)
+ *
+ * For robustness and simplicity, we allow objects to be notified more than
+ * once here in the same timer tick.
+ */
+ virtual void NotifyExpiredLocked(T*, const AutoLock&) = 0;
+
+ virtual Mutex& GetMutex() = 0;
+
+private:
+ class ExpirationTrackerObserver;
+ RefPtr<ExpirationTrackerObserver> mObserver;
+ nsTArray<T*> mGenerations[K];
+ nsCOMPtr<nsITimer> mTimer;
+ uint32_t mTimerPeriod;
+ uint32_t mNewestGeneration;
+ bool mInAgeOneGeneration;
+ const char* const mName; // Used for timer firing profiling.
+
+ /**
+ * Whenever "memory-pressure" is observed, it calls AgeAllGenerationsLocked()
+ * to minimize memory usage.
+ */
+ class ExpirationTrackerObserver final : public nsIObserver
+ {
+ public:
+ void Init(ExpirationTrackerImpl<T, K, Mutex, AutoLock>* aObj)
+ {
+ mOwner = aObj;
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(this, "memory-pressure", false);
+ }
+ }
+ void Destroy()
+ {
+ mOwner = nullptr;
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, "memory-pressure");
+ }
+ }
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ private:
+ ExpirationTrackerImpl<T, K, Mutex, AutoLock>* mOwner;
+ };
+
+ void HandleLowMemory() {
+ AutoLock lock(GetMutex());
+ AgeAllGenerationsLocked(lock);
+ }
+
+ void HandleTimeout() {
+ AutoLock lock(GetMutex());
+ AgeOneGenerationLocked(lock);
+ // Cancel the timer if we have no objects to track
+ if (IsEmptyLocked(lock)) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ }
+
+ static void TimerCallback(nsITimer* aTimer, void* aThis)
+ {
+ ExpirationTrackerImpl* tracker = static_cast<ExpirationTrackerImpl*>(aThis);
+ tracker->HandleTimeout();
+ }
+
+ nsresult CheckStartTimerLocked(const AutoLock& aAutoLock)
+ {
+ if (mTimer || !mTimerPeriod) {
+ return NS_OK;
+ }
+ mTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mTimer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!NS_IsMainThread()) {
+ // TimerCallback should always be run on the main thread to prevent races
+ // to the destruction of the tracker.
+ nsCOMPtr<nsIEventTarget> target = do_GetMainThread();
+ NS_ENSURE_STATE(target);
+ mTimer->SetTarget(target);
+ }
+ mTimer->InitWithNamedFuncCallback(TimerCallback, this, mTimerPeriod,
+ nsITimer::TYPE_REPEATING_SLACK, mName);
+ return NS_OK;
+ }
+};
+
+namespace detail {
+
+class PlaceholderLock {
+public:
+ void Lock() {}
+ void Unlock() {}
+};
+
+class PlaceholderAutoLock {
+public:
+ explicit PlaceholderAutoLock(PlaceholderLock&) { }
+ ~PlaceholderAutoLock() = default;
+
+};
+
+template<typename T, uint32_t K>
+using SingleThreadedExpirationTracker =
+ ExpirationTrackerImpl<T, K, PlaceholderLock, PlaceholderAutoLock>;
+
+} // namespace detail
+
+template<typename T, uint32_t K>
+class nsExpirationTracker : protected ::detail::SingleThreadedExpirationTracker<T, K>
+{
+ typedef ::detail::PlaceholderLock Lock;
+ typedef ::detail::PlaceholderAutoLock AutoLock;
+
+ Lock mLock;
+
+ AutoLock FakeLock() {
+ return AutoLock(mLock);
+ }
+
+ Lock& GetMutex() override
+ {
+ return mLock;
+ }
+
+ void NotifyExpiredLocked(T* aObject, const AutoLock&) override
+ {
+ NotifyExpired(aObject);
+ }
+
+protected:
+ virtual void NotifyExpired(T* aObj) = 0;
+
+public:
+ nsExpirationTracker(uint32_t aTimerPeriod, const char* aName)
+ : ::detail::SingleThreadedExpirationTracker<T, K>(aTimerPeriod, aName)
+ { }
+
+ virtual ~nsExpirationTracker()
+ { }
+
+ nsresult AddObject(T* aObj)
+ {
+ return this->AddObjectLocked(aObj, FakeLock());
+ }
+
+ void RemoveObject(T* aObj)
+ {
+ this->RemoveObjectLocked(aObj, FakeLock());
+ }
+
+ nsresult MarkUsed(T* aObj)
+ {
+ return this->MarkUsedLocked(aObj, FakeLock());
+ }
+
+ void AgeOneGeneration()
+ {
+ this->AgeOneGenerationLocked(FakeLock());
+ }
+
+ void AgeAllGenerations()
+ {
+ this->AgeAllGenerationsLocked(FakeLock());
+ }
+
+ class Iterator
+ {
+ private:
+ AutoLock mAutoLock;
+ typename ExpirationTrackerImpl<T, K, Lock, AutoLock>::Iterator mIterator;
+ public:
+ explicit Iterator(nsExpirationTracker<T, K>* aTracker)
+ : mAutoLock(aTracker->GetMutex())
+ , mIterator(aTracker, mAutoLock)
+ {
+ }
+
+ T* Next()
+ {
+ return mIterator.Next();
+ }
+ };
+
+ friend class Iterator;
+
+ bool IsEmpty()
+ {
+ return this->IsEmptyLocked(FakeLock());
+ }
+};
+
+template<typename T, uint32_t K, typename Mutex, typename AutoLock>
+NS_IMETHODIMP
+ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
+ExpirationTrackerObserver::Observe(
+ nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+ if (!strcmp(aTopic, "memory-pressure") && mOwner) {
+ mOwner->HandleLowMemory();
+ }
+ return NS_OK;
+}
+
+template<class T, uint32_t K, typename Mutex, typename AutoLock>
+NS_IMETHODIMP_(MozExternalRefCountType)
+ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
+ExpirationTrackerObserver::AddRef(void)
+{
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
+ NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
+ ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "ExpirationTrackerObserver", sizeof(*this));
+ return mRefCnt;
+}
+
+template<class T, uint32_t K, typename Mutex, typename AutoLock>
+NS_IMETHODIMP_(MozExternalRefCountType)
+ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
+ExpirationTrackerObserver::Release(void)
+{
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+ NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "ExpirationTrackerObserver");
+ if (mRefCnt == 0) {
+ NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
+ mRefCnt = 1; /* stabilize */
+ delete (this);
+ return 0;
+ }
+ return mRefCnt;
+}
+
+template<class T, uint32_t K, typename Mutex, typename AutoLock>
+NS_IMETHODIMP
+ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
+ExpirationTrackerObserver::QueryInterface(
+ REFNSIID aIID, void** aInstancePtr)
+{
+ NS_ASSERTION(aInstancePtr,
+ "QueryInterface requires a non-NULL destination!");
+ nsresult rv = NS_ERROR_FAILURE;
+ NS_INTERFACE_TABLE(ExpirationTrackerObserver, nsIObserver)
+ return rv;
+}
+
+#endif /*NSEXPIRATIONTRACKER_H_*/
diff --git a/xpcom/ds/nsHashPropertyBag.cpp b/xpcom/ds/nsHashPropertyBag.cpp
new file mode 100644
index 000000000..6f9fc8dec
--- /dev/null
+++ b/xpcom/ds/nsHashPropertyBag.cpp
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsHashPropertyBag.h"
+#include "nsArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsIVariant.h"
+#include "nsIProperty.h"
+#include "nsVariant.h"
+#include "mozilla/Attributes.h"
+
+/*
+ * nsHashPropertyBagBase implementation.
+ */
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::HasKey(const nsAString& aName, bool* aResult)
+{
+ *aResult = mPropertyHash.Get(aName, nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::Get(const nsAString& aName, nsIVariant** aResult)
+{
+ if (!mPropertyHash.Get(aName, aResult)) {
+ *aResult = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::GetProperty(const nsAString& aName, nsIVariant** aResult)
+{
+ bool isFound = mPropertyHash.Get(aName, aResult);
+ if (!isFound) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::SetProperty(const nsAString& aName, nsIVariant* aValue)
+{
+ if (NS_WARN_IF(!aValue)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mPropertyHash.Put(aName, aValue);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::DeleteProperty(const nsAString& aName)
+{
+ // is it too much to ask for ns*Hashtable to return
+ // a boolean indicating whether RemoveEntry succeeded
+ // or not?!?!
+ bool isFound = mPropertyHash.Get(aName, nullptr);
+ if (!isFound) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // then from the hash
+ mPropertyHash.Remove(aName);
+
+ return NS_OK;
+}
+
+
+//
+// nsSimpleProperty class and impl; used for GetEnumerator
+//
+
+class nsSimpleProperty final : public nsIProperty
+{
+ ~nsSimpleProperty() {}
+
+public:
+ nsSimpleProperty(const nsAString& aName, nsIVariant* aValue)
+ : mName(aName)
+ , mValue(aValue)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROPERTY
+protected:
+ nsString mName;
+ nsCOMPtr<nsIVariant> mValue;
+};
+
+NS_IMPL_ISUPPORTS(nsSimpleProperty, nsIProperty)
+
+NS_IMETHODIMP
+nsSimpleProperty::GetName(nsAString& aName)
+{
+ aName.Assign(mName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimpleProperty::GetValue(nsIVariant** aValue)
+{
+ NS_IF_ADDREF(*aValue = mValue);
+ return NS_OK;
+}
+
+// end nsSimpleProperty
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::GetEnumerator(nsISimpleEnumerator** aResult)
+{
+ nsCOMPtr<nsIMutableArray> propertyArray = nsArray::Create();
+ if (!propertyArray) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
+ const nsAString& key = iter.Key();
+ nsIVariant* data = iter.UserData();
+ nsSimpleProperty* sprop = new nsSimpleProperty(key, data);
+ propertyArray->AppendElement(sprop, false);
+ }
+
+ return NS_NewArrayEnumerator(aResult, propertyArray);
+}
+
+#define IMPL_GETSETPROPERTY_AS(Name, Type) \
+NS_IMETHODIMP \
+nsHashPropertyBagBase::GetPropertyAs ## Name (const nsAString & prop, Type *_retval) \
+{ \
+ nsIVariant* v = mPropertyHash.GetWeak(prop); \
+ if (!v) \
+ return NS_ERROR_NOT_AVAILABLE; \
+ return v->GetAs ## Name(_retval); \
+} \
+\
+NS_IMETHODIMP \
+nsHashPropertyBagBase::SetPropertyAs ## Name (const nsAString & prop, Type value) \
+{ \
+ nsCOMPtr<nsIWritableVariant> var = new nsVariant(); \
+ var->SetAs ## Name(value); \
+ return SetProperty(prop, var); \
+}
+
+IMPL_GETSETPROPERTY_AS(Int32, int32_t)
+IMPL_GETSETPROPERTY_AS(Uint32, uint32_t)
+IMPL_GETSETPROPERTY_AS(Int64, int64_t)
+IMPL_GETSETPROPERTY_AS(Uint64, uint64_t)
+IMPL_GETSETPROPERTY_AS(Double, double)
+IMPL_GETSETPROPERTY_AS(Bool, bool)
+
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::GetPropertyAsAString(const nsAString& aProp,
+ nsAString& aResult)
+{
+ nsIVariant* v = mPropertyHash.GetWeak(aProp);
+ if (!v) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return v->GetAsAString(aResult);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::GetPropertyAsACString(const nsAString& aProp,
+ nsACString& aResult)
+{
+ nsIVariant* v = mPropertyHash.GetWeak(aProp);
+ if (!v) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return v->GetAsACString(aResult);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::GetPropertyAsAUTF8String(const nsAString& aProp,
+ nsACString& aResult)
+{
+ nsIVariant* v = mPropertyHash.GetWeak(aProp);
+ if (!v) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return v->GetAsAUTF8String(aResult);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::GetPropertyAsInterface(const nsAString& aProp,
+ const nsIID& aIID,
+ void** aResult)
+{
+ nsIVariant* v = mPropertyHash.GetWeak(aProp);
+ if (!v) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ nsCOMPtr<nsISupports> val;
+ nsresult rv = v->GetAsISupports(getter_AddRefs(val));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!val) {
+ // We have a value, but it's null
+ *aResult = nullptr;
+ return NS_OK;
+ }
+ return val->QueryInterface(aIID, aResult);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::SetPropertyAsAString(const nsAString& aProp,
+ const nsAString& aValue)
+{
+ nsCOMPtr<nsIWritableVariant> var = new nsVariant();
+ var->SetAsAString(aValue);
+ return SetProperty(aProp, var);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::SetPropertyAsACString(const nsAString& aProp,
+ const nsACString& aValue)
+{
+ nsCOMPtr<nsIWritableVariant> var = new nsVariant();
+ var->SetAsACString(aValue);
+ return SetProperty(aProp, var);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::SetPropertyAsAUTF8String(const nsAString& aProp,
+ const nsACString& aValue)
+{
+ nsCOMPtr<nsIWritableVariant> var = new nsVariant();
+ var->SetAsAUTF8String(aValue);
+ return SetProperty(aProp, var);
+}
+
+NS_IMETHODIMP
+nsHashPropertyBagBase::SetPropertyAsInterface(const nsAString& aProp,
+ nsISupports* aValue)
+{
+ nsCOMPtr<nsIWritableVariant> var = new nsVariant();
+ var->SetAsISupports(aValue);
+ return SetProperty(aProp, var);
+}
+
+
+/*
+ * nsHashPropertyBag implementation.
+ */
+
+NS_IMPL_ADDREF(nsHashPropertyBag)
+NS_IMPL_RELEASE(nsHashPropertyBag)
+
+NS_INTERFACE_MAP_BEGIN(nsHashPropertyBag)
+ NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag, nsIWritablePropertyBag)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWritablePropertyBag)
+ NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2)
+ NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2)
+NS_INTERFACE_MAP_END
+
+
+/*
+ * nsHashPropertyBagCC implementation.
+ */
+
+NS_IMPL_CYCLE_COLLECTION(nsHashPropertyBagCC, mPropertyHash)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHashPropertyBagCC)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHashPropertyBagCC)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHashPropertyBagCC)
+ NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag, nsIWritablePropertyBag)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWritablePropertyBag)
+ NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2)
+ NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2)
+NS_INTERFACE_MAP_END
diff --git a/xpcom/ds/nsHashPropertyBag.h b/xpcom/ds/nsHashPropertyBag.h
new file mode 100644
index 000000000..e41c984ba
--- /dev/null
+++ b/xpcom/ds/nsHashPropertyBag.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsHashPropertyBag_h___
+#define nsHashPropertyBag_h___
+
+#include "nsIVariant.h"
+#include "nsIWritablePropertyBag.h"
+#include "nsIWritablePropertyBag2.h"
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsInterfaceHashtable.h"
+
+class nsHashPropertyBagBase
+ : public nsIWritablePropertyBag
+ , public nsIWritablePropertyBag2
+{
+public:
+ nsHashPropertyBagBase() {}
+
+ NS_DECL_NSIPROPERTYBAG
+ NS_DECL_NSIPROPERTYBAG2
+
+ NS_DECL_NSIWRITABLEPROPERTYBAG
+ NS_DECL_NSIWRITABLEPROPERTYBAG2
+
+protected:
+ // a hash table of string -> nsIVariant
+ nsInterfaceHashtable<nsStringHashKey, nsIVariant> mPropertyHash;
+};
+
+class nsHashPropertyBag : public nsHashPropertyBagBase
+{
+public:
+ nsHashPropertyBag() {}
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+ virtual ~nsHashPropertyBag() {}
+};
+
+/* A cycle collected nsHashPropertyBag for main-thread-only use. */
+class nsHashPropertyBagCC final : public nsHashPropertyBagBase
+{
+public:
+ nsHashPropertyBagCC() {}
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHashPropertyBagCC,
+ nsIWritablePropertyBag)
+protected:
+ virtual ~nsHashPropertyBagCC() {}
+};
+
+#endif /* nsHashPropertyBag_h___ */
diff --git a/xpcom/ds/nsIArray.idl b/xpcom/ds/nsIArray.idl
new file mode 100644
index 000000000..d591253e9
--- /dev/null
+++ b/xpcom/ds/nsIArray.idl
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsISimpleEnumerator;
+
+/**
+ * nsIArray
+ *
+ * An indexed collection of elements. Provides basic functionality for
+ * retrieving elements at a specific position, searching for
+ * elements. Indexes are zero-based, such that the last element in the
+ * array is stored at the index length-1.
+ *
+ * For an array which can be modified, see nsIMutableArray below.
+ *
+ * Neither interface makes any attempt to protect the individual
+ * elements from modification. The convention is that the elements of
+ * the array should not be modified. Documentation within a specific
+ * interface should describe variations from this convention.
+ *
+ * It is also convention that if an interface provides access to an
+ * nsIArray, that the array should not be QueryInterfaced to an
+ * nsIMutableArray for modification. If the interface in question had
+ * intended the array to be modified, it would have returned an
+ * nsIMutableArray!
+ *
+ * null is a valid entry in the array, and as such any nsISupports
+ * parameters may be null, except where noted.
+ */
+[scriptable, uuid(114744d9-c369-456e-b55a-52fe52880d2d)]
+interface nsIArray : nsISupports
+{
+ /**
+ * length
+ *
+ * number of elements in the array.
+ */
+ readonly attribute unsigned long length;
+
+ /**
+ * queryElementAt()
+ *
+ * Retrieve a specific element of the array, and QueryInterface it
+ * to the specified interface. null is a valid result for
+ * this method, but exceptions are thrown in other circumstances
+ *
+ * @param index position of element
+ * @param uuid the IID of the requested interface
+ * @param result the object, QI'd to the requested interface
+ *
+ * @throws NS_ERROR_NO_INTERFACE when an entry exists at the
+ * specified index, but the requested interface is not
+ * available.
+ * @throws NS_ERROR_ILLEGAL_VALUE when index > length-1
+ *
+ */
+ void queryElementAt(in unsigned long index,
+ in nsIIDRef uuid,
+ [iid_is(uuid), retval] out nsQIResult result);
+
+ /**
+ * indexOf()
+ *
+ * Get the position of a specific element. Note that since null is
+ * a valid input, exceptions are used to indicate that an element
+ * is not found.
+ *
+ * @param startIndex The initial element to search in the array
+ * To start at the beginning, use 0 as the
+ * startIndex
+ * @param element The element you are looking for
+ * @returns a number >= startIndex which is the position of the
+ * element in the array.
+ * @throws NS_ERROR_FAILURE if the element was not in the array.
+ */
+ unsigned long indexOf(in unsigned long startIndex,
+ in nsISupports element);
+
+ /**
+ * enumerate the array
+ *
+ * @returns a new enumerator positioned at the start of the array
+ * @throws NS_ERROR_FAILURE if the array is empty (to make it easy
+ * to detect errors), or NS_ERROR_OUT_OF_MEMORY if out of memory.
+ */
+ nsISimpleEnumerator enumerate();
+};
diff --git a/xpcom/ds/nsIArrayExtensions.idl b/xpcom/ds/nsIArrayExtensions.idl
new file mode 100644
index 000000000..3682d2ee7
--- /dev/null
+++ b/xpcom/ds/nsIArrayExtensions.idl
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIArray.idl"
+
+/**
+ * Helper interface for allowing scripts to treat nsIArray instances as if
+ * they were nsISupportsArray instances while iterating.
+ *
+ * nsISupportsArray is convenient to iterate over in JavaScript:
+ *
+ * for (let i = 0; i < array.Count(); ++i) {
+ * let elem = array.GetElementAt(i);
+ * ...
+ * }
+ *
+ * but doing the same with nsIArray is somewhat less convenient, since
+ * queryElementAt is not nearly so nice to use from JavaScript. So we provide
+ * this extension interface so interfaces that currently return
+ * nsISupportsArray can start returning nsIArrayExtensions and all JavaScript
+ * should Just Work. Eventually we'll roll this interface into nsIArray
+ * itself, possibly getting rid of the Count() method, as it duplicates
+ * nsIArray functionality.
+ */
+[scriptable, uuid(261d442e-050c-453d-8aaa-b3f23bcc528b)]
+interface nsIArrayExtensions : nsIArray
+{
+ /**
+ * Count()
+ *
+ * Retrieves the length of the array. This is an alias for the
+ * |nsIArray.length| attribute.
+ */
+ uint32_t Count();
+
+ /**
+ * GetElementAt()
+ *
+ * Retrieve a specific element of the array. null is a valid result for
+ * this method.
+ *
+ * Note: If the index is out of bounds null will be returned.
+ * This differs from the behavior of nsIArray.queryElementAt() which
+ * will throw if an invalid index is specified.
+ *
+ * @param index position of element
+ */
+ nsISupports GetElementAt(in uint32_t index);
+};
diff --git a/xpcom/ds/nsIAtom.idl b/xpcom/ds/nsIAtom.idl
new file mode 100644
index 000000000..c02540838
--- /dev/null
+++ b/xpcom/ds/nsIAtom.idl
@@ -0,0 +1,166 @@
+/* -*- 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 "nsISupports.idl"
+
+%{C++
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsStringBuffer.h"
+%}
+
+native MallocSizeOf(mozilla::MallocSizeOf);
+
+/*
+ * Should this really be scriptable? Using atoms from script or proxies
+ * could be dangerous since double-wrapping could lead to loss of
+ * pointer identity.
+ */
+
+[scriptable, builtinclass, uuid(8b8c11d4-3ed5-4079-8974-73c7576cdb34)]
+interface nsIAtom : nsISupports
+{
+ /**
+ * Get the Unicode or UTF8 value for the string
+ */
+ [binaryname(ScriptableToString)] AString toString();
+ [noscript] AUTF8String toUTF8String();
+
+ /**
+ * Compare the atom to a specific string value
+ * Note that this will NEVER return/throw an error condition.
+ */
+ [binaryname(ScriptableEquals)] boolean equals(in AString aString);
+
+ [noscript, notxpcom]
+ size_t SizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);
+
+%{C++
+ // note this is NOT virtual so this won't muck with the vtable!
+ inline bool Equals(const nsAString& aString) const {
+ return aString.Equals(nsDependentString(mString, mLength));
+ }
+
+ inline bool IsStaticAtom() const {
+ return mIsStatic;
+ }
+
+ inline char16ptr_t GetUTF16String() const {
+ return mString;
+ }
+
+ inline uint32_t GetLength() const {
+ return mLength;
+ }
+
+ inline void ToString(nsAString& aBuf) {
+ // See the comment on |mString|'s declaration.
+ nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
+ }
+
+ inline nsStringBuffer* GetStringBuffer() const {
+ // See the comment on |mString|'s declaration.
+ return nsStringBuffer::FromData(mString);
+ }
+
+ /**
+ * A hashcode that is better distributed than the actual atom
+ * pointer, for use in situations that need a well-distributed
+ * hashcode.
+ */
+ inline uint32_t hash() const {
+ return mHash;
+ }
+
+protected:
+ uint32_t mLength:31;
+ uint32_t mIsStatic:1;
+ uint32_t mHash;
+ /**
+ * WARNING! There is an invisible constraint on |mString|: the chars it
+ * points to must belong to an nsStringBuffer. This is so that the
+ * nsStringBuffer::FromData() calls above are valid.
+ */
+ char16_t* mString;
+%}
+};
+
+
+%{C++
+/*
+ * The four forms of NS_Atomize (for use with |nsCOMPtr<nsIAtom>|) return the
+ * atom for the string given. At any given time there will always be one atom
+ * representing a given string. Atoms are intended to make string comparison
+ * cheaper by simplifying it to pointer equality. A pointer to the atom that
+ * does not own a reference is not guaranteed to be valid.
+ */
+
+
+/**
+ * Find an atom that matches the given UTF-8 string.
+ * The string is assumed to be zero terminated. Never returns null.
+ */
+extern already_AddRefed<nsIAtom> NS_Atomize(const char* aUTF8String);
+
+/**
+ * Find an atom that matches the given UTF-8 string. Never returns null.
+ */
+extern already_AddRefed<nsIAtom> NS_Atomize(const nsACString& aUTF8String);
+
+/**
+ * Find an atom that matches the given UTF-16 string.
+ * The string is assumed to be zero terminated. Never returns null.
+ */
+extern already_AddRefed<nsIAtom> NS_Atomize(const char16_t* aUTF16String);
+
+/**
+ * Find an atom that matches the given UTF-16 string. Never returns null.
+ */
+extern already_AddRefed<nsIAtom> NS_Atomize(const nsAString& aUTF16String);
+
+/**
+ * Return a count of the total number of atoms currently
+ * alive in the system.
+ */
+extern nsrefcnt NS_GetNumberOfAtoms(void);
+
+/**
+ * Return a pointer for a static atom for the string or null if there's
+ * no static atom for this string.
+ */
+extern nsIAtom* NS_GetStaticAtom(const nsAString& aUTF16String);
+
+/**
+ * Seal the static atom table
+ */
+extern void NS_SealStaticAtomTable();
+
+class nsAtomString : public nsString
+{
+public:
+ explicit nsAtomString(nsIAtom* aAtom)
+ {
+ aAtom->ToString(*this);
+ }
+};
+
+class nsAtomCString : public nsCString
+{
+public:
+ explicit nsAtomCString(nsIAtom* aAtom)
+ {
+ aAtom->ToUTF8String(*this);
+ }
+};
+
+class nsDependentAtomString : public nsDependentString
+{
+public:
+ explicit nsDependentAtomString(nsIAtom* aAtom)
+ : nsDependentString(aAtom->GetUTF16String(), aAtom->GetLength())
+ {
+ }
+};
+
+%}
diff --git a/xpcom/ds/nsIAtomService.idl b/xpcom/ds/nsIAtomService.idl
new file mode 100644
index 000000000..a7885a413
--- /dev/null
+++ b/xpcom/ds/nsIAtomService.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIAtom;
+
+%{C++
+#define NS_ATOMSERVICE_CID \
+{ /* ed3db3fc-0168-4cab-8818-98f5475a490c */ \
+ 0xed3db3fc, \
+ 0x0168, \
+ 0x4cab, \
+ {0x88, 0x18, 0x98, 0xf5, 0x47, 0x5a, 0x49, 0x0c} }
+
+#define NS_ATOMSERVICE_CONTRACTID "@mozilla.org/atom-service;1"
+%}
+
+/*
+ * Should this really be scriptable? Using atoms from script or proxies
+ * could be dangerous since double-wrapping could lead to loss of
+ * pointer identity.
+ */
+
+[scriptable, uuid(9c1f50b9-f9eb-42d4-a8cb-2c7600aeb241)]
+interface nsIAtomService : nsISupports {
+
+ /**
+ * Version of NS_Atomize that doesn't require linking against the
+ * XPCOM library. See nsIAtom.idl.
+ */
+ nsIAtom getAtom(in AString value);
+};
diff --git a/xpcom/ds/nsICollection.idl b/xpcom/ds/nsICollection.idl
new file mode 100644
index 000000000..3cd851419
--- /dev/null
+++ b/xpcom/ds/nsICollection.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISerializable.idl"
+
+interface nsIEnumerator;
+
+[deprecated, scriptable, uuid(83b6019c-cbc4-11d2-8cca-0060b0fc14a3)]
+interface nsICollection : nsISerializable
+{
+
+ uint32_t Count();
+ nsISupports GetElementAt(in uint32_t index);
+ void QueryElementAt(in uint32_t index, in nsIIDRef uuid,
+ [iid_is(uuid),retval] out nsQIResult result);
+ void SetElementAt(in uint32_t index, in nsISupports item);
+ void AppendElement(in nsISupports item);
+ void RemoveElement(in nsISupports item);
+
+ /**
+ * This clashes with |nsISimpleEnumerator nsIArray.enumerate()| (only on the
+ * binary side), so it is renamed with a 'Deprecated' prefix in favor of the
+ * non-deprecated |nsIArray.enumerate|.
+ */
+ [binaryname(DeprecatedEnumerate)]
+ nsIEnumerator Enumerate();
+
+ void Clear();
+
+};
+
+%{C++
+
+#ifndef nsCOMPtr_h__
+#include "nsCOMPtr.h"
+#endif
+
+class MOZ_STACK_CLASS nsQueryElementAt : public nsCOMPtr_helper
+ {
+ public:
+ nsQueryElementAt( nsICollection* aCollection, uint32_t aIndex, nsresult* aErrorPtr )
+ : mCollection(aCollection),
+ mIndex(aIndex),
+ mErrorPtr(aErrorPtr)
+ {
+ // nothing else to do here
+ }
+
+ virtual nsresult NS_FASTCALL operator()( const nsIID& aIID, void** )
+ const override;
+
+ private:
+ nsICollection* MOZ_NON_OWNING_REF mCollection;
+ uint32_t mIndex;
+ nsresult* mErrorPtr;
+ };
+
+inline
+const nsQueryElementAt
+do_QueryElementAt( nsICollection* aCollection, uint32_t aIndex, nsresult* aErrorPtr = 0 )
+ {
+ return nsQueryElementAt(aCollection, aIndex, aErrorPtr);
+ }
+
+%}
diff --git a/xpcom/ds/nsIEnumerator.idl b/xpcom/ds/nsIEnumerator.idl
new file mode 100644
index 000000000..9482ef436
--- /dev/null
+++ b/xpcom/ds/nsIEnumerator.idl
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+%{C++
+#define NS_ENUMERATOR_FALSE 1
+%}
+/*
+ * DO NOT USE THIS INTERFACE. IT IS HORRIBLY BROKEN, USES NS_COMFALSE
+ * AND IS BASICALLY IMPOSSIBLE TO USE CORRECTLY THROUGH PROXIES OR
+ * XPCONNECT. IF YOU SEE NEW USES OF THIS INTERFACE IN CODE YOU ARE
+ * REVIEWING, YOU SHOULD INSIST ON nsISimpleEnumerator.
+ *
+ * DON'T MAKE ME COME OVER THERE.
+ */
+[deprecated, scriptable, uuid(ad385286-cbc4-11d2-8cca-0060b0fc14a3)]
+interface nsIEnumerator : nsISupports {
+ /** First will reset the list. will return NS_FAILED if no items
+ */
+ void first();
+
+ /** Next will advance the list. will return failed if already at end
+ */
+ void next();
+
+ /** CurrentItem will return the CurrentItem item it will fail if the
+ * list is empty
+ */
+ nsISupports currentItem();
+
+ /** return if the collection is at the end. that is the beginning following
+ * a call to Prev and it is the end of the list following a call to next
+ */
+ void isDone();
+};
+
+[deprecated, uuid(75f158a0-cadd-11d2-8cca-0060b0fc14a3)]
+interface nsIBidirectionalEnumerator : nsIEnumerator {
+
+ /** Last will reset the list to the end. will return NS_FAILED if no items
+ */
+ void last();
+
+ /** Prev will decrement the list. will return failed if already at beginning
+ */
+ void prev();
+};
diff --git a/xpcom/ds/nsIHashable.idl b/xpcom/ds/nsIHashable.idl
new file mode 100644
index 000000000..aa223787c
--- /dev/null
+++ b/xpcom/ds/nsIHashable.idl
@@ -0,0 +1,24 @@
+/* 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"
+
+/**
+ * Represents an object that can be stored in a hashtable.
+ */
+[scriptable, uuid(17e595fa-b57a-4933-bd0f-b1812e8ab188)]
+interface nsIHashable : nsISupports
+{
+ /**
+ * Is this object the equivalent of the other object?
+ */
+ boolean equals(in nsIHashable aOther);
+
+ /**
+ * A generated hashcode for this object. Objects that are equivalent
+ * must have the same hash code. Getting this property should never
+ * throw an exception!
+ */
+ readonly attribute unsigned long hashCode;
+};
diff --git a/xpcom/ds/nsIINIParser.idl b/xpcom/ds/nsIINIParser.idl
new file mode 100644
index 000000000..166828e5d
--- /dev/null
+++ b/xpcom/ds/nsIINIParser.idl
@@ -0,0 +1,58 @@
+/* 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"
+
+interface nsIUTF8StringEnumerator;
+interface nsIFile;
+
+[scriptable, uuid(7eb955f6-3e78-4d39-b72f-c1bf12a94bce)]
+interface nsIINIParser : nsISupports
+{
+ /**
+ * Enumerates the [section]s available in the INI file.
+ */
+ nsIUTF8StringEnumerator getSections();
+
+ /**
+ * Enumerates the keys available within a section.
+ */
+ nsIUTF8StringEnumerator getKeys(in AUTF8String aSection);
+
+ /**
+ * Get the value of a string for a particular section and key.
+ */
+ AUTF8String getString(in AUTF8String aSection, in AUTF8String aKey);
+};
+
+[scriptable, uuid(b67bb24b-31a3-4a6a-a5d9-0485c9af5a04)]
+interface nsIINIParserWriter : nsISupports
+{
+ /**
+ * Windows and the NSIS installer code sometimes expect INI files to be in
+ * UTF-16 encoding. On Windows only, this flag to writeFile can be used to
+ * change the encoding from its default UTF-8.
+ */
+ const unsigned long WRITE_UTF16 = 0x1;
+
+ /**
+ * Set the value of a string for a particular section and key.
+ */
+ void setString(in AUTF8String aSection, in AUTF8String aKey, in AUTF8String aValue);
+
+ /**
+ * Write to the INI file.
+ */
+ void writeFile([optional] in nsIFile aINIFile,
+ [optional] in unsigned long aFlags);
+};
+
+[scriptable, uuid(ccae7ea5-1218-4b51-aecb-c2d8ecd46af9)]
+interface nsIINIParserFactory : nsISupports
+{
+ /**
+ * Create an iniparser instance from a local file.
+ */
+ nsIINIParser createINIParser(in nsIFile aINIFile);
+};
diff --git a/xpcom/ds/nsIMutableArray.idl b/xpcom/ds/nsIMutableArray.idl
new file mode 100644
index 000000000..67e527a81
--- /dev/null
+++ b/xpcom/ds/nsIMutableArray.idl
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIArrayExtensions.idl"
+
+/**
+ * nsIMutableArray
+ * A separate set of methods that will act on the array. Consumers of
+ * nsIArray should not QueryInterface to nsIMutableArray unless they
+ * own the array.
+ *
+ * As above, it is legal to add null elements to the array. Note also
+ * that null elements can be created as a side effect of
+ * insertElementAt(). Conversely, if insertElementAt() is never used,
+ * and null elements are never explicitly added to the array, then it
+ * is guaranteed that queryElementAt() will never return a null value.
+ *
+ * Any of these methods may throw NS_ERROR_OUT_OF_MEMORY when the
+ * array must grow to complete the call, but the allocation fails.
+ */
+[scriptable, uuid(af059da0-c85b-40ec-af07-ae4bfdc192cc)]
+interface nsIMutableArray : nsIArrayExtensions
+{
+ /**
+ * appendElement()
+ *
+ * Append an element at the end of the array.
+ *
+ * @param element The element to append.
+ * @param weak Whether or not to store the element using a weak
+ * reference.
+ * @throws NS_ERROR_FAILURE when a weak reference is requested,
+ * but the element does not support
+ * nsIWeakReference.
+ */
+ void appendElement(in nsISupports element, in boolean weak);
+
+ /**
+ * removeElementAt()
+ *
+ * Remove an element at a specific position, moving all elements
+ * stored at a higher position down one.
+ * To remove a specific element, use indexOf() to find the index
+ * first, then call removeElementAt().
+ *
+ * @param index the position of the item
+ *
+ */
+ void removeElementAt(in unsigned long index);
+
+ /**
+ * insertElementAt()
+ *
+ * Insert an element at the given position, moving the element
+ * currently located in that position, and all elements in higher
+ * position, up by one.
+ *
+ * @param element The element to insert
+ * @param index The position in the array:
+ * If the position is lower than the current length
+ * of the array, the elements at that position and
+ * onwards are bumped one position up.
+ * If the position is equal to the current length
+ * of the array, the new element is appended.
+ * An index lower than 0 or higher than the current
+ * length of the array is invalid and will be ignored.
+ *
+ * @throws NS_ERROR_FAILURE when a weak reference is requested,
+ * but the element does not support
+ * nsIWeakReference.
+ */
+ void insertElementAt(in nsISupports element, in unsigned long index,
+ in boolean weak);
+
+ /**
+ * replaceElementAt()
+ *
+ * Replace the element at the given position.
+ *
+ * @param element The new element to insert
+ * @param index The position in the array
+ * If the position is lower than the current length
+ * of the array, an existing element will be replaced.
+ * If the position is equal to the current length
+ * of the array, the new element is appended.
+ * If the position is higher than the current length
+ * of the array, empty elements are appended followed
+ * by the new element at the specified position.
+ * An index lower than 0 is invalid and will be ignored.
+ *
+ * @param weak Whether or not to store the new element using a weak
+ * reference.
+ *
+ * @throws NS_ERROR_FAILURE when a weak reference is requested,
+ * but the element does not support
+ * nsIWeakReference.
+ */
+ void replaceElementAt(in nsISupports element, in unsigned long index,
+ in boolean weak);
+
+
+ /**
+ * clear()
+ *
+ * clear the entire array, releasing all stored objects
+ */
+ void clear();
+};
diff --git a/xpcom/ds/nsINIParserImpl.cpp b/xpcom/ds/nsINIParserImpl.cpp
new file mode 100644
index 000000000..24def4e58
--- /dev/null
+++ b/xpcom/ds/nsINIParserImpl.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsINIParserImpl.h"
+
+#include "nsINIParser.h"
+#include "nsStringEnumerator.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+
+class nsINIParserImpl final
+ : public nsIINIParser
+{
+ ~nsINIParserImpl() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINIPARSER
+
+ nsresult Init(nsIFile* aINIFile) { return mParser.Init(aINIFile); }
+
+private:
+ nsINIParser mParser;
+};
+
+NS_IMPL_ISUPPORTS(nsINIParserFactory,
+ nsIINIParserFactory,
+ nsIFactory)
+
+NS_IMETHODIMP
+nsINIParserFactory::CreateINIParser(nsIFile* aINIFile,
+ nsIINIParser** aResult)
+{
+ *aResult = nullptr;
+
+ RefPtr<nsINIParserImpl> p(new nsINIParserImpl());
+ if (!p) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = p->Init(aINIFile);
+
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*aResult = p);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsINIParserFactory::CreateInstance(nsISupports* aOuter,
+ REFNSIID aIID,
+ void** aResult)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ // We are our own singleton.
+ return QueryInterface(aIID, aResult);
+}
+
+NS_IMETHODIMP
+nsINIParserFactory::LockFactory(bool aLock)
+{
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsINIParserImpl,
+ nsIINIParser)
+
+static bool
+SectionCB(const char* aSection, void* aClosure)
+{
+ nsTArray<nsCString>* strings = static_cast<nsTArray<nsCString>*>(aClosure);
+ strings->AppendElement()->Assign(aSection);
+ return true;
+}
+
+NS_IMETHODIMP
+nsINIParserImpl::GetSections(nsIUTF8StringEnumerator** aResult)
+{
+ nsTArray<nsCString>* strings = new nsTArray<nsCString>;
+ if (!strings) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = mParser.GetSections(SectionCB, strings);
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_NewAdoptingUTF8StringEnumerator(aResult, strings);
+ }
+
+ if (NS_FAILED(rv)) {
+ delete strings;
+ }
+
+ return rv;
+}
+
+static bool
+KeyCB(const char* aKey, const char* aValue, void* aClosure)
+{
+ nsTArray<nsCString>* strings = static_cast<nsTArray<nsCString>*>(aClosure);
+ strings->AppendElement()->Assign(aKey);
+ return true;
+}
+
+NS_IMETHODIMP
+nsINIParserImpl::GetKeys(const nsACString& aSection,
+ nsIUTF8StringEnumerator** aResult)
+{
+ nsTArray<nsCString>* strings = new nsTArray<nsCString>;
+ if (!strings) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = mParser.GetStrings(PromiseFlatCString(aSection).get(),
+ KeyCB, strings);
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_NewAdoptingUTF8StringEnumerator(aResult, strings);
+ }
+
+ if (NS_FAILED(rv)) {
+ delete strings;
+ }
+
+ return rv;
+
+}
+
+NS_IMETHODIMP
+nsINIParserImpl::GetString(const nsACString& aSection,
+ const nsACString& aKey,
+ nsACString& aResult)
+{
+ return mParser.GetString(PromiseFlatCString(aSection).get(),
+ PromiseFlatCString(aKey).get(),
+ aResult);
+}
diff --git a/xpcom/ds/nsINIParserImpl.h b/xpcom/ds/nsINIParserImpl.h
new file mode 100644
index 000000000..8e63f36d7
--- /dev/null
+++ b/xpcom/ds/nsINIParserImpl.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsINIParserImpl_h__
+#define nsINIParserImpl_h__
+
+#include "nsIINIParser.h"
+#include "nsIFactory.h"
+#include "mozilla/Attributes.h"
+
+#define NS_INIPARSERFACTORY_CID \
+{ 0xdfac10a9, 0xdd24, 0x43cf, \
+ { 0xa0, 0x95, 0x6f, 0xfa, 0x2e, 0x4b, 0x6a, 0x6c } }
+
+#define NS_INIPARSERFACTORY_CONTRACTID \
+ "@mozilla.org/xpcom/ini-parser-factory;1"
+
+class nsINIParserFactory final
+ : public nsIINIParserFactory
+ , public nsIFactory
+{
+ ~nsINIParserFactory() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINIPARSERFACTORY
+ NS_DECL_NSIFACTORY
+};
+
+#endif // nsINIParserImpl_h__
diff --git a/xpcom/ds/nsINIProcessor.js b/xpcom/ds/nsINIProcessor.js
new file mode 100644
index 000000000..832f35a5f
--- /dev/null
+++ b/xpcom/ds/nsINIProcessor.js
@@ -0,0 +1,192 @@
+/* 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/. */
+
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function INIProcessorFactory() {
+}
+
+INIProcessorFactory.prototype = {
+ classID: Components.ID("{6ec5f479-8e13-4403-b6ca-fe4c2dca14fd}"),
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIINIParserFactory]),
+
+ createINIParser : function (aINIFile) {
+ return new INIProcessor(aINIFile);
+ }
+
+}; // end of INIProcessorFactory implementation
+
+const MODE_WRONLY = 0x02;
+const MODE_CREATE = 0x08;
+const MODE_TRUNCATE = 0x20;
+
+// nsIINIParser implementation
+function INIProcessor(aFile) {
+ this._iniFile = aFile;
+ this._iniData = {};
+ this._readFile();
+}
+
+INIProcessor.prototype = {
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIINIParser, Ci.nsIINIParserWriter]),
+
+ __utf8Converter : null, // UCS2 <--> UTF8 string conversion
+ get _utf8Converter() {
+ if (!this.__utf8Converter) {
+ this.__utf8Converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Ci.nsIScriptableUnicodeConverter);
+ this.__utf8Converter.charset = "UTF-8";
+ }
+ return this.__utf8Converter;
+ },
+
+ __utf16leConverter : null, // UCS2 <--> UTF16LE string conversion
+ get _utf16leConverter() {
+ if (!this.__utf16leConverter) {
+ this.__utf16leConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Ci.nsIScriptableUnicodeConverter);
+ this.__utf16leConverter.charset = "UTF-16LE";
+ }
+ return this.__utf16leConverter;
+ },
+
+ _utfConverterReset : function() {
+ this.__utf8Converter = null;
+ this.__utf16leConverter = null;
+ },
+
+ _iniFile : null,
+ _iniData : null,
+
+ /*
+ * Reads the INI file and stores the data internally.
+ */
+ _readFile : function() {
+ // If file doesn't exist, there's nothing to do.
+ if (!this._iniFile.exists() || 0 == this._iniFile.fileSize)
+ return;
+
+ let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]
+ .getService(Ci.nsIINIParserFactory).createINIParser(this._iniFile);
+ for (let section of XPCOMUtils.IterStringEnumerator(iniParser.getSections())) {
+ this._iniData[section] = {};
+ for (let key of XPCOMUtils.IterStringEnumerator(iniParser.getKeys(section))) {
+ this._iniData[section][key] = iniParser.getString(section, key);
+ }
+ }
+ },
+
+ // nsIINIParser
+
+ getSections : function() {
+ let sections = [];
+ for (let section in this._iniData)
+ sections.push(section);
+ return new stringEnumerator(sections);
+ },
+
+ getKeys : function(aSection) {
+ let keys = [];
+ if (aSection in this._iniData)
+ for (let key in this._iniData[aSection])
+ keys.push(key);
+ return new stringEnumerator(keys);
+ },
+
+ getString : function(aSection, aKey) {
+ if (!(aSection in this._iniData))
+ throw Cr.NS_ERROR_FAILURE;
+ if (!(aKey in this._iniData[aSection]))
+ throw Cr.NS_ERROR_FAILURE;
+ return this._iniData[aSection][aKey];
+ },
+
+
+ // nsIINIParserWriter
+
+ setString : function(aSection, aKey, aValue) {
+ const isSectionIllegal = /[\0\r\n\[\]]/;
+ const isKeyValIllegal = /[\0\r\n=]/;
+
+ if (isSectionIllegal.test(aSection))
+ throw Components.Exception("bad character in section name",
+ Cr.ERROR_ILLEGAL_VALUE);
+ if (isKeyValIllegal.test(aKey) || isKeyValIllegal.test(aValue))
+ throw Components.Exception("bad character in key/value",
+ Cr.ERROR_ILLEGAL_VALUE);
+
+ if (!(aSection in this._iniData))
+ this._iniData[aSection] = {};
+
+ this._iniData[aSection][aKey] = aValue;
+ },
+
+ writeFile : function(aFile, aFlags) {
+
+ let converter;
+ function writeLine(data) {
+ data += "\n";
+ data = converter.ConvertFromUnicode(data);
+ data += converter.Finish();
+ outputStream.write(data, data.length);
+ }
+
+ if (!aFile)
+ aFile = this._iniFile;
+
+ let safeStream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ safeStream.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE,
+ 0o600, null);
+
+ var outputStream = Cc["@mozilla.org/network/buffered-output-stream;1"].
+ createInstance(Ci.nsIBufferedOutputStream);
+ outputStream.init(safeStream, 8192);
+ outputStream.QueryInterface(Ci.nsISafeOutputStream); // for .finish()
+
+ if (Ci.nsIINIParserWriter.WRITE_UTF16 == aFlags
+ && 'nsIWindowsRegKey' in Ci) {
+ outputStream.write("\xFF\xFE", 2);
+ converter = this._utf16leConverter;
+ } else {
+ converter = this._utf8Converter;
+ }
+
+ for (let section in this._iniData) {
+ writeLine("[" + section + "]");
+ for (let key in this._iniData[section]) {
+ writeLine(key + "=" + this._iniData[section][key]);
+ }
+ }
+
+ outputStream.finish();
+ }
+};
+
+function stringEnumerator(stringArray) {
+ this._strings = stringArray;
+}
+stringEnumerator.prototype = {
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIUTF8StringEnumerator]),
+
+ _strings : null,
+ _enumIndex: 0,
+
+ hasMore : function() {
+ return (this._enumIndex < this._strings.length);
+ },
+
+ getNext : function() {
+ return this._strings[this._enumIndex++];
+ }
+};
+
+var component = [INIProcessorFactory];
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
diff --git a/xpcom/ds/nsINIProcessor.manifest b/xpcom/ds/nsINIProcessor.manifest
new file mode 100644
index 000000000..da19d2aed
--- /dev/null
+++ b/xpcom/ds/nsINIProcessor.manifest
@@ -0,0 +1,2 @@
+component {6ec5f479-8e13-4403-b6ca-fe4c2dca14fd} nsINIProcessor.js
+contract @mozilla.org/xpcom/ini-processor-factory;1 {6ec5f479-8e13-4403-b6ca-fe4c2dca14fd}
diff --git a/xpcom/ds/nsIObserver.idl b/xpcom/ds/nsIObserver.idl
new file mode 100644
index 000000000..cfb4f912b
--- /dev/null
+++ b/xpcom/ds/nsIObserver.idl
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * This interface is implemented by an object that wants
+ * to observe an event corresponding to a topic.
+ */
+
+[scriptable, function, uuid(DB242E01-E4D9-11d2-9DDE-000064657374)]
+interface nsIObserver : nsISupports {
+
+ /**
+ * Observe will be called when there is a notification for the
+ * topic |aTopic|. This assumes that the object implementing
+ * this interface has been registered with an observer service
+ * such as the nsIObserverService.
+ *
+ * If you expect multiple topics/subjects, the impl is
+ * responsible for filtering.
+ *
+ * You should not modify, add, remove, or enumerate
+ * notifications in the implemention of observe.
+ *
+ * @param aSubject : Notification specific interface pointer.
+ * @param aTopic : The notification topic or subject.
+ * @param aData : Notification specific wide string.
+ * subject event.
+ */
+ void observe( in nsISupports aSubject,
+ in string aTopic,
+ in wstring aData );
+
+};
+
diff --git a/xpcom/ds/nsIObserverService.idl b/xpcom/ds/nsIObserverService.idl
new file mode 100644
index 000000000..d5b40e227
--- /dev/null
+++ b/xpcom/ds/nsIObserverService.idl
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIObserver;
+interface nsISimpleEnumerator;
+
+/**
+ * nsIObserverService
+ *
+ * Service allows a client listener (nsIObserver) to register and unregister for
+ * notifications of specific string referenced topic. Service also provides a
+ * way to notify registered listeners and a way to enumerate registered client
+ * listeners.
+ */
+
+[scriptable, uuid(D07F5192-E3D1-11d2-8ACD-00105A1B8860)]
+interface nsIObserverService : nsISupports
+{
+
+ /**
+ * AddObserver
+ *
+ * Registers a given listener for a notifications regarding the specified
+ * topic.
+ *
+ * @param anObserve : The interface pointer which will receive notifications.
+ * @param aTopic : The notification topic or subject.
+ * @param ownsWeak : If set to false, the nsIObserverService will hold a
+ * strong reference to |anObserver|. If set to true and
+ * |anObserver| supports the nsIWeakReference interface,
+ * a weak reference will be held. Otherwise an error will be
+ * returned.
+ */
+ void addObserver( in nsIObserver anObserver, in string aTopic, in boolean ownsWeak);
+
+ /**
+ * removeObserver
+ *
+ * Unregisters a given listener from notifications regarding the specified
+ * topic.
+ *
+ * @param anObserver : The interface pointer which will stop recieving
+ * notifications.
+ * @param aTopic : The notification topic or subject.
+ */
+ void removeObserver( in nsIObserver anObserver, in string aTopic );
+
+ /**
+ * notifyObservers
+ *
+ * Notifies all registered listeners of the given topic.
+ *
+ * @param aSubject : Notification specific interface pointer.
+ * @param aTopic : The notification topic or subject.
+ * @param someData : Notification specific wide string.
+ */
+ void notifyObservers( in nsISupports aSubject,
+ in string aTopic,
+ in wstring someData );
+
+ /**
+ * enumerateObservers
+ *
+ * Returns an enumeration of all registered listeners.
+ *
+ * @param aTopic : The notification topic or subject.
+ */
+ nsISimpleEnumerator enumerateObservers( in string aTopic );
+
+
+};
+
+
diff --git a/xpcom/ds/nsIPersistentProperties.h b/xpcom/ds/nsIPersistentProperties.h
new file mode 100644
index 000000000..e886076e9
--- /dev/null
+++ b/xpcom/ds/nsIPersistentProperties.h
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef __gen_nsIPersistentProperties_h__
+#define __gen_nsIPersistentProperties_h__
+
+// "soft" switch over to an IDL generated header file
+#include "nsIPersistentProperties2.h"
+
+#endif /* __gen_nsIPersistentProperties_h__ */
diff --git a/xpcom/ds/nsIPersistentProperties2.idl b/xpcom/ds/nsIPersistentProperties2.idl
new file mode 100644
index 000000000..3f24bb773
--- /dev/null
+++ b/xpcom/ds/nsIPersistentProperties2.idl
@@ -0,0 +1,63 @@
+/* -*- 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 "nsIProperties.idl"
+
+interface nsIInputStream;
+interface nsIOutputStream;
+interface nsISimpleEnumerator;
+
+[scriptable, uuid(283EE646-1AEF-11D4-98B3-00C04fA0CE9A)]
+interface nsIPropertyElement : nsISupports {
+ attribute AUTF8String key;
+ attribute AString value;
+};
+
+[scriptable, uuid(706867af-0400-4faa-beb1-0dae87308784)]
+interface nsIPersistentProperties : nsIProperties
+{
+ /**
+ * load a set of name/value pairs from the input stream
+ * names and values should be in UTF8
+ */
+ void load(in nsIInputStream input);
+
+ /**
+ * output the values to the stream - results will be in UTF8
+ */
+ void save(in nsIOutputStream output, in AUTF8String header);
+
+ /**
+ * get an enumeration of nsIPropertyElement objects,
+ * which are read-only (i.e. setting properties on the element will
+ * not make changes back into the source nsIPersistentProperties
+ */
+ nsISimpleEnumerator enumerate();
+
+ /**
+ * shortcut to nsIProperty's get() which retrieves a string value
+ * directly (and thus faster)
+ */
+ AString getStringProperty(in AUTF8String key);
+
+ /**
+ * shortcut to nsIProperty's set() which sets a string value
+ * directly (and thus faster). If the given property already exists,
+ * then the old value will be returned
+ */
+ AString setStringProperty(in AUTF8String key, in AString value);
+};
+
+
+%{C++
+
+#define NS_IPERSISTENTPROPERTIES_CID \
+{ 0x2245e573, 0x9464, 0x11d2, \
+ { 0x9b, 0x8b, 0x0, 0x80, 0x5f, 0x8a, 0x16, 0xd9 } }
+
+#define NS_PERSISTENTPROPERTIES_CONTRACTID "@mozilla.org/persistent-properties;1"
+
+%}
+
diff --git a/xpcom/ds/nsIProperties.idl b/xpcom/ds/nsIProperties.idl
new file mode 100644
index 000000000..a87e14443
--- /dev/null
+++ b/xpcom/ds/nsIProperties.idl
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+/*
+ * Simple mapping service interface.
+ */
+
+[scriptable, uuid(78650582-4e93-4b60-8e85-26ebd3eb14ca)]
+interface nsIProperties : nsISupports
+{
+ /**
+ * Gets a property with a given name.
+ *
+ * @throws NS_ERROR_FAILURE if a property with that name doesn't exist.
+ * @throws NS_ERROR_NO_INTERFACE if the found property fails to QI to the
+ * given iid.
+ */
+ void get(in string prop, in nsIIDRef iid,
+ [iid_is(iid),retval] out nsQIResult result);
+
+ /**
+ * Sets a property with a given name to a given value.
+ */
+ void set(in string prop, in nsISupports value);
+
+ /**
+ * Returns true if the property with the given name exists.
+ */
+ boolean has(in string prop);
+
+ /**
+ * Undefines a property.
+ * @throws NS_ERROR_FAILURE if a property with that name doesn't
+ * already exist.
+ */
+ void undefine(in string prop);
+
+ /**
+ * Returns an array of the keys.
+ */
+ void getKeys(out uint32_t count, [array, size_is(count), retval] out string keys);
+};
diff --git a/xpcom/ds/nsIProperty.idl b/xpcom/ds/nsIProperty.idl
new file mode 100644
index 000000000..9e6e1e487
--- /dev/null
+++ b/xpcom/ds/nsIProperty.idl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* nsIVariant based Property support. */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(6dcf9030-a49f-11d5-910d-0010a4e73d9a)]
+interface nsIProperty : nsISupports
+{
+ /**
+ * Get the name of the property.
+ */
+ readonly attribute AString name;
+
+ /**
+ * Get the value of the property.
+ */
+ readonly attribute nsIVariant value;
+};
diff --git a/xpcom/ds/nsIPropertyBag.idl b/xpcom/ds/nsIPropertyBag.idl
new file mode 100644
index 000000000..1d1dfced7
--- /dev/null
+++ b/xpcom/ds/nsIPropertyBag.idl
@@ -0,0 +1,30 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* nsIVariant based Property Bag support. */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+interface nsISimpleEnumerator;
+
+[scriptable, uuid(bfcd37b0-a49f-11d5-910d-0010a4e73d9a)]
+interface nsIPropertyBag : nsISupports
+{
+ /**
+ * Get a nsISimpleEnumerator whose elements are nsIProperty objects.
+ */
+ readonly attribute nsISimpleEnumerator enumerator;
+
+ /**
+ * Get a property value for the given name.
+ * @throws NS_ERROR_FAILURE if a property with that name doesn't
+ * exist.
+ */
+ nsIVariant getProperty(in AString name);
+};
+
+
diff --git a/xpcom/ds/nsIPropertyBag2.idl b/xpcom/ds/nsIPropertyBag2.idl
new file mode 100644
index 000000000..7b4be7f4a
--- /dev/null
+++ b/xpcom/ds/nsIPropertyBag2.idl
@@ -0,0 +1,42 @@
+/* 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/. */
+
+/* nsIVariant based Property Bag support. */
+
+#include "nsIPropertyBag.idl"
+
+[scriptable, uuid(625cfd1e-da1e-4417-9ee9-dbc8e0b3fd79)]
+interface nsIPropertyBag2 : nsIPropertyBag
+{
+ // Accessing a property as a different type may attempt conversion to the
+ // requested value
+
+ int32_t getPropertyAsInt32 (in AString prop);
+ uint32_t getPropertyAsUint32 (in AString prop);
+ int64_t getPropertyAsInt64 (in AString prop);
+ uint64_t getPropertyAsUint64 (in AString prop);
+ double getPropertyAsDouble (in AString prop);
+ AString getPropertyAsAString (in AString prop);
+ ACString getPropertyAsACString (in AString prop);
+ AUTF8String getPropertyAsAUTF8String (in AString prop);
+ boolean getPropertyAsBool (in AString prop);
+
+ /**
+ * This method returns null if the value exists, but is null.
+ */
+ void getPropertyAsInterface (in AString prop,
+ in nsIIDRef iid,
+ [iid_is(iid), retval] out nsQIResult result);
+
+ /**
+ * This method returns null if the value does not exist,
+ * or exists but is null.
+ */
+ nsIVariant get (in AString prop);
+
+ /**
+ * Check for the existence of a key.
+ */
+ boolean hasKey (in AString prop);
+};
diff --git a/xpcom/ds/nsISerializable.idl b/xpcom/ds/nsISerializable.idl
new file mode 100644
index 000000000..f525f3a8f
--- /dev/null
+++ b/xpcom/ds/nsISerializable.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIObjectInputStream;
+interface nsIObjectOutputStream;
+
+[scriptable, uuid(91cca981-c26d-44a8-bebe-d9ed4891503a)]
+interface nsISerializable : nsISupports
+{
+ /**
+ * Initialize the object implementing nsISerializable, which must have
+ * been freshly constructed via CreateInstance. All data members that
+ * can't be set to default values must have been serialized by write,
+ * and should be read from aInputStream in the same order by this method.
+ */
+ void read(in nsIObjectInputStream aInputStream);
+
+ /**
+ * Serialize the object implementing nsISerializable to aOutputStream, by
+ * writing each data member that must be recovered later to reconstitute
+ * a working replica of this object, in a canonical member and byte order,
+ * to aOutputStream.
+ *
+ * NB: a class that implements nsISerializable *must* also implement
+ * nsIClassInfo, in particular nsIClassInfo::GetClassID.
+ */
+ void write(in nsIObjectOutputStream aOutputStream);
+};
diff --git a/xpcom/ds/nsISimpleEnumerator.idl b/xpcom/ds/nsISimpleEnumerator.idl
new file mode 100644
index 000000000..cbb0cd200
--- /dev/null
+++ b/xpcom/ds/nsISimpleEnumerator.idl
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * Used to enumerate over elements defined by its implementor.
+ * Although hasMoreElements() can be called independently of getNext(),
+ * getNext() must be pre-ceeded by a call to hasMoreElements(). There is
+ * no way to "reset" an enumerator, once you obtain one.
+ *
+ * @version 1.0
+ */
+
+[scriptable, uuid(D1899240-F9D2-11D2-BDD6-000064657374)]
+interface nsISimpleEnumerator : nsISupports {
+ /**
+ * Called to determine whether or not the enumerator has
+ * any elements that can be returned via getNext(). This method
+ * is generally used to determine whether or not to initiate or
+ * continue iteration over the enumerator, though it can be
+ * called without subsequent getNext() calls. Does not affect
+ * internal state of enumerator.
+ *
+ * @see getNext()
+ * @return true if there are remaining elements in the enumerator.
+ * false if there are no more elements in the enumerator.
+ */
+ boolean hasMoreElements();
+
+ /**
+ * Called to retrieve the next element in the enumerator. The "next"
+ * element is the first element upon the first call. Must be
+ * pre-ceeded by a call to hasMoreElements() which returns PR_TRUE.
+ * This method is generally called within a loop to iterate over
+ * the elements in the enumerator.
+ *
+ * @see hasMoreElements()
+ * @throws NS_ERROR_FAILURE if there are no more elements
+ * to enumerate.
+ * @return the next element in the enumeration.
+ */
+ nsISupports getNext();
+};
diff --git a/xpcom/ds/nsIStringEnumerator.idl b/xpcom/ds/nsIStringEnumerator.idl
new file mode 100644
index 000000000..d9c4130ee
--- /dev/null
+++ b/xpcom/ds/nsIStringEnumerator.idl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * Used to enumerate over an ordered list of strings.
+ */
+
+[scriptable, uuid(50d3ef6c-9380-4f06-9fb2-95488f7d141c)]
+interface nsIStringEnumerator : nsISupports
+{
+ boolean hasMore();
+ AString getNext();
+};
+
+[scriptable, uuid(9bdf1010-3695-4907-95ed-83d0410ec307)]
+interface nsIUTF8StringEnumerator : nsISupports
+{
+ boolean hasMore();
+ AUTF8String getNext();
+};
+
diff --git a/xpcom/ds/nsISupportsArray.idl b/xpcom/ds/nsISupportsArray.idl
new file mode 100644
index 000000000..30e2a5035
--- /dev/null
+++ b/xpcom/ds/nsISupportsArray.idl
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsICollection.idl"
+
+/*
+ * This entire interface is deprecated and should not be used.
+ * See nsIArray and nsIMutableArray for the new implementations.
+ *
+ * http://groups.google.com/groups?q=nsisupportsarray+group:netscape.public.mozilla.xpcom&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=3D779491.3050506%40netscape.com&rnum=2
+ * http://groups.google.com/groups?q=nsisupportsarray+group:netscape.public.mozilla.xpcom&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=al8412%245ab2%40ripley.netscape.com&rnum=8
+ */
+
+%{C++
+
+class nsIBidirectionalEnumerator;
+class nsISupportsArray;
+
+#define NS_SUPPORTSARRAY_CID \
+{ /* bda17d50-0d6b-11d3-9331-00104ba0fd40 */ \
+ 0xbda17d50, \
+ 0x0d6b, \
+ 0x11d3, \
+ {0x93, 0x31, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
+}
+#define NS_SUPPORTSARRAY_CONTRACTID "@mozilla.org/supports-array;1"
+
+%}
+
+[deprecated, scriptable, uuid(241addc8-3608-4e73-8083-2fd6fa09eba2)]
+interface nsISupportsArray : nsICollection {
+
+ [notxpcom] long IndexOf([const] in nsISupports aPossibleElement);
+
+ // xpcom-compatible versions
+ long GetIndexOf(in nsISupports aPossibleElement);
+
+ [notxpcom] boolean InsertElementAt(in nsISupports aElement,
+ in unsigned long aIndex);
+ [notxpcom] boolean ReplaceElementAt(in nsISupports aElement,
+ in unsigned long aIndex);
+
+ [notxpcom] boolean RemoveElementAt(in unsigned long aIndex);
+
+ // xpcom-compatible versions
+ void DeleteElementAt(in unsigned long aIndex);
+
+ nsISupportsArray clone();
+};
+
+%{C++
+
+// Construct and return a default implementation of nsISupportsArray:
+extern MOZ_MUST_USE nsresult
+NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult);
+
+%}
diff --git a/xpcom/ds/nsISupportsIterators.idl b/xpcom/ds/nsISupportsIterators.idl
new file mode 100644
index 000000000..8d47375d5
--- /dev/null
+++ b/xpcom/ds/nsISupportsIterators.idl
@@ -0,0 +1,292 @@
+/* -*- 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/. */
+
+/* nsISupportsIterators.idl --- IDL defining general purpose iterators */
+
+
+#include "nsISupports.idl"
+
+
+ /*
+ ...
+ */
+
+
+ /**
+ * ...
+ */
+[scriptable, uuid(7330650e-1dd2-11b2-a0c2-9ff86ee97bed)]
+interface nsIOutputIterator : nsISupports
+ {
+ /**
+ * Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
+ * The iterator and the underlying container or sequence cooperate to |Release()|
+ * the replaced element, if any and if necessary, and to |AddRef()| the new element.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @param anElementToPut the element to place into the underlying container or sequence
+ */
+ void putElement( in nsISupports anElementToPut );
+
+ /**
+ * Advance this iterator to the next position in the underlying container or sequence.
+ */
+ void stepForward();
+ };
+
+ /**
+ * ...
+ */
+[scriptable, uuid(85585e12-1dd2-11b2-a930-f6929058269a)]
+interface nsIInputIterator : nsISupports
+ {
+ /**
+ * Retrieve (and |AddRef()|) the element this iterator currently points to.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @result a new reference to the element this iterator currently points to (if any)
+ */
+ nsISupports getElement();
+
+ /**
+ * Advance this iterator to the next position in the underlying container or sequence.
+ */
+ void stepForward();
+
+ /**
+ * Test if |anotherIterator| points to the same position in the underlying container or sequence.
+ *
+ * The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
+ *
+ * @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
+ * @result true if |anotherIterator| points to the same position in the underlying container or sequence
+ */
+ boolean isEqualTo( in nsISupports anotherIterator );
+
+ /**
+ * Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
+ * The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
+ *
+ * @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
+ */
+ nsISupports clone();
+ };
+
+ /**
+ * ...
+ */
+[scriptable, uuid(8da01646-1dd2-11b2-98a7-c7009045be7e)]
+interface nsIForwardIterator : nsISupports
+ {
+ /**
+ * Retrieve (and |AddRef()|) the element this iterator currently points to.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @result a new reference to the element this iterator currently points to (if any)
+ */
+ nsISupports getElement();
+
+ /**
+ * Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
+ * The iterator and the underlying container or sequence cooperate to |Release()|
+ * the replaced element, if any and if necessary, and to |AddRef()| the new element.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @param anElementToPut the element to place into the underlying container or sequence
+ */
+ void putElement( in nsISupports anElementToPut );
+
+ /**
+ * Advance this iterator to the next position in the underlying container or sequence.
+ */
+ void stepForward();
+
+ /**
+ * Test if |anotherIterator| points to the same position in the underlying container or sequence.
+ *
+ * The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
+ *
+ * @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
+ * @result true if |anotherIterator| points to the same position in the underlying container or sequence
+ */
+ boolean isEqualTo( in nsISupports anotherIterator );
+
+ /**
+ * Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
+ * The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
+ *
+ * @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
+ */
+ nsISupports clone();
+ };
+
+ /**
+ * ...
+ */
+[scriptable, uuid(948defaa-1dd1-11b2-89f6-8ce81f5ebda9)]
+interface nsIBidirectionalIterator : nsISupports
+ {
+ /**
+ * Retrieve (and |AddRef()|) the element this iterator currently points to.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @result a new reference to the element this iterator currently points to (if any)
+ */
+ nsISupports getElement();
+
+ /**
+ * Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
+ * The iterator and the underlying container or sequence cooperate to |Release()|
+ * the replaced element, if any and if necessary, and to |AddRef()| the new element.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @param anElementToPut the element to place into the underlying container or sequence
+ */
+ void putElement( in nsISupports anElementToPut );
+
+ /**
+ * Advance this iterator to the next position in the underlying container or sequence.
+ */
+ void stepForward();
+
+ /**
+ * Move this iterator to the previous position in the underlying container or sequence.
+ */
+ void stepBackward();
+
+ /**
+ * Test if |anotherIterator| points to the same position in the underlying container or sequence.
+ *
+ * The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
+ *
+ * @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
+ * @result true if |anotherIterator| points to the same position in the underlying container or sequence
+ */
+ boolean isEqualTo( in nsISupports anotherIterator );
+
+ /**
+ * Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
+ * The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
+ *
+ * @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
+ */
+ nsISupports clone();
+ };
+
+ /**
+ * ...
+ */
+[scriptable, uuid(9bd6fdb0-1dd1-11b2-9101-d15375968230)]
+interface nsIRandomAccessIterator : nsISupports
+ {
+ /**
+ * Retrieve (and |AddRef()|) the element this iterator currently points to.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @result a new reference to the element this iterator currently points to (if any)
+ */
+ nsISupports getElement();
+
+ /**
+ * Retrieve (and |AddRef()|) an element at some offset from where this iterator currently points.
+ * The offset may be negative. |getElementAt(0)| is equivalent to |getElement()|.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @param anOffset a |0|-based offset from the position to which this iterator currently points
+ * @result a new reference to the indicated element (if any)
+ */
+ nsISupports getElementAt( in int32_t anOffset );
+
+ /**
+ * Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
+ * The iterator and the underlying container or sequence cooperate to |Release()|
+ * the replaced element, if any and if necessary, and to |AddRef()| the new element.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @param anElementToPut the element to place into the underlying container or sequence
+ */
+ void putElement( in nsISupports anElementToPut );
+
+ /**
+ * Put |anElementToPut| into the underlying container or sequence at the position |anOffset| away from that currently pointed to by this iterator.
+ * The iterator and the underlying container or sequence cooperate to |Release()|
+ * the replaced element, if any and if necessary, and to |AddRef()| the new element.
+ * |putElementAt(0, obj)| is equivalent to |putElement(obj)|.
+ *
+ * The result is undefined if this iterator currently points outside the
+ * useful range of the underlying container or sequence.
+ *
+ * @param anOffset a |0|-based offset from the position to which this iterator currently points
+ * @param anElementToPut the element to place into the underlying container or sequence
+ */
+ void putElementAt( in int32_t anOffset, in nsISupports anElementToPut );
+
+ /**
+ * Advance this iterator to the next position in the underlying container or sequence.
+ */
+ void stepForward();
+
+ /**
+ * Move this iterator by |anOffset| positions in the underlying container or sequence.
+ * |anOffset| may be negative. |stepForwardBy(1)| is equivalent to |stepForward()|.
+ * |stepForwardBy(0)| is a no-op.
+ *
+ * @param anOffset a |0|-based offset from the position to which this iterator currently points
+ */
+ void stepForwardBy( in int32_t anOffset );
+
+ /**
+ * Move this iterator to the previous position in the underlying container or sequence.
+ */
+ void stepBackward();
+
+ /**
+ * Move this iterator backwards by |anOffset| positions in the underlying container or sequence.
+ * |anOffset| may be negative. |stepBackwardBy(1)| is equivalent to |stepBackward()|.
+ * |stepBackwardBy(n)| is equivalent to |stepForwardBy(-n)|. |stepBackwardBy(0)| is a no-op.
+ *
+ * @param anOffset a |0|-based offset from the position to which this iterator currently points
+ */
+ void stepBackwardBy( in int32_t anOffset );
+
+ /**
+ * Test if |anotherIterator| points to the same position in the underlying container or sequence.
+ *
+ * The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
+ *
+ * @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
+ * @result true if |anotherIterator| points to the same position in the underlying container or sequence
+ */
+ boolean isEqualTo( in nsISupports anotherIterator );
+
+ /**
+ * Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
+ * The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
+ *
+ * @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
+ */
+ nsISupports clone();
+ };
+
+%{C++
+%}
diff --git a/xpcom/ds/nsISupportsPrimitives.idl b/xpcom/ds/nsISupportsPrimitives.idl
new file mode 100644
index 000000000..71940739c
--- /dev/null
+++ b/xpcom/ds/nsISupportsPrimitives.idl
@@ -0,0 +1,235 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* nsISupports wrappers for single primitive pieces of data. */
+
+#include "nsISupports.idl"
+
+/**
+ * Primitive base interface.
+ *
+ * These first three are pointer types and do data copying
+ * using the nsIMemory. Be careful!
+ */
+
+[scriptable, uuid(d0d4b136-1dd1-11b2-9371-f0727ef827c0)]
+interface nsISupportsPrimitive : nsISupports
+{
+ const unsigned short TYPE_ID = 1;
+ const unsigned short TYPE_CSTRING = 2;
+ const unsigned short TYPE_STRING = 3;
+ const unsigned short TYPE_PRBOOL = 4;
+ const unsigned short TYPE_PRUINT8 = 5;
+ const unsigned short TYPE_PRUINT16 = 6;
+ const unsigned short TYPE_PRUINT32 = 7;
+ const unsigned short TYPE_PRUINT64 = 8;
+ const unsigned short TYPE_PRTIME = 9;
+ const unsigned short TYPE_CHAR = 10;
+ const unsigned short TYPE_PRINT16 = 11;
+ const unsigned short TYPE_PRINT32 = 12;
+ const unsigned short TYPE_PRINT64 = 13;
+ const unsigned short TYPE_FLOAT = 14;
+ const unsigned short TYPE_DOUBLE = 15;
+ const unsigned short TYPE_VOID = 16;
+ const unsigned short TYPE_INTERFACE_POINTER = 17;
+
+ readonly attribute unsigned short type;
+};
+
+/**
+ * Scriptable storage for nsID structures
+ */
+
+[scriptable, uuid(d18290a0-4a1c-11d3-9890-006008962422)]
+interface nsISupportsID : nsISupportsPrimitive
+{
+ attribute nsIDPtr data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for ASCII strings
+ */
+
+[scriptable, uuid(d65ff270-4a1c-11d3-9890-006008962422)]
+interface nsISupportsCString : nsISupportsPrimitive
+{
+ attribute ACString data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for Unicode strings
+ */
+
+[scriptable, uuid(d79dc970-4a1c-11d3-9890-006008962422)]
+interface nsISupportsString : nsISupportsPrimitive
+{
+ attribute AString data;
+ wstring toString();
+};
+
+/**
+ * The rest are truly primitive and are passed by value
+ */
+
+/**
+ * Scriptable storage for booleans
+ */
+
+[scriptable, uuid(ddc3b490-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRBool : nsISupportsPrimitive
+{
+ attribute boolean data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for 8-bit integers
+ */
+
+[scriptable, uuid(dec2e4e0-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRUint8 : nsISupportsPrimitive
+{
+ attribute uint8_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for unsigned 16-bit integers
+ */
+
+[scriptable, uuid(dfacb090-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRUint16 : nsISupportsPrimitive
+{
+ attribute uint16_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for unsigned 32-bit integers
+ */
+
+[scriptable, uuid(e01dc470-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRUint32 : nsISupportsPrimitive
+{
+ attribute uint32_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for 64-bit integers
+ */
+
+[scriptable, uuid(e13567c0-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRUint64 : nsISupportsPrimitive
+{
+ attribute uint64_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for NSPR date/time values
+ */
+
+[scriptable, uuid(e2563630-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRTime : nsISupportsPrimitive
+{
+ attribute PRTime data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for single character values
+ * (often used to store an ASCII character)
+ */
+
+[scriptable, uuid(e2b05e40-4a1c-11d3-9890-006008962422)]
+interface nsISupportsChar : nsISupportsPrimitive
+{
+ attribute char data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for 16-bit integers
+ */
+
+[scriptable, uuid(e30d94b0-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRInt16 : nsISupportsPrimitive
+{
+ attribute int16_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for 32-bit integers
+ */
+
+[scriptable, uuid(e36c5250-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRInt32 : nsISupportsPrimitive
+{
+ attribute int32_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for 64-bit integers
+ */
+
+[scriptable, uuid(e3cb0ff0-4a1c-11d3-9890-006008962422)]
+interface nsISupportsPRInt64 : nsISupportsPrimitive
+{
+ attribute int64_t data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for floating point numbers
+ */
+
+[scriptable, uuid(abeaa390-4ac0-11d3-baea-00805f8a5dd7)]
+interface nsISupportsFloat : nsISupportsPrimitive
+{
+ attribute float data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for doubles
+ */
+
+[scriptable, uuid(b32523a0-4ac0-11d3-baea-00805f8a5dd7)]
+interface nsISupportsDouble : nsISupportsPrimitive
+{
+ attribute double data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for generic pointers
+ */
+
+[scriptable, uuid(464484f0-568d-11d3-baf8-00805f8a5dd7)]
+interface nsISupportsVoid : nsISupportsPrimitive
+{
+ [noscript] attribute voidPtr data;
+ string toString();
+};
+
+/**
+ * Scriptable storage for other XPCOM objects
+ */
+
+[scriptable, uuid(995ea724-1dd1-11b2-9211-c21bdd3e7ed0)]
+interface nsISupportsInterfacePointer : nsISupportsPrimitive
+{
+ attribute nsISupports data;
+ attribute nsIDPtr dataIID;
+
+ string toString();
+};
+
+
diff --git a/xpcom/ds/nsIVariant.idl b/xpcom/ds/nsIVariant.idl
new file mode 100644
index 000000000..ef5528463
--- /dev/null
+++ b/xpcom/ds/nsIVariant.idl
@@ -0,0 +1,155 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 long avoided variant support for xpcom. */
+
+#include "nsISupports.idl"
+
+[scriptable,uuid(4d12e540-83d7-11d5-90ed-0010a4e73d9a)]
+interface nsIDataType : nsISupports
+{
+ // These MUST match the declarations in xpt_struct.h.
+ // Otherwise the world is likely to explode.
+ // From xpt_struct.h ...
+ const uint16_t VTYPE_INT8 = 0; // TD_INT8 = 0,
+ const uint16_t VTYPE_INT16 = 1; // TD_INT16 = 1,
+ const uint16_t VTYPE_INT32 = 2; // TD_INT32 = 2,
+ const uint16_t VTYPE_INT64 = 3; // TD_INT64 = 3,
+ const uint16_t VTYPE_UINT8 = 4; // TD_UINT8 = 4,
+ const uint16_t VTYPE_UINT16 = 5; // TD_UINT16 = 5,
+ const uint16_t VTYPE_UINT32 = 6; // TD_UINT32 = 6,
+ const uint16_t VTYPE_UINT64 = 7; // TD_UINT64 = 7,
+ const uint16_t VTYPE_FLOAT = 8; // TD_FLOAT = 8,
+ const uint16_t VTYPE_DOUBLE = 9; // TD_DOUBLE = 9,
+ const uint16_t VTYPE_BOOL = 10; // TD_BOOL = 10,
+ const uint16_t VTYPE_CHAR = 11; // TD_CHAR = 11,
+ const uint16_t VTYPE_WCHAR = 12; // TD_WCHAR = 12,
+ const uint16_t VTYPE_VOID = 13; // TD_VOID = 13,
+ const uint16_t VTYPE_ID = 14; // TD_PNSIID = 14,
+ const uint16_t VTYPE_DOMSTRING = 15; // TD_DOMSTRING = 15,
+ const uint16_t VTYPE_CHAR_STR = 16; // TD_PSTRING = 16,
+ const uint16_t VTYPE_WCHAR_STR = 17; // TD_PWSTRING = 17,
+ const uint16_t VTYPE_INTERFACE = 18; // TD_INTERFACE_TYPE = 18,
+ const uint16_t VTYPE_INTERFACE_IS = 19; // TD_INTERFACE_IS_TYPE = 19,
+ const uint16_t VTYPE_ARRAY = 20; // TD_ARRAY = 20,
+ const uint16_t VTYPE_STRING_SIZE_IS = 21; // TD_PSTRING_SIZE_IS = 21,
+ const uint16_t VTYPE_WSTRING_SIZE_IS = 22; // TD_PWSTRING_SIZE_IS = 22,
+ const uint16_t VTYPE_UTF8STRING = 23; // TD_UTF8STRING = 23,
+ const uint16_t VTYPE_CSTRING = 24; // TD_CSTRING = 24,
+ const uint16_t VTYPE_ASTRING = 25; // TD_ASTRING = 25,
+ const uint16_t VTYPE_EMPTY_ARRAY = 254;
+ const uint16_t VTYPE_EMPTY = 255;
+};
+
+/**
+ * XPConnect has magic to transparently convert between nsIVariant and JS types.
+ * We mark the interface [scriptable] so that JS can use methods
+ * that refer to this interface. But we mark all the methods and attributes
+ * [noscript] since any nsIVariant object will be automatically converted to a
+ * JS type anyway.
+ */
+
+[scriptable, uuid(81e4c2de-acac-4ad6-901a-b5fb1b851a0d)]
+interface nsIVariant : nsISupports
+{
+ [noscript] readonly attribute uint16_t dataType;
+
+ [noscript] uint8_t getAsInt8();
+ [noscript] int16_t getAsInt16();
+ [noscript] int32_t getAsInt32();
+ [noscript] int64_t getAsInt64();
+ [noscript] uint8_t getAsUint8();
+ [noscript] uint16_t getAsUint16();
+ [noscript] uint32_t getAsUint32();
+ [noscript] uint64_t getAsUint64();
+ [noscript] float getAsFloat();
+ [noscript] double getAsDouble();
+ [noscript] boolean getAsBool();
+ [noscript] char getAsChar();
+ [noscript] wchar getAsWChar();
+ [notxpcom] nsresult getAsID(out nsID retval);
+ [noscript] AString getAsAString();
+ [noscript] DOMString getAsDOMString();
+ [noscript] ACString getAsACString();
+ [noscript] AUTF8String getAsAUTF8String();
+ [noscript] string getAsString();
+ [noscript] wstring getAsWString();
+ [noscript] nsISupports getAsISupports();
+ [noscript] jsval getAsJSVal();
+
+ [noscript] void getAsInterface(out nsIIDPtr iid,
+ [iid_is(iid), retval] out nsQIResult iface);
+
+ [notxpcom] nsresult getAsArray(out uint16_t type, out nsIID iid,
+ out uint32_t count, out voidPtr ptr);
+
+ [noscript] void getAsStringWithSize(out uint32_t size,
+ [size_is(size), retval] out string str);
+
+ [noscript] void getAsWStringWithSize(out uint32_t size,
+ [size_is(size), retval] out wstring str);
+};
+
+/**
+ * An object that implements nsIVariant may or may NOT also implement this
+ * nsIWritableVariant.
+ *
+ * If the 'writable' attribute is false then attempts to call any of the 'set'
+ * methods can be expected to fail. Setting the 'writable' attribute may or
+ * may not succeed.
+ *
+ */
+
+[scriptable, uuid(5586a590-8c82-11d5-90f3-0010a4e73d9a)]
+interface nsIWritableVariant : nsIVariant
+{
+ attribute boolean writable;
+
+ void setAsInt8(in uint8_t aValue);
+ void setAsInt16(in int16_t aValue);
+ void setAsInt32(in int32_t aValue);
+ void setAsInt64(in int64_t aValue);
+ void setAsUint8(in uint8_t aValue);
+ void setAsUint16(in uint16_t aValue);
+ void setAsUint32(in uint32_t aValue);
+ void setAsUint64(in uint64_t aValue);
+ void setAsFloat(in float aValue);
+ void setAsDouble(in double aValue);
+ void setAsBool(in boolean aValue);
+ void setAsChar(in char aValue);
+ void setAsWChar(in wchar aValue);
+ void setAsID(in nsIDRef aValue);
+ void setAsAString(in AString aValue);
+ void setAsDOMString(in DOMString aValue);
+ void setAsACString(in ACString aValue);
+ void setAsAUTF8String(in AUTF8String aValue);
+ void setAsString(in string aValue);
+ void setAsWString(in wstring aValue);
+ void setAsISupports(in nsISupports aValue);
+
+ void setAsInterface(in nsIIDRef iid,
+ [iid_is(iid)] in nsQIResult iface);
+
+ [noscript] void setAsArray(in uint16_t type, in nsIIDPtr iid,
+ in uint32_t count, in voidPtr ptr);
+
+ void setAsStringWithSize(in uint32_t size,
+ [size_is(size)] in string str);
+
+ void setAsWStringWithSize(in uint32_t size,
+ [size_is(size)] in wstring str);
+
+ void setAsVoid();
+ void setAsEmpty();
+ void setAsEmptyArray();
+
+ void setFromVariant(in nsIVariant aValue);
+};
+
+%{C++
+// The contractID for the generic implementation built in to xpcom.
+#define NS_VARIANT_CONTRACTID "@mozilla.org/variant;1"
+%}
diff --git a/xpcom/ds/nsIWindowsRegKey.idl b/xpcom/ds/nsIWindowsRegKey.idl
new file mode 100644
index 000000000..25be0995b
--- /dev/null
+++ b/xpcom/ds/nsIWindowsRegKey.idl
@@ -0,0 +1,332 @@
+/* -*- 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 "nsISupports.idl"
+
+native HKEY(HKEY);
+
+/**
+ * This interface is designed to provide scriptable access to the Windows
+ * registry system ("With Great Power Comes Great Responsibility"). The
+ * interface represents a single key in the registry.
+ *
+ * This interface is highly Win32 specific.
+ */
+[scriptable, uuid(2555b930-d64f-437e-9be7-0a2cb252c1f4)]
+interface nsIWindowsRegKey : nsISupports
+{
+ /**
+ * Root keys. The values for these keys correspond to the values from
+ * WinReg.h in the MS Platform SDK. The ROOT_KEY_ prefix corresponds to the
+ * HKEY_ prefix in the MS Platform SDK.
+ *
+ * This interface is not restricted to using only these root keys.
+ */
+ const unsigned long ROOT_KEY_CLASSES_ROOT = 0x80000000;
+ const unsigned long ROOT_KEY_CURRENT_USER = 0x80000001;
+ const unsigned long ROOT_KEY_LOCAL_MACHINE = 0x80000002;
+
+ /**
+ * Values for the mode parameter passed to the open and create methods.
+ * The values defined here correspond to the REGSAM values defined in
+ * WinNT.h in the MS Platform SDK, where ACCESS_ is replaced with KEY_.
+ *
+ * This interface is not restricted to using only these access types.
+ */
+ const unsigned long ACCESS_BASIC = 0x00020000;
+ const unsigned long ACCESS_QUERY_VALUE = 0x00000001;
+ const unsigned long ACCESS_SET_VALUE = 0x00000002;
+ const unsigned long ACCESS_CREATE_SUB_KEY = 0x00000004;
+ const unsigned long ACCESS_ENUMERATE_SUB_KEYS = 0x00000008;
+ const unsigned long ACCESS_NOTIFY = 0x00000010;
+ const unsigned long ACCESS_READ = ACCESS_BASIC |
+ ACCESS_QUERY_VALUE |
+ ACCESS_ENUMERATE_SUB_KEYS |
+ ACCESS_NOTIFY;
+ const unsigned long ACCESS_WRITE = ACCESS_BASIC |
+ ACCESS_SET_VALUE |
+ ACCESS_CREATE_SUB_KEY;
+ const unsigned long ACCESS_ALL = ACCESS_READ |
+ ACCESS_WRITE;
+ const unsigned long WOW64_32 = 0x00000200;
+ const unsigned long WOW64_64 = 0x00000100;
+
+
+ /**
+ * Values for the type of a registry value. The numeric values of these
+ * constants are taken directly from WinNT.h in the MS Platform SDK.
+ * The Microsoft documentation should be consulted for the exact meaning of
+ * these value types.
+ *
+ * This interface is somewhat restricted to using only these value types.
+ * There is no method that is directly equivalent to RegQueryValueEx or
+ * RegSetValueEx. In particular, this interface does not support the
+ * REG_MULTI_SZ and REG_EXPAND_SZ value types. It is still possible to
+ * enumerate values that have other types (i.e., getValueType may return a
+ * type not defined below).
+ */
+ const unsigned long TYPE_NONE = 0; // REG_NONE
+ const unsigned long TYPE_STRING = 1; // REG_SZ
+ const unsigned long TYPE_BINARY = 3; // REG_BINARY
+ const unsigned long TYPE_INT = 4; // REG_DWORD
+ const unsigned long TYPE_INT64 = 11; // REG_QWORD
+
+ /**
+ * This attribute exposes the native HKEY and is available to provide C++
+ * consumers with the flexibility of making other Windows registry API calls
+ * that are not exposed via this interface.
+ *
+ * It is possible to initialize this object by setting an HKEY on it. In
+ * that case, it is the responsibility of the consumer setting the HKEY to
+ * ensure that it is a valid HKEY.
+ *
+ * WARNING: Setting the key does not close the old key.
+ */
+ [noscript] attribute HKEY key;
+
+ /**
+ * This method closes the key. If the key is already closed, then this
+ * method does nothing.
+ */
+ void close();
+
+ /**
+ * This method opens an existing key. This method fails if the key
+ * does not exist.
+ *
+ * NOTE: On 32-bit Windows, it is valid to pass any HKEY as the rootKey
+ * parameter of this function. However, for compatibility with 64-bit
+ * Windows, that usage should probably be avoided in favor of openChild.
+ *
+ * @param rootKey
+ * A root key defined above or any valid HKEY on 32-bit Windows.
+ * @param relPath
+ * A relative path from the given root key.
+ * @param mode
+ * Access mode, which is a bit-wise OR of the ACCESS_ values defined
+ * above.
+ */
+ void open(in unsigned long rootKey, in AString relPath, in unsigned long mode);
+
+ /**
+ * This method opens an existing key or creates a new key.
+ *
+ * NOTE: On 32-bit Windows, it is valid to pass any HKEY as the rootKey
+ * parameter of this function. However, for compatibility with 64-bit
+ * Windows, that usage should probably be avoided in favor of createChild.
+ *
+ * @param rootKey
+ * A root key defined above or any valid HKEY on 32-bit Windows.
+ * @param relPath
+ * A relative path from the given root key.
+ * @param mode
+ * Access mode, which is a bit-wise OR of the ACCESS_ values defined
+ * above.
+ */
+ void create(in unsigned long rootKey, in AString relPath, in unsigned long mode);
+
+ /**
+ * This method opens a subkey relative to this key. This method fails if the
+ * key does not exist.
+ *
+ * @return nsIWindowsRegKey for the newly opened subkey.
+ */
+ nsIWindowsRegKey openChild(in AString relPath, in unsigned long mode);
+
+ /**
+ * This method opens or creates a subkey relative to this key.
+ *
+ * @return nsIWindowsRegKey for the newly opened or created subkey.
+ */
+ nsIWindowsRegKey createChild(in AString relPath, in unsigned long mode);
+
+ /**
+ * This attribute returns the number of child keys.
+ */
+ readonly attribute unsigned long childCount;
+
+ /**
+ * This method returns the name of the n'th child key.
+ *
+ * @param index
+ * The index of the requested child key.
+ */
+ AString getChildName(in unsigned long index);
+
+ /**
+ * This method checks to see if the key has a child by the given name.
+ *
+ * @param name
+ * The name of the requested child key.
+ */
+ boolean hasChild(in AString name);
+
+ /**
+ * This attribute returns the number of values under this key.
+ */
+ readonly attribute unsigned long valueCount;
+
+ /**
+ * This method returns the name of the n'th value under this key.
+ *
+ * @param index
+ * The index of the requested value.
+ */
+ AString getValueName(in unsigned long index);
+
+ /**
+ * This method checks to see if the key has a value by the given name.
+ *
+ * @param name
+ * The name of the requested value.
+ */
+ boolean hasValue(in AString name);
+
+ /**
+ * This method removes a child key and all of its values. This method will
+ * fail if the key has any children of its own.
+ *
+ * @param relPath
+ * The relative path from this key to the key to be removed.
+ */
+ void removeChild(in AString relPath);
+
+ /**
+ * This method removes the value with the given name.
+ *
+ * @param name
+ * The name of the value to be removed.
+ */
+ void removeValue(in AString name);
+
+ /**
+ * This method returns the type of the value with the given name. The return
+ * value is one of the "TYPE_" constants defined above.
+ *
+ * @param name
+ * The name of the value to query.
+ */
+ unsigned long getValueType(in AString name);
+
+ /**
+ * This method reads the string contents of the named value as a Unicode
+ * string.
+ *
+ * @param name
+ * The name of the value to query. This parameter can be the empty
+ * string to request the key's default value.
+ */
+ AString readStringValue(in AString name);
+
+ /**
+ * This method reads the integer contents of the named value.
+ *
+ * @param name
+ * The name of the value to query.
+ */
+ unsigned long readIntValue(in AString name);
+
+ /**
+ * This method reads the 64-bit integer contents of the named value.
+ *
+ * @param name
+ * The name of the value to query.
+ */
+ unsigned long long readInt64Value(in AString name);
+
+ /**
+ * This method reads the binary contents of the named value under this key.
+ *
+ * JavaScript callers should take care with the result of this method since
+ * it will be byte-expanded to form a JS string. (The binary data will be
+ * treated as an ISO-Latin-1 character string, which it is not).
+ *
+ * @param name
+ * The name of the value to query.
+ */
+ ACString readBinaryValue(in AString name);
+
+ /**
+ * This method writes the unicode string contents of the named value. The
+ * value will be created if it does not already exist.
+ *
+ * @param name
+ * The name of the value to modify. This parameter can be the empty
+ * string to modify the key's default value.
+ * @param data
+ * The data for the value to modify.
+ */
+ void writeStringValue(in AString name, in AString data);
+
+ /**
+ * This method writes the integer contents of the named value. The value
+ * will be created if it does not already exist.
+ *
+ * @param name
+ * The name of the value to modify.
+ * @param data
+ * The data for the value to modify.
+ */
+ void writeIntValue(in AString name, in unsigned long data);
+
+ /**
+ * This method writes the 64-bit integer contents of the named value. The
+ * value will be created if it does not already exist.
+ *
+ * @param name
+ * The name of the value to modify.
+ * @param data
+ * The data for the value to modify.
+ */
+ void writeInt64Value(in AString name, in unsigned long long data);
+
+ /**
+ * This method writes the binary contents of the named value. The value will
+ * be created if it does not already exist.
+ *
+ * JavaScript callers should take care with the value passed to this method
+ * since it will be truncated from a JS string (unicode) to a ISO-Latin-1
+ * string. (The binary data will be treated as an ISO-Latin-1 character
+ * string, which it is not). So, JavaScript callers should only pass
+ * character values in the range \u0000 to \u00FF, or else data loss will
+ * occur.
+ *
+ * @param name
+ * The name of the value to modify.
+ * @param data
+ * The data for the value to modify.
+ */
+ void writeBinaryValue(in AString name, in ACString data);
+
+ /**
+ * This method starts watching the key to see if any of its values have
+ * changed. The key must have been opened with mode including ACCESS_NOTIFY.
+ * If recurse is true, then this key and any of its descendant keys are
+ * watched. Otherwise, only this key is watched.
+ *
+ * @param recurse
+ * Indicates whether or not to also watch child keys.
+ */
+ void startWatching(in boolean recurse);
+
+ /**
+ * This method stops any watching of the key initiated by a call to
+ * startWatching. This method does nothing if the key is not being watched.
+ */
+ void stopWatching();
+
+ /**
+ * This method returns true if the key is being watched for changes (i.e.,
+ * if startWatching() was called).
+ */
+ boolean isWatching();
+
+ /**
+ * This method returns true if the key has changed and false otherwise.
+ * This method will always return false if startWatching was not called.
+ */
+ boolean hasChanged();
+};
diff --git a/xpcom/ds/nsIWritablePropertyBag.idl b/xpcom/ds/nsIWritablePropertyBag.idl
new file mode 100644
index 000000000..e916b7ccd
--- /dev/null
+++ b/xpcom/ds/nsIWritablePropertyBag.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* nsIVariant based writable Property Bag support. */
+
+#include "nsIPropertyBag.idl"
+
+[scriptable, uuid(96fc4671-eeb4-4823-9421-e50fb70ad353)]
+interface nsIWritablePropertyBag : nsIPropertyBag
+{
+ /**
+ * Set a property with the given name to the given value. If
+ * a property already exists with the given name, it is
+ * overwritten.
+ */
+ void setProperty(in AString name, in nsIVariant value);
+
+ /**
+ * Delete a property with the given name.
+ * @throws NS_ERROR_FAILURE if a property with that name doesn't
+ * exist.
+ */
+ void deleteProperty(in AString name);
+};
diff --git a/xpcom/ds/nsIWritablePropertyBag2.idl b/xpcom/ds/nsIWritablePropertyBag2.idl
new file mode 100644
index 000000000..50f093905
--- /dev/null
+++ b/xpcom/ds/nsIWritablePropertyBag2.idl
@@ -0,0 +1,22 @@
+/* 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/. */
+
+/* nsIVariant based Property Bag support. */
+
+#include "nsIPropertyBag2.idl"
+
+[scriptable, uuid(9cfd1587-360e-4957-a58f-4c2b1c5e7ed9)]
+interface nsIWritablePropertyBag2 : nsIPropertyBag2
+{
+ void setPropertyAsInt32 (in AString prop, in int32_t value);
+ void setPropertyAsUint32 (in AString prop, in uint32_t value);
+ void setPropertyAsInt64 (in AString prop, in int64_t value);
+ void setPropertyAsUint64 (in AString prop, in uint64_t value);
+ void setPropertyAsDouble (in AString prop, in double value);
+ void setPropertyAsAString (in AString prop, in AString value);
+ void setPropertyAsACString (in AString prop, in ACString value);
+ void setPropertyAsAUTF8String (in AString prop, in AUTF8String value);
+ void setPropertyAsBool (in AString prop, in boolean value);
+ void setPropertyAsInterface (in AString prop, in nsISupports value);
+};
diff --git a/xpcom/ds/nsMathUtils.h b/xpcom/ds/nsMathUtils.h
new file mode 100644
index 000000000..b10b8144e
--- /dev/null
+++ b/xpcom/ds/nsMathUtils.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMathUtils_h__
+#define nsMathUtils_h__
+
+#include "nscore.h"
+#include <cmath>
+#include <float.h>
+
+#ifdef SOLARIS
+#include <ieeefp.h>
+#endif
+
+/*
+ * round
+ */
+inline double
+NS_round(double aNum)
+{
+ return aNum >= 0.0 ? floor(aNum + 0.5) : ceil(aNum - 0.5);
+}
+inline float
+NS_roundf(float aNum)
+{
+ return aNum >= 0.0f ? floorf(aNum + 0.5f) : ceilf(aNum - 0.5f);
+}
+inline int32_t
+NS_lround(double aNum)
+{
+ return aNum >= 0.0 ? int32_t(aNum + 0.5) : int32_t(aNum - 0.5);
+}
+
+/* NS_roundup30 rounds towards infinity for positive and */
+/* negative numbers. */
+
+#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && !defined(__clang__)
+inline int32_t NS_lroundup30(float x)
+{
+ /* Code derived from Laurent de Soras' paper at */
+ /* http://ldesoras.free.fr/doc/articles/rounding_en.pdf */
+
+ /* Rounding up on Windows is expensive using the float to */
+ /* int conversion and the floor function. A faster */
+ /* approach is to use f87 rounding while assuming the */
+ /* default rounding mode of rounding to the nearest */
+ /* integer. This rounding mode, however, actually rounds */
+ /* to the nearest integer so we add the floating point */
+ /* number to itself and add our rounding factor before */
+ /* doing the conversion to an integer. We then do a right */
+ /* shift of one bit on the integer to divide by two. */
+
+ /* This routine doesn't handle numbers larger in magnitude */
+ /* than 2^30 but this is fine for NSToCoordRound because */
+ /* Coords are limited to 2^30 in magnitude. */
+
+ static const double round_to_nearest = 0.5f;
+ int i;
+
+ __asm {
+ fld x ; load fp argument
+ fadd st, st(0) ; double it
+ fadd round_to_nearest ; add the rounding factor
+ fistp dword ptr i ; convert the result to int
+ }
+ return i >> 1; /* divide by 2 */
+}
+#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
+
+inline int32_t
+NS_lroundf(float aNum)
+{
+ return aNum >= 0.0f ? int32_t(aNum + 0.5f) : int32_t(aNum - 0.5f);
+}
+
+/*
+ * hypot. We don't need a super accurate version of this, if a platform
+ * turns up with none of the possibilities below it would be okay to fall
+ * back to sqrt(x*x + y*y).
+ */
+inline double
+NS_hypot(double aNum1, double aNum2)
+{
+#ifdef __GNUC__
+ return __builtin_hypot(aNum1, aNum2);
+#elif defined _WIN32
+ return _hypot(aNum1, aNum2);
+#else
+ return hypot(aNum1, aNum2);
+#endif
+}
+
+/**
+ * Check whether a floating point number is finite (not +/-infinity and not a
+ * NaN value).
+ */
+inline bool
+NS_finite(double aNum)
+{
+#ifdef WIN32
+ // NOTE: '!!' casts an int to bool without spamming MSVC warning C4800.
+ return !!_finite(aNum);
+#elif defined(XP_DARWIN)
+ // Darwin has deprecated |finite| and recommends |isfinite|. The former is
+ // not present in the iOS SDK.
+ return std::isfinite(aNum);
+#else
+ return finite(aNum);
+#endif
+}
+
+/**
+ * Returns the result of the modulo of x by y using a floored division.
+ * fmod(x, y) is using a truncated division.
+ * The main difference is that the result of this method will have the sign of
+ * y while the result of fmod(x, y) will have the sign of x.
+ */
+inline double
+NS_floorModulo(double aNum1, double aNum2)
+{
+ return (aNum1 - aNum2 * floor(aNum1 / aNum2));
+}
+
+#endif
diff --git a/xpcom/ds/nsObserverList.cpp b/xpcom/ds/nsObserverList.cpp
new file mode 100644
index 000000000..fef8831b8
--- /dev/null
+++ b/xpcom/ds/nsObserverList.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsObserverList.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsISimpleEnumerator.h"
+#include "xpcpublic.h"
+
+nsresult
+nsObserverList::AddObserver(nsIObserver* anObserver, bool ownsWeak)
+{
+ NS_ASSERTION(anObserver, "Null input");
+
+ if (!ownsWeak) {
+ ObserverRef* o = mObservers.AppendElement(anObserver);
+ if (!o) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(anObserver);
+ if (!weak) {
+ return NS_NOINTERFACE;
+ }
+
+ ObserverRef* o = mObservers.AppendElement(weak);
+ if (!o) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsObserverList::RemoveObserver(nsIObserver* anObserver)
+{
+ NS_ASSERTION(anObserver, "Null input");
+
+ if (mObservers.RemoveElement(static_cast<nsISupports*>(anObserver))) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIWeakReference> observerRef = do_GetWeakReference(anObserver);
+ if (!observerRef) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mObservers.RemoveElement(observerRef)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void
+nsObserverList::GetObserverList(nsISimpleEnumerator** anEnumerator)
+{
+ RefPtr<nsObserverEnumerator> e(new nsObserverEnumerator(this));
+ e.forget(anEnumerator);
+}
+
+void
+nsObserverList::FillObserverArray(nsCOMArray<nsIObserver>& aArray)
+{
+ aArray.SetCapacity(mObservers.Length());
+
+ nsTArray<ObserverRef> observers(mObservers);
+
+ for (int32_t i = observers.Length() - 1; i >= 0; --i) {
+ if (observers[i].isWeakRef) {
+ nsCOMPtr<nsIObserver> o(do_QueryReferent(observers[i].asWeak()));
+ if (o) {
+ aArray.AppendObject(o);
+ } else {
+ // the object has gone away, remove the weakref
+ mObservers.RemoveElement(observers[i].asWeak());
+ }
+ } else {
+ aArray.AppendObject(observers[i].asObserver());
+ }
+ }
+}
+
+void
+nsObserverList::AppendStrongObservers(nsCOMArray<nsIObserver>& aArray)
+{
+ aArray.SetCapacity(aArray.Length() + mObservers.Length());
+
+ for (int32_t i = mObservers.Length() - 1; i >= 0; --i) {
+ if (!mObservers[i].isWeakRef) {
+ aArray.AppendObject(mObservers[i].asObserver());
+ }
+ }
+}
+
+void
+nsObserverList::NotifyObservers(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* someData)
+{
+ nsCOMArray<nsIObserver> observers;
+ FillObserverArray(observers);
+
+ for (int32_t i = 0; i < observers.Count(); ++i) {
+ observers[i]->Observe(aSubject, aTopic, someData);
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsObserverEnumerator, nsISimpleEnumerator)
+
+nsObserverEnumerator::nsObserverEnumerator(nsObserverList* aObserverList)
+ : mIndex(0)
+{
+ aObserverList->FillObserverArray(mObservers);
+}
+
+NS_IMETHODIMP
+nsObserverEnumerator::HasMoreElements(bool* aResult)
+{
+ *aResult = (mIndex < mObservers.Count());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsObserverEnumerator::GetNext(nsISupports** aResult)
+{
+ if (mIndex == mObservers.Count()) {
+ NS_ERROR("Enumerating after HasMoreElements returned false.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ NS_ADDREF(*aResult = mObservers[mIndex]);
+ ++mIndex;
+ return NS_OK;
+}
diff --git a/xpcom/ds/nsObserverList.h b/xpcom/ds/nsObserverList.h
new file mode 100644
index 000000000..90a685ea6
--- /dev/null
+++ b/xpcom/ds/nsObserverList.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsObserverList_h___
+#define nsObserverList_h___
+
+#include "nsISupports.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIObserver.h"
+#include "nsIWeakReference.h"
+#include "nsHashKeys.h"
+#include "nsISimpleEnumerator.h"
+#include "mozilla/Attributes.h"
+
+struct ObserverRef
+{
+ ObserverRef(const ObserverRef& aO) : isWeakRef(aO.isWeakRef), ref(aO.ref) {}
+ explicit ObserverRef(nsIObserver* aObserver) : isWeakRef(false), ref(aObserver) {}
+ explicit ObserverRef(nsIWeakReference* aWeak) : isWeakRef(true), ref(aWeak) {}
+
+ bool isWeakRef;
+ nsCOMPtr<nsISupports> ref;
+
+ nsIObserver* asObserver()
+ {
+ NS_ASSERTION(!isWeakRef, "Isn't a strong ref.");
+ return static_cast<nsIObserver*>((nsISupports*)ref);
+ }
+
+ nsIWeakReference* asWeak()
+ {
+ NS_ASSERTION(isWeakRef, "Isn't a weak ref.");
+ return static_cast<nsIWeakReference*>((nsISupports*)ref);
+ }
+
+ bool operator==(nsISupports* aRhs) const { return ref == aRhs; }
+};
+
+class nsObserverList : public nsCharPtrHashKey
+{
+ friend class nsObserverService;
+
+public:
+ explicit nsObserverList(const char* aKey) : nsCharPtrHashKey(aKey)
+ {
+ MOZ_COUNT_CTOR(nsObserverList);
+ }
+
+ ~nsObserverList()
+ {
+ MOZ_COUNT_DTOR(nsObserverList);
+ }
+
+ MOZ_MUST_USE nsresult AddObserver(nsIObserver* aObserver, bool aOwnsWeak);
+ MOZ_MUST_USE nsresult RemoveObserver(nsIObserver* aObserver);
+
+ void NotifyObservers(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aSomeData);
+ void GetObserverList(nsISimpleEnumerator** aEnumerator);
+
+ // Fill an array with the observers of this category.
+ // The array is filled in last-added-first order.
+ void FillObserverArray(nsCOMArray<nsIObserver>& aArray);
+
+ // Like FillObserverArray(), but only for strongly held observers.
+ void AppendStrongObservers(nsCOMArray<nsIObserver>& aArray);
+
+private:
+ nsTArray<ObserverRef> mObservers;
+};
+
+class nsObserverEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ explicit nsObserverEnumerator(nsObserverList* aObserverList);
+
+private:
+ ~nsObserverEnumerator() {}
+
+ int32_t mIndex; // Counts up from 0
+ nsCOMArray<nsIObserver> mObservers;
+};
+
+#endif /* nsObserverList_h___ */
diff --git a/xpcom/ds/nsObserverService.cpp b/xpcom/ds/nsObserverService.cpp
new file mode 100644
index 000000000..23cc54fa7
--- /dev/null
+++ b/xpcom/ds/nsObserverService.cpp
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+#include "nsAutoPtr.h"
+#include "nsIConsoleService.h"
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "nsIScriptError.h"
+#include "nsObserverService.h"
+#include "nsObserverList.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsEnumeratorUtils.h"
+#include "xpcpublic.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "mozilla/Services.h"
+
+#define NOTIFY_GLOBAL_OBSERVERS
+
+// Log module for nsObserverService logging...
+//
+// To enable logging (see prlog.h for full details):
+//
+// set MOZ_LOG=ObserverService:5
+// set MOZ_LOG_FILE=service.log
+//
+// This enables LogLevel::Debug level information and places all output in
+// the file service.log.
+static mozilla::LazyLogModule sObserverServiceLog("ObserverService");
+#define LOG(x) MOZ_LOG(sObserverServiceLog, mozilla::LogLevel::Debug, x)
+
+using namespace mozilla;
+
+NS_IMETHODIMP
+nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ struct SuspectObserver
+ {
+ SuspectObserver(const char* aTopic, size_t aReferentCount)
+ : mTopic(aTopic)
+ , mReferentCount(aReferentCount)
+ {}
+ const char* mTopic;
+ size_t mReferentCount;
+ };
+
+ size_t totalNumStrong = 0;
+ size_t totalNumWeakAlive = 0;
+ size_t totalNumWeakDead = 0;
+ nsTArray<SuspectObserver> suspectObservers;
+
+ for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
+ nsObserverList* observerList = iter.Get();
+ if (!observerList) {
+ continue;
+ }
+
+ size_t topicNumStrong = 0;
+ size_t topicNumWeakAlive = 0;
+ size_t topicNumWeakDead = 0;
+
+ nsTArray<ObserverRef>& observers = observerList->mObservers;
+ for (uint32_t i = 0; i < observers.Length(); i++) {
+ if (observers[i].isWeakRef) {
+ nsCOMPtr<nsIObserver> observerRef(
+ do_QueryReferent(observers[i].asWeak()));
+ if (observerRef) {
+ topicNumWeakAlive++;
+ } else {
+ topicNumWeakDead++;
+ }
+ } else {
+ topicNumStrong++;
+ }
+ }
+
+ totalNumStrong += topicNumStrong;
+ totalNumWeakAlive += topicNumWeakAlive;
+ totalNumWeakDead += topicNumWeakDead;
+
+ // Keep track of topics that have a suspiciously large number
+ // of referents (symptom of leaks).
+ size_t topicTotal = topicNumStrong + topicNumWeakAlive + topicNumWeakDead;
+ if (topicTotal > kSuspectReferentCount) {
+ SuspectObserver suspect(observerList->GetKey(), topicTotal);
+ suspectObservers.AppendElement(suspect);
+ }
+ }
+
+ // These aren't privacy-sensitive and so don't need anonymizing.
+ for (uint32_t i = 0; i < suspectObservers.Length(); i++) {
+ SuspectObserver& suspect = suspectObservers[i];
+ nsPrintfCString suspectPath("observer-service-suspect/referent(topic=%s)",
+ suspect.mTopic);
+ aHandleReport->Callback(
+ /* process */ EmptyCString(),
+ suspectPath, KIND_OTHER, UNITS_COUNT, suspect.mReferentCount,
+ NS_LITERAL_CSTRING("A topic with a suspiciously large number of "
+ "referents. This may be symptomatic of a leak "
+ "if the number of referents is high with "
+ "respect to the number of windows."),
+ aData);
+ }
+
+ MOZ_COLLECT_REPORT(
+ "observer-service/referent/strong", KIND_OTHER, UNITS_COUNT,
+ totalNumStrong,
+ "The number of strong references held by the observer service.");
+
+ MOZ_COLLECT_REPORT(
+ "observer-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
+ totalNumWeakAlive,
+ "The number of weak references held by the observer service that are "
+ "still alive.");
+
+ MOZ_COLLECT_REPORT(
+ "observer-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
+ totalNumWeakDead,
+ "The number of weak references held by the observer service that are "
+ "dead.");
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsObserverService Implementation
+
+NS_IMPL_ISUPPORTS(nsObserverService,
+ nsIObserverService,
+ nsObserverService,
+ nsIMemoryReporter)
+
+nsObserverService::nsObserverService()
+ : mShuttingDown(false)
+{
+}
+
+nsObserverService::~nsObserverService(void)
+{
+ Shutdown();
+}
+
+void
+nsObserverService::RegisterReporter()
+{
+ RegisterWeakMemoryReporter(this);
+}
+
+void
+nsObserverService::Shutdown()
+{
+ UnregisterWeakMemoryReporter(this);
+
+ mShuttingDown = true;
+
+ mObserverTopicTable.Clear();
+}
+
+nsresult
+nsObserverService::Create(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr)
+{
+ LOG(("nsObserverService::Create()"));
+
+ RefPtr<nsObserverService> os = new nsObserverService();
+
+ if (!os) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // The memory reporter can not be immediately registered here because
+ // the nsMemoryReporterManager may attempt to get the nsObserverService
+ // during initialization, causing a recursive GetService.
+ NS_DispatchToCurrentThread(NewRunnableMethod(os, &nsObserverService::RegisterReporter));
+
+ return os->QueryInterface(aIID, aInstancePtr);
+}
+
+#define NS_ENSURE_VALIDCALL \
+ if (!NS_IsMainThread()) { \
+ MOZ_CRASH("Using observer service off the main thread!"); \
+ return NS_ERROR_UNEXPECTED; \
+ } \
+ if (mShuttingDown) { \
+ NS_ERROR("Using observer service after XPCOM shutdown!"); \
+ return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \
+ }
+
+NS_IMETHODIMP
+nsObserverService::AddObserver(nsIObserver* aObserver, const char* aTopic,
+ bool aOwnsWeak)
+{
+ LOG(("nsObserverService::AddObserver(%p: %s)",
+ (void*)aObserver, aTopic));
+
+ NS_ENSURE_VALIDCALL
+ if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Specifically allow http-on-opening-request in the child process;
+ // see bug 1269765.
+ if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8) &&
+ strcmp(aTopic, "http-on-opening-request")) {
+ nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+ nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
+ error->Init(NS_LITERAL_STRING("http-on-* observers only work in the parent process"),
+ EmptyString(), EmptyString(), 0, 0,
+ nsIScriptError::warningFlag, "chrome javascript");
+ console->LogMessage(error);
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsObserverList* observerList = mObserverTopicTable.PutEntry(aTopic);
+ if (!observerList) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return observerList->AddObserver(aObserver, aOwnsWeak);
+}
+
+NS_IMETHODIMP
+nsObserverService::RemoveObserver(nsIObserver* aObserver, const char* aTopic)
+{
+ LOG(("nsObserverService::RemoveObserver(%p: %s)",
+ (void*)aObserver, aTopic));
+ NS_ENSURE_VALIDCALL
+ if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
+ if (!observerList) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* This death grip is to protect against stupid consumers who call
+ RemoveObserver from their Destructor, see bug 485834/bug 325392. */
+ nsCOMPtr<nsIObserver> kungFuDeathGrip(aObserver);
+ return observerList->RemoveObserver(aObserver);
+}
+
+NS_IMETHODIMP
+nsObserverService::EnumerateObservers(const char* aTopic,
+ nsISimpleEnumerator** anEnumerator)
+{
+ NS_ENSURE_VALIDCALL
+ if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
+ if (!observerList) {
+ return NS_NewEmptyEnumerator(anEnumerator);
+ }
+
+ observerList->GetObserverList(anEnumerator);
+ return NS_OK;
+}
+
+// Enumerate observers of aTopic and call Observe on each.
+NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aSomeData)
+{
+ LOG(("nsObserverService::NotifyObservers(%s)", aTopic));
+
+ NS_ENSURE_VALIDCALL
+ if (NS_WARN_IF(!aTopic)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
+ if (observerList) {
+ observerList->NotifyObservers(aSubject, aTopic, aSomeData);
+ }
+
+#ifdef NOTIFY_GLOBAL_OBSERVERS
+ observerList = mObserverTopicTable.GetEntry("*");
+ if (observerList) {
+ observerList->NotifyObservers(aSubject, aTopic, aSomeData);
+ }
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsObserverService::UnmarkGrayStrongObservers()
+{
+ NS_ENSURE_VALIDCALL
+
+ nsCOMArray<nsIObserver> strongObservers;
+ for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
+ nsObserverList* aObserverList = iter.Get();
+ if (aObserverList) {
+ aObserverList->AppendStrongObservers(strongObservers);
+ }
+ }
+
+ for (uint32_t i = 0; i < strongObservers.Length(); ++i) {
+ xpc_TryUnmarkWrappedGrayObject(strongObservers[i]);
+ }
+
+ return NS_OK;
+}
diff --git a/xpcom/ds/nsObserverService.h b/xpcom/ds/nsObserverService.h
new file mode 100644
index 000000000..fa0d9b9d8
--- /dev/null
+++ b/xpcom/ds/nsObserverService.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsObserverService_h___
+#define nsObserverService_h___
+
+#include "nsIObserverService.h"
+#include "nsObserverList.h"
+#include "nsIMemoryReporter.h"
+#include "nsTHashtable.h"
+#include "mozilla/Attributes.h"
+
+// {D07F5195-E3D1-11d2-8ACD-00105A1B8860}
+#define NS_OBSERVERSERVICE_CID \
+ { 0xd07f5195, 0xe3d1, 0x11d2, { 0x8a, 0xcd, 0x0, 0x10, 0x5a, 0x1b, 0x88, 0x60 } }
+
+class nsIMemoryReporter;
+
+class nsObserverService final
+ : public nsIObserverService
+ , public nsIMemoryReporter
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_OBSERVERSERVICE_CID)
+
+ nsObserverService();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVERSERVICE
+ NS_DECL_NSIMEMORYREPORTER
+
+ void Shutdown();
+
+ static MOZ_MUST_USE nsresult Create(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr);
+
+ // Unmark any strongly held observers implemented in JS so the cycle
+ // collector will not traverse them.
+ NS_IMETHOD UnmarkGrayStrongObservers();
+
+private:
+ ~nsObserverService(void);
+ void RegisterReporter();
+
+ static const size_t kSuspectReferentCount = 100;
+ bool mShuttingDown;
+ nsTHashtable<nsObserverList> mObserverTopicTable;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsObserverService, NS_OBSERVERSERVICE_CID)
+
+#endif /* nsObserverService_h___ */
diff --git a/xpcom/ds/nsPersistentProperties.cpp b/xpcom/ds/nsPersistentProperties.cpp
new file mode 100644
index 000000000..ea925874c
--- /dev/null
+++ b/xpcom/ds/nsPersistentProperties.cpp
@@ -0,0 +1,666 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsArrayEnumerator.h"
+#include "nsID.h"
+#include "nsCOMArray.h"
+#include "nsUnicharInputStream.h"
+#include "nsPrintfCString.h"
+#include "nsAutoPtr.h"
+
+#define PL_ARENA_CONST_ALIGN_MASK 3
+#include "nsPersistentProperties.h"
+#include "nsIProperties.h"
+
+struct PropertyTableEntry : public PLDHashEntryHdr
+{
+ // both of these are arena-allocated
+ const char* mKey;
+ const char16_t* mValue;
+};
+
+static char16_t*
+ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena)
+{
+ void* mem;
+ // add one to include the null terminator
+ int32_t len = (aString.Length() + 1) * sizeof(char16_t);
+ PL_ARENA_ALLOCATE(mem, aArena, len);
+ NS_ASSERTION(mem, "Couldn't allocate space!\n");
+ if (mem) {
+ memcpy(mem, aString.get(), len);
+ }
+ return static_cast<char16_t*>(mem);
+}
+
+static char*
+ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena)
+{
+ void* mem;
+ // add one to include the null terminator
+ int32_t len = (aString.Length() + 1) * sizeof(char);
+ PL_ARENA_ALLOCATE(mem, aArena, len);
+ NS_ASSERTION(mem, "Couldn't allocate space!\n");
+ if (mem) {
+ memcpy(mem, aString.get(), len);
+ }
+ return static_cast<char*>(mem);
+}
+
+static const struct PLDHashTableOps property_HashTableOps = {
+ PLDHashTable::HashStringKey,
+ PLDHashTable::MatchStringKey,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr,
+};
+
+//
+// parser stuff
+//
+enum EParserState
+{
+ eParserState_AwaitingKey,
+ eParserState_Key,
+ eParserState_AwaitingValue,
+ eParserState_Value,
+ eParserState_Comment
+};
+
+enum EParserSpecial
+{
+ eParserSpecial_None, // not parsing a special character
+ eParserSpecial_Escaped, // awaiting a special character
+ eParserSpecial_Unicode // parsing a \Uxxx value
+};
+
+class MOZ_STACK_CLASS nsPropertiesParser
+{
+public:
+ explicit nsPropertiesParser(nsIPersistentProperties* aProps)
+ : mHaveMultiLine(false)
+ , mState(eParserState_AwaitingKey)
+ , mProps(aProps)
+ {
+ }
+
+ void FinishValueState(nsAString& aOldValue)
+ {
+ static const char trimThese[] = " \t";
+ mKey.Trim(trimThese, false, true);
+
+ // This is really ugly hack but it should be fast
+ char16_t backup_char;
+ uint32_t minLength = mMinLength;
+ if (minLength) {
+ backup_char = mValue[minLength - 1];
+ mValue.SetCharAt('x', minLength - 1);
+ }
+ mValue.Trim(trimThese, false, true);
+ if (minLength) {
+ mValue.SetCharAt(backup_char, minLength - 1);
+ }
+
+ mProps->SetStringProperty(NS_ConvertUTF16toUTF8(mKey), mValue, aOldValue);
+ mSpecialState = eParserSpecial_None;
+ WaitForKey();
+ }
+
+ EParserState GetState() { return mState; }
+
+ static nsresult SegmentWriter(nsIUnicharInputStream* aStream,
+ void* aClosure,
+ const char16_t* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount);
+
+ nsresult ParseBuffer(const char16_t* aBuffer, uint32_t aBufferLength);
+
+private:
+ bool ParseValueCharacter(
+ char16_t aChar, // character that is just being parsed
+ const char16_t* aCur, // pointer to character aChar in the buffer
+ const char16_t*& aTokenStart, // string copying is done in blocks as big as
+ // possible, aTokenStart points to the beginning
+ // of this block
+ nsAString& aOldValue); // when duplicate property is found, new value
+ // is stored into hashtable and the old one is
+ // placed in this variable
+
+ void WaitForKey()
+ {
+ mState = eParserState_AwaitingKey;
+ }
+
+ void EnterKeyState()
+ {
+ mKey.Truncate();
+ mState = eParserState_Key;
+ }
+
+ void WaitForValue()
+ {
+ mState = eParserState_AwaitingValue;
+ }
+
+ void EnterValueState()
+ {
+ mValue.Truncate();
+ mMinLength = 0;
+ mState = eParserState_Value;
+ mSpecialState = eParserSpecial_None;
+ }
+
+ void EnterCommentState()
+ {
+ mState = eParserState_Comment;
+ }
+
+ nsAutoString mKey;
+ nsAutoString mValue;
+
+ uint32_t mUnicodeValuesRead; // should be 4!
+ char16_t mUnicodeValue; // currently parsed unicode value
+ bool mHaveMultiLine; // is TRUE when last processed characters form
+ // any of following sequences:
+ // - "\\\r"
+ // - "\\\n"
+ // - "\\\r\n"
+ // - any sequence above followed by any
+ // combination of ' ' and '\t'
+ bool mMultiLineCanSkipN; // TRUE if "\\\r" was detected
+ uint32_t mMinLength; // limit right trimming at the end to not trim
+ // escaped whitespaces
+ EParserState mState;
+ // if we see a '\' then we enter this special state
+ EParserSpecial mSpecialState;
+ nsCOMPtr<nsIPersistentProperties> mProps;
+};
+
+inline bool
+IsWhiteSpace(char16_t aChar)
+{
+ return (aChar == ' ') || (aChar == '\t') ||
+ (aChar == '\r') || (aChar == '\n');
+}
+
+inline bool
+IsEOL(char16_t aChar)
+{
+ return (aChar == '\r') || (aChar == '\n');
+}
+
+
+bool
+nsPropertiesParser::ParseValueCharacter(char16_t aChar, const char16_t* aCur,
+ const char16_t*& aTokenStart,
+ nsAString& aOldValue)
+{
+ switch (mSpecialState) {
+ // the normal state - look for special characters
+ case eParserSpecial_None:
+ switch (aChar) {
+ case '\\':
+ if (mHaveMultiLine) {
+ // there is nothing to append to mValue yet
+ mHaveMultiLine = false;
+ } else {
+ mValue += Substring(aTokenStart, aCur);
+ }
+
+ mSpecialState = eParserSpecial_Escaped;
+ break;
+
+ case '\n':
+ // if we detected multiline and got only "\\\r" ignore next "\n" if any
+ if (mHaveMultiLine && mMultiLineCanSkipN) {
+ // but don't allow another '\n' to be skipped
+ mMultiLineCanSkipN = false;
+ // Now there is nothing to append to the mValue since we are skipping
+ // whitespaces at the beginning of the new line of the multiline
+ // property. Set aTokenStart properly to ensure that nothing is appended
+ // if we find regular line-end or the end of the buffer.
+ aTokenStart = aCur + 1;
+ break;
+ }
+ MOZ_FALLTHROUGH;
+
+ case '\r':
+ // we're done! We have a key and value
+ mValue += Substring(aTokenStart, aCur);
+ FinishValueState(aOldValue);
+ mHaveMultiLine = false;
+ break;
+
+ default:
+ // there is nothing to do with normal characters,
+ // but handle multilines correctly
+ if (mHaveMultiLine) {
+ if (aChar == ' ' || aChar == '\t') {
+ // don't allow another '\n' to be skipped
+ mMultiLineCanSkipN = false;
+ // Now there is nothing to append to the mValue since we are skipping
+ // whitespaces at the beginning of the new line of the multiline
+ // property. Set aTokenStart properly to ensure that nothing is appended
+ // if we find regular line-end or the end of the buffer.
+ aTokenStart = aCur + 1;
+ break;
+ }
+ mHaveMultiLine = false;
+ aTokenStart = aCur;
+ }
+ break; // from switch on (aChar)
+ }
+ break; // from switch on (mSpecialState)
+
+ // saw a \ character, so parse the character after that
+ case eParserSpecial_Escaped:
+ // probably want to start parsing at the next token
+ // other characters, like 'u' might override this
+ aTokenStart = aCur + 1;
+ mSpecialState = eParserSpecial_None;
+
+ switch (aChar) {
+ // the easy characters - \t, \n, and so forth
+ case 't':
+ mValue += char16_t('\t');
+ mMinLength = mValue.Length();
+ break;
+ case 'n':
+ mValue += char16_t('\n');
+ mMinLength = mValue.Length();
+ break;
+ case 'r':
+ mValue += char16_t('\r');
+ mMinLength = mValue.Length();
+ break;
+ case '\\':
+ mValue += char16_t('\\');
+ break;
+
+ // switch to unicode mode!
+ case 'u':
+ case 'U':
+ mSpecialState = eParserSpecial_Unicode;
+ mUnicodeValuesRead = 0;
+ mUnicodeValue = 0;
+ break;
+
+ // a \ immediately followed by a newline means we're going multiline
+ case '\r':
+ case '\n':
+ mHaveMultiLine = true;
+ mMultiLineCanSkipN = (aChar == '\r');
+ mSpecialState = eParserSpecial_None;
+ break;
+
+ default:
+ // don't recognize the character, so just append it
+ mValue += aChar;
+ break;
+ }
+ break;
+
+ // we're in the middle of parsing a 4-character unicode value
+ // like \u5f39
+ case eParserSpecial_Unicode:
+ if ('0' <= aChar && aChar <= '9') {
+ mUnicodeValue =
+ (mUnicodeValue << 4) | (aChar - '0');
+ } else if ('a' <= aChar && aChar <= 'f') {
+ mUnicodeValue =
+ (mUnicodeValue << 4) | (aChar - 'a' + 0x0a);
+ } else if ('A' <= aChar && aChar <= 'F') {
+ mUnicodeValue =
+ (mUnicodeValue << 4) | (aChar - 'A' + 0x0a);
+ } else {
+ // non-hex character. Append what we have, and move on.
+ mValue += mUnicodeValue;
+ mMinLength = mValue.Length();
+ mSpecialState = eParserSpecial_None;
+
+ // leave aTokenStart at this unknown character, so it gets appended
+ aTokenStart = aCur;
+
+ // ensure parsing this non-hex character again
+ return false;
+ }
+
+ if (++mUnicodeValuesRead >= 4) {
+ aTokenStart = aCur + 1;
+ mSpecialState = eParserSpecial_None;
+ mValue += mUnicodeValue;
+ mMinLength = mValue.Length();
+ }
+
+ break;
+ }
+
+ return true;
+}
+
+nsresult
+nsPropertiesParser::SegmentWriter(nsIUnicharInputStream* aStream,
+ void* aClosure,
+ const char16_t* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ nsPropertiesParser* parser = static_cast<nsPropertiesParser*>(aClosure);
+ parser->ParseBuffer(aFromSegment, aCount);
+
+ *aWriteCount = aCount;
+ return NS_OK;
+}
+
+nsresult
+nsPropertiesParser::ParseBuffer(const char16_t* aBuffer,
+ uint32_t aBufferLength)
+{
+ const char16_t* cur = aBuffer;
+ const char16_t* end = aBuffer + aBufferLength;
+
+ // points to the start/end of the current key or value
+ const char16_t* tokenStart = nullptr;
+
+ // if we're in the middle of parsing a key or value, make sure
+ // the current token points to the beginning of the current buffer
+ if (mState == eParserState_Key ||
+ mState == eParserState_Value) {
+ tokenStart = aBuffer;
+ }
+
+ nsAutoString oldValue;
+
+ while (cur != end) {
+
+ char16_t c = *cur;
+
+ switch (mState) {
+ case eParserState_AwaitingKey:
+ if (c == '#' || c == '!') {
+ EnterCommentState();
+ }
+
+ else if (!IsWhiteSpace(c)) {
+ // not a comment, not whitespace, we must have found a key!
+ EnterKeyState();
+ tokenStart = cur;
+ }
+ break;
+
+ case eParserState_Key:
+ if (c == '=' || c == ':') {
+ mKey += Substring(tokenStart, cur);
+ WaitForValue();
+ }
+ break;
+
+ case eParserState_AwaitingValue:
+ if (IsEOL(c)) {
+ // no value at all! mimic the normal value-ending
+ EnterValueState();
+ FinishValueState(oldValue);
+ }
+
+ // ignore white space leading up to the value
+ else if (!IsWhiteSpace(c)) {
+ tokenStart = cur;
+ EnterValueState();
+
+ // make sure to handle this first character
+ if (ParseValueCharacter(c, cur, tokenStart, oldValue)) {
+ cur++;
+ }
+ // If the character isn't consumed, don't do cur++ and parse
+ // the character again. This can happen f.e. for char 'X' in sequence
+ // "\u00X". This character can be control character and must be
+ // processed again.
+ continue;
+ }
+ break;
+
+ case eParserState_Value:
+ if (ParseValueCharacter(c, cur, tokenStart, oldValue)) {
+ cur++;
+ }
+ // See few lines above for reason of doing this
+ continue;
+
+ case eParserState_Comment:
+ // stay in this state till we hit EOL
+ if (c == '\r' || c == '\n') {
+ WaitForKey();
+ }
+ break;
+ }
+
+ // finally, advance to the next character
+ cur++;
+ }
+
+ // if we're still parsing the value and are in eParserSpecial_None, then
+ // append whatever we have..
+ if (mState == eParserState_Value && tokenStart &&
+ mSpecialState == eParserSpecial_None) {
+ mValue += Substring(tokenStart, cur);
+ }
+ // if we're still parsing the key, then append whatever we have..
+ else if (mState == eParserState_Key && tokenStart) {
+ mKey += Substring(tokenStart, cur);
+ }
+
+ return NS_OK;
+}
+
+nsPersistentProperties::nsPersistentProperties()
+ : mIn(nullptr)
+ , mTable(&property_HashTableOps, sizeof(PropertyTableEntry), 16)
+{
+ PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048);
+}
+
+nsPersistentProperties::~nsPersistentProperties()
+{
+ PL_FinishArenaPool(&mArena);
+}
+
+nsresult
+nsPersistentProperties::Create(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ RefPtr<nsPersistentProperties> props = new nsPersistentProperties();
+ return props->QueryInterface(aIID, aResult);
+}
+
+NS_IMPL_ISUPPORTS(nsPersistentProperties, nsIPersistentProperties, nsIProperties)
+
+NS_IMETHODIMP
+nsPersistentProperties::Load(nsIInputStream* aIn)
+{
+ nsresult rv = NS_NewUnicharInputStream(aIn, getter_AddRefs(mIn));
+
+ if (rv != NS_OK) {
+ NS_WARNING("Error creating UnicharInputStream");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPropertiesParser parser(this);
+
+ uint32_t nProcessed;
+ // If this 4096 is changed to some other value, make sure to adjust
+ // the bug121341.properties test file accordingly.
+ while (NS_SUCCEEDED(rv = mIn->ReadSegments(nsPropertiesParser::SegmentWriter,
+ &parser, 4096, &nProcessed)) &&
+ nProcessed != 0);
+ mIn = nullptr;
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We may have an unprocessed value at this point
+ // if the last line did not have a proper line ending.
+ if (parser.GetState() == eParserState_Value) {
+ nsAutoString oldValue;
+ parser.FinishValueState(oldValue);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::SetStringProperty(const nsACString& aKey,
+ const nsAString& aNewValue,
+ nsAString& aOldValue)
+{
+ const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
+ auto entry = static_cast<PropertyTableEntry*>
+ (mTable.Add(flatKey.get()));
+
+ if (entry->mKey) {
+ aOldValue = entry->mValue;
+ NS_WARNING(nsPrintfCString("the property %s already exists",
+ flatKey.get()).get());
+ } else {
+ aOldValue.Truncate();
+ }
+
+ entry->mKey = ArenaStrdup(flatKey, &mArena);
+ entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::GetStringProperty(const nsACString& aKey,
+ nsAString& aValue)
+{
+ const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
+
+ auto entry = static_cast<PropertyTableEntry*>(mTable.Search(flatKey.get()));
+ if (!entry) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aValue = entry->mValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
+{
+ nsCOMArray<nsIPropertyElement> props;
+
+ // We know the necessary size; we can avoid growing it while adding elements
+ props.SetCapacity(mTable.EntryCount());
+
+ // Step through hash entries populating a transient array
+ for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PropertyTableEntry*>(iter.Get());
+
+ RefPtr<nsPropertyElement> element =
+ new nsPropertyElement(nsDependentCString(entry->mKey),
+ nsDependentString(entry->mValue));
+
+ if (!props.AppendObject(element)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return NS_NewArrayEnumerator(aResult, props);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XXX Some day we'll unify the nsIPersistentProperties interface with
+// nsIProperties, but until now...
+
+NS_IMETHODIMP
+nsPersistentProperties::Get(const char* aProp, const nsIID& aUUID,
+ void** aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::Set(const char* aProp, nsISupports* value)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP
+nsPersistentProperties::Undefine(const char* aProp)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::Has(const char* aProp, bool* aResult)
+{
+ *aResult = !!mTable.Search(aProp);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPersistentProperties::GetKeys(uint32_t* aCount, char*** aKeys)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PropertyElement
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+nsPropertyElement::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ RefPtr<nsPropertyElement> propElem = new nsPropertyElement();
+ return propElem->QueryInterface(aIID, aResult);
+}
+
+NS_IMPL_ISUPPORTS(nsPropertyElement, nsIPropertyElement)
+
+NS_IMETHODIMP
+nsPropertyElement::GetKey(nsACString& aReturnKey)
+{
+ aReturnKey = mKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPropertyElement::GetValue(nsAString& aReturnValue)
+{
+ aReturnValue = mValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPropertyElement::SetKey(const nsACString& aKey)
+{
+ mKey = aKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPropertyElement::SetValue(const nsAString& aValue)
+{
+ mValue = aValue;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/ds/nsPersistentProperties.h b/xpcom/ds/nsPersistentProperties.h
new file mode 100644
index 000000000..6c9e9cb51
--- /dev/null
+++ b/xpcom/ds/nsPersistentProperties.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPersistentProperties_h___
+#define nsPersistentProperties_h___
+
+#include "nsIPersistentProperties2.h"
+#include "PLDHashTable.h"
+#include "plarena.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+class nsIUnicharInputStream;
+
+class nsPersistentProperties final : public nsIPersistentProperties
+{
+public:
+ nsPersistentProperties();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPROPERTIES
+ NS_DECL_NSIPERSISTENTPROPERTIES
+
+ static MOZ_MUST_USE nsresult
+ Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+private:
+ ~nsPersistentProperties();
+
+protected:
+ nsCOMPtr<nsIUnicharInputStream> mIn;
+
+ PLDHashTable mTable;
+ PLArenaPool mArena;
+};
+
+class nsPropertyElement final : public nsIPropertyElement
+{
+public:
+ nsPropertyElement()
+ {
+ }
+
+ nsPropertyElement(const nsACString& aKey, const nsAString& aValue)
+ : mKey(aKey)
+ , mValue(aValue)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROPERTYELEMENT
+
+ static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+private:
+ ~nsPropertyElement() {}
+
+protected:
+ nsCString mKey;
+ nsString mValue;
+};
+
+#endif /* nsPersistentProperties_h___ */
diff --git a/xpcom/ds/nsProperties.cpp b/xpcom/ds/nsProperties.cpp
new file mode 100644
index 000000000..3a73ac7da
--- /dev/null
+++ b/xpcom/ds/nsProperties.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsProperties.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_AGGREGATED(nsProperties)
+NS_INTERFACE_MAP_BEGIN_AGGREGATED(nsProperties)
+ NS_INTERFACE_MAP_ENTRY(nsIProperties)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+nsProperties::Get(const char* prop, const nsIID& uuid, void** result)
+{
+ if (NS_WARN_IF(!prop)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsISupports> value;
+ if (!nsProperties_HashBase::Get(prop, getter_AddRefs(value))) {
+ return NS_ERROR_FAILURE;
+ }
+ return (value) ? value->QueryInterface(uuid, result) : NS_ERROR_NO_INTERFACE;
+}
+
+NS_IMETHODIMP
+nsProperties::Set(const char* prop, nsISupports* value)
+{
+ if (NS_WARN_IF(!prop)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ Put(prop, value);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProperties::Undefine(const char* prop)
+{
+ if (NS_WARN_IF(!prop)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsISupports> value;
+ if (!nsProperties_HashBase::Get(prop, getter_AddRefs(value))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ Remove(prop);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProperties::Has(const char* prop, bool* result)
+{
+ if (NS_WARN_IF(!prop)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsISupports> value;
+ *result = nsProperties_HashBase::Get(prop, getter_AddRefs(value));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProperties::GetKeys(uint32_t* aCount, char*** aKeys)
+{
+ if (NS_WARN_IF(!aCount) || NS_WARN_IF(!aKeys)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ uint32_t count = Count();
+ char** keys = (char**)moz_xmalloc(count * sizeof(char*));
+ uint32_t j = 0;
+
+ for (auto iter = this->Iter(); !iter.Done(); iter.Next()) {
+ const char* key = iter.Key();
+ keys[j] = strdup(key);
+
+ if (!keys[j]) {
+ // Free 'em all
+ for (uint32_t i = 0; i < j; i++) {
+ free(keys[i]);
+ }
+ free(keys);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ j++;
+ }
+
+ *aCount = count;
+ *aKeys = keys;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/ds/nsProperties.h b/xpcom/ds/nsProperties.h
new file mode 100644
index 000000000..6949017dd
--- /dev/null
+++ b/xpcom/ds/nsProperties.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsProperties_h___
+#define nsProperties_h___
+
+#include "nsIProperties.h"
+#include "nsInterfaceHashtable.h"
+#include "nsHashKeys.h"
+#include "nsAgg.h"
+#include "mozilla/Attributes.h"
+
+#define NS_PROPERTIES_CID \
+{ /* 4de2bc90-b1bf-11d3-93b6-00104ba0fd40 */ \
+ 0x4de2bc90, \
+ 0xb1bf, \
+ 0x11d3, \
+ {0x93, 0xb6, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
+}
+
+typedef nsInterfaceHashtable<nsCharPtrHashKey,
+ nsISupports> nsProperties_HashBase;
+
+class nsProperties final
+ : public nsIProperties
+ , public nsProperties_HashBase
+{
+public:
+ NS_DECL_AGGREGATED
+ NS_DECL_NSIPROPERTIES
+
+ explicit nsProperties(nsISupports *aOuter) { NS_INIT_AGGREGATED(aOuter); }
+ ~nsProperties() {}
+};
+
+#endif /* nsProperties_h___ */
diff --git a/xpcom/ds/nsStaticAtom.h b/xpcom/ds/nsStaticAtom.h
new file mode 100644
index 000000000..7e31623db
--- /dev/null
+++ b/xpcom/ds/nsStaticAtom.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsStaticAtom_h__
+#define nsStaticAtom_h__
+
+#include "nsIAtom.h"
+#include "nsStringBuffer.h"
+
+#define NS_STATIC_ATOM(buffer_name, atom_ptr) \
+ { (nsStringBuffer*) &buffer_name, atom_ptr }
+
+#define NS_STATIC_ATOM_BUFFER(buffer_name, str_data) \
+ static nsFakeStringBuffer<sizeof(str_data)> buffer_name = \
+ { 1, sizeof(str_data) * sizeof(char16_t), (u"" str_data) };
+
+/**
+ * Holds data used to initialize large number of atoms during startup. Use
+ * the above macros to initialize these structs. They should never be accessed
+ * directly other than from AtomTable.cpp
+ */
+struct nsStaticAtom
+{
+ // mStringBuffer points to the string buffer for a permanent atom, and is
+ // therefore safe as a non-owning reference.
+ nsStringBuffer* MOZ_NON_OWNING_REF mStringBuffer;
+ nsIAtom** mAtom;
+};
+
+/**
+ * This is a struct with the same binary layout as a nsStringBuffer.
+ */
+template<uint32_t size>
+struct nsFakeStringBuffer
+{
+ int32_t mRefCnt;
+ uint32_t mSize;
+ char16_t mStringData[size];
+};
+
+// Register an array of static atoms with the atom table
+template<uint32_t N>
+void
+NS_RegisterStaticAtoms(const nsStaticAtom (&aAtoms)[N])
+{
+ extern void RegisterStaticAtoms(const nsStaticAtom*, uint32_t aAtomCount);
+ RegisterStaticAtoms(aAtoms, N);
+}
+
+#endif
diff --git a/xpcom/ds/nsStaticNameTable.cpp b/xpcom/ds/nsStaticNameTable.cpp
new file mode 100644
index 000000000..26bd172a7
--- /dev/null
+++ b/xpcom/ds/nsStaticNameTable.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Class to manage lookup of static names in a table. */
+
+#include "nsCRT.h"
+
+#include "nscore.h"
+#include "mozilla/HashFunctions.h"
+#include "nsISupportsImpl.h"
+
+#define PL_ARENA_CONST_ALIGN_MASK 3
+#include "nsStaticNameTable.h"
+
+using namespace mozilla;
+
+struct NameTableKey
+{
+ NameTableKey(const nsDependentCString aNameArray[],
+ const nsAFlatCString* aKeyStr)
+ : mNameArray(aNameArray)
+ , mIsUnichar(false)
+ {
+ mKeyStr.m1b = aKeyStr;
+ }
+
+ NameTableKey(const nsDependentCString aNameArray[],
+ const nsAFlatString* aKeyStr)
+ : mNameArray(aNameArray)
+ , mIsUnichar(true)
+ {
+ mKeyStr.m2b = aKeyStr;
+ }
+
+ const nsDependentCString* mNameArray;
+ union
+ {
+ const nsAFlatCString* m1b;
+ const nsAFlatString* m2b;
+ } mKeyStr;
+ bool mIsUnichar;
+};
+
+struct NameTableEntry : public PLDHashEntryHdr
+{
+ int32_t mIndex;
+};
+
+static bool
+matchNameKeysCaseInsensitive(const PLDHashEntryHdr* aHdr, const void* aVoidKey)
+{
+ auto entry = static_cast<const NameTableEntry*>(aHdr);
+ auto key = static_cast<const NameTableKey*>(aVoidKey);
+ const nsDependentCString* name = &key->mNameArray[entry->mIndex];
+
+ return key->mIsUnichar
+ ? key->mKeyStr.m2b->LowerCaseEqualsASCII(name->get(), name->Length())
+ : key->mKeyStr.m1b->LowerCaseEqualsASCII(name->get(), name->Length());
+}
+
+/*
+ * caseInsensitiveHashKey is just like PLDHashTable::HashStringKey except it
+ * uses (*s & ~0x20) instead of simply *s. This means that "aFOO" and
+ * "afoo" and "aFoo" will all hash to the same thing. It also means
+ * that some strings that aren't case-insensensitively equal will hash
+ * to the same value, but it's just a hash function so it doesn't
+ * matter.
+ */
+static PLDHashNumber
+caseInsensitiveStringHashKey(const void* aKey)
+{
+ PLDHashNumber h = 0;
+ const NameTableKey* tableKey = static_cast<const NameTableKey*>(aKey);
+ if (tableKey->mIsUnichar) {
+ for (const char16_t* s = tableKey->mKeyStr.m2b->get();
+ *s != '\0';
+ s++) {
+ h = AddToHash(h, *s & ~0x20);
+ }
+ } else {
+ for (const unsigned char* s = reinterpret_cast<const unsigned char*>(
+ tableKey->mKeyStr.m1b->get());
+ *s != '\0';
+ s++) {
+ h = AddToHash(h, *s & ~0x20);
+ }
+ }
+ return h;
+}
+
+static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps = {
+ caseInsensitiveStringHashKey,
+ matchNameKeysCaseInsensitive,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr,
+};
+
+nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable(
+ const char* const aNames[], int32_t aLength)
+ : mNameArray(nullptr)
+ , mNameTable(&nametable_CaseInsensitiveHashTableOps,
+ sizeof(NameTableEntry), aLength)
+ , mNullStr("")
+{
+ MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable);
+
+ MOZ_ASSERT(aNames, "null name table");
+ MOZ_ASSERT(aLength, "0 length");
+
+ mNameArray = (nsDependentCString*)
+ moz_xmalloc(aLength * sizeof(nsDependentCString));
+
+ for (int32_t index = 0; index < aLength; ++index) {
+ const char* raw = aNames[index];
+#ifdef DEBUG
+ {
+ // verify invariants of contents
+ nsAutoCString temp1(raw);
+ nsDependentCString temp2(raw);
+ ToLowerCase(temp1);
+ MOZ_ASSERT(temp1.Equals(temp2), "upper case char in table");
+ MOZ_ASSERT(nsCRT::IsAscii(raw),
+ "non-ascii string in table -- "
+ "case-insensitive matching won't work right");
+ }
+#endif
+ // use placement-new to initialize the string object
+ nsDependentCString* strPtr = &mNameArray[index];
+ new (strPtr) nsDependentCString(raw);
+
+ NameTableKey key(mNameArray, strPtr);
+
+ auto entry = static_cast<NameTableEntry*>(mNameTable.Add(&key, fallible));
+ if (!entry) {
+ continue;
+ }
+
+ // If the entry already exists it's unlikely but possible that its index is
+ // zero, in which case this assertion won't fail. But if the index is
+ // non-zero (highly likely) then it will fail. In other words, this
+ // assertion is likely but not guaranteed to detect if an entry is already
+ // used.
+ MOZ_ASSERT(entry->mIndex == 0, "Entry already exists!");
+
+ entry->mIndex = index;
+ }
+#ifdef DEBUG
+ mNameTable.MarkImmutable();
+#endif
+}
+
+nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable()
+{
+ // manually call the destructor on placement-new'ed objects
+ for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) {
+ mNameArray[index].~nsDependentCString();
+ }
+ free((void*)mNameArray);
+ MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable);
+}
+
+int32_t
+nsStaticCaseInsensitiveNameTable::Lookup(const nsACString& aName)
+{
+ NS_ASSERTION(mNameArray, "not inited");
+
+ const nsAFlatCString& str = PromiseFlatCString(aName);
+
+ NameTableKey key(mNameArray, &str);
+ auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
+
+ return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
+}
+
+int32_t
+nsStaticCaseInsensitiveNameTable::Lookup(const nsAString& aName)
+{
+ NS_ASSERTION(mNameArray, "not inited");
+
+ const nsAFlatString& str = PromiseFlatString(aName);
+
+ NameTableKey key(mNameArray, &str);
+ auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
+
+ return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
+}
+
+const nsAFlatCString&
+nsStaticCaseInsensitiveNameTable::GetStringValue(int32_t aIndex)
+{
+ NS_ASSERTION(mNameArray, "not inited");
+
+ if ((NOT_FOUND < aIndex) && ((uint32_t)aIndex < mNameTable.EntryCount())) {
+ return mNameArray[aIndex];
+ }
+ return mNullStr;
+}
diff --git a/xpcom/ds/nsStaticNameTable.h b/xpcom/ds/nsStaticNameTable.h
new file mode 100644
index 000000000..0b9df3039
--- /dev/null
+++ b/xpcom/ds/nsStaticNameTable.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Classes to manage lookup of static names in a table. */
+
+#ifndef nsStaticNameTable_h___
+#define nsStaticNameTable_h___
+
+#include "PLDHashTable.h"
+#include "nsString.h"
+
+/* This class supports case insensitive lookup.
+ *
+ * It differs from atom tables:
+ * - It supports case insensitive lookup.
+ * - It has minimal footprint by not copying the string table.
+ * - It does no locking.
+ * - It returns zero based indexes and const nsCString& as required by its
+ * callers in the parser.
+ * - It is not an xpcom interface - meant for fast lookup in static tables.
+ *
+ * ***REQUIREMENTS***
+ * - It *requires* that all entries in the table be lowercase only.
+ * - It *requires* that the table of strings be in memory that lives at least
+ * as long as this table object - typically a static string array.
+ */
+
+class nsStaticCaseInsensitiveNameTable
+{
+public:
+ enum { NOT_FOUND = -1 };
+
+ int32_t Lookup(const nsACString& aName);
+ int32_t Lookup(const nsAString& aName);
+ const nsAFlatCString& GetStringValue(int32_t aIndex);
+
+ nsStaticCaseInsensitiveNameTable(const char* const aNames[], int32_t aLength);
+ ~nsStaticCaseInsensitiveNameTable();
+
+private:
+ nsDependentCString* mNameArray;
+ PLDHashTable mNameTable;
+ nsDependentCString mNullStr;
+};
+
+#endif /* nsStaticNameTable_h___ */
diff --git a/xpcom/ds/nsStringEnumerator.cpp b/xpcom/ds/nsStringEnumerator.cpp
new file mode 100644
index 000000000..bdcd53e1a
--- /dev/null
+++ b/xpcom/ds/nsStringEnumerator.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsStringEnumerator.h"
+#include "nsISimpleEnumerator.h"
+#include "nsSupportsPrimitives.h"
+#include "mozilla/Attributes.h"
+#include "nsTArray.h"
+
+//
+// nsStringEnumerator
+//
+
+class nsStringEnumerator final
+ : public nsIStringEnumerator
+ , public nsIUTF8StringEnumerator
+ , public nsISimpleEnumerator
+{
+public:
+ nsStringEnumerator(const nsTArray<nsString>* aArray, bool aOwnsArray)
+ : mArray(aArray)
+ , mIndex(0)
+ , mOwnsArray(aOwnsArray)
+ , mIsUnicode(true)
+ {}
+
+ nsStringEnumerator(const nsTArray<nsCString>* aArray, bool aOwnsArray)
+ : mCArray(aArray)
+ , mIndex(0)
+ , mOwnsArray(aOwnsArray)
+ , mIsUnicode(false)
+ {}
+
+ nsStringEnumerator(const nsTArray<nsString>* aArray, nsISupports* aOwner)
+ : mArray(aArray)
+ , mIndex(0)
+ , mOwner(aOwner)
+ , mOwnsArray(false)
+ , mIsUnicode(true)
+ {}
+
+ nsStringEnumerator(const nsTArray<nsCString>* aArray, nsISupports* aOwner)
+ : mCArray(aArray)
+ , mIndex(0)
+ , mOwner(aOwner)
+ , mOwnsArray(false)
+ , mIsUnicode(false)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+
+ // have to declare nsIStringEnumerator manually, because of
+ // overlapping method names
+ NS_IMETHOD GetNext(nsAString& aResult) override;
+ NS_DECL_NSISIMPLEENUMERATOR
+
+private:
+ ~nsStringEnumerator()
+ {
+ if (mOwnsArray) {
+ // const-casting is safe here, because the NS_New*
+ // constructors make sure mOwnsArray is consistent with
+ // the constness of the objects
+ if (mIsUnicode) {
+ delete const_cast<nsTArray<nsString>*>(mArray);
+ } else {
+ delete const_cast<nsTArray<nsCString>*>(mCArray);
+ }
+ }
+ }
+
+ union
+ {
+ const nsTArray<nsString>* mArray;
+ const nsTArray<nsCString>* mCArray;
+ };
+
+ inline uint32_t Count()
+ {
+ return mIsUnicode ? mArray->Length() : mCArray->Length();
+ }
+
+ uint32_t mIndex;
+
+ // the owner allows us to hold a strong reference to the object
+ // that owns the array. Having a non-null value in mOwner implies
+ // that mOwnsArray is false, because we rely on the real owner
+ // to release the array
+ nsCOMPtr<nsISupports> mOwner;
+ bool mOwnsArray;
+ bool mIsUnicode;
+};
+
+NS_IMPL_ISUPPORTS(nsStringEnumerator,
+ nsIStringEnumerator,
+ nsIUTF8StringEnumerator,
+ nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsStringEnumerator::HasMore(bool* aResult)
+{
+ *aResult = mIndex < Count();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringEnumerator::HasMoreElements(bool* aResult)
+{
+ return HasMore(aResult);
+}
+
+NS_IMETHODIMP
+nsStringEnumerator::GetNext(nsISupports** aResult)
+{
+ if (mIsUnicode) {
+ nsSupportsString* stringImpl = new nsSupportsString();
+ if (!stringImpl) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ stringImpl->SetData(mArray->ElementAt(mIndex++));
+ *aResult = stringImpl;
+ } else {
+ nsSupportsCString* cstringImpl = new nsSupportsCString();
+ if (!cstringImpl) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ cstringImpl->SetData(mCArray->ElementAt(mIndex++));
+ *aResult = cstringImpl;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringEnumerator::GetNext(nsAString& aResult)
+{
+ if (NS_WARN_IF(mIndex >= Count())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (mIsUnicode) {
+ aResult = mArray->ElementAt(mIndex++);
+ } else {
+ CopyUTF8toUTF16(mCArray->ElementAt(mIndex++), aResult);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringEnumerator::GetNext(nsACString& aResult)
+{
+ if (NS_WARN_IF(mIndex >= Count())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (mIsUnicode) {
+ CopyUTF16toUTF8(mArray->ElementAt(mIndex++), aResult);
+ } else {
+ aResult = mCArray->ElementAt(mIndex++);
+ }
+
+ return NS_OK;
+}
+
+template<class T>
+static inline nsresult
+StringEnumeratorTail(T** aResult)
+{
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+//
+// constructors
+//
+
+nsresult
+NS_NewStringEnumerator(nsIStringEnumerator** aResult,
+ const nsTArray<nsString>* aArray, nsISupports* aOwner)
+{
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = new nsStringEnumerator(aArray, aOwner);
+ return StringEnumeratorTail(aResult);
+}
+
+
+nsresult
+NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
+ const nsTArray<nsCString>* aArray,
+ nsISupports* aOwner)
+{
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = new nsStringEnumerator(aArray, aOwner);
+ return StringEnumeratorTail(aResult);
+}
+
+nsresult
+NS_NewAdoptingStringEnumerator(nsIStringEnumerator** aResult,
+ nsTArray<nsString>* aArray)
+{
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = new nsStringEnumerator(aArray, true);
+ return StringEnumeratorTail(aResult);
+}
+
+nsresult
+NS_NewAdoptingUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
+ nsTArray<nsCString>* aArray)
+{
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = new nsStringEnumerator(aArray, true);
+ return StringEnumeratorTail(aResult);
+}
+
+// const ones internally just forward to the non-const equivalents
+nsresult
+NS_NewStringEnumerator(nsIStringEnumerator** aResult,
+ const nsTArray<nsString>* aArray)
+{
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = new nsStringEnumerator(aArray, false);
+ return StringEnumeratorTail(aResult);
+}
+
+nsresult
+NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
+ const nsTArray<nsCString>* aArray)
+{
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = new nsStringEnumerator(aArray, false);
+ return StringEnumeratorTail(aResult);
+}
+
diff --git a/xpcom/ds/nsStringEnumerator.h b/xpcom/ds/nsStringEnumerator.h
new file mode 100644
index 000000000..226afd7f4
--- /dev/null
+++ b/xpcom/ds/nsStringEnumerator.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIStringEnumerator.h"
+#include "nsStringFwd.h"
+#include "nsTArrayForwardDeclare.h"
+
+// nsIStringEnumerator/nsIUTF8StringEnumerator implementations
+//
+// Currently all implementations support both interfaces. The
+// constructors below provide the most common interface for the given
+// type (i.e. nsIStringEnumerator for char16_t* strings, and so
+// forth) but any resulting enumerators can be queried to the other
+// type. Internally, the enumerators will hold onto the type that was
+// passed in and do conversion if GetNext() for the other type of
+// string is called.
+
+// There are a few different types of enumerators:
+
+//
+// These enumerators hold a pointer to the array. Be careful
+// because modifying the array may confuse the iterator, especially if
+// you insert or remove elements in the middle of the array.
+//
+
+// The non-adopting enumerator requires that the array sticks around
+// at least as long as the enumerator does. These are for constant
+// string arrays that the enumerator does not own, this could be used
+// in VERY specialized cases such as when the provider KNOWS that the
+// string enumerator will be consumed immediately, or will at least
+// outlast the array.
+// For example:
+//
+// nsTArray<nsCString> array;
+// array.AppendCString("abc");
+// array.AppendCString("def");
+// NS_NewStringEnumerator(&enumerator, &array, true);
+//
+// // call some internal method which iterates the enumerator
+// InternalMethod(enumerator);
+// NS_RELEASE(enumerator);
+//
+MOZ_MUST_USE nsresult
+NS_NewStringEnumerator(nsIStringEnumerator** aResult,
+ const nsTArray<nsString>* aArray,
+ nsISupports* aOwner);
+MOZ_MUST_USE nsresult
+NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
+ const nsTArray<nsCString>* aArray);
+
+MOZ_MUST_USE nsresult
+NS_NewStringEnumerator(nsIStringEnumerator** aResult,
+ const nsTArray<nsString>* aArray);
+
+// Adopting string enumerators assume ownership of the array and will
+// call |operator delete| on the array when the enumerator is destroyed
+// this is useful when the provider creates an array solely for the
+// purpose of creating the enumerator.
+// For example:
+//
+// nsTArray<nsCString>* array = new nsTArray<nsCString>;
+// array->AppendString("abcd");
+// NS_NewAdoptingStringEnumerator(&result, array);
+MOZ_MUST_USE nsresult
+NS_NewAdoptingStringEnumerator(nsIStringEnumerator** aResult,
+ nsTArray<nsString>* aArray);
+
+MOZ_MUST_USE nsresult
+NS_NewAdoptingUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
+ nsTArray<nsCString>* aArray);
+
+
+// these versions take a refcounted "owner" which will be addreffed
+// when the enumerator is created, and destroyed when the enumerator
+// is released. This allows providers to give non-owning pointers to
+// ns*StringArray member variables without worrying about lifetime
+// issues
+// For example:
+//
+// nsresult MyClass::Enumerate(nsIUTF8StringEnumerator** aResult) {
+// mCategoryList->AppendString("abcd");
+// return NS_NewStringEnumerator(aResult, mCategoryList, this);
+// }
+//
+MOZ_MUST_USE nsresult
+NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
+ const nsTArray<nsCString>* aArray,
+ nsISupports* aOwner);
diff --git a/xpcom/ds/nsSupportsArray.cpp b/xpcom/ds/nsSupportsArray.cpp
new file mode 100644
index 000000000..67de8ae16
--- /dev/null
+++ b/xpcom/ds/nsSupportsArray.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "nsArrayEnumerator.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsSupportsArray.h"
+#include "nsSupportsArrayEnumerator.h"
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+nsresult
+nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const
+{
+ nsresult status =
+ mCollection ? mCollection->QueryElementAt(mIndex, aIID, aResult) :
+ NS_ERROR_NULL_POINTER;
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+
+ return status;
+}
+
+nsSupportsArray::nsSupportsArray()
+{
+}
+
+nsSupportsArray::~nsSupportsArray()
+{
+ Clear();
+}
+
+nsresult
+nsSupportsArray::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsISupportsArray> it = new nsSupportsArray();
+
+ return it->QueryInterface(aIID, aResult);
+}
+
+NS_IMPL_ISUPPORTS(nsSupportsArray, nsIArray, nsISupportsArray, nsICollection,
+ nsISerializable)
+
+NS_IMETHODIMP
+nsSupportsArray::Read(nsIObjectInputStream* aStream)
+{
+ nsresult rv;
+
+ uint32_t newArraySize;
+ rv = aStream->Read32(&newArraySize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint32_t count;
+ rv = aStream->Read32(&count);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(count <= newArraySize, "overlarge mCount!");
+ if (count > newArraySize) {
+ count = newArraySize;
+ }
+
+ // Don't clear out our array until we know we have enough space for the new
+ // one and have successfully copied everything out of the stream.
+ nsCOMArray<nsISupports> tmp;
+ tmp.SetCapacity(newArraySize);
+ tmp.SetCount(count);
+
+ auto elems = tmp.Elements();
+ for (uint32_t i = 0; i < count; i++) {
+ rv = aStream->ReadObject(true, &elems[i]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // Now clear out existing refs and replace with the new array.
+ mArray.Clear();
+ mArray.SwapElements(tmp);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::Write(nsIObjectOutputStream* aStream)
+{
+ nsresult rv;
+
+ rv = aStream->Write32(mArray.Capacity());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = aStream->Write32(mArray.Length());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ for (size_t i = 0; i < mArray.Length(); i++) {
+ rv = aStream->WriteObject(mArray[i], true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::GetElementAt(uint32_t aIndex, nsISupports** aOutPtr)
+{
+ nsCOMPtr<nsISupports> elm = mArray.SafeElementAt(aIndex);
+ elm.forget(aOutPtr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(int32_t)
+nsSupportsArray::IndexOf(const nsISupports* aPossibleElement)
+{
+ // nsCOMArray takes a non-const param, but it just passes through to
+ // nsTArray which takes a const param.
+ return mArray.IndexOf(const_cast<nsISupports*>(aPossibleElement));
+}
+
+NS_IMETHODIMP_(bool)
+nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex)
+{
+ return mArray.InsertObjectAt(aElement, aIndex);
+}
+
+NS_IMETHODIMP_(bool)
+nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex)
+{
+ // nsCOMArray::ReplaceObjectAt will grow the array if necessary. Instead
+ // we do the bounds check and only replace if it's in range.
+ if (aIndex < mArray.Length()) {
+ mArray.ReplaceElementAt(aIndex, aElement);
+ return true;
+ }
+ return false;
+}
+
+NS_IMETHODIMP_(bool)
+nsSupportsArray::RemoveElementAt(uint32_t aIndex)
+{
+ return mArray.RemoveObjectAt(aIndex);
+}
+
+NS_IMETHODIMP
+nsSupportsArray::RemoveElement(nsISupports* aElement)
+{
+ return mArray.RemoveObject(aElement) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::Clear(void)
+{
+ mArray.Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::DeprecatedEnumerate(nsIEnumerator** aResult)
+{
+ RefPtr<nsSupportsArrayEnumerator> e = new nsSupportsArrayEnumerator(this);
+ e.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::Clone(nsISupportsArray** aResult)
+{
+ nsCOMPtr<nsISupportsArray> newArray;
+ nsresult rv = NS_NewISupportsArray(getter_AddRefs(newArray));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ for (size_t i = 0; i < mArray.Length(); i++) {
+ // AppendElement does an odd cast of bool to nsresult, we just cast back
+ // here.
+ if (!(bool)newArray->AppendElement(mArray[i])) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ newArray.forget(aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult)
+{
+ nsresult rv;
+ rv = nsSupportsArray::Create(nullptr, NS_GET_IID(nsISupportsArray),
+ (void**)aInstancePtrResult);
+ return rv;
+}
+
+/**
+ * nsIArray adapters.
+ */
+NS_IMETHODIMP
+nsSupportsArray::GetLength(uint32_t* aLength) {
+ return Count(aLength);
+}
+
+NS_IMETHODIMP
+nsSupportsArray::QueryElementAt(uint32_t aIndex, const nsIID& aIID, void** aResult)
+{
+ nsISupports* element = mArray.SafeElementAt(aIndex);
+ if (element) {
+ return element->QueryInterface(aIID, aResult);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::IndexOf(uint32_t aStartIndex, nsISupports* aElement, uint32_t* aResult)
+{
+ int32_t idx = mArray.IndexOf(aElement, aStartIndex);
+ if (idx < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = static_cast<uint32_t>(idx);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsArray::Enumerate(nsISimpleEnumerator** aResult)
+{
+ return NS_NewArrayEnumerator(aResult, this);
+}
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+
diff --git a/xpcom/ds/nsSupportsArray.h b/xpcom/ds/nsSupportsArray.h
new file mode 100644
index 000000000..eed611104
--- /dev/null
+++ b/xpcom/ds/nsSupportsArray.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSupportsArray_h__
+#define nsSupportsArray_h__
+
+#include "nsIArray.h"
+#include "nsCOMArray.h"
+#include "mozilla/Attributes.h"
+
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+#include "nsISupportsArray.h"
+
+class nsSupportsArray final : public nsISupportsArray,
+ public nsIArray
+{
+ ~nsSupportsArray(void); // nonvirtual since we're not subclassed
+
+public:
+ nsSupportsArray(void);
+
+ static MOZ_MUST_USE nsresult
+ Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSISERIALIZABLE
+
+ // nsICollection methods:
+ NS_IMETHOD Count(uint32_t* aResult) override
+ {
+ *aResult = mArray.Length();
+ return NS_OK;
+ }
+ NS_IMETHOD GetElementAt(uint32_t aIndex, nsISupports** aResult) override;
+ MOZ_MUST_USE NS_IMETHOD
+ SetElementAt(uint32_t aIndex, nsISupports* aValue) override
+ {
+ return ReplaceElementAt(aValue, aIndex) ? NS_OK : NS_ERROR_FAILURE;
+ }
+ MOZ_MUST_USE NS_IMETHOD AppendElement(nsISupports* aElement) override
+ {
+ // XXX Invalid cast of bool to nsresult (bug 778110)
+ return (nsresult)InsertElementAt(aElement, mArray.Length())/* ? NS_OK : NS_ERROR_FAILURE*/;
+ }
+ // XXX this is badly named - should be RemoveFirstElement
+ MOZ_MUST_USE NS_IMETHOD RemoveElement(nsISupports* aElement) override;
+ NS_IMETHOD DeprecatedEnumerate(nsIEnumerator** aResult) override;
+ NS_IMETHOD Clear(void) override;
+
+ // nsISupportsArray methods:
+ NS_IMETHOD_(int32_t) IndexOf(const nsISupports* aPossibleElement) override;
+
+ NS_IMETHOD GetIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override
+ {
+ *aResult = IndexOf(aPossibleElement);
+ return NS_OK;
+ }
+
+ MOZ_MUST_USE NS_IMETHOD_(bool)
+ InsertElementAt(nsISupports* aElement, uint32_t aIndex) override;
+
+ MOZ_MUST_USE NS_IMETHOD_(bool)
+ ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) override;
+
+ MOZ_MUST_USE NS_IMETHOD_(bool)
+ RemoveElementAt(uint32_t aIndex) override;
+
+ MOZ_MUST_USE NS_IMETHOD DeleteElementAt(uint32_t aIndex) override
+ {
+ return (RemoveElementAt(aIndex) ? NS_OK : NS_ERROR_FAILURE);
+ }
+
+ MOZ_MUST_USE NS_IMETHOD Clone(nsISupportsArray** aResult) override;
+
+ /**
+ * nsIArray adapters.
+ */
+ NS_DECL_NSIARRAY
+
+private:
+ // Copy constructors are not allowed
+ explicit nsSupportsArray(const nsISupportsArray& aOther);
+
+ nsCOMArray<nsISupports> mArray;
+};
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+
+#endif // nsSupportsArray_h__
diff --git a/xpcom/ds/nsSupportsArrayEnumerator.cpp b/xpcom/ds/nsSupportsArrayEnumerator.cpp
new file mode 100644
index 000000000..3eb6a7a58
--- /dev/null
+++ b/xpcom/ds/nsSupportsArrayEnumerator.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSupportsArrayEnumerator.h"
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+#include "nsISupportsArray.h"
+
+nsSupportsArrayEnumerator::nsSupportsArrayEnumerator(nsISupportsArray* array)
+ : mArray(array)
+ , mCursor(0)
+{
+ NS_ASSERTION(array, "null array");
+}
+
+nsSupportsArrayEnumerator::~nsSupportsArrayEnumerator()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsSupportsArrayEnumerator, nsIBidirectionalEnumerator,
+ nsIEnumerator)
+
+NS_IMETHODIMP
+nsSupportsArrayEnumerator::First()
+{
+ mCursor = 0;
+ uint32_t cnt;
+ nsresult rv = mArray->Count(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ int32_t end = (int32_t)cnt;
+ if (mCursor < end) {
+ return NS_OK;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+}
+
+NS_IMETHODIMP
+nsSupportsArrayEnumerator::Next()
+{
+ uint32_t cnt;
+ nsresult rv = mArray->Count(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ int32_t end = (int32_t)cnt;
+ if (mCursor < end) { // don't count upward forever
+ mCursor++;
+ }
+ if (mCursor < end) {
+ return NS_OK;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+}
+
+NS_IMETHODIMP
+nsSupportsArrayEnumerator::CurrentItem(nsISupports** aItem)
+{
+ NS_ASSERTION(aItem, "null out parameter");
+ uint32_t cnt;
+ nsresult rv = mArray->Count(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (mCursor >= 0 && mCursor < (int32_t)cnt) {
+ return mArray->GetElementAt(mCursor, aItem);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsSupportsArrayEnumerator::IsDone()
+{
+ uint32_t cnt;
+ nsresult rv = mArray->Count(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // XXX This is completely incompatible with the meaning of nsresult.
+ // NS_ENUMERATOR_FALSE is defined to be 1. (bug 778111)
+ return (mCursor >= 0 && mCursor < (int32_t)cnt)
+ ? (nsresult)NS_ENUMERATOR_FALSE : NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsSupportsArrayEnumerator::Last()
+{
+ uint32_t cnt;
+ nsresult rv = mArray->Count(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ mCursor = cnt - 1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsArrayEnumerator::Prev()
+{
+ if (mCursor >= 0) {
+ --mCursor;
+ }
+ if (mCursor >= 0) {
+ return NS_OK;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+}
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
diff --git a/xpcom/ds/nsSupportsArrayEnumerator.h b/xpcom/ds/nsSupportsArrayEnumerator.h
new file mode 100644
index 000000000..bd316d6b9
--- /dev/null
+++ b/xpcom/ds/nsSupportsArrayEnumerator.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSupportsArrayEnumerator_h___
+#define nsSupportsArrayEnumerator_h___
+
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+#include "nsIEnumerator.h"
+
+class nsISupportsArray;
+
+class nsSupportsArrayEnumerator final : public nsIBidirectionalEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit nsSupportsArrayEnumerator(nsISupportsArray* aArray);
+
+ // nsIEnumerator methods:
+ NS_DECL_NSIENUMERATOR
+
+ // nsIBidirectionalEnumerator methods:
+ NS_DECL_NSIBIDIRECTIONALENUMERATOR
+
+private:
+ ~nsSupportsArrayEnumerator();
+
+protected:
+ nsCOMPtr<nsISupportsArray> mArray;
+ int32_t mCursor;
+
+};
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+
+#endif // __nsSupportsArrayEnumerator_h
+
diff --git a/xpcom/ds/nsSupportsPrimitives.cpp b/xpcom/ds/nsSupportsPrimitives.cpp
new file mode 100644
index 000000000..aa929b9de
--- /dev/null
+++ b/xpcom/ds/nsSupportsPrimitives.cpp
@@ -0,0 +1,849 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSupportsPrimitives.h"
+#include "nsMemory.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Sprintf.h"
+#include <algorithm>
+
+template<typename T>
+static char*
+DataToString(const char* aFormat, T aData)
+{
+ static const int size = 32;
+ char buf[size];
+
+ int len = SprintfLiteral(buf, aFormat, aData);
+ MOZ_ASSERT(len >= 0);
+
+ return static_cast<char*>(nsMemory::Clone(buf, std::min(len + 1, size) *
+ sizeof(char)));
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsID, nsISupportsID, nsISupportsPrimitive)
+
+nsSupportsID::nsSupportsID()
+ : mData(nullptr)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsID::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_ID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsID::GetData(nsID** aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+
+ if (mData) {
+ *aData = static_cast<nsID*>(nsMemory::Clone(mData, sizeof(nsID)));
+ } else {
+ *aData = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsID::SetData(const nsID* aData)
+{
+ if (mData) {
+ free(mData);
+ }
+
+ if (aData) {
+ mData = static_cast<nsID*>(nsMemory::Clone(aData, sizeof(nsID)));
+ } else {
+ mData = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsID::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+
+ if (mData) {
+ *aResult = mData->ToString();
+ } else {
+ static const char nullStr[] = "null";
+ *aResult = static_cast<char*>(nsMemory::Clone(nullStr, sizeof(nullStr)));
+ }
+
+ return NS_OK;
+}
+
+/*****************************************************************************
+ * nsSupportsCString
+ *****************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsCString, nsISupportsCString,
+ nsISupportsPrimitive)
+
+NS_IMETHODIMP
+nsSupportsCString::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_CSTRING;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsCString::GetData(nsACString& aData)
+{
+ aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsCString::ToString(char** aResult)
+{
+ *aResult = ToNewCString(mData);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsCString::SetData(const nsACString& aData)
+{
+ bool ok = mData.Assign(aData, mozilla::fallible);
+ if (!ok) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+/*****************************************************************************
+ * nsSupportsString
+ *****************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsString, nsISupportsString,
+ nsISupportsPrimitive)
+
+NS_IMETHODIMP
+nsSupportsString::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_STRING;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsString::GetData(nsAString& aData)
+{
+ aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsString::ToString(char16_t** aResult)
+{
+ *aResult = ToNewUnicode(mData);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsString::SetData(const nsAString& aData)
+{
+ bool ok = mData.Assign(aData, mozilla::fallible);
+ if (!ok) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRBool, nsISupportsPRBool,
+ nsISupportsPrimitive)
+
+nsSupportsPRBool::nsSupportsPRBool()
+ : mData(false)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRBool::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRBOOL;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRBool::GetData(bool* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRBool::SetData(bool aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRBool::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ const char* str = mData ? "true" : "false";
+ *aResult = static_cast<char*>(nsMemory::Clone(str, (strlen(str) + 1) *
+ sizeof(char)));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRUint8, nsISupportsPRUint8,
+ nsISupportsPrimitive)
+
+nsSupportsPRUint8::nsSupportsPRUint8()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint8::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRUINT8;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint8::GetData(uint8_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint8::SetData(uint8_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint8::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%u", static_cast<unsigned int>(mData));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRUint16, nsISupportsPRUint16,
+ nsISupportsPrimitive)
+
+nsSupportsPRUint16::nsSupportsPRUint16()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint16::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRUINT16;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint16::GetData(uint16_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint16::SetData(uint16_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint16::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%u", static_cast<unsigned int>(mData));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRUint32, nsISupportsPRUint32,
+ nsISupportsPrimitive)
+
+nsSupportsPRUint32::nsSupportsPRUint32()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint32::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRUINT32;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint32::GetData(uint32_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint32::SetData(uint32_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint32::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%u", mData);
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRUint64, nsISupportsPRUint64,
+ nsISupportsPrimitive)
+
+nsSupportsPRUint64::nsSupportsPRUint64()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint64::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRUINT64;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint64::GetData(uint64_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint64::SetData(uint64_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRUint64::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%llu", mData);
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRTime, nsISupportsPRTime,
+ nsISupportsPrimitive)
+
+nsSupportsPRTime::nsSupportsPRTime()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRTime::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRTIME;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRTime::GetData(PRTime* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRTime::SetData(PRTime aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRTime::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%" PRIu64, mData);
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsChar, nsISupportsChar,
+ nsISupportsPrimitive)
+
+nsSupportsChar::nsSupportsChar()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsChar::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_CHAR;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsChar::GetData(char* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsChar::SetData(char aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsChar::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = static_cast<char*>(moz_xmalloc(2 * sizeof(char)));
+ *aResult[0] = mData;
+ *aResult[1] = '\0';
+
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRInt16, nsISupportsPRInt16,
+ nsISupportsPrimitive)
+
+nsSupportsPRInt16::nsSupportsPRInt16()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt16::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRINT16;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt16::GetData(int16_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt16::SetData(int16_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt16::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%d", static_cast<int>(mData));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRInt32, nsISupportsPRInt32,
+ nsISupportsPrimitive)
+
+nsSupportsPRInt32::nsSupportsPRInt32()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt32::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRINT32;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt32::GetData(int32_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt32::SetData(int32_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt32::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%d", mData);
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsPRInt64, nsISupportsPRInt64,
+ nsISupportsPrimitive)
+
+nsSupportsPRInt64::nsSupportsPRInt64()
+ : mData(0)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt64::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_PRINT64;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt64::GetData(int64_t* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt64::SetData(int64_t aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsPRInt64::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%" PRId64, mData);
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsFloat, nsISupportsFloat,
+ nsISupportsPrimitive)
+
+nsSupportsFloat::nsSupportsFloat()
+ : mData(float(0.0))
+{
+}
+
+NS_IMETHODIMP
+nsSupportsFloat::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_FLOAT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsFloat::GetData(float* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsFloat::SetData(float aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsFloat::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%f", static_cast<double>(mData));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsDouble, nsISupportsDouble,
+ nsISupportsPrimitive)
+
+nsSupportsDouble::nsSupportsDouble()
+ : mData(double(0.0))
+{
+}
+
+NS_IMETHODIMP
+nsSupportsDouble::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_DOUBLE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsDouble::GetData(double* aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsDouble::SetData(double aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsDouble::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ *aResult = DataToString("%f", mData);
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+
+NS_IMPL_ISUPPORTS(nsSupportsVoid, nsISupportsVoid,
+ nsISupportsPrimitive)
+
+nsSupportsVoid::nsSupportsVoid()
+ : mData(nullptr)
+{
+}
+
+NS_IMETHODIMP
+nsSupportsVoid::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_VOID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsVoid::GetData(void** aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsVoid::SetData(void* aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsVoid::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+ static const char str[] = "[raw data]";
+ *aResult = static_cast<char*>(nsMemory::Clone(str, sizeof(str)));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+
+NS_IMPL_ISUPPORTS(nsSupportsInterfacePointer,
+ nsISupportsInterfacePointer,
+ nsISupportsPrimitive)
+
+nsSupportsInterfacePointer::nsSupportsInterfacePointer()
+ : mIID(nullptr)
+{
+}
+
+nsSupportsInterfacePointer::~nsSupportsInterfacePointer()
+{
+ if (mIID) {
+ free(mIID);
+ }
+}
+
+NS_IMETHODIMP
+nsSupportsInterfacePointer::GetType(uint16_t* aType)
+{
+ NS_ASSERTION(aType, "Bad pointer");
+ *aType = TYPE_INTERFACE_POINTER;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsInterfacePointer::GetData(nsISupports** aData)
+{
+ NS_ASSERTION(aData, "Bad pointer");
+ *aData = mData;
+ NS_IF_ADDREF(*aData);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsInterfacePointer::SetData(nsISupports* aData)
+{
+ mData = aData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsInterfacePointer::GetDataIID(nsID** aIID)
+{
+ NS_ASSERTION(aIID, "Bad pointer");
+
+ if (mIID) {
+ *aIID = static_cast<nsID*>(nsMemory::Clone(mIID, sizeof(nsID)));
+ } else {
+ *aIID = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsInterfacePointer::SetDataIID(const nsID* aIID)
+{
+ if (mIID) {
+ free(mIID);
+ }
+
+ if (aIID) {
+ mIID = static_cast<nsID*>(nsMemory::Clone(aIID, sizeof(nsID)));
+ } else {
+ mIID = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsInterfacePointer::ToString(char** aResult)
+{
+ NS_ASSERTION(aResult, "Bad pointer");
+
+ static const char str[] = "[interface pointer]";
+ // jband sez: think about asking nsIInterfaceInfoManager whether
+ // the interface has a known human-readable name
+ *aResult = static_cast<char*>(nsMemory::Clone(str, sizeof(str)));
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsSupportsDependentCString, nsISupportsCString,
+ nsISupportsPrimitive)
+
+nsSupportsDependentCString::nsSupportsDependentCString(const char* aStr)
+ : mData(aStr)
+{ }
+
+NS_IMETHODIMP
+nsSupportsDependentCString::GetType(uint16_t* aType)
+{
+ if (NS_WARN_IF(!aType)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aType = TYPE_CSTRING;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsDependentCString::GetData(nsACString& aData)
+{
+ aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsDependentCString::ToString(char** aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = ToNewCString(mData);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSupportsDependentCString::SetData(const nsACString& aData)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/xpcom/ds/nsSupportsPrimitives.h b/xpcom/ds/nsSupportsPrimitives.h
new file mode 100644
index 000000000..17ed0f47f
--- /dev/null
+++ b/xpcom/ds/nsSupportsPrimitives.h
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSupportsPrimitives_h__
+#define nsSupportsPrimitives_h__
+
+#include "mozilla/Attributes.h"
+
+#include "nsISupportsPrimitives.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+class nsSupportsID final : public nsISupportsID
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSID
+
+ nsSupportsID();
+
+private:
+ ~nsSupportsID() {}
+
+ nsID* mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsCString final : public nsISupportsCString
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSCSTRING
+
+ nsSupportsCString() {}
+
+private:
+ ~nsSupportsCString() {}
+
+ nsCString mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsString final : public nsISupportsString
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSSTRING
+
+ nsSupportsString() {}
+
+private:
+ ~nsSupportsString() {}
+
+ nsString mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRBool final : public nsISupportsPRBool
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRBOOL
+
+ nsSupportsPRBool();
+
+private:
+ ~nsSupportsPRBool() {}
+
+ bool mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRUint8 final : public nsISupportsPRUint8
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRUINT8
+
+ nsSupportsPRUint8();
+
+private:
+ ~nsSupportsPRUint8() {}
+
+ uint8_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRUint16 final : public nsISupportsPRUint16
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRUINT16
+
+ nsSupportsPRUint16();
+
+private:
+ ~nsSupportsPRUint16() {}
+
+ uint16_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRUint32 final : public nsISupportsPRUint32
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRUINT32
+
+ nsSupportsPRUint32();
+
+private:
+ ~nsSupportsPRUint32() {}
+
+ uint32_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRUint64 final : public nsISupportsPRUint64
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRUINT64
+
+ nsSupportsPRUint64();
+
+private:
+ ~nsSupportsPRUint64() {}
+
+ uint64_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRTime final : public nsISupportsPRTime
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRTIME
+
+ nsSupportsPRTime();
+
+private:
+ ~nsSupportsPRTime() {}
+
+ PRTime mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsChar final : public nsISupportsChar
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSCHAR
+
+ nsSupportsChar();
+
+private:
+ ~nsSupportsChar() {}
+
+ char mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRInt16 final : public nsISupportsPRInt16
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRINT16
+
+ nsSupportsPRInt16();
+
+private:
+ ~nsSupportsPRInt16() {}
+
+ int16_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRInt32 final : public nsISupportsPRInt32
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRINT32
+
+ nsSupportsPRInt32();
+
+private:
+ ~nsSupportsPRInt32() {}
+
+ int32_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsPRInt64 final : public nsISupportsPRInt64
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSPRINT64
+
+ nsSupportsPRInt64();
+
+private:
+ ~nsSupportsPRInt64() {}
+
+ int64_t mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsFloat final : public nsISupportsFloat
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSFLOAT
+
+ nsSupportsFloat();
+
+private:
+ ~nsSupportsFloat() {}
+
+ float mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsDouble final : public nsISupportsDouble
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSDOUBLE
+
+ nsSupportsDouble();
+
+private:
+ ~nsSupportsDouble() {}
+
+ double mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsVoid final : public nsISupportsVoid
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSVOID
+
+ nsSupportsVoid();
+
+private:
+ ~nsSupportsVoid() {}
+
+ void* mData;
+};
+
+/***************************************************************************/
+
+class nsSupportsInterfacePointer final : public nsISupportsInterfacePointer
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSINTERFACEPOINTER
+
+ nsSupportsInterfacePointer();
+
+private:
+ ~nsSupportsInterfacePointer();
+
+ nsCOMPtr<nsISupports> mData;
+ nsID* mIID;
+};
+
+/***************************************************************************/
+
+/**
+ * Wraps a static const char* buffer for use with nsISupportsCString
+ *
+ * Only use this class with static buffers, or arena-allocated buffers of
+ * permanent lifetime!
+ */
+class nsSupportsDependentCString final : public nsISupportsCString
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSCSTRING
+
+ explicit nsSupportsDependentCString(const char* aStr);
+
+private:
+ ~nsSupportsDependentCString() {}
+
+ nsDependentCString mData;
+};
+
+#endif /* nsSupportsPrimitives_h__ */
diff --git a/xpcom/ds/nsVariant.cpp b/xpcom/ds/nsVariant.cpp
new file mode 100644
index 000000000..edb020139
--- /dev/null
+++ b/xpcom/ds/nsVariant.cpp
@@ -0,0 +1,2220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsVariant.h"
+#include "prprf.h"
+#include "prdtoa.h"
+#include <math.h>
+#include "nsCycleCollectionParticipant.h"
+#include "xpt_struct.h"
+#include "nsReadableUtils.h"
+#include "nsMemory.h"
+#include "nsString.h"
+#include "nsCRTGlue.h"
+
+/***************************************************************************/
+// Helpers for static convert functions...
+
+static nsresult
+String2Double(const char* aString, double* aResult)
+{
+ char* next;
+ double value = PR_strtod(aString, &next);
+ if (next == aString) {
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+ *aResult = value;
+ return NS_OK;
+}
+
+static nsresult
+AString2Double(const nsAString& aString, double* aResult)
+{
+ char* pChars = ToNewCString(aString);
+ if (!pChars) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ nsresult rv = String2Double(pChars, aResult);
+ free(pChars);
+ return rv;
+}
+
+static nsresult
+AUTF8String2Double(const nsAUTF8String& aString, double* aResult)
+{
+ return String2Double(PromiseFlatUTF8String(aString).get(), aResult);
+}
+
+static nsresult
+ACString2Double(const nsACString& aString, double* aResult)
+{
+ return String2Double(PromiseFlatCString(aString).get(), aResult);
+}
+
+// Fills aOutData with double, uint32_t, or int32_t.
+// Returns NS_OK, an error code, or a non-NS_OK success code
+nsresult
+nsDiscriminatedUnion::ToManageableNumber(nsDiscriminatedUnion* aOutData) const
+{
+ nsresult rv;
+
+ switch (mType) {
+ // This group results in a int32_t...
+
+#define CASE__NUMBER_INT32(type_, member_) \
+ case nsIDataType::type_ : \
+ aOutData->u.mInt32Value = u.member_ ; \
+ aOutData->mType = nsIDataType::VTYPE_INT32; \
+ return NS_OK;
+
+ CASE__NUMBER_INT32(VTYPE_INT8, mInt8Value)
+ CASE__NUMBER_INT32(VTYPE_INT16, mInt16Value)
+ CASE__NUMBER_INT32(VTYPE_INT32, mInt32Value)
+ CASE__NUMBER_INT32(VTYPE_UINT8, mUint8Value)
+ CASE__NUMBER_INT32(VTYPE_UINT16, mUint16Value)
+ CASE__NUMBER_INT32(VTYPE_BOOL, mBoolValue)
+ CASE__NUMBER_INT32(VTYPE_CHAR, mCharValue)
+ CASE__NUMBER_INT32(VTYPE_WCHAR, mWCharValue)
+
+#undef CASE__NUMBER_INT32
+
+ // This group results in a uint32_t...
+
+ case nsIDataType::VTYPE_UINT32:
+ aOutData->u.mInt32Value = u.mUint32Value;
+ aOutData->mType = nsIDataType::VTYPE_INT32;
+ return NS_OK;
+
+ // This group results in a double...
+
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT64:
+ // XXX Need boundary checking here.
+ // We may need to return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
+ aOutData->u.mDoubleValue = double(u.mInt64Value);
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_FLOAT:
+ aOutData->u.mDoubleValue = u.mFloatValue;
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_DOUBLE:
+ aOutData->u.mDoubleValue = u.mDoubleValue;
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ rv = String2Double(u.str.mStringValue, &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_ASTRING:
+ rv = AString2Double(*u.mAStringValue, &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ rv = AUTF8String2Double(*u.mUTF8StringValue,
+ &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ rv = ACString2Double(*u.mCStringValue,
+ &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ rv = AString2Double(nsDependentString(u.wstr.mWStringValue),
+ &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+
+ // This group fails...
+
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ID:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+/***************************************************************************/
+// Array helpers...
+
+void
+nsDiscriminatedUnion::FreeArray()
+{
+ NS_ASSERTION(mType == nsIDataType::VTYPE_ARRAY, "bad FreeArray call");
+ NS_ASSERTION(u.array.mArrayValue, "bad array");
+ NS_ASSERTION(u.array.mArrayCount, "bad array count");
+
+#define CASE__FREE_ARRAY_PTR(type_, ctype_) \
+ case nsIDataType::type_ : \
+ { \
+ ctype_** p = (ctype_**) u.array.mArrayValue; \
+ for (uint32_t i = u.array.mArrayCount; i > 0; p++, i--) \
+ if (*p) \
+ free((char*)*p); \
+ break; \
+ }
+
+#define CASE__FREE_ARRAY_IFACE(type_, ctype_) \
+ case nsIDataType::type_ : \
+ { \
+ ctype_** p = (ctype_**) u.array.mArrayValue; \
+ for (uint32_t i = u.array.mArrayCount; i > 0; p++, i--) \
+ if (*p) \
+ (*p)->Release(); \
+ break; \
+ }
+
+ switch (u.array.mArrayType) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ case nsIDataType::VTYPE_BOOL:
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ break;
+
+ // XXX We ASSUME that "array of nsID" means "array of pointers to nsID".
+ CASE__FREE_ARRAY_PTR(VTYPE_ID, nsID)
+ CASE__FREE_ARRAY_PTR(VTYPE_CHAR_STR, char)
+ CASE__FREE_ARRAY_PTR(VTYPE_WCHAR_STR, char16_t)
+ CASE__FREE_ARRAY_IFACE(VTYPE_INTERFACE, nsISupports)
+ CASE__FREE_ARRAY_IFACE(VTYPE_INTERFACE_IS, nsISupports)
+
+ // The rest are illegal.
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ default:
+ NS_ERROR("bad type in array!");
+ break;
+ }
+
+ // Free the array memory.
+ free((char*)u.array.mArrayValue);
+
+#undef CASE__FREE_ARRAY_PTR
+#undef CASE__FREE_ARRAY_IFACE
+}
+
+static nsresult
+CloneArray(uint16_t aInType, const nsIID* aInIID,
+ uint32_t aInCount, void* aInValue,
+ uint16_t* aOutType, nsIID* aOutIID,
+ uint32_t* aOutCount, void** aOutValue)
+{
+ NS_ASSERTION(aInCount, "bad param");
+ NS_ASSERTION(aInValue, "bad param");
+ NS_ASSERTION(aOutType, "bad param");
+ NS_ASSERTION(aOutCount, "bad param");
+ NS_ASSERTION(aOutValue, "bad param");
+
+ uint32_t allocatedValueCount = 0;
+ nsresult rv = NS_OK;
+ uint32_t i;
+
+ // First we figure out the size of the elements for the new u.array.
+
+ size_t elementSize;
+ size_t allocSize;
+
+ switch (aInType) {
+ case nsIDataType::VTYPE_INT8:
+ elementSize = sizeof(int8_t);
+ break;
+ case nsIDataType::VTYPE_INT16:
+ elementSize = sizeof(int16_t);
+ break;
+ case nsIDataType::VTYPE_INT32:
+ elementSize = sizeof(int32_t);
+ break;
+ case nsIDataType::VTYPE_INT64:
+ elementSize = sizeof(int64_t);
+ break;
+ case nsIDataType::VTYPE_UINT8:
+ elementSize = sizeof(uint8_t);
+ break;
+ case nsIDataType::VTYPE_UINT16:
+ elementSize = sizeof(uint16_t);
+ break;
+ case nsIDataType::VTYPE_UINT32:
+ elementSize = sizeof(uint32_t);
+ break;
+ case nsIDataType::VTYPE_UINT64:
+ elementSize = sizeof(uint64_t);
+ break;
+ case nsIDataType::VTYPE_FLOAT:
+ elementSize = sizeof(float);
+ break;
+ case nsIDataType::VTYPE_DOUBLE:
+ elementSize = sizeof(double);
+ break;
+ case nsIDataType::VTYPE_BOOL:
+ elementSize = sizeof(bool);
+ break;
+ case nsIDataType::VTYPE_CHAR:
+ elementSize = sizeof(char);
+ break;
+ case nsIDataType::VTYPE_WCHAR:
+ elementSize = sizeof(char16_t);
+ break;
+
+ // XXX We ASSUME that "array of nsID" means "array of pointers to nsID".
+ case nsIDataType::VTYPE_ID:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ elementSize = sizeof(void*);
+ break;
+
+ // The rest are illegal.
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ default:
+ NS_ERROR("bad type in array!");
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+
+ // Alloc the u.array.
+
+ allocSize = aInCount * elementSize;
+ *aOutValue = moz_xmalloc(allocSize);
+ if (!*aOutValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Clone the elements.
+
+ switch (aInType) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ case nsIDataType::VTYPE_BOOL:
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ memcpy(*aOutValue, aInValue, allocSize);
+ break;
+
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ if (aOutIID) {
+ *aOutIID = *aInIID;
+ }
+ MOZ_FALLTHROUGH;
+
+ case nsIDataType::VTYPE_INTERFACE: {
+ memcpy(*aOutValue, aInValue, allocSize);
+
+ nsISupports** p = (nsISupports**)*aOutValue;
+ for (i = aInCount; i > 0; ++p, --i)
+ if (*p) {
+ (*p)->AddRef();
+ }
+ break;
+ }
+
+ // XXX We ASSUME that "array of nsID" means "array of pointers to nsID".
+ case nsIDataType::VTYPE_ID: {
+ nsID** inp = (nsID**)aInValue;
+ nsID** outp = (nsID**)*aOutValue;
+ for (i = aInCount; i > 0; --i) {
+ nsID* idp = *(inp++);
+ if (idp) {
+ if (!(*(outp++) = (nsID*)nsMemory::Clone((char*)idp, sizeof(nsID)))) {
+ goto bad;
+ }
+ } else {
+ *(outp++) = nullptr;
+ }
+ allocatedValueCount++;
+ }
+ break;
+ }
+
+ case nsIDataType::VTYPE_CHAR_STR: {
+ char** inp = (char**)aInValue;
+ char** outp = (char**)*aOutValue;
+ for (i = aInCount; i > 0; i--) {
+ char* str = *(inp++);
+ if (str) {
+ if (!(*(outp++) = (char*)nsMemory::Clone(
+ str, (strlen(str) + 1) * sizeof(char)))) {
+ goto bad;
+ }
+ } else {
+ *(outp++) = nullptr;
+ }
+ allocatedValueCount++;
+ }
+ break;
+ }
+
+ case nsIDataType::VTYPE_WCHAR_STR: {
+ char16_t** inp = (char16_t**)aInValue;
+ char16_t** outp = (char16_t**)*aOutValue;
+ for (i = aInCount; i > 0; i--) {
+ char16_t* str = *(inp++);
+ if (str) {
+ if (!(*(outp++) = (char16_t*)nsMemory::Clone(
+ str, (NS_strlen(str) + 1) * sizeof(char16_t)))) {
+ goto bad;
+ }
+ } else {
+ *(outp++) = nullptr;
+ }
+ allocatedValueCount++;
+ }
+ break;
+ }
+
+ // The rest are illegal.
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ default:
+ NS_ERROR("bad type in array!");
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+ *aOutType = aInType;
+ *aOutCount = aInCount;
+ return NS_OK;
+
+bad:
+ if (*aOutValue) {
+ char** p = (char**)*aOutValue;
+ for (i = allocatedValueCount; i > 0; ++p, --i)
+ if (*p) {
+ free(*p);
+ }
+ free((char*)*aOutValue);
+ *aOutValue = nullptr;
+ }
+ return rv;
+}
+
+/***************************************************************************/
+
+#define TRIVIAL_DATA_CONVERTER(type_, member_, retval_) \
+ if (mType == nsIDataType::type_) { \
+ *retval_ = u.member_; \
+ return NS_OK; \
+ }
+
+#define NUMERIC_CONVERSION_METHOD_BEGIN(type_, Ctype_, name_) \
+nsresult \
+nsDiscriminatedUnion::ConvertTo##name_ (Ctype_* aResult) const \
+{ \
+ TRIVIAL_DATA_CONVERTER(type_, m##name_##Value, aResult) \
+ nsDiscriminatedUnion tempData; \
+ nsresult rv = ToManageableNumber(&tempData); \
+ /* */ \
+ /* NOTE: rv may indicate a success code that we want to preserve */ \
+ /* For the final return. So all the return cases below should return */ \
+ /* this rv when indicating success. */ \
+ /* */ \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ switch(tempData.mType) \
+ {
+
+#define CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(Ctype_) \
+ case nsIDataType::VTYPE_INT32: \
+ *aResult = ( Ctype_ ) tempData.u.mInt32Value; \
+ return rv;
+
+#define CASE__NUMERIC_CONVERSION_INT32_MIN_MAX(Ctype_, min_, max_) \
+ case nsIDataType::VTYPE_INT32: \
+ { \
+ int32_t value = tempData.u.mInt32Value; \
+ if (value < min_ || value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return rv; \
+ }
+
+#define CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(Ctype_) \
+ case nsIDataType::VTYPE_UINT32: \
+ *aResult = ( Ctype_ ) tempData.u.mUint32Value; \
+ return rv;
+
+#define CASE__NUMERIC_CONVERSION_UINT32_MAX(Ctype_, max_) \
+ case nsIDataType::VTYPE_UINT32: \
+ { \
+ uint32_t value = tempData.u.mUint32Value; \
+ if (value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return rv; \
+ }
+
+#define CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(Ctype_) \
+ case nsIDataType::VTYPE_DOUBLE: \
+ *aResult = ( Ctype_ ) tempData.u.mDoubleValue; \
+ return rv;
+
+#define CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX(Ctype_, min_, max_) \
+ case nsIDataType::VTYPE_DOUBLE: \
+ { \
+ double value = tempData.u.mDoubleValue; \
+ if (value < min_ || value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return rv; \
+ }
+
+#define CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(Ctype_, min_, max_) \
+ case nsIDataType::VTYPE_DOUBLE: \
+ { \
+ double value = tempData.u.mDoubleValue; \
+ if (value < min_ || value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return (0.0 == fmod(value,1.0)) ? \
+ rv : NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA; \
+ }
+
+#define CASES__NUMERIC_CONVERSION_NORMAL(Ctype_, min_, max_) \
+ CASE__NUMERIC_CONVERSION_INT32_MIN_MAX(Ctype_, min_, max_) \
+ CASE__NUMERIC_CONVERSION_UINT32_MAX(Ctype_, max_) \
+ CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(Ctype_, min_, max_)
+
+#define NUMERIC_CONVERSION_METHOD_END \
+ default: \
+ NS_ERROR("bad type returned from ToManageableNumber"); \
+ return NS_ERROR_CANNOT_CONVERT_DATA; \
+ } \
+}
+
+#define NUMERIC_CONVERSION_METHOD_NORMAL(type_, Ctype_, name_, min_, max_) \
+ NUMERIC_CONVERSION_METHOD_BEGIN(type_, Ctype_, name_) \
+ CASES__NUMERIC_CONVERSION_NORMAL(Ctype_, min_, max_) \
+ NUMERIC_CONVERSION_METHOD_END
+
+/***************************************************************************/
+// These expand into full public methods...
+
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_INT8, uint8_t, Int8, (-127 - 1), 127)
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_INT16, int16_t, Int16, (-32767 - 1), 32767)
+
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_INT32, int32_t, Int32)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(int32_t)
+ CASE__NUMERIC_CONVERSION_UINT32_MAX(int32_t, 2147483647)
+ CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(int32_t, (-2147483647 - 1), 2147483647)
+NUMERIC_CONVERSION_METHOD_END
+
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_UINT8, uint8_t, Uint8, 0, 255)
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_UINT16, uint16_t, Uint16, 0, 65535)
+
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_UINT32, uint32_t, Uint32)
+ CASE__NUMERIC_CONVERSION_INT32_MIN_MAX(uint32_t, 0, 2147483647)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(uint32_t)
+ CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(uint32_t, 0, 4294967295U)
+NUMERIC_CONVERSION_METHOD_END
+
+// XXX toFloat convertions need to be fixed!
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_FLOAT, float, Float)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(float)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(float)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(float)
+NUMERIC_CONVERSION_METHOD_END
+
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_DOUBLE, double, Double)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(double)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(double)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(double)
+NUMERIC_CONVERSION_METHOD_END
+
+// XXX toChar convertions need to be fixed!
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_CHAR, char, Char)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(char)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(char)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(char)
+NUMERIC_CONVERSION_METHOD_END
+
+// XXX toWChar convertions need to be fixed!
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_WCHAR, char16_t, WChar)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(char16_t)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(char16_t)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(char16_t)
+NUMERIC_CONVERSION_METHOD_END
+
+#undef NUMERIC_CONVERSION_METHOD_BEGIN
+#undef CASE__NUMERIC_CONVERSION_INT32_JUST_CAST
+#undef CASE__NUMERIC_CONVERSION_INT32_MIN_MAX
+#undef CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST
+#undef CASE__NUMERIC_CONVERSION_UINT32_MIN_MAX
+#undef CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST
+#undef CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX
+#undef CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT
+#undef CASES__NUMERIC_CONVERSION_NORMAL
+#undef NUMERIC_CONVERSION_METHOD_END
+#undef NUMERIC_CONVERSION_METHOD_NORMAL
+
+/***************************************************************************/
+
+// Just leverage a numeric converter for bool (but restrict the values).
+// XXX Is this really what we want to do?
+
+nsresult
+nsDiscriminatedUnion::ConvertToBool(bool* aResult) const
+{
+ TRIVIAL_DATA_CONVERTER(VTYPE_BOOL, mBoolValue, aResult)
+
+ double val;
+ nsresult rv = ConvertToDouble(&val);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aResult = 0.0 != val;
+ return rv;
+}
+
+/***************************************************************************/
+
+nsresult
+nsDiscriminatedUnion::ConvertToInt64(int64_t* aResult) const
+{
+ TRIVIAL_DATA_CONVERTER(VTYPE_INT64, mInt64Value, aResult)
+ TRIVIAL_DATA_CONVERTER(VTYPE_UINT64, mUint64Value, aResult)
+
+ nsDiscriminatedUnion tempData;
+ nsresult rv = ToManageableNumber(&tempData);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ switch (tempData.mType) {
+ case nsIDataType::VTYPE_INT32:
+ *aResult = tempData.u.mInt32Value;
+ return rv;
+ case nsIDataType::VTYPE_UINT32:
+ *aResult = tempData.u.mUint32Value;
+ return rv;
+ case nsIDataType::VTYPE_DOUBLE:
+ // XXX should check for data loss here!
+ *aResult = tempData.u.mDoubleValue;
+ return rv;
+ default:
+ NS_ERROR("bad type returned from ToManageableNumber");
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToUint64(uint64_t* aResult) const
+{
+ return ConvertToInt64((int64_t*)aResult);
+}
+
+/***************************************************************************/
+
+bool
+nsDiscriminatedUnion::String2ID(nsID* aPid) const
+{
+ nsAutoString tempString;
+ nsAString* pString;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ return aPid->Parse(u.str.mStringValue);
+ case nsIDataType::VTYPE_CSTRING:
+ return aPid->Parse(PromiseFlatCString(*u.mCStringValue).get());
+ case nsIDataType::VTYPE_UTF8STRING:
+ return aPid->Parse(PromiseFlatUTF8String(*u.mUTF8StringValue).get());
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ pString = u.mAStringValue;
+ break;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ tempString.Assign(u.wstr.mWStringValue);
+ pString = &tempString;
+ break;
+ default:
+ NS_ERROR("bad type in call to String2ID");
+ return false;
+ }
+
+ char* pChars = ToNewCString(*pString);
+ if (!pChars) {
+ return false;
+ }
+ bool result = aPid->Parse(pChars);
+ free(pChars);
+ return result;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToID(nsID* aResult) const
+{
+ nsID id;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_ID:
+ *aResult = u.mIDValue;
+ return NS_OK;
+ case nsIDataType::VTYPE_INTERFACE:
+ *aResult = NS_GET_IID(nsISupports);
+ return NS_OK;
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ *aResult = u.iface.mInterfaceID;
+ return NS_OK;
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ if (!String2ID(&id)) {
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+ *aResult = id;
+ return NS_OK;
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+/***************************************************************************/
+
+nsresult
+nsDiscriminatedUnion::ToString(nsACString& aOutString) const
+{
+ char* ptr;
+
+ switch (mType) {
+ // all the stuff we don't handle...
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_WCHAR:
+ NS_ERROR("ToString being called for a string type - screwy logic!");
+ MOZ_FALLTHROUGH;
+
+ // XXX We might want stringified versions of these... ???
+
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_EMPTY:
+ aOutString.SetIsVoid(true);
+ return NS_OK;
+
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+
+ // nsID has its own text formatter.
+
+ case nsIDataType::VTYPE_ID:
+ ptr = u.mIDValue.ToString();
+ if (!ptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ aOutString.Assign(ptr);
+ free(ptr);
+ return NS_OK;
+
+ // Can't use PR_smprintf for floats, since it's locale-dependent
+#define CASE__APPENDFLOAT_NUMBER(type_, member_) \
+ case nsIDataType::type_ : \
+ { \
+ nsAutoCString str; \
+ str.AppendFloat(u.member_); \
+ aOutString.Assign(str); \
+ return NS_OK; \
+ }
+
+ CASE__APPENDFLOAT_NUMBER(VTYPE_FLOAT, mFloatValue)
+ CASE__APPENDFLOAT_NUMBER(VTYPE_DOUBLE, mDoubleValue)
+
+#undef CASE__APPENDFLOAT_NUMBER
+
+ // the rest can be PR_smprintf'd and use common code.
+
+#define CASE__SMPRINTF_NUMBER(type_, format_, cast_, member_) \
+ case nsIDataType::type_: \
+ ptr = PR_smprintf( format_ , (cast_) u.member_); \
+ break;
+
+ CASE__SMPRINTF_NUMBER(VTYPE_INT8, "%d", int, mInt8Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_INT16, "%d", int, mInt16Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_INT32, "%d", int, mInt32Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_INT64, "%lld", int64_t, mInt64Value)
+
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT8, "%u", unsigned, mUint8Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT16, "%u", unsigned, mUint16Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT32, "%u", unsigned, mUint32Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT64, "%llu", int64_t, mUint64Value)
+
+ // XXX Would we rather print "true" / "false" ?
+ CASE__SMPRINTF_NUMBER(VTYPE_BOOL, "%d", int, mBoolValue)
+
+ CASE__SMPRINTF_NUMBER(VTYPE_CHAR, "%c", char, mCharValue)
+
+#undef CASE__SMPRINTF_NUMBER
+ }
+
+ if (!ptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ aOutString.Assign(ptr);
+ PR_smprintf_free(ptr);
+ return NS_OK;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToAString(nsAString& aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ aResult.Assign(*u.mAStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ CopyASCIItoUTF16(*u.mCStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ CopyUTF8toUTF16(*u.mUTF8StringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ CopyASCIItoUTF16(u.str.mStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ aResult.Assign(u.wstr.mWStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ CopyASCIItoUTF16(nsDependentCString(u.str.mStringValue,
+ u.str.mStringLength),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ aResult.Assign(u.wstr.mWStringValue, u.wstr.mWStringLength);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR:
+ aResult.Assign(u.mWCharValue);
+ return NS_OK;
+ default: {
+ nsAutoCString tempCString;
+ nsresult rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ CopyASCIItoUTF16(tempCString, aResult);
+ return NS_OK;
+ }
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToACString(nsACString& aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ LossyCopyUTF16toASCII(*u.mAStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ aResult.Assign(*u.mCStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ // XXX This is an extra copy that should be avoided
+ // once Jag lands support for UTF8String and associated
+ // conversion methods.
+ LossyCopyUTF16toASCII(NS_ConvertUTF8toUTF16(*u.mUTF8StringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ aResult.Assign(*u.str.mStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ LossyCopyUTF16toASCII(nsDependentString(u.wstr.mWStringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ aResult.Assign(u.str.mStringValue, u.str.mStringLength);
+ return NS_OK;
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ LossyCopyUTF16toASCII(nsDependentString(u.wstr.mWStringValue,
+ u.wstr.mWStringLength),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR: {
+ const char16_t* str = &u.mWCharValue;
+ LossyCopyUTF16toASCII(Substring(str, 1), aResult);
+ return NS_OK;
+ }
+ default:
+ return ToString(aResult);
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToAUTF8String(nsAUTF8String& aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ CopyUTF16toUTF8(*u.mAStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ // XXX Extra copy, can be removed if we're sure CSTRING can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(*u.mCStringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ aResult.Assign(*u.mUTF8StringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ // XXX Extra copy, can be removed if we're sure CHAR_STR can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(u.str.mStringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ CopyUTF16toUTF8(u.wstr.mWStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ // XXX Extra copy, can be removed if we're sure CHAR_STR can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(
+ nsDependentCString(u.str.mStringValue,
+ u.str.mStringLength)), aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ CopyUTF16toUTF8(nsDependentString(u.wstr.mWStringValue,
+ u.wstr.mWStringLength),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR: {
+ const char16_t* str = &u.mWCharValue;
+ CopyUTF16toUTF8(Substring(str, 1), aResult);
+ return NS_OK;
+ }
+ default: {
+ nsAutoCString tempCString;
+ nsresult rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // XXX Extra copy, can be removed if we're sure tempCString can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(tempCString), aResult);
+ return NS_OK;
+ }
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToString(char** aResult) const
+{
+ uint32_t ignored;
+ return ConvertToStringWithSize(&ignored, aResult);
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToWString(char16_t** aResult) const
+{
+ uint32_t ignored;
+ return ConvertToWStringWithSize(&ignored, aResult);
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToStringWithSize(uint32_t* aSize, char** aStr) const
+{
+ nsAutoString tempString;
+ nsAutoCString tempCString;
+ nsresult rv;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ *aSize = u.mAStringValue->Length();
+ *aStr = ToNewCString(*u.mAStringValue);
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ *aSize = u.mCStringValue->Length();
+ *aStr = ToNewCString(*u.mCStringValue);
+ break;
+ case nsIDataType::VTYPE_UTF8STRING: {
+ // XXX This is doing 1 extra copy. Need to fix this
+ // when Jag lands UTF8String
+ // we want:
+ // *aSize = *mUTF8StringValue->Length();
+ // *aStr = ToNewCString(*mUTF8StringValue);
+ // But this will have to do for now.
+ const NS_ConvertUTF8toUTF16 tempString16(*u.mUTF8StringValue);
+ *aSize = tempString16.Length();
+ *aStr = ToNewCString(tempString16);
+ break;
+ }
+ case nsIDataType::VTYPE_CHAR_STR: {
+ nsDependentCString cString(u.str.mStringValue);
+ *aSize = cString.Length();
+ *aStr = ToNewCString(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR_STR: {
+ nsDependentString string(u.wstr.mWStringValue);
+ *aSize = string.Length();
+ *aStr = ToNewCString(string);
+ break;
+ }
+ case nsIDataType::VTYPE_STRING_SIZE_IS: {
+ nsDependentCString cString(u.str.mStringValue,
+ u.str.mStringLength);
+ *aSize = cString.Length();
+ *aStr = ToNewCString(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
+ nsDependentString string(u.wstr.mWStringValue,
+ u.wstr.mWStringLength);
+ *aSize = string.Length();
+ *aStr = ToNewCString(string);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR:
+ tempString.Assign(u.mWCharValue);
+ *aSize = tempString.Length();
+ *aStr = ToNewCString(tempString);
+ break;
+ default:
+ rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aSize = tempCString.Length();
+ *aStr = ToNewCString(tempCString);
+ break;
+ }
+
+ return *aStr ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+nsresult
+nsDiscriminatedUnion::ConvertToWStringWithSize(uint32_t* aSize, char16_t** aStr) const
+{
+ nsAutoString tempString;
+ nsAutoCString tempCString;
+ nsresult rv;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ *aSize = u.mAStringValue->Length();
+ *aStr = ToNewUnicode(*u.mAStringValue);
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ *aSize = u.mCStringValue->Length();
+ *aStr = ToNewUnicode(*u.mCStringValue);
+ break;
+ case nsIDataType::VTYPE_UTF8STRING: {
+ *aStr = UTF8ToNewUnicode(*u.mUTF8StringValue, aSize);
+ break;
+ }
+ case nsIDataType::VTYPE_CHAR_STR: {
+ nsDependentCString cString(u.str.mStringValue);
+ *aSize = cString.Length();
+ *aStr = ToNewUnicode(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR_STR: {
+ nsDependentString string(u.wstr.mWStringValue);
+ *aSize = string.Length();
+ *aStr = ToNewUnicode(string);
+ break;
+ }
+ case nsIDataType::VTYPE_STRING_SIZE_IS: {
+ nsDependentCString cString(u.str.mStringValue,
+ u.str.mStringLength);
+ *aSize = cString.Length();
+ *aStr = ToNewUnicode(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
+ nsDependentString string(u.wstr.mWStringValue,
+ u.wstr.mWStringLength);
+ *aSize = string.Length();
+ *aStr = ToNewUnicode(string);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR:
+ tempString.Assign(u.mWCharValue);
+ *aSize = tempString.Length();
+ *aStr = ToNewUnicode(tempString);
+ break;
+ default:
+ rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aSize = tempCString.Length();
+ *aStr = ToNewUnicode(tempCString);
+ break;
+ }
+
+ return *aStr ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToISupports(nsISupports** aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ if (u.iface.mInterfaceValue) {
+ return u.iface.mInterfaceValue->
+ QueryInterface(NS_GET_IID(nsISupports), (void**)aResult);
+ } else {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToInterface(nsIID** aIID,
+ void** aInterface) const
+{
+ const nsIID* piid;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ piid = &NS_GET_IID(nsISupports);
+ break;
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ piid = &u.iface.mInterfaceID;
+ break;
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+ *aIID = (nsIID*)nsMemory::Clone(piid, sizeof(nsIID));
+ if (!*aIID) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (u.iface.mInterfaceValue) {
+ return u.iface.mInterfaceValue->QueryInterface(*piid, aInterface);
+ }
+
+ *aInterface = nullptr;
+ return NS_OK;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToArray(uint16_t* aType, nsIID* aIID,
+ uint32_t* aCount, void** aPtr) const
+{
+ // XXX perhaps we'd like to add support for converting each of the various
+ // types into an array containing one element of that type. We can leverage
+ // CloneArray to do this if we want to support this.
+
+ if (mType == nsIDataType::VTYPE_ARRAY) {
+ return CloneArray(u.array.mArrayType, &u.array.mArrayInterfaceID,
+ u.array.mArrayCount, u.array.mArrayValue,
+ aType, aIID, aCount, aPtr);
+ }
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+}
+
+/***************************************************************************/
+// static setter functions...
+
+#define DATA_SETTER_PROLOGUE \
+ Cleanup()
+
+#define DATA_SETTER_EPILOGUE(type_) \
+ mType = nsIDataType::type_;
+
+#define DATA_SETTER(type_, member_, value_) \
+ DATA_SETTER_PROLOGUE; \
+ u.member_ = value_; \
+ DATA_SETTER_EPILOGUE(type_)
+
+#define DATA_SETTER_WITH_CAST(type_, member_, cast_, value_) \
+ DATA_SETTER_PROLOGUE; \
+ u.member_ = cast_ value_; \
+ DATA_SETTER_EPILOGUE(type_)
+
+
+/********************************************/
+
+#define CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(type_) \
+ { \
+
+#define CASE__SET_FROM_VARIANT_VTYPE__GETTER(member_, name_) \
+ rv = aValue->GetAs##name_ (&(u.member_ ));
+
+#define CASE__SET_FROM_VARIANT_VTYPE__GETTER_CAST(cast_, member_, name_) \
+ rv = aValue->GetAs##name_ ( cast_ &(u.member_ ));
+
+#define CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(type_) \
+ if (NS_SUCCEEDED(rv)) { \
+ mType = nsIDataType::type_ ; \
+ } \
+ break; \
+ }
+
+#define CASE__SET_FROM_VARIANT_TYPE(type_, member_, name_) \
+ case nsIDataType::type_: \
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(type_) \
+ CASE__SET_FROM_VARIANT_VTYPE__GETTER(member_, name_) \
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(type_)
+
+#define CASE__SET_FROM_VARIANT_VTYPE_CAST(type_, cast_, member_, name_) \
+ case nsIDataType::type_ : \
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(type_) \
+ CASE__SET_FROM_VARIANT_VTYPE__GETTER_CAST(cast_, member_, name_) \
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(type_)
+
+
+nsresult
+nsDiscriminatedUnion::SetFromVariant(nsIVariant* aValue)
+{
+ uint16_t type;
+ nsresult rv;
+
+ Cleanup();
+
+ rv = aValue->GetDataType(&type);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ switch (type) {
+ CASE__SET_FROM_VARIANT_VTYPE_CAST(VTYPE_INT8, (uint8_t*), mInt8Value,
+ Int8)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_INT16, mInt16Value, Int16)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_INT32, mInt32Value, Int32)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_UINT8, mUint8Value, Uint8)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_UINT16, mUint16Value, Uint16)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_UINT32, mUint32Value, Uint32)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_FLOAT, mFloatValue, Float)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_DOUBLE, mDoubleValue, Double)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_BOOL , mBoolValue, Bool)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_CHAR, mCharValue, Char)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_WCHAR, mWCharValue, WChar)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_ID, mIDValue, ID)
+
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_ASTRING);
+ u.mAStringValue = new nsString();
+ if (!u.mAStringValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = aValue->GetAsAString(*u.mAStringValue);
+ if (NS_FAILED(rv)) {
+ delete u.mAStringValue;
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_ASTRING)
+
+ case nsIDataType::VTYPE_CSTRING:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_CSTRING);
+ u.mCStringValue = new nsCString();
+ if (!u.mCStringValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = aValue->GetAsACString(*u.mCStringValue);
+ if (NS_FAILED(rv)) {
+ delete u.mCStringValue;
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_CSTRING)
+
+ case nsIDataType::VTYPE_UTF8STRING:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_UTF8STRING);
+ u.mUTF8StringValue = new nsUTF8String();
+ if (!u.mUTF8StringValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = aValue->GetAsAUTF8String(*u.mUTF8StringValue);
+ if (NS_FAILED(rv)) {
+ delete u.mUTF8StringValue;
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_UTF8STRING)
+
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_STRING_SIZE_IS);
+ rv = aValue->GetAsStringWithSize(&u.str.mStringLength,
+ &u.str.mStringValue);
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_STRING_SIZE_IS)
+
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_INTERFACE_IS);
+ // XXX This iid handling is ugly!
+ nsIID* iid;
+ rv = aValue->GetAsInterface(&iid, (void**)&u.iface.mInterfaceValue);
+ if (NS_SUCCEEDED(rv)) {
+ u.iface.mInterfaceID = *iid;
+ free((char*)iid);
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_INTERFACE_IS)
+
+ case nsIDataType::VTYPE_ARRAY:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_ARRAY);
+ rv = aValue->GetAsArray(&u.array.mArrayType,
+ &u.array.mArrayInterfaceID,
+ &u.array.mArrayCount,
+ &u.array.mArrayValue);
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_ARRAY)
+
+ case nsIDataType::VTYPE_VOID:
+ SetToVoid();
+ rv = NS_OK;
+ break;
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ SetToEmptyArray();
+ rv = NS_OK;
+ break;
+ case nsIDataType::VTYPE_EMPTY:
+ SetToEmpty();
+ rv = NS_OK;
+ break;
+ default:
+ NS_ERROR("bad type in variant!");
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+void
+nsDiscriminatedUnion::SetFromInt8(uint8_t aValue)
+{
+ DATA_SETTER_WITH_CAST(VTYPE_INT8, mInt8Value, (uint8_t), aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInt16(int16_t aValue)
+{
+ DATA_SETTER(VTYPE_INT16, mInt16Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInt32(int32_t aValue)
+{
+ DATA_SETTER(VTYPE_INT32, mInt32Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInt64(int64_t aValue)
+{
+ DATA_SETTER(VTYPE_INT64, mInt64Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint8(uint8_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT8, mUint8Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint16(uint16_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT16, mUint16Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint32(uint32_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT32, mUint32Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint64(uint64_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT64, mUint64Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromFloat(float aValue)
+{
+ DATA_SETTER(VTYPE_FLOAT, mFloatValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromDouble(double aValue)
+{
+ DATA_SETTER(VTYPE_DOUBLE, mDoubleValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromBool(bool aValue)
+{
+ DATA_SETTER(VTYPE_BOOL, mBoolValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromChar(char aValue)
+{
+ DATA_SETTER(VTYPE_CHAR, mCharValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromWChar(char16_t aValue)
+{
+ DATA_SETTER(VTYPE_WCHAR, mWCharValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromID(const nsID& aValue)
+{
+ DATA_SETTER(VTYPE_ID, mIDValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromAString(const nsAString& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mAStringValue = new nsString(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_ASTRING);
+}
+
+void
+nsDiscriminatedUnion::SetFromDOMString(const nsAString& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mAStringValue = new nsString(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_DOMSTRING);
+}
+
+void
+nsDiscriminatedUnion::SetFromACString(const nsACString& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mCStringValue = new nsCString(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_CSTRING);
+}
+
+void
+nsDiscriminatedUnion::SetFromAUTF8String(const nsAUTF8String& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mUTF8StringValue = new nsUTF8String(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_UTF8STRING);
+}
+
+nsresult
+nsDiscriminatedUnion::SetFromString(const char* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return SetFromStringWithSize(strlen(aValue), aValue);
+}
+nsresult
+nsDiscriminatedUnion::SetFromWString(const char16_t* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return SetFromWStringWithSize(NS_strlen(aValue), aValue);
+}
+void
+nsDiscriminatedUnion::SetFromISupports(nsISupports* aValue)
+{
+ return SetFromInterface(NS_GET_IID(nsISupports), aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInterface(const nsIID& aIID, nsISupports* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ NS_IF_ADDREF(aValue);
+ u.iface.mInterfaceValue = aValue;
+ u.iface.mInterfaceID = aIID;
+ DATA_SETTER_EPILOGUE(VTYPE_INTERFACE_IS);
+}
+nsresult
+nsDiscriminatedUnion::SetFromArray(uint16_t aType, const nsIID* aIID,
+ uint32_t aCount, void* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue || !aCount) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv = CloneArray(aType, aIID, aCount, aValue,
+ &u.array.mArrayType,
+ &u.array.mArrayInterfaceID,
+ &u.array.mArrayCount,
+ &u.array.mArrayValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ DATA_SETTER_EPILOGUE(VTYPE_ARRAY);
+ return NS_OK;
+}
+nsresult
+nsDiscriminatedUnion::SetFromStringWithSize(uint32_t aSize,
+ const char* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if (!(u.str.mStringValue =
+ (char*)nsMemory::Clone(aValue, (aSize + 1) * sizeof(char)))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ u.str.mStringLength = aSize;
+ DATA_SETTER_EPILOGUE(VTYPE_STRING_SIZE_IS);
+ return NS_OK;
+}
+nsresult
+nsDiscriminatedUnion::SetFromWStringWithSize(uint32_t aSize,
+ const char16_t* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if (!(u.wstr.mWStringValue =
+ (char16_t*)nsMemory::Clone(aValue, (aSize + 1) * sizeof(char16_t)))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ u.wstr.mWStringLength = aSize;
+ DATA_SETTER_EPILOGUE(VTYPE_WSTRING_SIZE_IS);
+ return NS_OK;
+}
+void
+nsDiscriminatedUnion::AllocateWStringWithSize(uint32_t aSize)
+{
+ DATA_SETTER_PROLOGUE;
+ u.wstr.mWStringValue = (char16_t*)moz_xmalloc((aSize + 1) * sizeof(char16_t));
+ u.wstr.mWStringValue[aSize] = '\0';
+ u.wstr.mWStringLength = aSize;
+ DATA_SETTER_EPILOGUE(VTYPE_WSTRING_SIZE_IS);
+}
+void
+nsDiscriminatedUnion::SetToVoid()
+{
+ DATA_SETTER_PROLOGUE;
+ DATA_SETTER_EPILOGUE(VTYPE_VOID);
+}
+void
+nsDiscriminatedUnion::SetToEmpty()
+{
+ DATA_SETTER_PROLOGUE;
+ DATA_SETTER_EPILOGUE(VTYPE_EMPTY);
+}
+void
+nsDiscriminatedUnion::SetToEmptyArray()
+{
+ DATA_SETTER_PROLOGUE;
+ DATA_SETTER_EPILOGUE(VTYPE_EMPTY_ARRAY);
+}
+
+/***************************************************************************/
+
+void
+nsDiscriminatedUnion::Cleanup()
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ case nsIDataType::VTYPE_BOOL:
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ID:
+ break;
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ delete u.mAStringValue;
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ delete u.mCStringValue;
+ break;
+ case nsIDataType::VTYPE_UTF8STRING:
+ delete u.mUTF8StringValue;
+ break;
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ free((char*)u.str.mStringValue);
+ break;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ free((char*)u.wstr.mWStringValue);
+ break;
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ NS_IF_RELEASE(u.iface.mInterfaceValue);
+ break;
+ case nsIDataType::VTYPE_ARRAY:
+ FreeArray();
+ break;
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ break;
+ default:
+ NS_ERROR("bad type in variant!");
+ break;
+ }
+
+ mType = nsIDataType::VTYPE_EMPTY;
+}
+
+void
+nsDiscriminatedUnion::Traverse(nsCycleCollectionTraversalCallback& aCb) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mData");
+ aCb.NoteXPCOMChild(u.iface.mInterfaceValue);
+ break;
+ case nsIDataType::VTYPE_ARRAY:
+ switch (u.array.mArrayType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS: {
+ nsISupports** p = (nsISupports**)u.array.mArrayValue;
+ for (uint32_t i = u.array.mArrayCount; i > 0; ++p, --i) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mData[i]");
+ aCb.NoteXPCOMChild(*p);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/***************************************************************************/
+/***************************************************************************/
+// members...
+
+nsVariantBase::nsVariantBase()
+ : mWritable(true)
+{
+#ifdef DEBUG
+ {
+ // Assert that the nsIDataType consts match the values #defined in
+ // xpt_struct.h. Bad things happen somewhere if they don't.
+ struct THE_TYPES
+ {
+ uint16_t a;
+ uint16_t b;
+ };
+ static const THE_TYPES array[] = {
+ {nsIDataType::VTYPE_INT8 , TD_INT8 },
+ {nsIDataType::VTYPE_INT16 , TD_INT16 },
+ {nsIDataType::VTYPE_INT32 , TD_INT32 },
+ {nsIDataType::VTYPE_INT64 , TD_INT64 },
+ {nsIDataType::VTYPE_UINT8 , TD_UINT8 },
+ {nsIDataType::VTYPE_UINT16 , TD_UINT16 },
+ {nsIDataType::VTYPE_UINT32 , TD_UINT32 },
+ {nsIDataType::VTYPE_UINT64 , TD_UINT64 },
+ {nsIDataType::VTYPE_FLOAT , TD_FLOAT },
+ {nsIDataType::VTYPE_DOUBLE , TD_DOUBLE },
+ {nsIDataType::VTYPE_BOOL , TD_BOOL },
+ {nsIDataType::VTYPE_CHAR , TD_CHAR },
+ {nsIDataType::VTYPE_WCHAR , TD_WCHAR },
+ {nsIDataType::VTYPE_VOID , TD_VOID },
+ {nsIDataType::VTYPE_ID , TD_PNSIID },
+ {nsIDataType::VTYPE_DOMSTRING , TD_DOMSTRING },
+ {nsIDataType::VTYPE_CHAR_STR , TD_PSTRING },
+ {nsIDataType::VTYPE_WCHAR_STR , TD_PWSTRING },
+ {nsIDataType::VTYPE_INTERFACE , TD_INTERFACE_TYPE },
+ {nsIDataType::VTYPE_INTERFACE_IS , TD_INTERFACE_IS_TYPE},
+ {nsIDataType::VTYPE_ARRAY , TD_ARRAY },
+ {nsIDataType::VTYPE_STRING_SIZE_IS , TD_PSTRING_SIZE_IS },
+ {nsIDataType::VTYPE_WSTRING_SIZE_IS , TD_PWSTRING_SIZE_IS },
+ {nsIDataType::VTYPE_UTF8STRING , TD_UTF8STRING },
+ {nsIDataType::VTYPE_CSTRING , TD_CSTRING },
+ {nsIDataType::VTYPE_ASTRING , TD_ASTRING }
+ };
+ static const int length = sizeof(array) / sizeof(array[0]);
+ static bool inited = false;
+ if (!inited) {
+ for (int i = 0; i < length; ++i) {
+ NS_ASSERTION(array[i].a == array[i].b, "bad const declaration");
+ }
+ inited = true;
+ }
+ }
+#endif
+}
+
+// For all the data getters we just forward to the static (and sharable)
+// 'ConvertTo' functions.
+
+NS_IMETHODIMP
+nsVariantBase::GetDataType(uint16_t* aDataType)
+{
+ *aDataType = mData.GetType();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt8(uint8_t* aResult)
+{
+ return mData.ConvertToInt8(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt16(int16_t* aResult)
+{
+ return mData.ConvertToInt16(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt32(int32_t* aResult)
+{
+ return mData.ConvertToInt32(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt64(int64_t* aResult)
+{
+ return mData.ConvertToInt64(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint8(uint8_t* aResult)
+{
+ return mData.ConvertToUint8(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint16(uint16_t* aResult)
+{
+ return mData.ConvertToUint16(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint32(uint32_t* aResult)
+{
+ return mData.ConvertToUint32(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint64(uint64_t* aResult)
+{
+ return mData.ConvertToUint64(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsFloat(float* aResult)
+{
+ return mData.ConvertToFloat(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsDouble(double* aResult)
+{
+ return mData.ConvertToDouble(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsBool(bool* aResult)
+{
+ return mData.ConvertToBool(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsChar(char* aResult)
+{
+ return mData.ConvertToChar(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsWChar(char16_t* aResult)
+{
+ return mData.ConvertToWChar(aResult);
+}
+
+NS_IMETHODIMP_(nsresult)
+nsVariantBase::GetAsID(nsID* aResult)
+{
+ return mData.ConvertToID(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsAString(nsAString& aResult)
+{
+ return mData.ConvertToAString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsDOMString(nsAString& aResult)
+{
+ // A DOMString maps to an AString internally, so we can re-use
+ // ConvertToAString here.
+ return mData.ConvertToAString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsACString(nsACString& aResult)
+{
+ return mData.ConvertToACString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsAUTF8String(nsAUTF8String& aResult)
+{
+ return mData.ConvertToAUTF8String(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsString(char** aResult)
+{
+ return mData.ConvertToString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsWString(char16_t** aResult)
+{
+ return mData.ConvertToWString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsISupports(nsISupports** aResult)
+{
+ return mData.ConvertToISupports(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsJSVal(JS::MutableHandleValue)
+{
+ // Can only get the jsval from an XPCVariant.
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInterface(nsIID** aIID, void** aInterface)
+{
+ return mData.ConvertToInterface(aIID, aInterface);
+}
+
+NS_IMETHODIMP_(nsresult)
+nsVariantBase::GetAsArray(uint16_t* aType, nsIID* aIID,
+ uint32_t* aCount, void** aPtr)
+{
+ return mData.ConvertToArray(aType, aIID, aCount, aPtr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsStringWithSize(uint32_t* aSize, char** aStr)
+{
+ return mData.ConvertToStringWithSize(aSize, aStr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsWStringWithSize(uint32_t* aSize, char16_t** aStr)
+{
+ return mData.ConvertToWStringWithSize(aSize, aStr);
+}
+
+/***************************************************************************/
+
+NS_IMETHODIMP
+nsVariantBase::GetWritable(bool* aWritable)
+{
+ *aWritable = mWritable;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsVariantBase::SetWritable(bool aWritable)
+{
+ if (!mWritable && aWritable) {
+ return NS_ERROR_FAILURE;
+ }
+ mWritable = aWritable;
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+// For all the data setters we just forward to the static (and sharable)
+// 'SetFrom' functions.
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt8(uint8_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt8(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt16(int16_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt16(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt32(int32_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt32(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt64(int64_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt64(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint8(uint8_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint8(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint16(uint16_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint16(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint32(uint32_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint32(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint64(uint64_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint64(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsFloat(float aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromFloat(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsDouble(double aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromDouble(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsBool(bool aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromBool(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsChar(char aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromChar(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsWChar(char16_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromWChar(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsID(const nsID& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromID(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsAString(const nsAString& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromAString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsDOMString(const nsAString& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+
+ mData.SetFromDOMString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsACString(const nsACString& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromACString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsAUTF8String(const nsAUTF8String& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromAUTF8String(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsString(const char* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromString(aValue);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsWString(const char16_t* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromWString(aValue);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsISupports(nsISupports* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromISupports(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInterface(const nsIID& aIID, void* aInterface)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInterface(aIID, (nsISupports*)aInterface);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsArray(uint16_t aType, const nsIID* aIID,
+ uint32_t aCount, void* aPtr)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromArray(aType, aIID, aCount, aPtr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsStringWithSize(uint32_t aSize, const char* aStr)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromStringWithSize(aSize, aStr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsWStringWithSize(uint32_t aSize, const char16_t* aStr)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromWStringWithSize(aSize, aStr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsVoid()
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetToVoid();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsEmpty()
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetToEmpty();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsEmptyArray()
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetToEmptyArray();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetFromVariant(nsIVariant* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromVariant(aValue);
+}
+
+/* nsVariant implementation */
+
+NS_IMPL_ISUPPORTS(nsVariant, nsIVariant, nsIWritableVariant)
+
+
+/* nsVariantCC implementation */
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsVariantCC)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsIVariant)
+ NS_INTERFACE_MAP_ENTRY(nsIWritableVariant)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsVariantCC)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsVariantCC)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsVariantCC)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsVariantCC)
+ tmp->mData.Traverse(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsVariantCC)
+ tmp->mData.Cleanup();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
diff --git a/xpcom/ds/nsVariant.h b/xpcom/ds/nsVariant.h
new file mode 100644
index 000000000..5be2d18ee
--- /dev/null
+++ b/xpcom/ds/nsVariant.h
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsVariant_h
+#define nsVariant_h
+
+#include "nsIVariant.h"
+#include "nsStringFwd.h"
+#include "mozilla/Attributes.h"
+#include "nsCycleCollectionParticipant.h"
+
+/**
+ * Map the nsAUTF8String, nsUTF8String classes to the nsACString and
+ * nsCString classes respectively for now. These defines need to be removed
+ * once Jag lands his nsUTF8String implementation.
+ */
+#define nsAUTF8String nsACString
+#define nsUTF8String nsCString
+#define PromiseFlatUTF8String PromiseFlatCString
+
+/**
+ * nsDiscriminatedUnion is a class that nsIVariant implementors can use
+ * to hold the underlying data.
+ */
+
+class nsDiscriminatedUnion
+{
+public:
+
+ nsDiscriminatedUnion() : mType(nsIDataType::VTYPE_EMPTY) {}
+ nsDiscriminatedUnion(const nsDiscriminatedUnion&) = delete;
+ nsDiscriminatedUnion(nsDiscriminatedUnion&&) = delete;
+
+ ~nsDiscriminatedUnion() { Cleanup(); }
+
+ nsDiscriminatedUnion& operator=(const nsDiscriminatedUnion&) = delete;
+ nsDiscriminatedUnion& operator=(nsDiscriminatedUnion&&) = delete;
+
+ void Cleanup();
+
+ uint16_t GetType() const { return mType; }
+
+ MOZ_MUST_USE nsresult ConvertToInt8(uint8_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToInt16(int16_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToInt32(int32_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToInt64(int64_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToUint8(uint8_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToUint16(uint16_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToUint32(uint32_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToUint64(uint64_t* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToFloat(float* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToDouble(double* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToBool(bool* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToChar(char* aResult) const;
+ MOZ_MUST_USE nsresult ConvertToWChar(char16_t* aResult) const;
+
+ MOZ_MUST_USE nsresult ConvertToID(nsID* aResult) const;
+
+ MOZ_MUST_USE nsresult ConvertToAString(nsAString& aResult) const;
+ MOZ_MUST_USE nsresult ConvertToAUTF8String(nsAUTF8String& aResult) const;
+ MOZ_MUST_USE nsresult ConvertToACString(nsACString& aResult) const;
+ MOZ_MUST_USE nsresult ConvertToString(char** aResult) const;
+ MOZ_MUST_USE nsresult ConvertToWString(char16_t** aResult) const;
+ MOZ_MUST_USE nsresult ConvertToStringWithSize(uint32_t* aSize, char** aStr) const;
+ MOZ_MUST_USE nsresult ConvertToWStringWithSize(uint32_t* aSize, char16_t** aStr) const;
+
+ MOZ_MUST_USE nsresult ConvertToISupports(nsISupports** aResult) const;
+ MOZ_MUST_USE nsresult ConvertToInterface(nsIID** aIID, void** aInterface) const;
+ MOZ_MUST_USE nsresult ConvertToArray(uint16_t* aType, nsIID* aIID,
+ uint32_t* aCount, void** aPtr) const;
+
+ MOZ_MUST_USE nsresult SetFromVariant(nsIVariant* aValue);
+
+ void SetFromInt8(uint8_t aValue);
+ void SetFromInt16(int16_t aValue);
+ void SetFromInt32(int32_t aValue);
+ void SetFromInt64(int64_t aValue);
+ void SetFromUint8(uint8_t aValue);
+ void SetFromUint16(uint16_t aValue);
+ void SetFromUint32(uint32_t aValue);
+ void SetFromUint64(uint64_t aValue);
+ void SetFromFloat(float aValue);
+ void SetFromDouble(double aValue);
+ void SetFromBool(bool aValue);
+ void SetFromChar(char aValue);
+ void SetFromWChar(char16_t aValue);
+ void SetFromID(const nsID& aValue);
+ void SetFromAString(const nsAString& aValue);
+ void SetFromDOMString(const nsAString& aValue);
+ void SetFromAUTF8String(const nsAUTF8String& aValue);
+ void SetFromACString(const nsACString& aValue);
+ MOZ_MUST_USE nsresult SetFromString(const char* aValue);
+ MOZ_MUST_USE nsresult SetFromWString(const char16_t* aValue);
+ void SetFromISupports(nsISupports* aValue);
+ void SetFromInterface(const nsIID& aIID, nsISupports* aValue);
+ MOZ_MUST_USE nsresult SetFromArray(uint16_t aType, const nsIID* aIID,
+ uint32_t aCount, void* aValue);
+ MOZ_MUST_USE nsresult SetFromStringWithSize(uint32_t aSize,
+ const char* aValue);
+ MOZ_MUST_USE nsresult SetFromWStringWithSize(uint32_t aSize,
+ const char16_t* aValue);
+
+ // Like SetFromWStringWithSize, but leaves the string uninitialized. It does
+ // does write the null-terminator.
+ void AllocateWStringWithSize(uint32_t aSize);
+
+ void SetToVoid();
+ void SetToEmpty();
+ void SetToEmptyArray();
+
+ void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
+
+private:
+ MOZ_MUST_USE nsresult
+ ToManageableNumber(nsDiscriminatedUnion* aOutData) const;
+ void FreeArray();
+ MOZ_MUST_USE bool String2ID(nsID* aPid) const;
+ MOZ_MUST_USE nsresult ToString(nsACString& aOutString) const;
+
+public:
+ union
+ {
+ int8_t mInt8Value;
+ int16_t mInt16Value;
+ int32_t mInt32Value;
+ int64_t mInt64Value;
+ uint8_t mUint8Value;
+ uint16_t mUint16Value;
+ uint32_t mUint32Value;
+ uint64_t mUint64Value;
+ float mFloatValue;
+ double mDoubleValue;
+ bool mBoolValue;
+ char mCharValue;
+ char16_t mWCharValue;
+ nsIID mIDValue;
+ nsAString* mAStringValue;
+ nsAUTF8String* mUTF8StringValue;
+ nsACString* mCStringValue;
+ struct
+ {
+ // This is an owning reference that cannot be an nsCOMPtr because
+ // nsDiscriminatedUnion needs to be POD. AddRef/Release are manually
+ // called on this.
+ nsISupports* MOZ_OWNING_REF mInterfaceValue;
+ nsIID mInterfaceID;
+ } iface;
+ struct
+ {
+ nsIID mArrayInterfaceID;
+ void* mArrayValue;
+ uint32_t mArrayCount;
+ uint16_t mArrayType;
+ } array;
+ struct
+ {
+ char* mStringValue;
+ uint32_t mStringLength;
+ } str;
+ struct
+ {
+ char16_t* mWStringValue;
+ uint32_t mWStringLength;
+ } wstr;
+ } u;
+ uint16_t mType;
+};
+
+/**
+ * nsVariant implements the generic variant support. The xpcom module registers
+ * a factory (see NS_VARIANT_CONTRACTID in nsIVariant.idl) that will create
+ * these objects. They are created 'empty' and 'writable'.
+ *
+ * nsIVariant users won't usually need to see this class.
+ */
+class nsVariantBase : public nsIWritableVariant
+{
+public:
+ NS_DECL_NSIVARIANT
+ NS_DECL_NSIWRITABLEVARIANT
+
+ nsVariantBase();
+
+protected:
+ ~nsVariantBase() {};
+
+ nsDiscriminatedUnion mData;
+ bool mWritable;
+};
+
+class nsVariant final : public nsVariantBase
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsVariant() {};
+
+private:
+ ~nsVariant() {};
+};
+
+class nsVariantCC final : public nsVariantBase
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(nsVariantCC)
+
+ nsVariantCC() {};
+
+private:
+ ~nsVariantCC() {};
+};
+
+/**
+ * Users of nsIVariant should be using the contractID and not this CID.
+ * - see NS_VARIANT_CONTRACTID in nsIVariant.idl.
+ */
+
+#define NS_VARIANT_CID \
+{ /* 0D6EA1D0-879C-11d5-90EF-0010A4E73D9A */ \
+ 0xd6ea1d0, \
+ 0x879c, \
+ 0x11d5, \
+ {0x90, 0xef, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a}}
+
+#endif // nsVariant_h
diff --git a/xpcom/ds/nsWhitespaceTokenizer.h b/xpcom/ds/nsWhitespaceTokenizer.h
new file mode 100644
index 000000000..bfe32f8dc
--- /dev/null
+++ b/xpcom/ds/nsWhitespaceTokenizer.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsWhitespaceTokenizer_h
+#define __nsWhitespaceTokenizer_h
+
+#include "mozilla/RangedPtr.h"
+#include "nsDependentSubstring.h"
+#include "nsCRT.h"
+
+template<typename DependentSubstringType, bool IsWhitespace(char16_t)>
+class nsTWhitespaceTokenizer
+{
+ typedef typename DependentSubstringType::char_type CharType;
+ typedef typename DependentSubstringType::substring_type SubstringType;
+
+public:
+ explicit nsTWhitespaceTokenizer(const SubstringType& aSource)
+ : mIter(aSource.Data(), aSource.Length())
+ , mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
+ aSource.Length())
+ , mWhitespaceBeforeFirstToken(false)
+ , mWhitespaceAfterCurrentToken(false)
+ {
+ while (mIter < mEnd && IsWhitespace(*mIter)) {
+ mWhitespaceBeforeFirstToken = true;
+ ++mIter;
+ }
+ }
+
+ /**
+ * Checks if any more tokens are available.
+ */
+ bool hasMoreTokens() const
+ {
+ return mIter < mEnd;
+ }
+
+ /*
+ * Returns true if there is whitespace prior to the first token.
+ */
+ bool whitespaceBeforeFirstToken() const
+ {
+ return mWhitespaceBeforeFirstToken;
+ }
+
+ /*
+ * Returns true if there is any whitespace after the current token.
+ * This is always true unless we're reading the last token.
+ */
+ bool whitespaceAfterCurrentToken() const
+ {
+ return mWhitespaceAfterCurrentToken;
+ }
+
+ /**
+ * Returns the next token.
+ */
+ const DependentSubstringType nextToken()
+ {
+ const mozilla::RangedPtr<const CharType> tokenStart = mIter;
+ while (mIter < mEnd && !IsWhitespace(*mIter)) {
+ ++mIter;
+ }
+ const mozilla::RangedPtr<const CharType> tokenEnd = mIter;
+ mWhitespaceAfterCurrentToken = false;
+ while (mIter < mEnd && IsWhitespace(*mIter)) {
+ mWhitespaceAfterCurrentToken = true;
+ ++mIter;
+ }
+ return Substring(tokenStart.get(), tokenEnd.get());
+ }
+
+private:
+ mozilla::RangedPtr<const CharType> mIter;
+ const mozilla::RangedPtr<const CharType> mEnd;
+ bool mWhitespaceBeforeFirstToken;
+ bool mWhitespaceAfterCurrentToken;
+};
+
+template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
+class nsWhitespaceTokenizerTemplate
+ : public nsTWhitespaceTokenizer<nsDependentSubstring, IsWhitespace>
+{
+public:
+ explicit nsWhitespaceTokenizerTemplate(const nsSubstring& aSource)
+ : nsTWhitespaceTokenizer<nsDependentSubstring, IsWhitespace>(aSource)
+ {
+ }
+};
+
+typedef nsWhitespaceTokenizerTemplate<> nsWhitespaceTokenizer;
+
+template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
+class nsCWhitespaceTokenizerTemplate
+ : public nsTWhitespaceTokenizer<nsDependentCSubstring, IsWhitespace>
+{
+public:
+ explicit nsCWhitespaceTokenizerTemplate(const nsCSubstring& aSource)
+ : nsTWhitespaceTokenizer<nsDependentCSubstring, IsWhitespace>(aSource)
+ {
+ }
+};
+
+typedef nsCWhitespaceTokenizerTemplate<> nsCWhitespaceTokenizer;
+
+#endif /* __nsWhitespaceTokenizer_h */
diff --git a/xpcom/ds/nsWindowsRegKey.cpp b/xpcom/ds/nsWindowsRegKey.cpp
new file mode 100644
index 000000000..55571d8f9
--- /dev/null
+++ b/xpcom/ds/nsWindowsRegKey.cpp
@@ -0,0 +1,579 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+#include <shlwapi.h>
+#include <stdlib.h>
+#include "nsWindowsRegKey.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+#include "nsAutoPtr.h"
+
+//-----------------------------------------------------------------------------
+
+// According to MSDN, the following limits apply (in characters excluding room
+// for terminating null character):
+#define MAX_KEY_NAME_LEN 255
+#define MAX_VALUE_NAME_LEN 16383
+
+class nsWindowsRegKey final : public nsIWindowsRegKey
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWINDOWSREGKEY
+
+ nsWindowsRegKey()
+ : mKey(nullptr)
+ , mWatchEvent(nullptr)
+ , mWatchRecursive(FALSE)
+ {
+ }
+
+private:
+ ~nsWindowsRegKey()
+ {
+ Close();
+ }
+
+ HKEY mKey;
+ HANDLE mWatchEvent;
+ BOOL mWatchRecursive;
+};
+
+NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetKey(HKEY* aKey)
+{
+ *aKey = mKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::SetKey(HKEY aKey)
+{
+ // We do not close the older aKey!
+ StopWatching();
+
+ mKey = aKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::Close()
+{
+ StopWatching();
+
+ if (mKey) {
+ RegCloseKey(mKey);
+ mKey = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::Open(uint32_t aRootKey, const nsAString& aPath,
+ uint32_t aMode)
+{
+ Close();
+
+ LONG rv = RegOpenKeyExW((HKEY)(intptr_t)aRootKey,
+ PromiseFlatString(aPath).get(), 0, (REGSAM)aMode,
+ &mKey);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::Create(uint32_t aRootKey, const nsAString& aPath,
+ uint32_t aMode)
+{
+ Close();
+
+ DWORD disposition;
+ LONG rv = RegCreateKeyExW((HKEY)(intptr_t)aRootKey,
+ PromiseFlatString(aPath).get(), 0, nullptr,
+ REG_OPTION_NON_VOLATILE, (REGSAM)aMode, nullptr,
+ &mKey, &disposition);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::OpenChild(const nsAString& aPath, uint32_t aMode,
+ nsIWindowsRegKey** aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
+
+ nsresult rv = child->Open((uintptr_t)mKey, aPath, aMode);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ child.swap(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::CreateChild(const nsAString& aPath, uint32_t aMode,
+ nsIWindowsRegKey** aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
+
+ nsresult rv = child->Create((uintptr_t)mKey, aPath, aMode);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ child.swap(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetChildCount(uint32_t* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD numSubKeys;
+ LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys,
+ nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = numSubKeys;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetChildName(uint32_t aIndex, nsAString& aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ FILETIME lastWritten;
+
+ wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
+ DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
+
+ LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
+ nullptr, &lastWritten);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
+ }
+
+ aResult.Assign(nameBuf, nameLen);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::HasChild(const nsAString& aName, bool* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // Check for the existence of a child key by opening the key with minimal
+ // rights. Perhaps there is a more efficient way to do this?
+
+ HKEY key;
+ LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(aName).get(), 0,
+ STANDARD_RIGHTS_READ, &key);
+
+ if ((*aResult = (rv == ERROR_SUCCESS && key))) {
+ RegCloseKey(key);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetValueCount(uint32_t* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD numValues;
+ LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, &numValues, nullptr, nullptr,
+ nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = numValues;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetValueName(uint32_t aIndex, nsAString& aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ wchar_t nameBuf[MAX_VALUE_NAME_LEN];
+ DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
+
+ LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
+ nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
+ }
+
+ aResult.Assign(nameBuf, nameLen);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::HasValue(const nsAString& aName, bool* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ nullptr, nullptr);
+
+ *aResult = (rv == ERROR_SUCCESS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::RemoveChild(const nsAString& aName)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(aName).get());
+
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::RemoveValue(const nsAString& aName)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegDeleteValueW(mKey, PromiseFlatString(aName).get());
+
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetValueType(const nsAString& aName, uint32_t* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
+ (LPDWORD)aResult, nullptr, nullptr);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadStringValue(const nsAString& aName, nsAString& aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD type, size;
+
+ const nsString& flatName = PromiseFlatString(aName);
+
+ LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // This must be a string type in order to fetch the value as a string.
+ // We're being a bit forgiving here by allowing types other than REG_SZ.
+ if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_MULTI_SZ) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // The buffer size must be a multiple of 2.
+ if (size % 2 != 0) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (size == 0) {
+ aResult.Truncate();
+ return NS_OK;
+ }
+
+ // |size| may or may not include the terminating null character.
+ DWORD resultLen = size / 2;
+
+ if (!aResult.SetLength(resultLen, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsAString::iterator begin;
+ aResult.BeginWriting(begin);
+
+ rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE)begin.get(),
+ &size);
+
+ if (!aResult.CharAt(resultLen - 1)) {
+ // The string passed to us had a null terminator in the final position.
+ aResult.Truncate(resultLen - 1);
+ }
+
+ // Expand the environment variables if needed
+ if (type == REG_EXPAND_SZ) {
+ const nsString& flatSource = PromiseFlatString(aResult);
+ resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
+ if (resultLen > 1) {
+ nsAutoString expandedResult;
+ // |resultLen| includes the terminating null character
+ --resultLen;
+ if (!expandedResult.SetLength(resultLen, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsAString::iterator begin;
+ expandedResult.BeginWriting(begin);
+
+ resultLen = ExpandEnvironmentStringsW(flatSource.get(),
+ wwc(begin.get()),
+ resultLen + 1);
+ if (resultLen <= 0) {
+ rv = ERROR_UNKNOWN_FEATURE;
+ aResult.Truncate();
+ } else {
+ rv = ERROR_SUCCESS;
+ aResult = expandedResult;
+ }
+ } else if (resultLen == 1) {
+ // It apparently expands to nothing (just a null terminator).
+ aResult.Truncate();
+ }
+ }
+
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadIntValue(const nsAString& aName, uint32_t* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD size = sizeof(*aResult);
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ (LPBYTE)aResult, &size);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadInt64Value(const nsAString& aName, uint64_t* aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD size = sizeof(*aResult);
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ (LPBYTE)aResult, &size);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadBinaryValue(const nsAString& aName, nsACString& aResult)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD size;
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
+ nullptr, nullptr, &size);
+
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!size) {
+ aResult.Truncate();
+ return NS_OK;
+ }
+
+ if (!aResult.SetLength(size, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsACString::iterator begin;
+ aResult.BeginWriting(begin);
+
+ rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ (LPBYTE)begin.get(), &size);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteStringValue(const nsAString& aName,
+ const nsAString& aValue)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // Need to indicate complete size of buffer including null terminator.
+ const nsString& flatValue = PromiseFlatString(aValue);
+
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_SZ,
+ (const BYTE*)flatValue.get(),
+ (flatValue.Length() + 1) * sizeof(char16_t));
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteIntValue(const nsAString& aName, uint32_t aValue)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_DWORD,
+ (const BYTE*)&aValue, sizeof(aValue));
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteInt64Value(const nsAString& aName, uint64_t aValue)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_QWORD,
+ (const BYTE*)&aValue, sizeof(aValue));
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteBinaryValue(const nsAString& aName,
+ const nsACString& aValue)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ const nsCString& flatValue = PromiseFlatCString(aValue);
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_BINARY,
+ (const BYTE*)flatValue.get(), flatValue.Length());
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::StartWatching(bool aRecurse)
+{
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (mWatchEvent) {
+ return NS_OK;
+ }
+
+ mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ if (!mWatchEvent) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ DWORD filter = REG_NOTIFY_CHANGE_NAME |
+ REG_NOTIFY_CHANGE_ATTRIBUTES |
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_SECURITY;
+
+ LONG rv = RegNotifyChangeKeyValue(mKey, aRecurse, filter, mWatchEvent, TRUE);
+ if (rv != ERROR_SUCCESS) {
+ StopWatching();
+ // On older versions of Windows, this call is not implemented, so simply
+ // return NS_OK in those cases and pretend that the watching is happening.
+ return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ mWatchRecursive = aRecurse;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::StopWatching()
+{
+ if (mWatchEvent) {
+ CloseHandle(mWatchEvent);
+ mWatchEvent = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::HasChanged(bool* aResult)
+{
+ if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
+ // An event only gets signaled once, then it's done, so we have to set up
+ // another event to watch.
+ StopWatching();
+ StartWatching(mWatchRecursive);
+ *aResult = true;
+ } else {
+ *aResult = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::IsWatching(bool* aResult)
+{
+ *aResult = (mWatchEvent != nullptr);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+void
+NS_NewWindowsRegKey(nsIWindowsRegKey** aResult)
+{
+ RefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
+ key.forget(aResult);
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+nsWindowsRegKeyConstructor(nsISupports* aDelegate, const nsIID& aIID,
+ void** aResult)
+{
+ if (aDelegate) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsIWindowsRegKey> key;
+ NS_NewWindowsRegKey(getter_AddRefs(key));
+ return key->QueryInterface(aIID, aResult);
+}
diff --git a/xpcom/ds/nsWindowsRegKey.h b/xpcom/ds/nsWindowsRegKey.h
new file mode 100644
index 000000000..d7930579a
--- /dev/null
+++ b/xpcom/ds/nsWindowsRegKey.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWindowsRegKey_h__
+#define nsWindowsRegKey_h__
+
+//-----------------------------------------------------------------------------
+
+#include "nsIWindowsRegKey.h"
+
+/**
+ * This ContractID may be used to instantiate a windows registry key object
+ * via the XPCOM component manager.
+ */
+#define NS_WINDOWSREGKEY_CONTRACTID "@mozilla.org/windows-registry-key;1"
+
+/**
+ * This function may be used to instantiate a windows registry key object prior
+ * to XPCOM being initialized.
+ */
+extern "C" void NS_NewWindowsRegKey(nsIWindowsRegKey** aResult);
+
+//-----------------------------------------------------------------------------
+
+#ifdef IMPL_LIBXUL
+
+// a53bc624-d577-4839-b8ec-bb5040a52ff4
+#define NS_WINDOWSREGKEY_CID \
+ { 0xa53bc624, 0xd577, 0x4839, \
+ { 0xb8, 0xec, 0xbb, 0x50, 0x40, 0xa5, 0x2f, 0xf4 } }
+
+extern MOZ_MUST_USE nsresult nsWindowsRegKeyConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aResult);
+
+#endif // IMPL_LIBXUL
+
+//-----------------------------------------------------------------------------
+
+#endif // nsWindowsRegKey_h__
diff --git a/xpcom/glue/AppData.cpp b/xpcom/glue/AppData.cpp
new file mode 100644
index 000000000..845267e60
--- /dev/null
+++ b/xpcom/glue/AppData.cpp
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/AppData.h"
+#include "nsXULAppAPI.h"
+#include "nsINIParser.h"
+#include "nsIFile.h"
+#include "nsCRTGlue.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+void
+SetAllocatedString(const char*& aStr, const char* aNewValue)
+{
+ NS_Free(const_cast<char*>(aStr));
+ if (aNewValue) {
+ aStr = NS_strdup(aNewValue);
+ } else {
+ aStr = nullptr;
+ }
+}
+
+void
+SetAllocatedString(const char*& aStr, const nsACString& aNewValue)
+{
+ NS_Free(const_cast<char*>(aStr));
+ if (aNewValue.IsEmpty()) {
+ aStr = nullptr;
+ } else {
+ aStr = ToNewCString(aNewValue);
+ }
+}
+
+ScopedAppData::ScopedAppData(const nsXREAppData* aAppData)
+{
+ Zero();
+
+ this->size = aAppData->size;
+
+ SetAllocatedString(this->vendor, aAppData->vendor);
+ SetAllocatedString(this->name, aAppData->name);
+ SetAllocatedString(this->remotingName, aAppData->remotingName);
+ SetAllocatedString(this->version, aAppData->version);
+ SetAllocatedString(this->buildID, aAppData->buildID);
+ SetAllocatedString(this->ID, aAppData->ID);
+ SetAllocatedString(this->copyright, aAppData->copyright);
+ SetAllocatedString(this->profile, aAppData->profile);
+ SetStrongPtr(this->directory, aAppData->directory);
+ this->flags = aAppData->flags;
+
+ if (aAppData->size > offsetof(nsXREAppData, xreDirectory)) {
+ SetStrongPtr(this->xreDirectory, aAppData->xreDirectory);
+ SetAllocatedString(this->minVersion, aAppData->minVersion);
+ SetAllocatedString(this->maxVersion, aAppData->maxVersion);
+ }
+
+ if (aAppData->size > offsetof(nsXREAppData, crashReporterURL)) {
+ SetAllocatedString(this->crashReporterURL, aAppData->crashReporterURL);
+ }
+
+ if (aAppData->size > offsetof(nsXREAppData, UAName)) {
+ SetAllocatedString(this->UAName, aAppData->UAName);
+ }
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ sandboxBrokerServices = aAppData->sandboxBrokerServices;
+#endif
+}
+
+ScopedAppData::~ScopedAppData()
+{
+ SetAllocatedString(this->vendor, nullptr);
+ SetAllocatedString(this->name, nullptr);
+ SetAllocatedString(this->remotingName, nullptr);
+ SetAllocatedString(this->version, nullptr);
+ SetAllocatedString(this->buildID, nullptr);
+ SetAllocatedString(this->ID, nullptr);
+ SetAllocatedString(this->copyright, nullptr);
+ SetAllocatedString(this->profile, nullptr);
+
+ NS_IF_RELEASE(this->directory);
+
+ SetStrongPtr(this->xreDirectory, (nsIFile*)nullptr);
+ SetAllocatedString(this->minVersion, nullptr);
+ SetAllocatedString(this->maxVersion, nullptr);
+
+ SetAllocatedString(this->crashReporterURL, nullptr);
+ SetAllocatedString(this->UAName, nullptr);
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/AppData.h b/xpcom/glue/AppData.h
new file mode 100644
index 000000000..0134df32c
--- /dev/null
+++ b/xpcom/glue/AppData.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AppData_h
+#define mozilla_AppData_h
+
+#include "nsXREAppData.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsISupportsUtils.h"
+
+namespace mozilla {
+
+// Like nsXREAppData, but releases all strong refs/allocated memory
+// in the destructor.
+class ScopedAppData : public nsXREAppData
+{
+public:
+ ScopedAppData()
+ {
+ Zero();
+ this->size = sizeof(*this);
+ }
+
+ explicit ScopedAppData(const nsXREAppData* aAppData);
+
+ void Zero() { memset(this, 0, sizeof(*this)); }
+
+ ~ScopedAppData();
+};
+
+/**
+ * Given |aStr| is holding a string allocated with NS_Alloc, or null:
+ * replace the value in |aStr| with a new value.
+ *
+ * @param aNewValue Null is permitted. The string is cloned with NS_strdup.
+ */
+void SetAllocatedString(const char*& aStr, const char* aNewValue);
+
+/**
+ * Given "str" is holding a string allocated with NS_Alloc, or null:
+ * replace the value in "str" with a new value.
+ *
+ * @param aNewValue If |aNewValue| is the empty string, |aStr| will be set
+ * to null.
+ */
+void SetAllocatedString(const char*& aStr, const nsACString& aNewValue);
+
+template<class T>
+void
+SetStrongPtr(T*& aPtr, T* aNewValue)
+{
+ NS_IF_RELEASE(aPtr);
+ aPtr = aNewValue;
+ NS_IF_ADDREF(aPtr);
+}
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/AutoRestore.h b/xpcom/glue/AutoRestore.h
new file mode 100644
index 000000000..20909c92c
--- /dev/null
+++ b/xpcom/glue/AutoRestore.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* functions for restoring saved values at the end of a C++ scope */
+
+#ifndef mozilla_AutoRestore_h_
+#define mozilla_AutoRestore_h_
+
+#include "mozilla/Attributes.h" // MOZ_STACK_CLASS
+#include "mozilla/GuardObjects.h"
+
+namespace mozilla {
+
+/**
+ * Save the current value of a variable and restore it when the object
+ * goes out of scope. For example:
+ * {
+ * AutoRestore<bool> savePainting(mIsPainting);
+ * mIsPainting = true;
+ *
+ * // ... your code here ...
+ *
+ * // mIsPainting is reset to its old value at the end of this block
+ * }
+ */
+template<class T>
+class MOZ_RAII AutoRestore
+{
+private:
+ T& mLocation;
+ T mValue;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+public:
+ explicit AutoRestore(T& aValue MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLocation(aValue)
+ , mValue(aValue)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ }
+ ~AutoRestore()
+ {
+ mLocation = mValue;
+ }
+ T SavedValue() const
+ {
+ return mValue;
+ }
+};
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_AutoRestore_h_) */
diff --git a/xpcom/glue/BlockingResourceBase.cpp b/xpcom/glue/BlockingResourceBase.cpp
new file mode 100644
index 000000000..ada02c72c
--- /dev/null
+++ b/xpcom/glue/BlockingResourceBase.cpp
@@ -0,0 +1,511 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/BlockingResourceBase.h"
+
+#ifdef DEBUG
+#include "prthread.h"
+
+#include "nsAutoPtr.h"
+
+#ifndef MOZ_CALLSTACK_DISABLED
+#include "CodeAddressService.h"
+#include "nsHashKeys.h"
+#include "mozilla/StackWalk.h"
+#include "nsTHashtable.h"
+#endif
+
+#include "mozilla/CondVar.h"
+#include "mozilla/DeadlockDetector.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Mutex.h"
+
+#if defined(MOZILLA_INTERNAL_API)
+#include "GeckoProfiler.h"
+#endif //MOZILLA_INTERNAL_API
+
+#endif // ifdef DEBUG
+
+namespace mozilla {
+//
+// BlockingResourceBase implementation
+//
+
+// static members
+const char* const BlockingResourceBase::kResourceTypeName[] = {
+ // needs to be kept in sync with BlockingResourceType
+ "Mutex", "ReentrantMonitor", "CondVar"
+};
+
+#ifdef DEBUG
+
+PRCallOnceType BlockingResourceBase::sCallOnce;
+unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1;
+BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
+
+
+void
+BlockingResourceBase::StackWalkCallback(uint32_t aFrameNumber, void* aPc,
+ void* aSp, void* aClosure)
+{
+#ifndef MOZ_CALLSTACK_DISABLED
+ AcquisitionState* state = (AcquisitionState*)aClosure;
+ state->AppendElement(aPc);
+#endif
+}
+
+void
+BlockingResourceBase::GetStackTrace(AcquisitionState& aState)
+{
+#ifndef MOZ_CALLSTACK_DISABLED
+ // Skip this function and the calling function.
+ const uint32_t kSkipFrames = 2;
+
+ aState.Clear();
+
+ // NB: Ignore the return value, there's nothing useful we can do if this
+ // this fails.
+ MozStackWalk(StackWalkCallback, kSkipFrames, 24, &aState, 0, nullptr);
+#endif
+}
+
+/**
+ * PrintCycle
+ * Append to |aOut| detailed information about the circular
+ * dependency in |aCycle|. Returns true if it *appears* that this
+ * cycle may represent an imminent deadlock, but this is merely a
+ * heuristic; the value returned may be a false positive or false
+ * negative.
+ *
+ * *NOT* thread safe. Calls |Print()|.
+ *
+ * FIXME bug 456272 hack alert: because we can't write call
+ * contexts into strings, all info is written to stderr, but only
+ * some info is written into |aOut|
+ */
+bool
+PrintCycle(const BlockingResourceBase::DDT::ResourceAcquisitionArray* aCycle,
+ nsACString& aOut)
+{
+ NS_ASSERTION(aCycle->Length() > 1, "need > 1 element for cycle!");
+
+ bool maybeImminent = true;
+
+ fputs("=== Cyclical dependency starts at\n", stderr);
+ aOut += "Cyclical dependency starts at\n";
+
+ const BlockingResourceBase::DDT::ResourceAcquisitionArray::elem_type res =
+ aCycle->ElementAt(0);
+ maybeImminent &= res->Print(aOut);
+
+ BlockingResourceBase::DDT::ResourceAcquisitionArray::index_type i;
+ BlockingResourceBase::DDT::ResourceAcquisitionArray::size_type len =
+ aCycle->Length();
+ const BlockingResourceBase::DDT::ResourceAcquisitionArray::elem_type* it =
+ 1 + aCycle->Elements();
+ for (i = 1; i < len - 1; ++i, ++it) {
+ fputs("\n--- Next dependency:\n", stderr);
+ aOut += "\nNext dependency:\n";
+
+ maybeImminent &= (*it)->Print(aOut);
+ }
+
+ fputs("\n=== Cycle completed at\n", stderr);
+ aOut += "Cycle completed at\n";
+ (*it)->Print(aOut);
+
+ return maybeImminent;
+}
+
+#ifndef MOZ_CALLSTACK_DISABLED
+struct CodeAddressServiceLock final
+{
+ static void Unlock() { }
+ static void Lock() { }
+ static bool IsLocked() { return true; }
+};
+
+struct CodeAddressServiceStringAlloc final
+{
+ static char* copy(const char* aString) { return ::strdup(aString); }
+ static void free(char* aString) { ::free(aString); }
+};
+
+class CodeAddressServiceStringTable final
+{
+public:
+ CodeAddressServiceStringTable() : mSet(32) {}
+
+ const char* Intern(const char* aString)
+ {
+ nsCharPtrHashKey* e = mSet.PutEntry(aString);
+ return e->GetKey();
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mSet.SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+ typedef nsTHashtable<nsCharPtrHashKey> StringSet;
+ StringSet mSet;
+};
+
+typedef CodeAddressService<CodeAddressServiceStringTable,
+ CodeAddressServiceStringAlloc,
+ CodeAddressServiceLock> WalkTheStackCodeAddressService;
+#endif
+
+bool
+BlockingResourceBase::Print(nsACString& aOut) const
+{
+ fprintf(stderr, "--- %s : %s",
+ kResourceTypeName[mType], mName);
+ aOut += BlockingResourceBase::kResourceTypeName[mType];
+ aOut += " : ";
+ aOut += mName;
+
+ bool acquired = IsAcquired();
+
+ if (acquired) {
+ fputs(" (currently acquired)\n", stderr);
+ aOut += " (currently acquired)\n";
+ }
+
+ fputs(" calling context\n", stderr);
+#ifdef MOZ_CALLSTACK_DISABLED
+ fputs(" [stack trace unavailable]\n", stderr);
+#else
+ const AcquisitionState& state = acquired ? mAcquired : mFirstSeen;
+
+ WalkTheStackCodeAddressService addressService;
+
+ for (uint32_t i = 0; i < state.Length(); i++) {
+ const size_t kMaxLength = 1024;
+ char buffer[kMaxLength];
+ addressService.GetLocation(i + 1, state[i], buffer, kMaxLength);
+ const char* fmt = " %s\n";
+ aOut.AppendLiteral(" ");
+ aOut.Append(buffer);
+ aOut.AppendLiteral("\n");
+ fprintf(stderr, fmt, buffer);
+ }
+
+#endif
+
+ return acquired;
+}
+
+
+BlockingResourceBase::BlockingResourceBase(
+ const char* aName,
+ BlockingResourceBase::BlockingResourceType aType)
+ : mName(aName)
+ , mType(aType)
+#ifdef MOZ_CALLSTACK_DISABLED
+ , mAcquired(false)
+#else
+ , mAcquired()
+#endif
+{
+ MOZ_ASSERT(mName, "Name must be nonnull");
+ // PR_CallOnce guaranatees that InitStatics is called in a
+ // thread-safe way
+ if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics)) {
+ NS_RUNTIMEABORT("can't initialize blocking resource static members");
+ }
+
+ mChainPrev = 0;
+ sDeadlockDetector->Add(this);
+}
+
+
+BlockingResourceBase::~BlockingResourceBase()
+{
+ // we don't check for really obviously bad things like freeing
+ // Mutexes while they're still locked. it is assumed that the
+ // base class, or its underlying primitive, will check for such
+ // stupid mistakes.
+ mChainPrev = 0; // racy only for stupidly buggy client code
+ if (sDeadlockDetector) {
+ sDeadlockDetector->Remove(this);
+ }
+}
+
+
+size_t
+BlockingResourceBase::SizeOfDeadlockDetector(MallocSizeOf aMallocSizeOf)
+{
+ return sDeadlockDetector ?
+ sDeadlockDetector->SizeOfIncludingThis(aMallocSizeOf) : 0;
+}
+
+
+PRStatus
+BlockingResourceBase::InitStatics()
+{
+ PR_NewThreadPrivateIndex(&sResourceAcqnChainFrontTPI, 0);
+ sDeadlockDetector = new DDT();
+ if (!sDeadlockDetector) {
+ NS_RUNTIMEABORT("can't allocate deadlock detector");
+ }
+ return PR_SUCCESS;
+}
+
+
+void
+BlockingResourceBase::Shutdown()
+{
+ delete sDeadlockDetector;
+ sDeadlockDetector = 0;
+}
+
+
+void
+BlockingResourceBase::CheckAcquire()
+{
+ if (mType == eCondVar) {
+ NS_NOTYETIMPLEMENTED(
+ "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars");
+ return;
+ }
+
+ BlockingResourceBase* chainFront = ResourceChainFront();
+ nsAutoPtr<DDT::ResourceAcquisitionArray> cycle(
+ sDeadlockDetector->CheckAcquisition(
+ chainFront ? chainFront : 0, this));
+ if (!cycle) {
+ return;
+ }
+
+#ifndef MOZ_CALLSTACK_DISABLED
+ // Update the current stack before printing.
+ GetStackTrace(mAcquired);
+#endif
+
+ fputs("###!!! ERROR: Potential deadlock detected:\n", stderr);
+ nsAutoCString out("Potential deadlock detected:\n");
+ bool maybeImminent = PrintCycle(cycle, out);
+
+ if (maybeImminent) {
+ fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
+ out.AppendLiteral("\n###!!! Deadlock may happen NOW!\n\n");
+ } else {
+ fputs("\nDeadlock may happen for some other execution\n\n",
+ stderr);
+ out.AppendLiteral("\nDeadlock may happen for some other execution\n\n");
+ }
+
+ // XXX can customize behavior on whether we /think/ deadlock is
+ // XXX about to happen. for example:
+ // XXX if (maybeImminent)
+ // NS_RUNTIMEABORT(out.get());
+ NS_ERROR(out.get());
+}
+
+
+void
+BlockingResourceBase::Acquire()
+{
+ if (mType == eCondVar) {
+ NS_NOTYETIMPLEMENTED(
+ "FIXME bug 456272: annots. to allow Acquire()ing condvars");
+ return;
+ }
+ NS_ASSERTION(!IsAcquired(),
+ "reacquiring already acquired resource");
+
+ ResourceChainAppend(ResourceChainFront());
+
+#ifdef MOZ_CALLSTACK_DISABLED
+ mAcquired = true;
+#else
+ // Take a stack snapshot.
+ GetStackTrace(mAcquired);
+ if (mFirstSeen.IsEmpty()) {
+ mFirstSeen = mAcquired;
+ }
+#endif
+}
+
+
+void
+BlockingResourceBase::Release()
+{
+ if (mType == eCondVar) {
+ NS_NOTYETIMPLEMENTED(
+ "FIXME bug 456272: annots. to allow Release()ing condvars");
+ return;
+ }
+
+ BlockingResourceBase* chainFront = ResourceChainFront();
+ NS_ASSERTION(chainFront && IsAcquired(),
+ "Release()ing something that hasn't been Acquire()ed");
+
+ if (chainFront == this) {
+ ResourceChainRemove();
+ } else {
+ // not an error, but makes code hard to reason about.
+ NS_WARNING("Resource acquired is being released in non-LIFO order; why?\n");
+ nsCString tmp;
+ Print(tmp);
+
+ // remove this resource from wherever it lives in the chain
+ // we walk backwards in order of acquisition:
+ // (1) ...node<-prev<-curr...
+ // / /
+ // (2) ...prev<-curr...
+ BlockingResourceBase* curr = chainFront;
+ BlockingResourceBase* prev = nullptr;
+ while (curr && (prev = curr->mChainPrev) && (prev != this)) {
+ curr = prev;
+ }
+ if (prev == this) {
+ curr->mChainPrev = prev->mChainPrev;
+ }
+ }
+
+ ClearAcquisitionState();
+}
+
+
+//
+// Debug implementation of (OffTheBooks)Mutex
+void
+OffTheBooksMutex::Lock()
+{
+ CheckAcquire();
+ PR_Lock(mLock);
+ Acquire(); // protected by mLock
+}
+
+void
+OffTheBooksMutex::Unlock()
+{
+ Release(); // protected by mLock
+ PRStatus status = PR_Unlock(mLock);
+ NS_ASSERTION(PR_SUCCESS == status, "bad Mutex::Unlock()");
+}
+
+
+//
+// Debug implementation of ReentrantMonitor
+void
+ReentrantMonitor::Enter()
+{
+ BlockingResourceBase* chainFront = ResourceChainFront();
+
+ // the code below implements monitor reentrancy semantics
+
+ if (this == chainFront) {
+ // immediately re-entered the monitor: acceptable
+ PR_EnterMonitor(mReentrantMonitor);
+ ++mEntryCount;
+ return;
+ }
+
+ // this is sort of a hack around not recording the thread that
+ // owns this monitor
+ if (chainFront) {
+ for (BlockingResourceBase* br = ResourceChainPrev(chainFront);
+ br;
+ br = ResourceChainPrev(br)) {
+ if (br == this) {
+ NS_WARNING(
+ "Re-entering ReentrantMonitor after acquiring other resources.");
+
+ // show the caller why this is potentially bad
+ CheckAcquire();
+
+ PR_EnterMonitor(mReentrantMonitor);
+ ++mEntryCount;
+ return;
+ }
+ }
+ }
+
+ CheckAcquire();
+ PR_EnterMonitor(mReentrantMonitor);
+ NS_ASSERTION(mEntryCount == 0, "ReentrantMonitor isn't free!");
+ Acquire(); // protected by mReentrantMonitor
+ mEntryCount = 1;
+}
+
+void
+ReentrantMonitor::Exit()
+{
+ if (--mEntryCount == 0) {
+ Release(); // protected by mReentrantMonitor
+ }
+ PRStatus status = PR_ExitMonitor(mReentrantMonitor);
+ NS_ASSERTION(PR_SUCCESS == status, "bad ReentrantMonitor::Exit()");
+}
+
+nsresult
+ReentrantMonitor::Wait(PRIntervalTime aInterval)
+{
+ AssertCurrentThreadIn();
+
+ // save monitor state and reset it to empty
+ int32_t savedEntryCount = mEntryCount;
+ AcquisitionState savedAcquisitionState = GetAcquisitionState();
+ BlockingResourceBase* savedChainPrev = mChainPrev;
+ mEntryCount = 0;
+ ClearAcquisitionState();
+ mChainPrev = 0;
+
+ nsresult rv;
+#if defined(MOZILLA_INTERNAL_API)
+ {
+ GeckoProfilerSleepRAII profiler_sleep;
+#endif //MOZILLA_INTERNAL_API
+
+ // give up the monitor until we're back from Wait()
+ rv = PR_Wait(mReentrantMonitor, aInterval) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+
+#if defined(MOZILLA_INTERNAL_API)
+ }
+#endif //MOZILLA_INTERNAL_API
+
+ // restore saved state
+ mEntryCount = savedEntryCount;
+ SetAcquisitionState(savedAcquisitionState);
+ mChainPrev = savedChainPrev;
+
+ return rv;
+}
+
+
+//
+// Debug implementation of CondVar
+nsresult
+CondVar::Wait(PRIntervalTime aInterval)
+{
+ AssertCurrentThreadOwnsMutex();
+
+ // save mutex state and reset to empty
+ AcquisitionState savedAcquisitionState = mLock->GetAcquisitionState();
+ BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
+ mLock->ClearAcquisitionState();
+ mLock->mChainPrev = 0;
+
+ // give up mutex until we're back from Wait()
+ nsresult rv =
+ PR_WaitCondVar(mCvar, aInterval) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+
+ // restore saved state
+ mLock->SetAcquisitionState(savedAcquisitionState);
+ mLock->mChainPrev = savedChainPrev;
+
+ return rv;
+}
+
+#endif // ifdef DEBUG
+
+
+} // namespace mozilla
diff --git a/xpcom/glue/BlockingResourceBase.h b/xpcom/glue/BlockingResourceBase.h
new file mode 100644
index 000000000..d70fbcbbf
--- /dev/null
+++ b/xpcom/glue/BlockingResourceBase.h
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef mozilla_BlockingResourceBase_h
+#define mozilla_BlockingResourceBase_h
+
+#include "mozilla/Logging.h"
+
+#include "nscore.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsISupportsImpl.h"
+
+#ifdef DEBUG
+
+// NB: Comment this out to enable callstack tracking.
+#define MOZ_CALLSTACK_DISABLED
+
+#include "prinit.h"
+
+#include "nsStringGlue.h"
+
+#ifndef MOZ_CALLSTACK_DISABLED
+#include "nsTArray.h"
+#endif
+
+#include "nsXPCOM.h"
+#endif
+
+//
+// This header is not meant to be included by client code.
+//
+
+namespace mozilla {
+
+#ifdef DEBUG
+template <class T> class DeadlockDetector;
+#endif
+
+/**
+ * BlockingResourceBase
+ * Base class of resources that might block clients trying to acquire them.
+ * Does debugging and deadlock detection in DEBUG builds.
+ **/
+class BlockingResourceBase
+{
+public:
+ // Needs to be kept in sync with kResourceTypeNames.
+ enum BlockingResourceType { eMutex, eReentrantMonitor, eCondVar };
+
+ /**
+ * kResourceTypeName
+ * Human-readable version of BlockingResourceType enum.
+ */
+ static const char* const kResourceTypeName[];
+
+
+#ifdef DEBUG
+
+ static size_t
+ SizeOfDeadlockDetector(MallocSizeOf aMallocSizeOf);
+
+ /**
+ * Print
+ * Write a description of this blocking resource to |aOut|. If
+ * the resource appears to be currently acquired, the current
+ * acquisition context is printed and true is returned.
+ * Otherwise, we print the context from |aFirstSeen|, the
+ * first acquisition from which the code calling |Print()|
+ * became interested in us, and return false.
+ *
+ * *NOT* thread safe. Reads |mAcquisitionContext| without
+ * synchronization, but this will not cause correctness
+ * problems.
+ *
+ * FIXME bug 456272: hack alert: because we can't write call
+ * contexts into strings, all info is written to stderr, but
+ * only some info is written into |aOut|
+ */
+ bool Print(nsACString& aOut) const;
+
+ size_t
+ SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ // NB: |mName| is not reported as it's expected to be a static string.
+ // If we switch to a nsString it should be added to the tally.
+ // |mChainPrev| is not reported because its memory is not owned.
+ size_t n = aMallocSizeOf(this);
+ return n;
+ }
+
+ // ``DDT'' = ``Deadlock Detector Type''
+ typedef DeadlockDetector<BlockingResourceBase> DDT;
+
+protected:
+#ifdef MOZ_CALLSTACK_DISABLED
+ typedef bool AcquisitionState;
+#else
+ typedef AutoTArray<void*, 24> AcquisitionState;
+#endif
+
+ /**
+ * BlockingResourceBase
+ * Initialize this blocking resource. Also hooks the resource into
+ * instrumentation code.
+ *
+ * Thread safe.
+ *
+ * @param aName A meaningful, unique name that can be used in
+ * error messages, et al.
+ * @param aType The specific type of |this|, if any.
+ **/
+ BlockingResourceBase(const char* aName, BlockingResourceType aType);
+
+ ~BlockingResourceBase();
+
+ /**
+ * CheckAcquire
+ *
+ * Thread safe.
+ **/
+ void CheckAcquire();
+
+ /**
+ * Acquire
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ **/
+ void Acquire(); //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * Release
+ * Remove this resource from the current thread's acquisition chain.
+ * The resource does not have to be at the front of the chain, although
+ * it is confusing to release resources in a different order than they
+ * are acquired. This generates a warning.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ **/
+ void Release(); //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * ResourceChainFront
+ *
+ * Thread safe.
+ *
+ * @return the front of the resource acquisition chain, i.e., the last
+ * resource acquired.
+ */
+ static BlockingResourceBase* ResourceChainFront()
+ {
+ return
+ (BlockingResourceBase*)PR_GetThreadPrivate(sResourceAcqnChainFrontTPI);
+ }
+
+ /**
+ * ResourceChainPrev
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ static BlockingResourceBase* ResourceChainPrev(
+ const BlockingResourceBase* aResource)
+ {
+ return aResource->mChainPrev;
+ } //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * ResourceChainAppend
+ * Set |this| to the front of the resource acquisition chain, and link
+ * |this| to |aPrev|.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void ResourceChainAppend(BlockingResourceBase* aPrev)
+ {
+ mChainPrev = aPrev;
+ PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, this);
+ } //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * ResourceChainRemove
+ * Remove |this| from the front of the resource acquisition chain.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void ResourceChainRemove()
+ {
+ NS_ASSERTION(this == ResourceChainFront(), "not at chain front");
+ PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, mChainPrev);
+ } //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * GetAcquisitionState
+ * Return whether or not this resource was acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ AcquisitionState GetAcquisitionState()
+ {
+ return mAcquired;
+ }
+
+ /**
+ * SetAcquisitionState
+ * Set whether or not this resource was acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void SetAcquisitionState(const AcquisitionState& aAcquisitionState)
+ {
+ mAcquired = aAcquisitionState;
+ }
+
+ /**
+ * ClearAcquisitionState
+ * Indicate this resource is not acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void ClearAcquisitionState()
+ {
+#ifdef MOZ_CALLSTACK_DISABLED
+ mAcquired = false;
+#else
+ mAcquired.Clear();
+#endif
+ }
+
+ /**
+ * IsAcquired
+ * Indicates if this resource is acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ bool IsAcquired() const
+ {
+#ifdef MOZ_CALLSTACK_DISABLED
+ return mAcquired;
+#else
+ return !mAcquired.IsEmpty();
+#endif
+ }
+
+ /**
+ * mChainPrev
+ * A series of resource acquisitions creates a chain of orders. This
+ * chain is implemented as a linked list; |mChainPrev| points to the
+ * resource most recently Acquire()'d before this one.
+ **/
+ BlockingResourceBase* mChainPrev;
+
+private:
+ /**
+ * mName
+ * A descriptive name for this resource. Used in error
+ * messages etc.
+ */
+ const char* mName;
+
+ /**
+ * mType
+ * The more specific type of this resource. Used to implement
+ * special semantics (e.g., reentrancy of monitors).
+ **/
+ BlockingResourceType mType;
+
+ /**
+ * mAcquired
+ * Indicates if this resource is currently acquired.
+ */
+ AcquisitionState mAcquired;
+
+#ifndef MOZ_CALLSTACK_DISABLED
+ /**
+ * mFirstSeen
+ * Inidicates where this resource was first acquired.
+ */
+ AcquisitionState mFirstSeen;
+#endif
+
+ /**
+ * sCallOnce
+ * Ensures static members are initialized only once, and in a
+ * thread-safe way.
+ */
+ static PRCallOnceType sCallOnce;
+
+ /**
+ * sResourceAcqnChainFrontTPI
+ * Thread-private index to the front of each thread's resource
+ * acquisition chain.
+ */
+ static unsigned sResourceAcqnChainFrontTPI;
+
+ /**
+ * sDeadlockDetector
+ * Does as named.
+ */
+ static DDT* sDeadlockDetector;
+
+ /**
+ * InitStatics
+ * Inititialize static members of BlockingResourceBase that can't
+ * be statically initialized.
+ *
+ * *NOT* thread safe.
+ */
+ static PRStatus InitStatics();
+
+ /**
+ * Shutdown
+ * Free static members.
+ *
+ * *NOT* thread safe.
+ */
+ static void Shutdown();
+
+ static void StackWalkCallback(uint32_t aFrameNumber, void* aPc,
+ void* aSp, void* aClosure);
+ static void GetStackTrace(AcquisitionState& aState);
+
+# ifdef MOZILLA_INTERNAL_API
+ // so it can call BlockingResourceBase::Shutdown()
+ friend void LogTerm();
+# endif // ifdef MOZILLA_INTERNAL_API
+
+#else // non-DEBUG implementation
+
+ BlockingResourceBase(const char* aName, BlockingResourceType aType) {}
+
+ ~BlockingResourceBase() {}
+
+#endif
+};
+
+
+} // namespace mozilla
+
+
+#endif // mozilla_BlockingResourceBase_h
diff --git a/xpcom/glue/CondVar.h b/xpcom/glue/CondVar.h
new file mode 100644
index 000000000..5d05464ec
--- /dev/null
+++ b/xpcom/glue/CondVar.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CondVar_h
+#define mozilla_CondVar_h
+
+#include "prcvar.h"
+
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/Mutex.h"
+
+#ifdef MOZILLA_INTERNAL_API
+#include "GeckoProfiler.h"
+#endif //MOZILLA_INTERNAL_API
+
+namespace mozilla {
+
+
+/**
+ * CondVar
+ * Vanilla condition variable. Please don't use this unless you have a
+ * compelling reason --- Monitor provides a simpler API.
+ */
+class CondVar : BlockingResourceBase
+{
+public:
+ /**
+ * CondVar
+ *
+ * The CALLER owns |aLock|.
+ *
+ * @param aLock A Mutex to associate with this condition variable.
+ * @param aName A name which can reference this monitor
+ * @returns If failure, nullptr.
+ * If success, a valid Monitor* which must be destroyed
+ * by Monitor::DestroyMonitor()
+ **/
+ CondVar(Mutex& aLock, const char* aName)
+ : BlockingResourceBase(aName, eCondVar)
+ , mLock(&aLock)
+ {
+ MOZ_COUNT_CTOR(CondVar);
+ // |aLock| must necessarily already be known to the deadlock detector
+ mCvar = PR_NewCondVar(mLock->mLock);
+ if (!mCvar) {
+ NS_RUNTIMEABORT("Can't allocate mozilla::CondVar");
+ }
+ }
+
+ /**
+ * ~CondVar
+ * Clean up after this CondVar, but NOT its associated Mutex.
+ **/
+ ~CondVar()
+ {
+ NS_ASSERTION(mCvar && mLock,
+ "improperly constructed CondVar or double free");
+ PR_DestroyCondVar(mCvar);
+ mCvar = 0;
+ mLock = 0;
+ MOZ_COUNT_DTOR(CondVar);
+ }
+
+#ifndef DEBUG
+ /**
+ * Wait
+ * @see prcvar.h
+ **/
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+
+#ifdef MOZILLA_INTERNAL_API
+ GeckoProfilerSleepRAII profiler_sleep;
+#endif //MOZILLA_INTERNAL_API
+ // NSPR checks for lock ownership
+ return PR_WaitCondVar(mCvar, aInterval) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+ }
+#else
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT);
+#endif // ifndef DEBUG
+
+ /**
+ * Notify
+ * @see prcvar.h
+ **/
+ nsresult Notify()
+ {
+ // NSPR checks for lock ownership
+ return PR_NotifyCondVar(mCvar) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ /**
+ * NotifyAll
+ * @see prcvar.h
+ **/
+ nsresult NotifyAll()
+ {
+ // NSPR checks for lock ownership
+ return PR_NotifyAllCondVar(mCvar) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+#ifdef DEBUG
+ /**
+ * AssertCurrentThreadOwnsMutex
+ * @see Mutex::AssertCurrentThreadOwns
+ **/
+ void AssertCurrentThreadOwnsMutex()
+ {
+ mLock->AssertCurrentThreadOwns();
+ }
+
+ /**
+ * AssertNotCurrentThreadOwnsMutex
+ * @see Mutex::AssertNotCurrentThreadOwns
+ **/
+ void AssertNotCurrentThreadOwnsMutex()
+ {
+ mLock->AssertNotCurrentThreadOwns();
+ }
+
+#else
+ void AssertCurrentThreadOwnsMutex() {}
+ void AssertNotCurrentThreadOwnsMutex() {}
+
+#endif // ifdef DEBUG
+
+private:
+ CondVar();
+ CondVar(CondVar&);
+ CondVar& operator=(CondVar&);
+
+ Mutex* mLock;
+ PRCondVar* mCvar;
+};
+
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_CondVar_h
diff --git a/xpcom/glue/DeadlockDetector.h b/xpcom/glue/DeadlockDetector.h
new file mode 100644
index 000000000..382e4ef4a
--- /dev/null
+++ b/xpcom/glue/DeadlockDetector.h
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_DeadlockDetector_h
+#define mozilla_DeadlockDetector_h
+
+#include "mozilla/Attributes.h"
+
+#include <stdlib.h>
+
+#include "prlock.h"
+
+#include "nsClassHashtable.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+/**
+ * DeadlockDetector
+ *
+ * The following is an approximate description of how the deadlock detector
+ * works.
+ *
+ * The deadlock detector ensures that all blocking resources are
+ * acquired according to a partial order P. One type of blocking
+ * resource is a lock. If a lock l1 is acquired (locked) before l2,
+ * then we say that |l1 <_P l2|. The detector flags an error if two
+ * locks l1 and l2 have an inconsistent ordering in P; that is, if
+ * both |l1 <_P l2| and |l2 <_P l1|. This is a potential error
+ * because a thread acquiring l1,l2 according to the first order might
+ * race with a thread acquiring them according to the second order.
+ * If this happens under the right conditions, then the acquisitions
+ * will deadlock.
+ *
+ * This deadlock detector doesn't know at compile-time what P is. So,
+ * it tries to discover the order at run time. More precisely, it
+ * finds <i>some</i> order P, then tries to find chains of resource
+ * acquisitions that violate P. An example acquisition sequence, and
+ * the orders they impose, is
+ * l1.lock() // current chain: [ l1 ]
+ * // order: { }
+ *
+ * l2.lock() // current chain: [ l1, l2 ]
+ * // order: { l1 <_P l2 }
+ *
+ * l3.lock() // current chain: [ l1, l2, l3 ]
+ * // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
+ * // (note: <_P is transitive, so also |l1 <_P l3|)
+ *
+ * l2.unlock() // current chain: [ l1, l3 ]
+ * // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
+ * // (note: it's OK, but weird, that l2 was unlocked out
+ * // of order. we still have l1 <_P l3).
+ *
+ * l2.lock() // current chain: [ l1, l3, l2 ]
+ * // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3,
+ * l3 <_P l2 (!!!) }
+ * BEEP BEEP! Here the detector will flag a potential error, since
+ * l2 and l3 were used inconsistently (and potentially in ways that
+ * would deadlock).
+ */
+template<typename T>
+class DeadlockDetector
+{
+public:
+ typedef nsTArray<const T*> ResourceAcquisitionArray;
+
+private:
+ struct OrderingEntry;
+ typedef nsTArray<OrderingEntry*> HashEntryArray;
+ typedef typename HashEntryArray::index_type index_type;
+ typedef typename HashEntryArray::size_type size_type;
+ static const index_type NoIndex = HashEntryArray::NoIndex;
+
+ /**
+ * Value type for the ordering table. Contains the other
+ * resources on which an ordering constraint |key < other|
+ * exists. The catch is that we also store the calling context at
+ * which the other resource was acquired; this improves the
+ * quality of error messages when potential deadlock is detected.
+ */
+ struct OrderingEntry
+ {
+ explicit OrderingEntry(const T* aResource)
+ : mOrderedLT() // FIXME bug 456272: set to empirical dep size?
+ , mExternalRefs()
+ , mResource(aResource)
+ {
+ }
+ ~OrderingEntry()
+ {
+ }
+
+ size_t
+ SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = aMallocSizeOf(this);
+ n += mOrderedLT.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ n += mExternalRefs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ return n;
+ }
+
+ HashEntryArray mOrderedLT; // this <_o Other
+ HashEntryArray mExternalRefs; // hash entries that reference this
+ const T* mResource;
+ };
+
+ // Throwaway RAII lock to make the following code safer.
+ struct PRAutoLock
+ {
+ explicit PRAutoLock(PRLock* aLock) : mLock(aLock) { PR_Lock(mLock); }
+ ~PRAutoLock() { PR_Unlock(mLock); }
+ PRLock* mLock;
+ };
+
+public:
+ static const uint32_t kDefaultNumBuckets;
+
+ /**
+ * DeadlockDetector
+ * Create a new deadlock detector.
+ *
+ * @param aNumResourcesGuess Guess at approximate number of resources
+ * that will be checked.
+ */
+ explicit DeadlockDetector(uint32_t aNumResourcesGuess = kDefaultNumBuckets)
+ : mOrdering(aNumResourcesGuess)
+ {
+ mLock = PR_NewLock();
+ if (!mLock) {
+ NS_RUNTIMEABORT("couldn't allocate deadlock detector lock");
+ }
+ }
+
+ /**
+ * ~DeadlockDetector
+ *
+ * *NOT* thread safe.
+ */
+ ~DeadlockDetector()
+ {
+ PR_DestroyLock(mLock);
+ }
+
+ size_t
+ SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = aMallocSizeOf(this);
+
+ {
+ PRAutoLock _(mLock);
+ n += mOrdering.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mOrdering.ConstIter(); !iter.Done(); iter.Next()) {
+ // NB: Key is accounted for in the entry.
+ n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ }
+
+ return n;
+ }
+
+ /**
+ * Add
+ * Make the deadlock detector aware of |aResource|.
+ *
+ * WARNING: The deadlock detector owns |aResource|.
+ *
+ * Thread safe.
+ *
+ * @param aResource Resource to make deadlock detector aware of.
+ */
+ void Add(const T* aResource)
+ {
+ PRAutoLock _(mLock);
+ mOrdering.Put(aResource, new OrderingEntry(aResource));
+ }
+
+ void Remove(const T* aResource)
+ {
+ PRAutoLock _(mLock);
+
+ OrderingEntry* entry = mOrdering.Get(aResource);
+
+ // Iterate the external refs and remove the entry from them.
+ HashEntryArray& refs = entry->mExternalRefs;
+ for (index_type i = 0; i < refs.Length(); i++) {
+ refs[i]->mOrderedLT.RemoveElementSorted(entry);
+ }
+
+ // Iterate orders and remove this entry from their refs.
+ HashEntryArray& orders = entry->mOrderedLT;
+ for (index_type i = 0; i < orders.Length(); i++) {
+ orders[i]->mExternalRefs.RemoveElementSorted(entry);
+ }
+
+ // Now the entry can be safely removed.
+ mOrdering.Remove(aResource);
+ }
+
+ /**
+ * CheckAcquisition This method is called after acquiring |aLast|,
+ * but before trying to acquire |aProposed|.
+ * It determines whether actually trying to acquire |aProposed|
+ * will create problems. It is OK if |aLast| is nullptr; this is
+ * interpreted as |aProposed| being the thread's first acquisition
+ * of its current chain.
+ *
+ * Iff acquiring |aProposed| may lead to deadlock for some thread
+ * interleaving (including the current one!), the cyclical
+ * dependency from which this was deduced is returned. Otherwise,
+ * 0 is returned.
+ *
+ * If a potential deadlock is detected and a resource cycle is
+ * returned, it is the *caller's* responsibility to free it.
+ *
+ * Thread safe.
+ *
+ * @param aLast Last resource acquired by calling thread (or 0).
+ * @param aProposed Resource calling thread proposes to acquire.
+ */
+ ResourceAcquisitionArray* CheckAcquisition(const T* aLast,
+ const T* aProposed)
+ {
+ if (!aLast) {
+ // don't check if |0 < aProposed|; just vamoose
+ return 0;
+ }
+
+ NS_ASSERTION(aProposed, "null resource");
+ PRAutoLock _(mLock);
+
+ OrderingEntry* proposed = mOrdering.Get(aProposed);
+ NS_ASSERTION(proposed, "missing ordering entry");
+
+ OrderingEntry* current = mOrdering.Get(aLast);
+ NS_ASSERTION(current, "missing ordering entry");
+
+ // this is the crux of the deadlock detector algorithm
+
+ if (current == proposed) {
+ // reflexive deadlock. fastpath b/c InTransitiveClosure is
+ // not applicable here.
+ ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray();
+ if (!cycle) {
+ NS_RUNTIMEABORT("can't allocate dep. cycle array");
+ }
+ cycle->AppendElement(current->mResource);
+ cycle->AppendElement(aProposed);
+ return cycle;
+ }
+ if (InTransitiveClosure(current, proposed)) {
+ // we've already established |aLast < aProposed|. all is well.
+ return 0;
+ }
+ if (InTransitiveClosure(proposed, current)) {
+ // the order |aProposed < aLast| has been deduced, perhaps
+ // transitively. we're attempting to violate that
+ // constraint by acquiring resources in the order
+ // |aLast < aProposed|, and thus we may deadlock under the
+ // right conditions.
+ ResourceAcquisitionArray* cycle = GetDeductionChain(proposed, current);
+ // show how acquiring |aProposed| would complete the cycle
+ cycle->AppendElement(aProposed);
+ return cycle;
+ }
+ // |aLast|, |aProposed| are unordered according to our
+ // poset. this is fine, but we now need to add this
+ // ordering constraint.
+ current->mOrderedLT.InsertElementSorted(proposed);
+ proposed->mExternalRefs.InsertElementSorted(current);
+ return 0;
+ }
+
+ /**
+ * Return true iff |aTarget| is in the transitive closure of |aStart|
+ * over the ordering relation `<_this'.
+ *
+ * @precondition |aStart != aTarget|
+ */
+ bool InTransitiveClosure(const OrderingEntry* aStart,
+ const OrderingEntry* aTarget) const
+ {
+ // NB: Using a static comparator rather than default constructing one shows
+ // a 9% improvement in scalability tests on some systems.
+ static nsDefaultComparator<const OrderingEntry*, const OrderingEntry*> comp;
+ if (aStart->mOrderedLT.BinaryIndexOf(aTarget, comp) != NoIndex) {
+ return true;
+ }
+
+ index_type i = 0;
+ size_type len = aStart->mOrderedLT.Length();
+ for (auto it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) {
+ if (InTransitiveClosure(*it, aTarget)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return an array of all resource acquisitions
+ * aStart <_this r1 <_this r2 <_ ... <_ aTarget
+ * from which |aStart <_this aTarget| was deduced, including
+ * |aStart| and |aTarget|.
+ *
+ * Nb: there may be multiple deductions of |aStart <_this
+ * aTarget|. This function returns the first ordering found by
+ * depth-first search.
+ *
+ * Nb: |InTransitiveClosure| could be replaced by this function.
+ * However, this one is more expensive because we record the DFS
+ * search stack on the heap whereas the other doesn't.
+ *
+ * @precondition |aStart != aTarget|
+ */
+ ResourceAcquisitionArray* GetDeductionChain(const OrderingEntry* aStart,
+ const OrderingEntry* aTarget)
+ {
+ ResourceAcquisitionArray* chain = new ResourceAcquisitionArray();
+ if (!chain) {
+ NS_RUNTIMEABORT("can't allocate dep. cycle array");
+ }
+ chain->AppendElement(aStart->mResource);
+
+ NS_ASSERTION(GetDeductionChain_Helper(aStart, aTarget, chain),
+ "GetDeductionChain called when there's no deadlock");
+ return chain;
+ }
+
+ // precondition: |aStart != aTarget|
+ // invariant: |aStart| is the last element in |aChain|
+ bool GetDeductionChain_Helper(const OrderingEntry* aStart,
+ const OrderingEntry* aTarget,
+ ResourceAcquisitionArray* aChain)
+ {
+ if (aStart->mOrderedLT.BinaryIndexOf(aTarget) != NoIndex) {
+ aChain->AppendElement(aTarget->mResource);
+ return true;
+ }
+
+ index_type i = 0;
+ size_type len = aStart->mOrderedLT.Length();
+ for (auto it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) {
+ aChain->AppendElement((*it)->mResource);
+ if (GetDeductionChain_Helper(*it, aTarget, aChain)) {
+ return true;
+ }
+ aChain->RemoveElementAt(aChain->Length() - 1);
+ }
+ return false;
+ }
+
+ /**
+ * The partial order on resource acquisitions used by the deadlock
+ * detector.
+ */
+ nsClassHashtable<nsPtrHashKey<const T>, OrderingEntry> mOrdering;
+
+
+ /**
+ * Protects contentious methods.
+ * Nb: can't use mozilla::Mutex since we are used as its deadlock
+ * detector.
+ */
+ PRLock* mLock;
+
+private:
+ DeadlockDetector(const DeadlockDetector& aDD) = delete;
+ DeadlockDetector& operator=(const DeadlockDetector& aDD) = delete;
+};
+
+
+template<typename T>
+// FIXME bug 456272: tune based on average workload
+const uint32_t DeadlockDetector<T>::kDefaultNumBuckets = 32;
+
+
+} // namespace mozilla
+
+#endif // ifndef mozilla_DeadlockDetector_h
diff --git a/xpcom/glue/EnumeratedArrayCycleCollection.h b/xpcom/glue/EnumeratedArrayCycleCollection.h
new file mode 100644
index 000000000..faabb5a52
--- /dev/null
+++ b/xpcom/glue/EnumeratedArrayCycleCollection.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef EnumeratedArrayCycleCollection_h_
+#define EnumeratedArrayCycleCollection_h_
+
+#include "mozilla/EnumeratedArray.h"
+#include "nsCycleCollectionTraversalCallback.h"
+
+template<typename IndexType,
+ IndexType SizeAsEnumValue,
+ typename ValueType>
+inline void
+ImplCycleCollectionUnlink(mozilla::EnumeratedArray<IndexType,
+ SizeAsEnumValue,
+ ValueType>& aField)
+{
+ for (size_t i = 0; i < size_t(SizeAsEnumValue); ++i) {
+ aField[IndexType(i)] = nullptr;
+ }
+}
+
+template<typename IndexType,
+ IndexType SizeAsEnumValue,
+ typename ValueType>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ mozilla::EnumeratedArray<IndexType,
+ SizeAsEnumValue,
+ ValueType>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ for (size_t i = 0; i < size_t(SizeAsEnumValue); ++i) {
+ ImplCycleCollectionTraverse(aCallback, aField[IndexType(i)], aName, aFlags);
+ }
+}
+
+#endif // EnumeratedArrayCycleCollection_h_
diff --git a/xpcom/glue/FileUtils.cpp b/xpcom/glue/FileUtils.cpp
new file mode 100644
index 000000000..699812461
--- /dev/null
+++ b/xpcom/glue/FileUtils.cpp
@@ -0,0 +1,568 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "private/pprio.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/FileUtils.h"
+
+#if defined(XP_MACOSX)
+#include <fcntl.h>
+#include <unistd.h>
+#include <mach/machine.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <limits.h>
+#elif defined(XP_UNIX)
+#include <fcntl.h>
+#include <unistd.h>
+#if defined(LINUX)
+#include <elf.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#endif
+
+// Functions that are not to be used in standalone glue must be implemented
+// within this #if block
+#if !defined(XPCOM_GLUE)
+
+bool
+mozilla::fallocate(PRFileDesc* aFD, int64_t aLength)
+{
+#if defined(HAVE_POSIX_FALLOCATE)
+ return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
+#elif defined(XP_WIN)
+ int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
+ if (oldpos == -1) {
+ return false;
+ }
+
+ if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
+ return false;
+ }
+
+ bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
+
+ PR_Seek64(aFD, oldpos, PR_SEEK_SET);
+ return retval;
+#elif defined(XP_MACOSX)
+ int fd = PR_FileDesc2NativeHandle(aFD);
+ fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
+ // Try to get a continous chunk of disk space
+ int ret = fcntl(fd, F_PREALLOCATE, &store);
+ if (ret == -1) {
+ // OK, perhaps we are too fragmented, allocate non-continuous
+ store.fst_flags = F_ALLOCATEALL;
+ ret = fcntl(fd, F_PREALLOCATE, &store);
+ if (ret == -1) {
+ return false;
+ }
+ }
+ return ftruncate(fd, aLength) == 0;
+#elif defined(XP_UNIX)
+ // The following is copied from fcntlSizeHint in sqlite
+ /* If the OS does not have posix_fallocate(), fake it. First use
+ ** ftruncate() to set the file size, then write a single byte to
+ ** the last byte in each block within the extended region. This
+ ** is the same technique used by glibc to implement posix_fallocate()
+ ** on systems that do not have a real fallocate() system call.
+ */
+ int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
+ if (oldpos == -1) {
+ return false;
+ }
+
+ struct stat buf;
+ int fd = PR_FileDesc2NativeHandle(aFD);
+ if (fstat(fd, &buf)) {
+ return false;
+ }
+
+ if (buf.st_size >= aLength) {
+ return false;
+ }
+
+ const int nBlk = buf.st_blksize;
+
+ if (!nBlk) {
+ return false;
+ }
+
+ if (ftruncate(fd, aLength)) {
+ return false;
+ }
+
+ int nWrite; // Return value from write()
+ int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to
+ while (iWrite < aLength) {
+ nWrite = 0;
+ if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
+ nWrite = PR_Write(aFD, "", 1);
+ }
+ if (nWrite != 1) {
+ break;
+ }
+ iWrite += nBlk;
+ }
+
+ PR_Seek64(aFD, oldpos, PR_SEEK_SET);
+ return nWrite == 1;
+#endif
+ return false;
+}
+
+#ifdef ReadSysFile_PRESENT
+
+bool
+mozilla::ReadSysFile(
+ const char* aFilename,
+ char* aBuf,
+ size_t aBufSize)
+{
+ int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_RDONLY));
+ if (fd < 0) {
+ return false;
+ }
+ ScopedClose autoClose(fd);
+ if (aBufSize == 0) {
+ return true;
+ }
+ ssize_t bytesRead;
+ size_t offset = 0;
+ do {
+ bytesRead = MOZ_TEMP_FAILURE_RETRY(read(fd, aBuf + offset,
+ aBufSize - offset));
+ if (bytesRead == -1) {
+ return false;
+ }
+ offset += bytesRead;
+ } while (bytesRead > 0 && offset < aBufSize);
+ MOZ_ASSERT(offset <= aBufSize);
+ if (offset > 0 && aBuf[offset - 1] == '\n') {
+ offset--;
+ }
+ if (offset == aBufSize) {
+ MOZ_ASSERT(offset > 0);
+ offset--;
+ }
+ aBuf[offset] = '\0';
+ return true;
+}
+
+bool
+mozilla::ReadSysFile(
+ const char* aFilename,
+ int* aVal)
+{
+ char valBuf[32];
+ if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) {
+ return false;
+ }
+ return sscanf(valBuf, "%d", aVal) == 1;
+}
+
+bool
+mozilla::ReadSysFile(
+ const char* aFilename,
+ bool* aVal)
+{
+ int v;
+ if (!ReadSysFile(aFilename, &v)) {
+ return false;
+ }
+ *aVal = (v != 0);
+ return true;
+}
+
+#endif /* ReadSysFile_PRESENT */
+
+#ifdef WriteSysFile_PRESENT
+
+bool
+mozilla::WriteSysFile(
+ const char* aFilename,
+ const char* aBuf)
+{
+ size_t aBufSize = strlen(aBuf);
+ int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_WRONLY));
+ if (fd < 0) {
+ return false;
+ }
+ ScopedClose autoClose(fd);
+ ssize_t bytesWritten;
+ size_t offset = 0;
+ do {
+ bytesWritten = MOZ_TEMP_FAILURE_RETRY(write(fd, aBuf + offset,
+ aBufSize - offset));
+ if (bytesWritten == -1) {
+ return false;
+ }
+ offset += bytesWritten;
+ } while (bytesWritten > 0 && offset < aBufSize);
+ MOZ_ASSERT(offset == aBufSize);
+ return true;
+}
+
+#endif /* WriteSysFile_PRESENT */
+
+void
+mozilla::ReadAheadLib(nsIFile* aFile)
+{
+#if defined(XP_WIN)
+ nsAutoString path;
+ if (!aFile || NS_FAILED(aFile->GetPath(path))) {
+ return;
+ }
+ ReadAheadLib(path.get());
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ nsAutoCString nativePath;
+ if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
+ return;
+ }
+ ReadAheadLib(nativePath.get());
+#endif
+}
+
+void
+mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
+ const size_t aCount, mozilla::filedesc_t* aOutFd)
+{
+#if defined(XP_WIN)
+ nsAutoString path;
+ if (!aFile || NS_FAILED(aFile->GetPath(path))) {
+ return;
+ }
+ ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ nsAutoCString nativePath;
+ if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
+ return;
+ }
+ ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
+#endif
+}
+
+#endif // !defined(XPCOM_GLUE)
+
+#if defined(LINUX) && !defined(ANDROID)
+
+static const unsigned int bufsize = 4096;
+
+#ifdef __LP64__
+typedef Elf64_Ehdr Elf_Ehdr;
+typedef Elf64_Phdr Elf_Phdr;
+static const unsigned char ELFCLASS = ELFCLASS64;
+typedef Elf64_Off Elf_Off;
+#else
+typedef Elf32_Ehdr Elf_Ehdr;
+typedef Elf32_Phdr Elf_Phdr;
+static const unsigned char ELFCLASS = ELFCLASS32;
+typedef Elf32_Off Elf_Off;
+#endif
+
+#elif defined(XP_MACOSX)
+
+#if defined(__i386__)
+static const uint32_t CPU_TYPE = CPU_TYPE_X86;
+#elif defined(__x86_64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
+#elif defined(__ppc__)
+static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
+#elif defined(__ppc64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
+#else
+#error Unsupported CPU type
+#endif
+
+#ifdef __LP64__
+#undef LC_SEGMENT
+#define LC_SEGMENT LC_SEGMENT_64
+#undef MH_MAGIC
+#define MH_MAGIC MH_MAGIC_64
+#define cpu_mach_header mach_header_64
+#define segment_command segment_command_64
+#else
+#define cpu_mach_header mach_header
+#endif
+
+class ScopedMMap
+{
+public:
+ explicit ScopedMMap(const char* aFilePath)
+ : buf(nullptr)
+ {
+ fd = open(aFilePath, O_RDONLY);
+ if (fd < 0) {
+ return;
+ }
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ return;
+ }
+ size = st.st_size;
+ buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ }
+ ~ScopedMMap()
+ {
+ if (buf) {
+ munmap(buf, size);
+ }
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+ operator char*() { return buf; }
+ int getFd() { return fd; }
+private:
+ int fd;
+ char* buf;
+ size_t size;
+};
+#endif
+
+void
+mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
+ const size_t aCount)
+{
+#if defined(XP_WIN)
+
+ LARGE_INTEGER fpOriginal;
+ LARGE_INTEGER fpOffset;
+#if defined(HAVE_LONG_LONG)
+ fpOffset.QuadPart = 0;
+#else
+ fpOffset.u.LowPart = 0;
+ fpOffset.u.HighPart = 0;
+#endif
+
+ // Get the current file pointer so that we can restore it. This isn't
+ // really necessary other than to provide the same semantics regarding the
+ // file pointer that other platforms do
+ if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
+ return;
+ }
+
+ if (aOffset) {
+#if defined(HAVE_LONG_LONG)
+ fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
+#else
+ fpOffset.u.LowPart = aOffset;
+ fpOffset.u.HighPart = 0;
+#endif
+
+ if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
+ return;
+ }
+ }
+
+ char buf[64 * 1024];
+ size_t totalBytesRead = 0;
+ DWORD dwBytesRead;
+ // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
+ // Abort when underfilling because during testing the buffers are read fully
+ // A buffer that's not keeping up would imply that readahead isn't working right
+ while (totalBytesRead < aCount &&
+ ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
+ dwBytesRead == sizeof(buf)) {
+ totalBytesRead += dwBytesRead;
+ }
+
+ // Restore the file pointer
+ SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
+
+#elif defined(LINUX) && !defined(ANDROID)
+
+ readahead(aFd, aOffset, aCount);
+
+#elif defined(XP_MACOSX)
+
+ struct radvisory ra;
+ ra.ra_offset = aOffset;
+ ra.ra_count = aCount;
+ // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
+ fcntl(aFd, F_RDADVISE, &ra);
+
+#endif
+}
+
+void
+mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath)
+{
+ if (!aFilePath) {
+ return;
+ }
+#if defined(XP_WIN)
+ ReadAheadFile(aFilePath);
+#elif defined(LINUX) && !defined(ANDROID)
+ int fd = open(aFilePath, O_RDONLY);
+ if (fd < 0) {
+ return;
+ }
+
+ union
+ {
+ char buf[bufsize];
+ Elf_Ehdr ehdr;
+ } elf;
+ // Read ELF header (ehdr) and program header table (phdr).
+ // We check that the ELF magic is found, that the ELF class matches
+ // our own, and that the program header table as defined in the ELF
+ // headers fits in the buffer we read.
+ if ((read(fd, elf.buf, bufsize) <= 0) ||
+ (memcmp(elf.buf, ELFMAG, 4)) ||
+ (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
+ // Upcast e_phentsize so the multiplication is done in the same precision
+ // as the subsequent addition, to satisfy static analyzers and avoid
+ // issues with abnormally large program header tables.
+ (elf.ehdr.e_phoff + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) *
+ elf.ehdr.e_phnum) >= bufsize)) {
+ close(fd);
+ return;
+ }
+ // The program header table contains segment definitions. One such
+ // segment type is PT_LOAD, which describes how the dynamic loader
+ // is going to map the file in memory. We use that information to
+ // find the biggest offset from the library that will be mapped in
+ // memory.
+ Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
+ Elf_Off end = 0;
+ for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
+ if ((phdr->p_type == PT_LOAD) &&
+ (end < phdr->p_offset + phdr->p_filesz)) {
+ end = phdr->p_offset + phdr->p_filesz;
+ }
+ }
+ // Let the kernel read ahead what the dynamic loader is going to
+ // map in memory soon after.
+ if (end > 0) {
+ ReadAhead(fd, 0, end);
+ }
+ close(fd);
+#elif defined(XP_MACOSX)
+ ScopedMMap buf(aFilePath);
+ char* base = buf;
+ if (!base) {
+ return;
+ }
+
+ // An OSX binary might either be a fat (universal) binary or a
+ // Mach-O binary. A fat binary actually embeds several Mach-O
+ // binaries. If we have a fat binary, find the offset where the
+ // Mach-O binary for our CPU type can be found.
+ struct fat_header* fh = (struct fat_header*)base;
+
+ if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
+ uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
+ struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
+ for (; nfat_arch; arch++, nfat_arch--) {
+ if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
+ base += OSSwapBigToHostInt32(arch->offset);
+ break;
+ }
+ }
+ if (base == buf) {
+ return;
+ }
+ }
+
+ // Check Mach-O magic in the Mach header
+ struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
+ if (mh->magic != MH_MAGIC) {
+ return;
+ }
+
+ // The Mach header is followed by a sequence of load commands.
+ // Each command has a header containing the command type and the
+ // command size. LD_SEGMENT commands describes how the dynamic
+ // loader is going to map the file in memory. We use that
+ // information to find the biggest offset from the library that
+ // will be mapped in memory.
+ char* cmd = &base[sizeof(struct cpu_mach_header)];
+ uint32_t end = 0;
+ for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
+ struct segment_command* sh = (struct segment_command*)cmd;
+ if (sh->cmd != LC_SEGMENT) {
+ continue;
+ }
+ if (end < sh->fileoff + sh->filesize) {
+ end = sh->fileoff + sh->filesize;
+ }
+ cmd += sh->cmdsize;
+ }
+ // Let the kernel read ahead what the dynamic loader is going to
+ // map in memory soon after.
+ if (end > 0) {
+ ReadAhead(buf.getFd(), base - buf, end);
+ }
+#endif
+}
+
+void
+mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
+ const size_t aCount, mozilla::filedesc_t* aOutFd)
+{
+#if defined(XP_WIN)
+ if (!aFilePath) {
+ if (aOutFd) {
+ *aOutFd = INVALID_HANDLE_VALUE;
+ }
+ return;
+ }
+ HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
+ if (aOutFd) {
+ *aOutFd = fd;
+ }
+ if (fd == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ ReadAhead(fd, aOffset, aCount);
+ if (!aOutFd) {
+ CloseHandle(fd);
+ }
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ if (!aFilePath) {
+ if (aOutFd) {
+ *aOutFd = -1;
+ }
+ return;
+ }
+ int fd = open(aFilePath, O_RDONLY);
+ if (aOutFd) {
+ *aOutFd = fd;
+ }
+ if (fd < 0) {
+ return;
+ }
+ size_t count;
+ if (aCount == SIZE_MAX) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ if (!aOutFd) {
+ close(fd);
+ }
+ return;
+ }
+ count = st.st_size;
+ } else {
+ count = aCount;
+ }
+ ReadAhead(fd, aOffset, count);
+ if (!aOutFd) {
+ close(fd);
+ }
+#endif
+}
+
diff --git a/xpcom/glue/FileUtils.h b/xpcom/glue/FileUtils.h
new file mode 100644
index 000000000..aaf912b21
--- /dev/null
+++ b/xpcom/glue/FileUtils.h
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_FileUtils_h
+#define mozilla_FileUtils_h
+
+#include "nscore.h" // nullptr
+
+#if defined(XP_UNIX)
+# include <unistd.h>
+#elif defined(XP_WIN)
+# include <io.h>
+#endif
+#include "prio.h"
+
+#include "mozilla/Scoped.h"
+#include "nsIFile.h"
+#include <errno.h>
+#include <limits.h>
+
+namespace mozilla {
+
+#if defined(XP_WIN)
+typedef void* filedesc_t;
+typedef const wchar_t* pathstr_t;
+#else
+typedef int filedesc_t;
+typedef const char* pathstr_t;
+#endif
+
+/**
+ * ScopedCloseFD is a RAII wrapper for POSIX file descriptors
+ *
+ * Instances |close()| their fds when they go out of scope.
+ */
+struct ScopedCloseFDTraits
+{
+ typedef int type;
+ static type empty() { return -1; }
+ static void release(type aFd)
+ {
+ if (aFd != -1) {
+ while (close(aFd) == -1 && errno == EINTR) {
+ }
+ }
+ }
+};
+typedef Scoped<ScopedCloseFDTraits> ScopedClose;
+
+#if !defined(XPCOM_GLUE)
+
+/**
+ * AutoFDClose is a RAII wrapper for PRFileDesc.
+ *
+ * Instances |PR_Close| their fds when they go out of scope.
+ **/
+struct ScopedClosePRFDTraits
+{
+ typedef PRFileDesc* type;
+ static type empty() { return nullptr; }
+ static void release(type aFd)
+ {
+ if (aFd) {
+ PR_Close(aFd);
+ }
+ }
+};
+typedef Scoped<ScopedClosePRFDTraits> AutoFDClose;
+
+/* RAII wrapper for FILE descriptors */
+struct ScopedCloseFileTraits
+{
+ typedef FILE* type;
+ static type empty() { return nullptr; }
+ static void release(type aFile)
+ {
+ if (aFile) {
+ fclose(aFile);
+ }
+ }
+};
+typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile;
+
+/**
+ * Fallocate efficiently and continuously allocates files via fallocate-type APIs.
+ * This is useful for avoiding fragmentation.
+ * On sucess the file be padded with zeros to grow to aLength.
+ *
+ * @param aFD file descriptor.
+ * @param aLength length of file to grow to.
+ * @return true on success.
+ */
+bool fallocate(PRFileDesc* aFD, int64_t aLength);
+
+/**
+ * Use readahead to preload shared libraries into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFile nsIFile representing path to shared library
+ */
+void ReadAheadLib(nsIFile* aFile);
+
+/**
+ * Use readahead to preload a file into the file cache before reading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFile nsIFile representing path to shared library
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will
+ * return its internal, opened file descriptor instead of closing it.
+ */
+void ReadAheadFile(nsIFile* aFile, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX,
+ filedesc_t* aOutFd = nullptr);
+
+#endif // !defined(XPCOM_GLUE)
+
+/**
+ * Use readahead to preload shared libraries into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFilePath path to shared library
+ */
+void ReadAheadLib(pathstr_t aFilePath);
+
+/**
+ * Use readahead to preload a file into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFilePath path to shared library
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will
+ * return its internal, opened file descriptor instead of closing it.
+ */
+void ReadAheadFile(pathstr_t aFilePath, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX,
+ filedesc_t* aOutFd = nullptr);
+
+/**
+ * Use readahead to preload a file into the file cache before reading.
+ * When this function exits, the file pointer is guaranteed to be in the same
+ * position it was in before this function was called.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFd file descriptor opened for read access
+ * (on Windows, file must be opened with FILE_FLAG_SEQUENTIAL_SCAN)
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ */
+void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX);
+
+
+#if defined(MOZ_WIDGET_GONK) || defined(XP_UNIX)
+#define MOZ_TEMP_FAILURE_RETRY(exp) (__extension__({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+}))
+#endif
+
+/* Define ReadSysFile() and WriteSysFile() only on GONK to avoid unnecessary
+ * libxul bloat. Also define it in debug builds, so that unit tests for it can
+ * be written and run in non-GONK builds. */
+#if (defined(MOZ_WIDGET_GONK) || defined(DEBUG)) && defined(XP_UNIX)
+
+#ifndef ReadSysFile_PRESENT
+#define ReadSysFile_PRESENT
+#endif /* ReadSysFile_PRESENT */
+
+#ifndef WriteSysFile_PRESENT
+#define WriteSysFile_PRESENT
+#endif /* WriteSysFile_PRESENT */
+
+/**
+ * Read the contents of a file.
+ * This function is intended for reading a single-lined text files from
+ * /sys/. If the file ends with a newline ('\n') then it will be discarded.
+ * The output buffer will always be '\0'-terminated on successful completion.
+ * If aBufSize == 0, then this function will return true if the file exists
+ * and is readable (it will not attempt to read anything from it).
+ * On failure the contents of aBuf after this call will be undefined and the
+ * value of the global variable errno will be set accordingly.
+ * @return true on success, notice that less than requested bytes could have
+ * been read if the file was smaller
+ */
+bool ReadSysFile(const char* aFilename, char* aBuf, size_t aBufSize);
+
+/**
+ * Parse the contents of a file, assuming it contains a decimal integer.
+ * @return true on success
+ */
+bool ReadSysFile(const char* aFilename, int* aVal);
+
+/**
+ * Parse the contents of a file, assuming it contains a boolean value
+ * (either 0 or 1).
+ * @return true on success
+ */
+bool ReadSysFile(const char* aFilename, bool* aVal);
+
+bool WriteSysFile(const char* aFilename, const char* aBuf);
+
+#endif /* (MOZ_WIDGET_GONK || DEBUG) && XP_UNIX */
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/GenericFactory.cpp b/xpcom/glue/GenericFactory.cpp
new file mode 100644
index 000000000..5bf0b97a4
--- /dev/null
+++ b/xpcom/glue/GenericFactory.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/GenericFactory.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(GenericFactory, nsIFactory)
+
+NS_IMETHODIMP
+GenericFactory::CreateInstance(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ return mCtor(aOuter, aIID, aResult);
+}
+
+NS_IMETHODIMP
+GenericFactory::LockFactory(bool aLock)
+{
+ NS_ERROR("Vestigial method, never called!");
+ return NS_ERROR_FAILURE;
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/GenericFactory.h b/xpcom/glue/GenericFactory.h
new file mode 100644
index 000000000..4561f2a2d
--- /dev/null
+++ b/xpcom/glue/GenericFactory.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_GenericFactory_h
+#define mozilla_GenericFactory_h
+
+#include "mozilla/Attributes.h"
+
+#include "mozilla/Module.h"
+
+namespace mozilla {
+
+/**
+ * A generic factory which uses a constructor function to create instances.
+ * This class is intended for use by the component manager and the generic
+ * module.
+ */
+class GenericFactory final : public nsIFactory
+{
+ ~GenericFactory() {}
+
+public:
+ typedef Module::ConstructorProcPtr ConstructorProcPtr;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIFACTORY
+
+ explicit GenericFactory(ConstructorProcPtr aCtor)
+ : mCtor(aCtor)
+ {
+ NS_ASSERTION(mCtor, "GenericFactory with no constructor");
+ }
+
+private:
+ ConstructorProcPtr mCtor;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_GenericFactory_h
diff --git a/xpcom/glue/GenericModule.cpp b/xpcom/glue/GenericModule.cpp
new file mode 100644
index 000000000..0b5cd3a7c
--- /dev/null
+++ b/xpcom/glue/GenericModule.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/GenericFactory.h"
+
+#include "nsICategoryManager.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPCOMCID.h"
+#include "nsStringAPI.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(GenericModule, nsIModule)
+
+NS_IMETHODIMP
+GenericModule::GetClassObject(nsIComponentManager* aCompMgr,
+ const nsCID& aCID,
+ const nsIID& aIID,
+ void** aResult)
+{
+ for (const Module::CIDEntry* e = mData->mCIDs; e->cid; ++e) {
+ if (e->cid->Equals(aCID)) {
+ nsCOMPtr<nsIFactory> f;
+ if (e->getFactoryProc) {
+ f = e->getFactoryProc(*mData, *e);
+ } else {
+ NS_ASSERTION(e->constructorProc, "No constructor proc?");
+ f = new GenericFactory(e->constructorProc);
+ }
+ if (!f) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return f->QueryInterface(aIID, aResult);
+ }
+ }
+ NS_ERROR("Asking a module for a CID it doesn't implement.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericModule::RegisterSelf(nsIComponentManager* aCompMgr,
+ nsIFile* aLocation,
+ const char* aLoaderStr,
+ const char* aType)
+{
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(aCompMgr);
+ for (const Module::CIDEntry* e = mData->mCIDs; e->cid; ++e) {
+ registrar->RegisterFactoryLocation(*e->cid, "", nullptr, aLocation,
+ aLoaderStr, aType);
+ }
+
+ for (const Module::ContractIDEntry* e = mData->mContractIDs;
+ e && e->contractid;
+ ++e) {
+ registrar->RegisterFactoryLocation(*e->cid, "", e->contractid, aLocation,
+ aLoaderStr, aType);
+ }
+
+ nsCOMPtr<nsICategoryManager> catman;
+ for (const Module::CategoryEntry* e = mData->mCategoryEntries;
+ e && e->category;
+ ++e) {
+ if (!catman) {
+ catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ }
+
+ nsAutoCString prevValue;
+ catman->AddCategoryEntry(e->category, e->entry, e->value, true, true,
+ getter_Copies(prevValue));
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GenericModule::UnregisterSelf(nsIComponentManager* aCompMgr,
+ nsIFile* aFile,
+ const char* aLoaderStr)
+{
+ NS_ERROR("Nobody should ever call UnregisterSelf!");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericModule::CanUnload(nsIComponentManager* aCompMgr, bool* aResult)
+{
+ NS_ERROR("Nobody should ever call CanUnload!");
+ *aResult = false;
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/IntentionalCrash.h b/xpcom/glue/IntentionalCrash.h
new file mode 100644
index 000000000..d01006dff
--- /dev/null
+++ b/xpcom/glue/IntentionalCrash.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string>
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#ifndef mozilla_IntentionalCrash_h
+#define mozilla_IntentionalCrash_h
+
+namespace mozilla {
+
+inline void
+NoteIntentionalCrash(const char* aProcessType)
+{
+ char* f = getenv("XPCOM_MEM_BLOAT_LOG");
+ if (!f) {
+ return;
+ }
+
+ fprintf(stderr, "XPCOM_MEM_BLOAT_LOG: %s\n", f);
+
+ std::string bloatLog(f);
+
+ bool hasExt = false;
+ if (bloatLog.size() >= 4 &&
+ bloatLog.compare(bloatLog.size() - 4, 4, ".log", 4) == 0) {
+ hasExt = true;
+ bloatLog.erase(bloatLog.size() - 4, 4);
+ }
+
+ std::ostringstream bloatName;
+ bloatName << bloatLog << "_" << aProcessType << "_pid" << getpid();
+ if (hasExt) {
+ bloatName << ".log";
+ }
+
+ fprintf(stderr, "Writing to log: %s\n", bloatName.str().c_str());
+
+ FILE* processfd = fopen(bloatName.str().c_str(), "a");
+ fprintf(processfd, "==> process %d will purposefully crash\n", getpid());
+ fclose(processfd);
+}
+
+} // namespace mozilla
+
+#endif // mozilla_IntentionalCrash_h
diff --git a/xpcom/glue/MainThreadUtils.h b/xpcom/glue/MainThreadUtils.h
new file mode 100644
index 000000000..8d76f84ec
--- /dev/null
+++ b/xpcom/glue/MainThreadUtils.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MainThreadUtils_h_
+#define MainThreadUtils_h_
+
+#include "nscore.h"
+
+class nsIThread;
+
+/**
+ * Get a reference to the main thread.
+ *
+ * @param aResult
+ * The resulting nsIThread object.
+ */
+extern nsresult NS_GetMainThread(nsIThread** aResult);
+
+#ifdef MOZILLA_INTERNAL_API
+// Fast access to the current thread. Do not release the returned pointer! If
+// you want to use this pointer from some other thread, then you will need to
+// AddRef it. Otherwise, you should only consider this pointer valid from code
+// running on the current thread.
+extern nsIThread* NS_GetCurrentThread();
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+bool NS_IsMainThread();
+#else
+/**
+ * Test to see if the current thread is the main thread.
+ *
+ * @returns true if the current thread is the main thread, and false
+ * otherwise.
+ */
+extern bool NS_IsMainThread();
+#endif
+
+#endif // MainThreadUtils_h_
diff --git a/xpcom/glue/Monitor.h b/xpcom/glue/Monitor.h
new file mode 100644
index 000000000..60750acb3
--- /dev/null
+++ b/xpcom/glue/Monitor.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Monitor_h
+#define mozilla_Monitor_h
+
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+
+/**
+ * Monitor provides a *non*-reentrant monitor: *not* a Java-style
+ * monitor. If your code needs support for reentrancy, use
+ * ReentrantMonitor instead. (Rarely should reentrancy be needed.)
+ *
+ * Instead of directly calling Monitor methods, it's safer and simpler
+ * to instead use the RAII wrappers MonitorAutoLock and
+ * MonitorAutoUnlock.
+ */
+class Monitor
+{
+public:
+ explicit Monitor(const char* aName)
+ : mMutex(aName)
+ , mCondVar(mMutex, "[Monitor.mCondVar]")
+ {
+ }
+
+ ~Monitor() {}
+
+ void Lock() { mMutex.Lock(); }
+ void Unlock() { mMutex.Unlock(); }
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mCondVar.Wait(aInterval);
+ }
+
+ nsresult Notify() { return mCondVar.Notify(); }
+ nsresult NotifyAll() { return mCondVar.NotifyAll(); }
+
+ void AssertCurrentThreadOwns() const
+ {
+ mMutex.AssertCurrentThreadOwns();
+ }
+
+ void AssertNotCurrentThreadOwns() const
+ {
+ mMutex.AssertNotCurrentThreadOwns();
+ }
+
+private:
+ Monitor();
+ Monitor(const Monitor&);
+ Monitor& operator=(const Monitor&);
+
+ Mutex mMutex;
+ CondVar mCondVar;
+};
+
+/**
+ * Lock the monitor for the lexical scope instances of this class are
+ * bound to (except for MonitorAutoUnlock in nested scopes).
+ *
+ * The monitor must be unlocked when instances of this class are
+ * created.
+ */
+class MOZ_STACK_CLASS MonitorAutoLock
+{
+public:
+ explicit MonitorAutoLock(Monitor& aMonitor)
+ : mMonitor(&aMonitor)
+ {
+ mMonitor->Lock();
+ }
+
+ ~MonitorAutoLock()
+ {
+ mMonitor->Unlock();
+ }
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mMonitor->Wait(aInterval);
+ }
+
+ nsresult Notify() { return mMonitor->Notify(); }
+ nsresult NotifyAll() { return mMonitor->NotifyAll(); }
+
+private:
+ MonitorAutoLock();
+ MonitorAutoLock(const MonitorAutoLock&);
+ MonitorAutoLock& operator=(const MonitorAutoLock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ Monitor* mMonitor;
+};
+
+/**
+ * Unlock the monitor for the lexical scope instances of this class
+ * are bound to (except for MonitorAutoLock in nested scopes).
+ *
+ * The monitor must be locked by the current thread when instances of
+ * this class are created.
+ */
+class MOZ_STACK_CLASS MonitorAutoUnlock
+{
+public:
+ explicit MonitorAutoUnlock(Monitor& aMonitor)
+ : mMonitor(&aMonitor)
+ {
+ mMonitor->Unlock();
+ }
+
+ ~MonitorAutoUnlock()
+ {
+ mMonitor->Lock();
+ }
+
+private:
+ MonitorAutoUnlock();
+ MonitorAutoUnlock(const MonitorAutoUnlock&);
+ MonitorAutoUnlock& operator=(const MonitorAutoUnlock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ Monitor* mMonitor;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_Monitor_h
diff --git a/xpcom/glue/Mutex.h b/xpcom/glue/Mutex.h
new file mode 100644
index 000000000..16ad44f4c
--- /dev/null
+++ b/xpcom/glue/Mutex.h
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Mutex_h
+#define mozilla_Mutex_h
+
+#include "prlock.h"
+
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/GuardObjects.h"
+
+//
+// Provides:
+//
+// - Mutex, a non-recursive mutex
+// - MutexAutoLock, an RAII class for ensuring that Mutexes are properly
+// locked and unlocked
+// - MutexAutoUnlock, complementary sibling to MutexAutoLock
+//
+// - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
+// - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
+// an OffTheBooksMutex.
+//
+// Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
+// calls to Lock and Unlock.
+//
+namespace mozilla {
+
+/**
+ * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
+ * include leak checking. Sometimes you want to intentionally "leak" a mutex
+ * until shutdown; in these cases, OffTheBooksMutex is for you.
+ */
+class OffTheBooksMutex : BlockingResourceBase
+{
+public:
+ /**
+ * @param aName A name which can reference this lock
+ * @returns If failure, nullptr
+ * If success, a valid Mutex* which must be destroyed
+ * by Mutex::DestroyMutex()
+ **/
+ explicit OffTheBooksMutex(const char* aName)
+ : BlockingResourceBase(aName, eMutex)
+ {
+ mLock = PR_NewLock();
+ if (!mLock) {
+ NS_RUNTIMEABORT("Can't allocate mozilla::Mutex");
+ }
+ }
+
+ ~OffTheBooksMutex()
+ {
+ NS_ASSERTION(mLock,
+ "improperly constructed Lock or double free");
+ // NSPR does consistency checks for us
+ PR_DestroyLock(mLock);
+ mLock = 0;
+ }
+
+#ifndef DEBUG
+ /**
+ * Lock
+ * @see prlock.h
+ **/
+ void Lock() { PR_Lock(mLock); }
+
+ /**
+ * Unlock
+ * @see prlock.h
+ **/
+ void Unlock() { PR_Unlock(mLock); }
+
+ /**
+ * AssertCurrentThreadOwns
+ * @see prlock.h
+ **/
+ void AssertCurrentThreadOwns() const {}
+
+ /**
+ * AssertNotCurrentThreadOwns
+ * @see prlock.h
+ **/
+ void AssertNotCurrentThreadOwns() const {}
+
+#else
+ void Lock();
+ void Unlock();
+
+ void AssertCurrentThreadOwns() const
+ {
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mLock);
+ }
+
+ void AssertNotCurrentThreadOwns() const
+ {
+ // FIXME bug 476536
+ }
+
+#endif // ifndef DEBUG
+
+private:
+ OffTheBooksMutex();
+ OffTheBooksMutex(const OffTheBooksMutex&);
+ OffTheBooksMutex& operator=(const OffTheBooksMutex&);
+
+ PRLock* mLock;
+
+ friend class CondVar;
+
+ // MozPromise needs to access mLock for debugging purpose.
+ template<typename, typename, bool>
+ friend class MozPromise;
+};
+
+/**
+ * Mutex
+ * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
+ * mutex within a scope, instead of calling Lock/Unlock directly.
+ */
+class Mutex : public OffTheBooksMutex
+{
+public:
+ explicit Mutex(const char* aName)
+ : OffTheBooksMutex(aName)
+ {
+ MOZ_COUNT_CTOR(Mutex);
+ }
+
+ ~Mutex()
+ {
+ MOZ_COUNT_DTOR(Mutex);
+ }
+
+private:
+ Mutex();
+ Mutex(const Mutex&);
+ Mutex& operator=(const Mutex&);
+};
+
+/**
+ * MutexAutoLock
+ * Acquires the Mutex when it enters scope, and releases it when it leaves
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
+ */
+template<typename T>
+class MOZ_RAII BaseAutoLock
+{
+public:
+ /**
+ * Constructor
+ * The constructor aquires the given lock. The destructor
+ * releases the lock.
+ *
+ * @param aLock A valid mozilla::Mutex* returned by
+ * mozilla::Mutex::NewMutex.
+ **/
+ explicit BaseAutoLock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLock(&aLock)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ NS_ASSERTION(mLock, "null mutex");
+ mLock->Lock();
+ }
+
+ ~BaseAutoLock(void)
+ {
+ mLock->Unlock();
+ }
+
+private:
+ BaseAutoLock();
+ BaseAutoLock(BaseAutoLock&);
+ BaseAutoLock& operator=(BaseAutoLock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ T* mLock;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+typedef BaseAutoLock<Mutex> MutexAutoLock;
+typedef BaseAutoLock<OffTheBooksMutex> OffTheBooksMutexAutoLock;
+
+/**
+ * MutexAutoUnlock
+ * Releases the Mutex when it enters scope, and re-acquires it when it leaves
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
+ */
+template<typename T>
+class MOZ_RAII BaseAutoUnlock
+{
+public:
+ explicit BaseAutoUnlock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLock(&aLock)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ NS_ASSERTION(mLock, "null lock");
+ mLock->Unlock();
+ }
+
+ ~BaseAutoUnlock()
+ {
+ mLock->Lock();
+ }
+
+private:
+ BaseAutoUnlock();
+ BaseAutoUnlock(BaseAutoUnlock&);
+ BaseAutoUnlock& operator=(BaseAutoUnlock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ T* mLock;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+typedef BaseAutoUnlock<Mutex> MutexAutoUnlock;
+typedef BaseAutoUnlock<OffTheBooksMutex> OffTheBooksMutexAutoUnlock;
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_Mutex_h
diff --git a/xpcom/glue/Observer.h b/xpcom/glue/Observer.h
new file mode 100644
index 000000000..958e5e4a9
--- /dev/null
+++ b/xpcom/glue/Observer.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Observer_h
+#define mozilla_Observer_h
+
+#include "nsTArray.h"
+
+namespace mozilla {
+
+/**
+ * Observer<T> provides a way for a class to observe something.
+ * When an event has to be broadcasted to all Observer<T>, Notify() method
+ * is called.
+ * T represents the type of the object passed in argument to Notify().
+ *
+ * @see ObserverList.
+ */
+template<class T>
+class Observer
+{
+public:
+ virtual ~Observer() {}
+ virtual void Notify(const T& aParam) = 0;
+};
+
+/**
+ * ObserverList<T> tracks Observer<T> and can notify them when Broadcast() is
+ * called.
+ * T represents the type of the object passed in argument to Broadcast() and
+ * sent to Observer<T> objects through Notify().
+ *
+ * @see Observer.
+ */
+template<class T>
+class ObserverList
+{
+public:
+ /**
+ * Note: When calling AddObserver, it's up to the caller to make sure the
+ * object isn't going to be release as long as RemoveObserver hasn't been
+ * called.
+ *
+ * @see RemoveObserver()
+ */
+ void AddObserver(Observer<T>* aObserver)
+ {
+ mObservers.AppendElement(aObserver);
+ }
+
+ /**
+ * Remove the observer from the observer list.
+ * @return Whether the observer has been found in the list.
+ */
+ bool RemoveObserver(Observer<T>* aObserver)
+ {
+ return mObservers.RemoveElement(aObserver);
+ }
+
+ uint32_t Length()
+ {
+ return mObservers.Length();
+ }
+
+ void Broadcast(const T& aParam)
+ {
+ nsTArray<Observer<T>*> observersCopy(mObservers);
+ uint32_t size = observersCopy.Length();
+ for (uint32_t i = 0; i < size; ++i) {
+ observersCopy[i]->Notify(aParam);
+ }
+ }
+
+protected:
+ nsTArray<Observer<T>*> mObservers;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_Observer_h
diff --git a/xpcom/glue/PLDHashTable.cpp b/xpcom/glue/PLDHashTable.cpp
new file mode 100644
index 000000000..6152e9000
--- /dev/null
+++ b/xpcom/glue/PLDHashTable.cpp
@@ -0,0 +1,801 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "PLDHashTable.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/OperatorNewExtensions.h"
+#include "nsAlgorithm.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ChaosMode.h"
+
+using namespace mozilla;
+
+#ifdef DEBUG
+
+class AutoReadOp
+{
+ Checker& mChk;
+public:
+ explicit AutoReadOp(Checker& aChk) : mChk(aChk) { mChk.StartReadOp(); }
+ ~AutoReadOp() { mChk.EndReadOp(); }
+};
+
+class AutoWriteOp
+{
+ Checker& mChk;
+public:
+ explicit AutoWriteOp(Checker& aChk) : mChk(aChk) { mChk.StartWriteOp(); }
+ ~AutoWriteOp() { mChk.EndWriteOp(); }
+};
+
+class AutoIteratorRemovalOp
+{
+ Checker& mChk;
+public:
+ explicit AutoIteratorRemovalOp(Checker& aChk)
+ : mChk(aChk)
+ {
+ mChk.StartIteratorRemovalOp();
+ }
+ ~AutoIteratorRemovalOp() { mChk.EndIteratorRemovalOp(); }
+};
+
+class AutoDestructorOp
+{
+ Checker& mChk;
+public:
+ explicit AutoDestructorOp(Checker& aChk)
+ : mChk(aChk)
+ {
+ mChk.StartDestructorOp();
+ }
+ ~AutoDestructorOp() { mChk.EndDestructorOp(); }
+};
+
+#endif
+
+/* static */ PLDHashNumber
+PLDHashTable::HashStringKey(const void* aKey)
+{
+ return HashString(static_cast<const char*>(aKey));
+}
+
+/* static */ PLDHashNumber
+PLDHashTable::HashVoidPtrKeyStub(const void* aKey)
+{
+ return (PLDHashNumber)(ptrdiff_t)aKey >> 2;
+}
+
+/* static */ bool
+PLDHashTable::MatchEntryStub(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
+
+ return stub->key == aKey;
+}
+
+/* static */ bool
+PLDHashTable::MatchStringKey(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
+
+ // XXX tolerate null keys on account of sloppy Mozilla callers.
+ return stub->key == aKey ||
+ (stub->key && aKey &&
+ strcmp((const char*)stub->key, (const char*)aKey) == 0);
+}
+
+/* static */ void
+PLDHashTable::MoveEntryStub(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo)
+{
+ memcpy(aTo, aFrom, aTable->mEntrySize);
+}
+
+/* static */ void
+PLDHashTable::ClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
+{
+ memset(aEntry, 0, aTable->mEntrySize);
+}
+
+static const PLDHashTableOps gStubOps = {
+ PLDHashTable::HashVoidPtrKeyStub,
+ PLDHashTable::MatchEntryStub,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+/* static */ const PLDHashTableOps*
+PLDHashTable::StubOps()
+{
+ return &gStubOps;
+}
+
+static bool
+SizeOfEntryStore(uint32_t aCapacity, uint32_t aEntrySize, uint32_t* aNbytes)
+{
+ uint64_t nbytes64 = uint64_t(aCapacity) * uint64_t(aEntrySize);
+ *aNbytes = aCapacity * aEntrySize;
+ return uint64_t(*aNbytes) == nbytes64; // returns false on overflow
+}
+
+// Compute max and min load numbers (entry counts). We have a secondary max
+// that allows us to overload a table reasonably if it cannot be grown further
+// (i.e. if ChangeTable() fails). The table slows down drastically if the
+// secondary max is too close to 1, but 0.96875 gives only a slight slowdown
+// while allowing 1.3x more elements.
+static inline uint32_t
+MaxLoad(uint32_t aCapacity)
+{
+ return aCapacity - (aCapacity >> 2); // == aCapacity * 0.75
+}
+static inline uint32_t
+MaxLoadOnGrowthFailure(uint32_t aCapacity)
+{
+ return aCapacity - (aCapacity >> 5); // == aCapacity * 0.96875
+}
+static inline uint32_t
+MinLoad(uint32_t aCapacity)
+{
+ return aCapacity >> 2; // == aCapacity * 0.25
+}
+
+// Compute the minimum capacity (and the Log2 of that capacity) for a table
+// containing |aLength| elements while respecting the following contraints:
+// - table must be at most 75% full;
+// - capacity must be a power of two;
+// - capacity cannot be too small.
+static inline void
+BestCapacity(uint32_t aLength, uint32_t* aCapacityOut,
+ uint32_t* aLog2CapacityOut)
+{
+ // Compute the smallest capacity allowing |aLength| elements to be inserted
+ // without rehashing.
+ uint32_t capacity = (aLength * 4 + (3 - 1)) / 3; // == ceil(aLength * 4 / 3)
+ if (capacity < PLDHashTable::kMinCapacity) {
+ capacity = PLDHashTable::kMinCapacity;
+ }
+
+ // Round up capacity to next power-of-two.
+ uint32_t log2 = CeilingLog2(capacity);
+ capacity = 1u << log2;
+ MOZ_ASSERT(capacity <= PLDHashTable::kMaxCapacity);
+
+ *aCapacityOut = capacity;
+ *aLog2CapacityOut = log2;
+}
+
+/* static */ MOZ_ALWAYS_INLINE uint32_t
+PLDHashTable::HashShift(uint32_t aEntrySize, uint32_t aLength)
+{
+ if (aLength > kMaxInitialLength) {
+ MOZ_CRASH("Initial length is too large");
+ }
+
+ uint32_t capacity, log2;
+ BestCapacity(aLength, &capacity, &log2);
+
+ uint32_t nbytes;
+ if (!SizeOfEntryStore(capacity, aEntrySize, &nbytes)) {
+ MOZ_CRASH("Initial entry store size is too large");
+ }
+
+ // Compute the hashShift value.
+ return kHashBits - log2;
+}
+
+PLDHashTable::PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
+ uint32_t aLength)
+ : mOps(aOps)
+ , mHashShift(HashShift(aEntrySize, aLength))
+ , mEntrySize(aEntrySize)
+ , mEntryCount(0)
+ , mRemovedCount(0)
+ , mEntryStore()
+#ifdef DEBUG
+ , mChecker()
+#endif
+{
+}
+
+PLDHashTable&
+PLDHashTable::operator=(PLDHashTable&& aOther)
+{
+ if (this == &aOther) {
+ return *this;
+ }
+
+ // Destruct |this|.
+ this->~PLDHashTable();
+
+ // |mOps| and |mEntrySize| are const so we can't assign them. Instead, we
+ // require that they are equal. The justification for this is that they're
+ // conceptually part of the type -- indeed, if PLDHashTable was a templated
+ // type like nsTHashtable, they *would* be part of the type -- so it only
+ // makes sense to assign in cases where they match.
+ MOZ_RELEASE_ASSERT(mOps == aOther.mOps);
+ MOZ_RELEASE_ASSERT(mEntrySize == aOther.mEntrySize);
+
+ // Move non-const pieces over.
+ mHashShift = Move(aOther.mHashShift);
+ mEntryCount = Move(aOther.mEntryCount);
+ mRemovedCount = Move(aOther.mRemovedCount);
+ mEntryStore = Move(aOther.mEntryStore);
+#ifdef DEBUG
+ mChecker = Move(aOther.mChecker);
+#endif
+
+ // Clear up |aOther| so its destruction will be a no-op.
+ {
+#ifdef DEBUG
+ AutoDestructorOp op(mChecker);
+#endif
+ aOther.mEntryStore.Set(nullptr);
+ }
+
+ return *this;
+}
+
+PLDHashNumber
+PLDHashTable::Hash1(PLDHashNumber aHash0)
+{
+ return aHash0 >> mHashShift;
+}
+
+// Double hashing needs the second hash code to be relatively prime to table
+// size, so we simply make hash2 odd.
+void
+PLDHashTable::Hash2(PLDHashNumber aHash,
+ uint32_t& aHash2Out, uint32_t& aSizeMaskOut)
+{
+ uint32_t sizeLog2 = kHashBits - mHashShift;
+ aHash2Out = ((aHash << sizeLog2) >> mHashShift) | 1;
+ aSizeMaskOut = (PLDHashNumber(1) << sizeLog2) - 1;
+}
+
+// Reserve mKeyHash 0 for free entries and 1 for removed-entry sentinels. Note
+// that a removed-entry sentinel need be stored only if the removed entry had
+// a colliding entry added after it. Therefore we can use 1 as the collision
+// flag in addition to the removed-entry sentinel value. Multiplicative hash
+// uses the high order bits of mKeyHash, so this least-significant reservation
+// should not hurt the hash function's effectiveness much.
+
+// Match an entry's mKeyHash against an unstored one computed from a key.
+/* static */ bool
+PLDHashTable::MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aKeyHash)
+{
+ return (aEntry->mKeyHash & ~kCollisionFlag) == aKeyHash;
+}
+
+// Compute the address of the indexed entry in table.
+PLDHashEntryHdr*
+PLDHashTable::AddressEntry(uint32_t aIndex)
+{
+ return reinterpret_cast<PLDHashEntryHdr*>(
+ mEntryStore.Get() + aIndex * mEntrySize);
+}
+
+PLDHashTable::~PLDHashTable()
+{
+#ifdef DEBUG
+ AutoDestructorOp op(mChecker);
+#endif
+
+ if (!mEntryStore.Get()) {
+ return;
+ }
+
+ // Clear any remaining live entries.
+ char* entryAddr = mEntryStore.Get();
+ char* entryLimit = entryAddr + Capacity() * mEntrySize;
+ while (entryAddr < entryLimit) {
+ PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
+ if (EntryIsLive(entry)) {
+ mOps->clearEntry(this, entry);
+ }
+ entryAddr += mEntrySize;
+ }
+
+ // Entry storage is freed last, by ~EntryStore().
+}
+
+void
+PLDHashTable::ClearAndPrepareForLength(uint32_t aLength)
+{
+ // Get these values before the destructor clobbers them.
+ const PLDHashTableOps* ops = mOps;
+ uint32_t entrySize = mEntrySize;
+
+ this->~PLDHashTable();
+ new (KnownNotNull, this) PLDHashTable(ops, entrySize, aLength);
+}
+
+void
+PLDHashTable::Clear()
+{
+ ClearAndPrepareForLength(kDefaultInitialLength);
+}
+
+// If |Reason| is |ForAdd|, the return value is always non-null and it may be
+// a previously-removed entry. If |Reason| is |ForSearchOrRemove|, the return
+// value is null on a miss, and will never be a previously-removed entry on a
+// hit. This distinction is a bit grotty but this function is hot enough that
+// these differences are worthwhile.
+template <PLDHashTable::SearchReason Reason>
+PLDHashEntryHdr* NS_FASTCALL
+PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+ NS_ASSERTION(!(aKeyHash & kCollisionFlag),
+ "!(aKeyHash & kCollisionFlag)");
+
+ // Compute the primary hash address.
+ PLDHashNumber hash1 = Hash1(aKeyHash);
+ PLDHashEntryHdr* entry = AddressEntry(hash1);
+
+ // Miss: return space for a new entry.
+ if (EntryIsFree(entry)) {
+ return (Reason == ForAdd) ? entry : nullptr;
+ }
+
+ // Hit: return entry.
+ PLDHashMatchEntry matchEntry = mOps->matchEntry;
+ if (MatchEntryKeyhash(entry, aKeyHash) &&
+ matchEntry(entry, aKey)) {
+ return entry;
+ }
+
+ // Collision: double hash.
+ PLDHashNumber hash2;
+ uint32_t sizeMask;
+ Hash2(aKeyHash, hash2, sizeMask);
+
+ // Save the first removed entry pointer so Add() can recycle it. (Only used
+ // if Reason==ForAdd.)
+ PLDHashEntryHdr* firstRemoved = nullptr;
+
+ for (;;) {
+ if (Reason == ForAdd) {
+ if (MOZ_UNLIKELY(EntryIsRemoved(entry))) {
+ if (!firstRemoved) {
+ firstRemoved = entry;
+ }
+ } else {
+ entry->mKeyHash |= kCollisionFlag;
+ }
+ }
+
+ hash1 -= hash2;
+ hash1 &= sizeMask;
+
+ entry = AddressEntry(hash1);
+ if (EntryIsFree(entry)) {
+ return (Reason == ForAdd) ? (firstRemoved ? firstRemoved : entry)
+ : nullptr;
+ }
+
+ if (MatchEntryKeyhash(entry, aKeyHash) &&
+ matchEntry(entry, aKey)) {
+ return entry;
+ }
+ }
+
+ // NOTREACHED
+ return nullptr;
+}
+
+// This is a copy of SearchTable(), used by ChangeTable(), hardcoded to
+// 1. assume |Reason| is |ForAdd|,
+// 2. assume that |aKey| will never match an existing entry, and
+// 3. assume that no entries have been removed from the current table
+// structure.
+// Avoiding the need for |aKey| means we can avoid needing a way to map entries
+// to keys, which means callers can use complex key types more easily.
+MOZ_ALWAYS_INLINE PLDHashEntryHdr*
+PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+ NS_ASSERTION(!(aKeyHash & kCollisionFlag),
+ "!(aKeyHash & kCollisionFlag)");
+
+ // Compute the primary hash address.
+ PLDHashNumber hash1 = Hash1(aKeyHash);
+ PLDHashEntryHdr* entry = AddressEntry(hash1);
+
+ // Miss: return space for a new entry.
+ if (EntryIsFree(entry)) {
+ return entry;
+ }
+
+ // Collision: double hash.
+ PLDHashNumber hash2;
+ uint32_t sizeMask;
+ Hash2(aKeyHash, hash2, sizeMask);
+
+ for (;;) {
+ NS_ASSERTION(!EntryIsRemoved(entry),
+ "!EntryIsRemoved(entry)");
+ entry->mKeyHash |= kCollisionFlag;
+
+ hash1 -= hash2;
+ hash1 &= sizeMask;
+
+ entry = AddressEntry(hash1);
+ if (EntryIsFree(entry)) {
+ return entry;
+ }
+ }
+
+ // NOTREACHED
+}
+
+bool
+PLDHashTable::ChangeTable(int32_t aDeltaLog2)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+
+ // Look, but don't touch, until we succeed in getting new entry store.
+ int32_t oldLog2 = kHashBits - mHashShift;
+ int32_t newLog2 = oldLog2 + aDeltaLog2;
+ uint32_t newCapacity = 1u << newLog2;
+ if (newCapacity > kMaxCapacity) {
+ return false;
+ }
+
+ uint32_t nbytes;
+ if (!SizeOfEntryStore(newCapacity, mEntrySize, &nbytes)) {
+ return false; // overflowed
+ }
+
+ char* newEntryStore = (char*)malloc(nbytes);
+ if (!newEntryStore) {
+ return false;
+ }
+
+ // We can't fail from here on, so update table parameters.
+ mHashShift = kHashBits - newLog2;
+ mRemovedCount = 0;
+
+ // Assign the new entry store to table.
+ memset(newEntryStore, 0, nbytes);
+ char* oldEntryStore;
+ char* oldEntryAddr;
+ oldEntryAddr = oldEntryStore = mEntryStore.Get();
+ mEntryStore.Set(newEntryStore);
+ PLDHashMoveEntry moveEntry = mOps->moveEntry;
+
+ // Copy only live entries, leaving removed ones behind.
+ uint32_t oldCapacity = 1u << oldLog2;
+ for (uint32_t i = 0; i < oldCapacity; ++i) {
+ PLDHashEntryHdr* oldEntry = (PLDHashEntryHdr*)oldEntryAddr;
+ if (EntryIsLive(oldEntry)) {
+ oldEntry->mKeyHash &= ~kCollisionFlag;
+ PLDHashEntryHdr* newEntry = FindFreeEntry(oldEntry->mKeyHash);
+ NS_ASSERTION(EntryIsFree(newEntry), "EntryIsFree(newEntry)");
+ moveEntry(this, oldEntry, newEntry);
+ newEntry->mKeyHash = oldEntry->mKeyHash;
+ }
+ oldEntryAddr += mEntrySize;
+ }
+
+ free(oldEntryStore);
+ return true;
+}
+
+MOZ_ALWAYS_INLINE PLDHashNumber
+PLDHashTable::ComputeKeyHash(const void* aKey)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+
+ PLDHashNumber keyHash = mOps->hashKey(aKey);
+ keyHash *= kGoldenRatio;
+
+ // Avoid 0 and 1 hash codes, they indicate free and removed entries.
+ if (keyHash < 2) {
+ keyHash -= 2;
+ }
+ keyHash &= ~kCollisionFlag;
+
+ return keyHash;
+}
+
+PLDHashEntryHdr*
+PLDHashTable::Search(const void* aKey)
+{
+#ifdef DEBUG
+ AutoReadOp op(mChecker);
+#endif
+
+ PLDHashEntryHdr* entry = mEntryStore.Get()
+ ? SearchTable<ForSearchOrRemove>(aKey,
+ ComputeKeyHash(aKey))
+ : nullptr;
+ return entry;
+}
+
+PLDHashEntryHdr*
+PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
+{
+#ifdef DEBUG
+ AutoWriteOp op(mChecker);
+#endif
+
+ // Allocate the entry storage if it hasn't already been allocated.
+ if (!mEntryStore.Get()) {
+ uint32_t nbytes;
+ // We already checked this in the constructor, so it must still be true.
+ MOZ_RELEASE_ASSERT(SizeOfEntryStore(CapacityFromHashShift(), mEntrySize,
+ &nbytes));
+ mEntryStore.Set((char*)malloc(nbytes));
+ if (!mEntryStore.Get()) {
+ return nullptr;
+ }
+ memset(mEntryStore.Get(), 0, nbytes);
+ }
+
+ // If alpha is >= .75, grow or compress the table. If aKey is already in the
+ // table, we may grow once more than necessary, but only if we are on the
+ // edge of being overloaded.
+ uint32_t capacity = Capacity();
+ if (mEntryCount + mRemovedCount >= MaxLoad(capacity)) {
+ // Compress if a quarter or more of all entries are removed.
+ int deltaLog2;
+ if (mRemovedCount >= capacity >> 2) {
+ deltaLog2 = 0;
+ } else {
+ deltaLog2 = 1;
+ }
+
+ // Grow or compress the table. If ChangeTable() fails, allow overloading up
+ // to the secondary max. Once we hit the secondary max, return null.
+ if (!ChangeTable(deltaLog2) &&
+ mEntryCount + mRemovedCount >= MaxLoadOnGrowthFailure(capacity)) {
+ return nullptr;
+ }
+ }
+
+ // Look for entry after possibly growing, so we don't have to add it,
+ // then skip it while growing the table and re-add it after.
+ PLDHashNumber keyHash = ComputeKeyHash(aKey);
+ PLDHashEntryHdr* entry = SearchTable<ForAdd>(aKey, keyHash);
+ if (!EntryIsLive(entry)) {
+ // Initialize the entry, indicating that it's no longer free.
+ if (EntryIsRemoved(entry)) {
+ mRemovedCount--;
+ keyHash |= kCollisionFlag;
+ }
+ if (mOps->initEntry) {
+ mOps->initEntry(entry, aKey);
+ }
+ entry->mKeyHash = keyHash;
+ mEntryCount++;
+ }
+
+ return entry;
+}
+
+PLDHashEntryHdr*
+PLDHashTable::Add(const void* aKey)
+{
+ PLDHashEntryHdr* entry = Add(aKey, fallible);
+ if (!entry) {
+ if (!mEntryStore.Get()) {
+ // We OOM'd while allocating the initial entry storage.
+ uint32_t nbytes;
+ (void) SizeOfEntryStore(CapacityFromHashShift(), mEntrySize, &nbytes);
+ NS_ABORT_OOM(nbytes);
+ } else {
+ // We failed to resize the existing entry storage, either due to OOM or
+ // because we exceeded the maximum table capacity or size; report it as
+ // an OOM. The multiplication by 2 gets us the size we tried to allocate,
+ // which is double the current size.
+ NS_ABORT_OOM(2 * EntrySize() * EntryCount());
+ }
+ }
+ return entry;
+}
+
+void
+PLDHashTable::Remove(const void* aKey)
+{
+#ifdef DEBUG
+ AutoWriteOp op(mChecker);
+#endif
+
+ PLDHashEntryHdr* entry = mEntryStore.Get()
+ ? SearchTable<ForSearchOrRemove>(aKey,
+ ComputeKeyHash(aKey))
+ : nullptr;
+ if (entry) {
+ RawRemove(entry);
+ ShrinkIfAppropriate();
+ }
+}
+
+void
+PLDHashTable::RemoveEntry(PLDHashEntryHdr* aEntry)
+{
+#ifdef DEBUG
+ AutoWriteOp op(mChecker);
+#endif
+
+ RawRemove(aEntry);
+ ShrinkIfAppropriate();
+}
+
+void
+PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
+{
+ // Unfortunately, we can only do weak checking here. That's because
+ // RawRemove() can be called legitimately while an Enumerate() call is
+ // active, which doesn't fit well into how Checker's mState variable works.
+ MOZ_ASSERT(mChecker.IsWritable());
+
+ MOZ_ASSERT(mEntryStore.Get());
+
+ MOZ_ASSERT(EntryIsLive(aEntry), "EntryIsLive(aEntry)");
+
+ // Load keyHash first in case clearEntry() goofs it.
+ PLDHashNumber keyHash = aEntry->mKeyHash;
+ mOps->clearEntry(this, aEntry);
+ if (keyHash & kCollisionFlag) {
+ MarkEntryRemoved(aEntry);
+ mRemovedCount++;
+ } else {
+ MarkEntryFree(aEntry);
+ }
+ mEntryCount--;
+}
+
+// Shrink or compress if a quarter or more of all entries are removed, or if the
+// table is underloaded according to the minimum alpha, and is not minimal-size
+// already.
+void
+PLDHashTable::ShrinkIfAppropriate()
+{
+ uint32_t capacity = Capacity();
+ if (mRemovedCount >= capacity >> 2 ||
+ (capacity > kMinCapacity && mEntryCount <= MinLoad(capacity))) {
+ uint32_t log2;
+ BestCapacity(mEntryCount, &capacity, &log2);
+
+ int32_t deltaLog2 = log2 - (kHashBits - mHashShift);
+ MOZ_ASSERT(deltaLog2 <= 0);
+
+ (void) ChangeTable(deltaLog2);
+ }
+}
+
+size_t
+PLDHashTable::ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+#ifdef DEBUG
+ AutoReadOp op(mChecker);
+#endif
+
+ return aMallocSizeOf(mEntryStore.Get());
+}
+
+size_t
+PLDHashTable::ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+}
+
+PLDHashTable::Iterator::Iterator(Iterator&& aOther)
+ : mTable(aOther.mTable)
+ , mStart(aOther.mStart)
+ , mLimit(aOther.mLimit)
+ , mCurrent(aOther.mCurrent)
+ , mNexts(aOther.mNexts)
+ , mNextsLimit(aOther.mNextsLimit)
+ , mHaveRemoved(aOther.mHaveRemoved)
+{
+ // No need to change |mChecker| here.
+ aOther.mTable = nullptr;
+ aOther.mStart = nullptr;
+ aOther.mLimit = nullptr;
+ aOther.mCurrent = nullptr;
+ aOther.mNexts = 0;
+ aOther.mNextsLimit = 0;
+ aOther.mHaveRemoved = false;
+}
+
+PLDHashTable::Iterator::Iterator(PLDHashTable* aTable)
+ : mTable(aTable)
+ , mStart(mTable->mEntryStore.Get())
+ , mLimit(mTable->mEntryStore.Get() + mTable->Capacity() * mTable->mEntrySize)
+ , mCurrent(mTable->mEntryStore.Get())
+ , mNexts(0)
+ , mNextsLimit(mTable->EntryCount())
+ , mHaveRemoved(false)
+{
+#ifdef DEBUG
+ mTable->mChecker.StartReadOp();
+#endif
+
+ if (ChaosMode::isActive(ChaosFeature::HashTableIteration) &&
+ mTable->Capacity() > 0) {
+ // Start iterating at a random entry. It would be even more chaotic to
+ // iterate in fully random order, but that's harder.
+ mCurrent += ChaosMode::randomUint32LessThan(mTable->Capacity()) *
+ mTable->mEntrySize;
+ }
+
+ // Advance to the first live entry, if there is one.
+ if (!Done()) {
+ while (IsOnNonLiveEntry()) {
+ MoveToNextEntry();
+ }
+ }
+}
+
+PLDHashTable::Iterator::~Iterator()
+{
+ if (mTable) {
+ if (mHaveRemoved) {
+ mTable->ShrinkIfAppropriate();
+ }
+#ifdef DEBUG
+ mTable->mChecker.EndReadOp();
+#endif
+ }
+}
+
+MOZ_ALWAYS_INLINE bool
+PLDHashTable::Iterator::IsOnNonLiveEntry() const
+{
+ MOZ_ASSERT(!Done());
+ return !EntryIsLive(reinterpret_cast<PLDHashEntryHdr*>(mCurrent));
+}
+
+MOZ_ALWAYS_INLINE void
+PLDHashTable::Iterator::MoveToNextEntry()
+{
+ mCurrent += mTable->mEntrySize;
+ if (mCurrent == mLimit) {
+ mCurrent = mStart; // Wrap-around. Possible due to Chaos Mode.
+ }
+}
+
+void
+PLDHashTable::Iterator::Next()
+{
+ MOZ_ASSERT(!Done());
+
+ mNexts++;
+
+ // Advance to the next live entry, if there is one.
+ if (!Done()) {
+ do {
+ MoveToNextEntry();
+ } while (IsOnNonLiveEntry());
+ }
+}
+
+void
+PLDHashTable::Iterator::Remove()
+{
+ // This cast is needed for the same reason as the one in the destructor.
+ mTable->RawRemove(Get());
+ mHaveRemoved = true;
+}
+
+#ifdef DEBUG
+void
+PLDHashTable::MarkImmutable()
+{
+ mChecker.SetNonWritable();
+}
+#endif
diff --git a/xpcom/glue/PLDHashTable.h b/xpcom/glue/PLDHashTable.h
new file mode 100644
index 000000000..cd1323dbe
--- /dev/null
+++ b/xpcom/glue/PLDHashTable.h
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PLDHashTable_h
+#define PLDHashTable_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE
+#include "mozilla/fallible.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "mozilla/Types.h"
+#include "nscore.h"
+
+typedef uint32_t PLDHashNumber;
+
+class PLDHashTable;
+struct PLDHashTableOps;
+
+// Table entry header structure.
+//
+// In order to allow in-line allocation of key and value, we do not declare
+// either here. Instead, the API uses const void *key as a formal parameter.
+// The key need not be stored in the entry; it may be part of the value, but
+// need not be stored at all.
+//
+// Callback types are defined below and grouped into the PLDHashTableOps
+// structure, for single static initialization per hash table sub-type.
+//
+// Each hash table sub-type should make its entry type a subclass of
+// PLDHashEntryHdr. The mKeyHash member contains the result of multiplying the
+// hash code returned from the hashKey callback (see below) by kGoldenRatio,
+// then constraining the result to avoid the magic 0 and 1 values. The stored
+// mKeyHash value is table size invariant, and it is maintained automatically
+// -- users need never access it.
+struct PLDHashEntryHdr
+{
+private:
+ friend class PLDHashTable;
+
+ PLDHashNumber mKeyHash;
+};
+
+#ifdef DEBUG
+
+// This class does three kinds of checking:
+//
+// - that calls to one of |mOps| or to an enumerator do not cause re-entry into
+// the table in an unsafe way;
+//
+// - that multiple threads do not access the table in an unsafe way;
+//
+// - that a table marked as immutable is not modified.
+//
+// "Safe" here means that multiple concurrent read operations are ok, but a
+// write operation (i.e. one that can cause the entry storage to be reallocated
+// or destroyed) cannot safely run concurrently with another read or write
+// operation. This meaning of "safe" is only partial; for example, it does not
+// cover whether a single entry in the table is modified by two separate
+// threads. (Doing such checking would be much harder.)
+//
+// It does this with two variables:
+//
+// - mState, which embodies a tri-stage tagged union with the following
+// variants:
+// - Idle
+// - Read(n), where 'n' is the number of concurrent read operations
+// - Write
+//
+// - mIsWritable, which indicates if the table is mutable.
+//
+class Checker
+{
+public:
+ constexpr Checker() : mState(kIdle), mIsWritable(1) {}
+
+ Checker& operator=(Checker&& aOther) {
+ // Atomic<> doesn't have an |operator=(Atomic<>&&)|.
+ mState = uint32_t(aOther.mState);
+ mIsWritable = uint32_t(aOther.mIsWritable);
+
+ aOther.mState = kIdle;
+
+ return *this;
+ }
+
+ static bool IsIdle(uint32_t aState) { return aState == kIdle; }
+ static bool IsRead(uint32_t aState) { return kRead1 <= aState &&
+ aState <= kReadMax; }
+ static bool IsRead1(uint32_t aState) { return aState == kRead1; }
+ static bool IsWrite(uint32_t aState) { return aState == kWrite; }
+
+ bool IsIdle() const { return mState == kIdle; }
+
+ bool IsWritable() const { return !!mIsWritable; }
+
+ void SetNonWritable() { mIsWritable = 0; }
+
+ // NOTE: the obvious way to implement these functions is to (a) check
+ // |mState| is reasonable, and then (b) update |mState|. But the lack of
+ // atomicity in such an implementation can cause problems if we get unlucky
+ // thread interleaving between (a) and (b).
+ //
+ // So instead for |mState| we are careful to (a) first get |mState|'s old
+ // value and assign it a new value in single atomic operation, and only then
+ // (b) check the old value was reasonable. This ensures we don't have
+ // interleaving problems.
+ //
+ // For |mIsWritable| we don't need to be as careful because it can only in
+ // transition in one direction (from writable to non-writable).
+
+ void StartReadOp()
+ {
+ uint32_t oldState = mState++; // this is an atomic increment
+ MOZ_ASSERT(IsIdle(oldState) || IsRead(oldState));
+ MOZ_ASSERT(oldState < kReadMax); // check for overflow
+ }
+
+ void EndReadOp()
+ {
+ uint32_t oldState = mState--; // this is an atomic decrement
+ MOZ_ASSERT(IsRead(oldState));
+ }
+
+ void StartWriteOp()
+ {
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kWrite);
+ MOZ_ASSERT(IsIdle(oldState));
+ }
+
+ void EndWriteOp()
+ {
+ // Check again that the table is writable, in case it was marked as
+ // non-writable just after the IsWritable() assertion in StartWriteOp()
+ // occurred.
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kIdle);
+ MOZ_ASSERT(IsWrite(oldState));
+ }
+
+ void StartIteratorRemovalOp()
+ {
+ // When doing removals at the end of iteration, we go from Read1 state to
+ // Write and then back.
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kWrite);
+ MOZ_ASSERT(IsRead1(oldState));
+ }
+
+ void EndIteratorRemovalOp()
+ {
+ // Check again that the table is writable, in case it was marked as
+ // non-writable just after the IsWritable() assertion in
+ // StartIteratorRemovalOp() occurred.
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kRead1);
+ MOZ_ASSERT(IsWrite(oldState));
+ }
+
+ void StartDestructorOp()
+ {
+ // A destructor op is like a write, but the table doesn't need to be
+ // writable.
+ uint32_t oldState = mState.exchange(kWrite);
+ MOZ_ASSERT(IsIdle(oldState));
+ }
+
+ void EndDestructorOp()
+ {
+ uint32_t oldState = mState.exchange(kIdle);
+ MOZ_ASSERT(IsWrite(oldState));
+ }
+
+private:
+ // Things of note about the representation of |mState|.
+ // - The values between kRead1..kReadMax represent valid Read(n) values.
+ // - kIdle and kRead1 are deliberately chosen so that incrementing the -
+ // former gives the latter.
+ // - 9999 concurrent readers should be enough for anybody.
+ static const uint32_t kIdle = 0;
+ static const uint32_t kRead1 = 1;
+ static const uint32_t kReadMax = 9999;
+ static const uint32_t kWrite = 10000;
+
+ mutable mozilla::Atomic<uint32_t> mState;
+ mutable mozilla::Atomic<uint32_t> mIsWritable;
+};
+#endif
+
+// A PLDHashTable may be allocated on the stack or within another structure or
+// class. No entry storage is allocated until the first element is added. This
+// means that empty hash tables are cheap, which is good because they are
+// common.
+//
+// There used to be a long, math-heavy comment here about the merits of
+// double hashing vs. chaining; it was removed in bug 1058335. In short, double
+// hashing is more space-efficient unless the element size gets large (in which
+// case you should keep using double hashing but switch to using pointer
+// elements). Also, with double hashing, you can't safely hold an entry pointer
+// and use it after an add or remove operation, unless you sample Generation()
+// before adding or removing, and compare the sample after, dereferencing the
+// entry pointer only if Generation() has not changed.
+class PLDHashTable
+{
+private:
+ // This class maintains the invariant that every time the entry store is
+ // changed, the generation is updated.
+ class EntryStore
+ {
+ private:
+ char* mEntryStore;
+ uint32_t mGeneration;
+
+ public:
+ EntryStore() : mEntryStore(nullptr), mGeneration(0) {}
+
+ ~EntryStore()
+ {
+ free(mEntryStore);
+ mEntryStore = nullptr;
+ mGeneration++; // a little paranoid, but why not be extra safe?
+ }
+
+ char* Get() { return mEntryStore; }
+ const char* Get() const { return mEntryStore; }
+
+ void Set(char* aEntryStore)
+ {
+ mEntryStore = aEntryStore;
+ mGeneration++;
+ }
+
+ uint32_t Generation() const { return mGeneration; }
+ };
+
+ const PLDHashTableOps* const mOps; // Virtual operations; see below.
+ int16_t mHashShift; // Multiplicative hash shift.
+ const uint32_t mEntrySize; // Number of bytes in an entry.
+ uint32_t mEntryCount; // Number of entries in table.
+ uint32_t mRemovedCount; // Removed entry sentinels in table.
+ EntryStore mEntryStore; // (Lazy) entry storage and generation.
+
+#ifdef DEBUG
+ mutable Checker mChecker;
+#endif
+
+public:
+ // Table capacity limit; do not exceed. The max capacity used to be 1<<23 but
+ // that occasionally that wasn't enough. Making it much bigger than 1<<26
+ // probably isn't worthwhile -- tables that big are kind of ridiculous.
+ // Also, the growth operation will (deliberately) fail if |capacity *
+ // mEntrySize| overflows a uint32_t, and mEntrySize is always at least 8
+ // bytes.
+ static const uint32_t kMaxCapacity = ((uint32_t)1 << 26);
+
+ static const uint32_t kMinCapacity = 8;
+
+ // Making this half of kMaxCapacity ensures it'll fit. Nobody should need an
+ // initial length anywhere nearly this large, anyway.
+ static const uint32_t kMaxInitialLength = kMaxCapacity / 2;
+
+ // This gives a default initial capacity of 8.
+ static const uint32_t kDefaultInitialLength = 4;
+
+ // Initialize the table with |aOps| and |aEntrySize|. The table's initial
+ // capacity is chosen such that |aLength| elements can be inserted without
+ // rehashing; if |aLength| is a power-of-two, this capacity will be
+ // |2*length|. However, because entry storage is allocated lazily, this
+ // initial capacity won't be relevant until the first element is added; prior
+ // to that the capacity will be zero.
+ //
+ // This will crash if |aEntrySize| and/or |aLength| are too large.
+ PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
+ uint32_t aLength = kDefaultInitialLength);
+
+ PLDHashTable(PLDHashTable&& aOther)
+ // These two fields are |const|. Initialize them here because the
+ // move assignment operator cannot modify them.
+ : mOps(aOther.mOps)
+ , mEntrySize(aOther.mEntrySize)
+ // Initialize this field because it is required for a safe call to the
+ // destructor, which the move assignment operator does.
+ , mEntryStore()
+#ifdef DEBUG
+ , mChecker()
+#endif
+ {
+ *this = mozilla::Move(aOther);
+ }
+
+ PLDHashTable& operator=(PLDHashTable&& aOther);
+
+ ~PLDHashTable();
+
+ // This should be used rarely.
+ const PLDHashTableOps* Ops() const { return mOps; }
+
+ // Size in entries (gross, not net of free and removed sentinels) for table.
+ // This can be zero if no elements have been added yet, in which case the
+ // entry storage will not have yet been allocated.
+ uint32_t Capacity() const
+ {
+ return mEntryStore.Get() ? CapacityFromHashShift() : 0;
+ }
+
+ uint32_t EntrySize() const { return mEntrySize; }
+ uint32_t EntryCount() const { return mEntryCount; }
+ uint32_t Generation() const { return mEntryStore.Generation(); }
+
+ // To search for a |key| in |table|, call:
+ //
+ // entry = table.Search(key);
+ //
+ // If |entry| is non-null, |key| was found. If |entry| is null, key was not
+ // found.
+ PLDHashEntryHdr* Search(const void* aKey);
+
+ // To add an entry identified by |key| to table, call:
+ //
+ // entry = table.Add(key, mozilla::fallible);
+ //
+ // If |entry| is null upon return, then the table is severely overloaded and
+ // memory can't be allocated for entry storage.
+ //
+ // Otherwise, |aEntry->mKeyHash| has been set so that
+ // PLDHashTable::EntryIsFree(entry) is false, and it is up to the caller to
+ // initialize the key and value parts of the entry sub-type, if they have not
+ // been set already (i.e. if entry was not already in the table, and if the
+ // optional initEntry hook was not used).
+ PLDHashEntryHdr* Add(const void* aKey, const mozilla::fallible_t&);
+
+ // This is like the other Add() function, but infallible, and so never
+ // returns null.
+ PLDHashEntryHdr* Add(const void* aKey);
+
+ // To remove an entry identified by |key| from table, call:
+ //
+ // table.Remove(key);
+ //
+ // If |key|'s entry is found, it is cleared (via table->mOps->clearEntry).
+ // The table's capacity may be reduced afterwards.
+ void Remove(const void* aKey);
+
+ // To remove an entry found by a prior search, call:
+ //
+ // table.RemoveEntry(entry);
+ //
+ // The entry, which must be present and in use, is cleared (via
+ // table->mOps->clearEntry). The table's capacity may be reduced afterwards.
+ void RemoveEntry(PLDHashEntryHdr* aEntry);
+
+ // Remove an entry already accessed via Search() or Add().
+ //
+ // NB: this is a "raw" or low-level method. It does not shrink the table if
+ // it is underloaded. Don't use it unless necessary and you know what you are
+ // doing, and if so, please explain in a comment why it is necessary instead
+ // of RemoveEntry().
+ void RawRemove(PLDHashEntryHdr* aEntry);
+
+ // This function is equivalent to
+ // ClearAndPrepareForLength(kDefaultInitialLength).
+ void Clear();
+
+ // This function clears the table's contents and frees its entry storage,
+ // leaving it in a empty state ready to be used again. Afterwards, when the
+ // first element is added the entry storage that gets allocated will have a
+ // capacity large enough to fit |aLength| elements without rehashing.
+ //
+ // It's conceptually the same as calling the destructor and then re-calling
+ // the constructor with the original |aOps| and |aEntrySize| arguments, and
+ // a new |aLength| argument.
+ void ClearAndPrepareForLength(uint32_t aLength);
+
+ // Measure the size of the table's entry storage. If the entries contain
+ // pointers to other heap blocks, you have to iterate over the table and
+ // measure those separately; hence the "Shallow" prefix.
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ // Like ShallowSizeOfExcludingThis(), but includes sizeof(*this).
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+#ifdef DEBUG
+ // Mark a table as immutable for the remainder of its lifetime. This
+ // changes the implementation from asserting one set of invariants to
+ // asserting a different set.
+ void MarkImmutable();
+#endif
+
+ // If you use PLDHashEntryStub or a subclass of it as your entry struct, and
+ // if your entries move via memcpy and clear via memset(0), you can use these
+ // stub operations.
+ static const PLDHashTableOps* StubOps();
+
+ // The individual stub operations in StubOps().
+ static PLDHashNumber HashVoidPtrKeyStub(const void* aKey);
+ static bool MatchEntryStub(const PLDHashEntryHdr* aEntry, const void* aKey);
+ static void MoveEntryStub(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+ static void ClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
+
+ // Hash/match operations for tables holding C strings.
+ static PLDHashNumber HashStringKey(const void* aKey);
+ static bool MatchStringKey(const PLDHashEntryHdr* aEntry, const void* aKey);
+
+ // This is an iterator for PLDHashtable. Assertions will detect some, but not
+ // all, mid-iteration table modifications that might invalidate (e.g.
+ // reallocate) the entry storage.
+ //
+ // Any element can be removed during iteration using Remove(). If any
+ // elements are removed, the table may be resized once iteration ends.
+ //
+ // Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // auto entry = static_cast<FooEntry*>(iter.Get());
+ // // ... do stuff with |entry| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ // or:
+ //
+ // for (PLDHashTable::Iterator iter(&table); !iter.Done(); iter.Next()) {
+ // auto entry = static_cast<FooEntry*>(iter.Get());
+ // // ... do stuff with |entry| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ // The latter form is more verbose but is easier to work with when
+ // making subclasses of Iterator.
+ //
+ class Iterator
+ {
+ public:
+ explicit Iterator(PLDHashTable* aTable);
+ Iterator(Iterator&& aOther);
+ ~Iterator();
+
+ // Have we finished?
+ bool Done() const { return mNexts == mNextsLimit; }
+
+ // Get the current entry.
+ PLDHashEntryHdr* Get() const
+ {
+ MOZ_ASSERT(!Done());
+
+ PLDHashEntryHdr* entry = reinterpret_cast<PLDHashEntryHdr*>(mCurrent);
+ MOZ_ASSERT(EntryIsLive(entry));
+ return entry;
+ }
+
+ // Advance to the next entry.
+ void Next();
+
+ // Remove the current entry. Must only be called once per entry, and Get()
+ // must not be called on that entry afterwards.
+ void Remove();
+
+ protected:
+ PLDHashTable* mTable; // Main table pointer.
+
+ private:
+ char* mStart; // The first entry.
+ char* mLimit; // One past the last entry.
+ char* mCurrent; // Pointer to the current entry.
+ uint32_t mNexts; // Number of Next() calls.
+ uint32_t mNextsLimit; // Next() call limit.
+
+ bool mHaveRemoved; // Have any elements been removed?
+
+ bool IsOnNonLiveEntry() const;
+ void MoveToNextEntry();
+
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ // Use this if you need to initialize an Iterator in a const method. If you
+ // use this case, you should not call Remove() on the iterator.
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<PLDHashTable*>(this));
+ }
+
+private:
+ // Multiplicative hash uses an unsigned 32 bit integer and the golden ratio,
+ // expressed as a fixed-point 32-bit fraction.
+ static const uint32_t kHashBits = 32;
+ static const uint32_t kGoldenRatio = 0x9E3779B9U;
+
+ static uint32_t HashShift(uint32_t aEntrySize, uint32_t aLength);
+
+ static const PLDHashNumber kCollisionFlag = 1;
+
+ static bool EntryIsFree(PLDHashEntryHdr* aEntry)
+ {
+ return aEntry->mKeyHash == 0;
+ }
+ static bool EntryIsRemoved(PLDHashEntryHdr* aEntry)
+ {
+ return aEntry->mKeyHash == 1;
+ }
+ static bool EntryIsLive(PLDHashEntryHdr* aEntry)
+ {
+ return aEntry->mKeyHash >= 2;
+ }
+
+ static void MarkEntryFree(PLDHashEntryHdr* aEntry)
+ {
+ aEntry->mKeyHash = 0;
+ }
+ static void MarkEntryRemoved(PLDHashEntryHdr* aEntry)
+ {
+ aEntry->mKeyHash = 1;
+ }
+
+ PLDHashNumber Hash1(PLDHashNumber aHash0);
+ void Hash2(PLDHashNumber aHash, uint32_t& aHash2Out, uint32_t& aSizeMaskOut);
+
+ static bool MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aHash);
+ PLDHashEntryHdr* AddressEntry(uint32_t aIndex);
+
+ // We store mHashShift rather than sizeLog2 to optimize the collision-free
+ // case in SearchTable.
+ uint32_t CapacityFromHashShift() const
+ {
+ return ((uint32_t)1 << (kHashBits - mHashShift));
+ }
+
+ PLDHashNumber ComputeKeyHash(const void* aKey);
+
+ enum SearchReason { ForSearchOrRemove, ForAdd };
+
+ template <SearchReason Reason>
+ PLDHashEntryHdr* NS_FASTCALL
+ SearchTable(const void* aKey, PLDHashNumber aKeyHash);
+
+ PLDHashEntryHdr* FindFreeEntry(PLDHashNumber aKeyHash);
+
+ bool ChangeTable(int aDeltaLog2);
+
+ void ShrinkIfAppropriate();
+
+ PLDHashTable(const PLDHashTable& aOther) = delete;
+ PLDHashTable& operator=(const PLDHashTable& aOther) = delete;
+};
+
+// Compute the hash code for a given key to be looked up, added, or removed.
+// A hash code may have any PLDHashNumber value.
+typedef PLDHashNumber (*PLDHashHashKey)(const void* aKey);
+
+// Compare the key identifying aEntry with the provided key parameter. Return
+// true if keys match, false otherwise.
+typedef bool (*PLDHashMatchEntry)(const PLDHashEntryHdr* aEntry,
+ const void* aKey);
+
+// Copy the data starting at aFrom to the new entry storage at aTo. Do not add
+// reference counts for any strong references in the entry, however, as this
+// is a "move" operation: the old entry storage at from will be freed without
+// any reference-decrementing callback shortly.
+typedef void (*PLDHashMoveEntry)(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+
+// Clear the entry and drop any strong references it holds. This callback is
+// invoked by Remove(), but only if the given key is found in the table.
+typedef void (*PLDHashClearEntry)(PLDHashTable* aTable,
+ PLDHashEntryHdr* aEntry);
+
+// Initialize a new entry, apart from mKeyHash. This function is called when
+// Add() finds no existing entry for the given key, and must add a new one. At
+// that point, |aEntry->mKeyHash| is not set yet, to avoid claiming the last
+// free entry in a severely overloaded table.
+typedef void (*PLDHashInitEntry)(PLDHashEntryHdr* aEntry, const void* aKey);
+
+// Finally, the "vtable" structure for PLDHashTable. The first four hooks
+// must be provided by implementations; they're called unconditionally by the
+// generic PLDHashTable.cpp code. Hooks after these may be null.
+//
+// Summary of allocation-related hook usage with C++ placement new emphasis:
+// initEntry Call placement new using default key-based ctor.
+// moveEntry Call placement new using copy ctor, run dtor on old
+// entry storage.
+// clearEntry Run dtor on entry.
+//
+// Note the reason why initEntry is optional: the default hooks (stubs) clear
+// entry storage: On successful Add(tbl, key), the returned entry pointer
+// addresses an entry struct whose mKeyHash member has been set non-zero, but
+// all other entry members are still clear (null). Add() callers can test such
+// members to see whether the entry was newly created by the Add() call that
+// just succeeded. If placement new or similar initialization is required,
+// define an |initEntry| hook. Of course, the |clearEntry| hook must zero or
+// null appropriately.
+//
+// XXX assumes 0 is null for pointer types.
+struct PLDHashTableOps
+{
+ // Mandatory hooks. All implementations must provide these.
+ PLDHashHashKey hashKey;
+ PLDHashMatchEntry matchEntry;
+ PLDHashMoveEntry moveEntry;
+ PLDHashClearEntry clearEntry;
+
+ // Optional hooks start here. If null, these are not called.
+ PLDHashInitEntry initEntry;
+};
+
+// A minimal entry is a subclass of PLDHashEntryHdr and has a void* key pointer.
+struct PLDHashEntryStub : public PLDHashEntryHdr
+{
+ const void* key;
+};
+
+#endif /* PLDHashTable_h */
diff --git a/xpcom/glue/ReentrantMonitor.h b/xpcom/glue/ReentrantMonitor.h
new file mode 100644
index 000000000..0798fe2af
--- /dev/null
+++ b/xpcom/glue/ReentrantMonitor.h
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ReentrantMonitor_h
+#define mozilla_ReentrantMonitor_h
+
+#include "prmon.h"
+
+#ifdef MOZILLA_INTERNAL_API
+#include "GeckoProfiler.h"
+#endif //MOZILLA_INTERNAL_API
+
+#include "mozilla/BlockingResourceBase.h"
+
+//
+// Provides:
+//
+// - ReentrantMonitor, a Java-like monitor
+// - ReentrantMonitorAutoEnter, an RAII class for ensuring that
+// ReentrantMonitors are properly entered and exited
+//
+// Using ReentrantMonitorAutoEnter is MUCH preferred to making bare calls to
+// ReentrantMonitor.Enter and Exit.
+//
+namespace mozilla {
+
+
+/**
+ * ReentrantMonitor
+ * Java-like monitor.
+ * When possible, use ReentrantMonitorAutoEnter to hold this monitor within a
+ * scope, instead of calling Enter/Exit directly.
+ **/
+class ReentrantMonitor : BlockingResourceBase
+{
+public:
+ /**
+ * ReentrantMonitor
+ * @param aName A name which can reference this monitor
+ */
+ explicit ReentrantMonitor(const char* aName)
+ : BlockingResourceBase(aName, eReentrantMonitor)
+#ifdef DEBUG
+ , mEntryCount(0)
+#endif
+ {
+ MOZ_COUNT_CTOR(ReentrantMonitor);
+ mReentrantMonitor = PR_NewMonitor();
+ if (!mReentrantMonitor) {
+ NS_RUNTIMEABORT("Can't allocate mozilla::ReentrantMonitor");
+ }
+ }
+
+ /**
+ * ~ReentrantMonitor
+ **/
+ ~ReentrantMonitor()
+ {
+ NS_ASSERTION(mReentrantMonitor,
+ "improperly constructed ReentrantMonitor or double free");
+ PR_DestroyMonitor(mReentrantMonitor);
+ mReentrantMonitor = 0;
+ MOZ_COUNT_DTOR(ReentrantMonitor);
+ }
+
+#ifndef DEBUG
+ /**
+ * Enter
+ * @see prmon.h
+ **/
+ void Enter() { PR_EnterMonitor(mReentrantMonitor); }
+
+ /**
+ * Exit
+ * @see prmon.h
+ **/
+ void Exit() { PR_ExitMonitor(mReentrantMonitor); }
+
+ /**
+ * Wait
+ * @see prmon.h
+ **/
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+#ifdef MOZILLA_INTERNAL_API
+ GeckoProfilerSleepRAII profiler_sleep;
+#endif //MOZILLA_INTERNAL_API
+ return PR_Wait(mReentrantMonitor, aInterval) == PR_SUCCESS ?
+ NS_OK : NS_ERROR_FAILURE;
+ }
+
+#else // ifndef DEBUG
+ void Enter();
+ void Exit();
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT);
+
+#endif // ifndef DEBUG
+
+ /**
+ * Notify
+ * @see prmon.h
+ **/
+ nsresult Notify()
+ {
+ return PR_Notify(mReentrantMonitor) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+ }
+
+ /**
+ * NotifyAll
+ * @see prmon.h
+ **/
+ nsresult NotifyAll()
+ {
+ return PR_NotifyAll(mReentrantMonitor) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+ }
+
+#ifdef DEBUG
+ /**
+ * AssertCurrentThreadIn
+ * @see prmon.h
+ **/
+ void AssertCurrentThreadIn()
+ {
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mReentrantMonitor);
+ }
+
+ /**
+ * AssertNotCurrentThreadIn
+ * @see prmon.h
+ **/
+ void AssertNotCurrentThreadIn()
+ {
+ // FIXME bug 476536
+ }
+
+#else
+ void AssertCurrentThreadIn() {}
+ void AssertNotCurrentThreadIn() {}
+
+#endif // ifdef DEBUG
+
+private:
+ ReentrantMonitor();
+ ReentrantMonitor(const ReentrantMonitor&);
+ ReentrantMonitor& operator=(const ReentrantMonitor&);
+
+ PRMonitor* mReentrantMonitor;
+#ifdef DEBUG
+ int32_t mEntryCount;
+#endif
+};
+
+
+/**
+ * ReentrantMonitorAutoEnter
+ * Enters the ReentrantMonitor when it enters scope, and exits it when
+ * it leaves scope.
+ *
+ * MUCH PREFERRED to bare calls to ReentrantMonitor.Enter and Exit.
+ */
+class MOZ_STACK_CLASS ReentrantMonitorAutoEnter
+{
+public:
+ /**
+ * Constructor
+ * The constructor aquires the given lock. The destructor
+ * releases the lock.
+ *
+ * @param aReentrantMonitor A valid mozilla::ReentrantMonitor*.
+ **/
+ explicit ReentrantMonitorAutoEnter(mozilla::ReentrantMonitor& aReentrantMonitor)
+ : mReentrantMonitor(&aReentrantMonitor)
+ {
+ NS_ASSERTION(mReentrantMonitor, "null monitor");
+ mReentrantMonitor->Enter();
+ }
+
+ ~ReentrantMonitorAutoEnter(void)
+ {
+ mReentrantMonitor->Exit();
+ }
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mReentrantMonitor->Wait(aInterval);
+ }
+
+ nsresult Notify() { return mReentrantMonitor->Notify(); }
+ nsresult NotifyAll() { return mReentrantMonitor->NotifyAll(); }
+
+private:
+ ReentrantMonitorAutoEnter();
+ ReentrantMonitorAutoEnter(const ReentrantMonitorAutoEnter&);
+ ReentrantMonitorAutoEnter& operator=(const ReentrantMonitorAutoEnter&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ mozilla::ReentrantMonitor* mReentrantMonitor;
+};
+
+/**
+ * ReentrantMonitorAutoExit
+ * Exit the ReentrantMonitor when it enters scope, and enters it when it leaves
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to ReentrantMonitor.Exit and Enter.
+ */
+class MOZ_STACK_CLASS ReentrantMonitorAutoExit
+{
+public:
+ /**
+ * Constructor
+ * The constructor releases the given lock. The destructor
+ * acquires the lock. The lock must be held before constructing
+ * this object!
+ *
+ * @param aReentrantMonitor A valid mozilla::ReentrantMonitor*. It
+ * must be already locked.
+ **/
+ explicit ReentrantMonitorAutoExit(ReentrantMonitor& aReentrantMonitor)
+ : mReentrantMonitor(&aReentrantMonitor)
+ {
+ NS_ASSERTION(mReentrantMonitor, "null monitor");
+ mReentrantMonitor->AssertCurrentThreadIn();
+ mReentrantMonitor->Exit();
+ }
+
+ ~ReentrantMonitorAutoExit(void)
+ {
+ mReentrantMonitor->Enter();
+ }
+
+private:
+ ReentrantMonitorAutoExit();
+ ReentrantMonitorAutoExit(const ReentrantMonitorAutoExit&);
+ ReentrantMonitorAutoExit& operator=(const ReentrantMonitorAutoExit&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ ReentrantMonitor* mReentrantMonitor;
+};
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_ReentrantMonitor_h
diff --git a/xpcom/glue/moz.build b/xpcom/glue/moz.build
new file mode 100644
index 000000000..95c18b273
--- /dev/null
+++ b/xpcom/glue/moz.build
@@ -0,0 +1,123 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('nsString*'):
+ BUG_COMPONENT = ('Core', 'String')
+
+DIRS += ['standalone']
+
+# On win we build two glue libs - glue linked to crt dlls here and in staticruntime we build
+# a statically linked glue lib.
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DIRS += ['staticruntime']
+
+EXPORTS += [
+ 'MainThreadUtils.h',
+ 'nsArrayEnumerator.h',
+ 'nsArrayUtils.h',
+ 'nsBaseHashtable.h',
+ 'nsCategoryCache.h',
+ 'nsClassHashtable.h',
+ 'nsCOMArray.h',
+ 'nsComponentManagerUtils.h',
+ 'nsCOMPtr.h',
+ 'nsCRTGlue.h',
+ 'nsCycleCollectionNoteChild.h',
+ 'nsCycleCollectionNoteRootCallback.h',
+ 'nsCycleCollectionParticipant.h',
+ 'nsCycleCollectionTraversalCallback.h',
+ 'nsDataHashtable.h',
+ 'nsDebug.h',
+ 'nsDeque.h',
+ 'nsEnumeratorUtils.h',
+ 'nsHashKeys.h',
+ 'nsIClassInfoImpl.h',
+ 'nsID.h',
+ 'nsIInterfaceRequestorUtils.h',
+ 'nsINIParser.h',
+ 'nsInterfaceHashtable.h',
+ 'nsISupportsImpl.h',
+ 'nsISupportsUtils.h',
+ 'nsIWeakReferenceUtils.h',
+ 'nsJSThingHashtable.h',
+ 'nsMemory.h',
+ 'nsPointerHashKeys.h',
+ 'nsProxyRelease.h',
+ 'nsQuickSort.h',
+ 'nsRefPtrHashtable.h',
+ 'nsServiceManagerUtils.h',
+ 'nsStringAPI.h',
+ 'nsStringGlue.h',
+ 'nsTArray-inl.h',
+ 'nsTArray.h',
+ 'nsTArrayForwardDeclare.h',
+ 'nsTextFormatter.h',
+ 'nsTHashtable.h',
+ 'nsThreadUtils.h',
+ 'nsTObserverArray.h',
+ 'nsTPriorityQueue.h',
+ 'nsTWeakRef.h',
+ 'nsVersionComparator.h',
+ 'nsWeakReference.h',
+ 'nsXPTCUtils.h',
+ 'PLDHashTable.h',
+]
+
+EXPORTS.mozilla += [
+ 'AppData.h',
+ 'AutoRestore.h',
+ 'BlockingResourceBase.h',
+ 'CondVar.h',
+ 'DeadlockDetector.h',
+ 'EnumeratedArrayCycleCollection.h',
+ 'FileUtils.h',
+ 'GenericFactory.h',
+ 'IntentionalCrash.h',
+ 'Monitor.h',
+ 'Mutex.h',
+ 'Observer.h',
+ 'ReentrantMonitor.h',
+]
+
+include('objs.mozbuild')
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ 'GenericModule.cpp',
+ 'nsStringAPI.cpp',
+]
+
+Library('xpcomglue_s')
+
+SDK_LIBRARY = True
+
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+LOCAL_INCLUDES += [
+ '../build',
+ '../threads',
+]
+
+if CONFIG['ENABLE_TESTS']:
+ DIRS += ['tests/gtest']
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
diff --git a/xpcom/glue/nsArrayEnumerator.cpp b/xpcom/glue/nsArrayEnumerator.cpp
new file mode 100644
index 000000000..2d2ef6da7
--- /dev/null
+++ b/xpcom/glue/nsArrayEnumerator.cpp
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Attributes.h"
+
+#include "nsArrayEnumerator.h"
+
+#include "nsIArray.h"
+#include "nsISimpleEnumerator.h"
+
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class nsSimpleArrayEnumerator final : public nsISimpleEnumerator
+{
+public:
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsSimpleArrayEnumerator methods
+ explicit nsSimpleArrayEnumerator(nsIArray* aValueArray)
+ : mValueArray(aValueArray)
+ , mIndex(0)
+ {
+ }
+
+private:
+ ~nsSimpleArrayEnumerator() {}
+
+protected:
+ nsCOMPtr<nsIArray> mValueArray;
+ uint32_t mIndex;
+};
+
+NS_IMPL_ISUPPORTS(nsSimpleArrayEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsSimpleArrayEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mValueArray) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ uint32_t cnt;
+ nsresult rv = mValueArray->GetLength(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aResult = (mIndex < cnt);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimpleArrayEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mValueArray) {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+
+ uint32_t cnt;
+ nsresult rv = mValueArray->GetLength(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (mIndex >= cnt) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return mValueArray->QueryElementAt(mIndex++, NS_GET_IID(nsISupports),
+ (void**)aResult);
+}
+
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult, nsIArray* aArray)
+{
+ RefPtr<nsSimpleArrayEnumerator> enumer = new nsSimpleArrayEnumerator(aArray);
+ enumer.forget(aResult);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// enumerator implementation for nsCOMArray
+// creates a snapshot of the array in question
+// you MUST use NS_NewArrayEnumerator to create this, so that
+// allocation is done correctly
+class nsCOMArrayEnumerator final : public nsISimpleEnumerator
+{
+public:
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsSimpleArrayEnumerator methods
+ nsCOMArrayEnumerator() : mIndex(0) {}
+
+ // specialized operator to make sure we make room for mValues
+ void* operator new(size_t aSize, const nsCOMArray_base& aArray) CPP_THROW_NEW;
+ void operator delete(void* aPtr) { ::operator delete(aPtr); }
+
+private:
+ ~nsCOMArrayEnumerator(void);
+
+protected:
+ uint32_t mIndex; // current position
+ uint32_t mArraySize; // size of the array
+
+ // this is actually bigger
+ nsISupports* mValueArray[1];
+};
+
+NS_IMPL_ISUPPORTS(nsCOMArrayEnumerator, nsISimpleEnumerator)
+
+nsCOMArrayEnumerator::~nsCOMArrayEnumerator()
+{
+ // only release the entries that we haven't visited yet
+ for (; mIndex < mArraySize; ++mIndex) {
+ NS_IF_RELEASE(mValueArray[mIndex]);
+ }
+}
+
+NS_IMETHODIMP
+nsCOMArrayEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aResult = (mIndex < mArraySize);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCOMArrayEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (mIndex >= mArraySize) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // pass the ownership of the reference to the caller. Since
+ // we AddRef'ed during creation of |this|, there is no need
+ // to AddRef here
+ *aResult = mValueArray[mIndex++];
+
+ // this really isn't necessary. just pretend this happens, since
+ // we'll never visit this value again!
+ // mValueArray[(mIndex-1)] = nullptr;
+
+ return NS_OK;
+}
+
+void*
+nsCOMArrayEnumerator::operator new(size_t aSize,
+ const nsCOMArray_base& aArray) CPP_THROW_NEW
+{
+ // create enough space such that mValueArray points to a large
+ // enough value. Note that the initial value of aSize gives us
+ // space for mValueArray[0], so we must subtract
+ aSize += (aArray.Count() - 1) * sizeof(aArray[0]);
+
+ // do the actual allocation
+ nsCOMArrayEnumerator* result =
+ static_cast<nsCOMArrayEnumerator*>(::operator new(aSize));
+
+ // now need to copy over the values, and addref each one
+ // now this might seem like a lot of work, but we're actually just
+ // doing all our AddRef's ahead of time since GetNext() doesn't
+ // need to AddRef() on the way out
+ uint32_t i;
+ uint32_t max = result->mArraySize = aArray.Count();
+ for (i = 0; i < max; ++i) {
+ result->mValueArray[i] = aArray[i];
+ NS_IF_ADDREF(result->mValueArray[i]);
+ }
+
+ return result;
+}
+
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
+ const nsCOMArray_base& aArray)
+{
+ RefPtr<nsCOMArrayEnumerator> enumerator = new (aArray) nsCOMArrayEnumerator();
+ enumerator.forget(aResult);
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsArrayEnumerator.h b/xpcom/glue/nsArrayEnumerator.h
new file mode 100644
index 000000000..341ae86ed
--- /dev/null
+++ b/xpcom/glue/nsArrayEnumerator.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsArrayEnumerator_h__
+#define nsArrayEnumerator_h__
+
+// enumerator implementation for nsIArray
+
+#include "nscore.h"
+
+class nsISimpleEnumerator;
+class nsIArray;
+class nsCOMArray_base;
+
+// Create an enumerator for an existing nsIArray implementation
+// The enumerator holds an owning reference to the array.
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
+ nsIArray* aArray);
+
+// create an enumerator for an existing nsCOMArray<T> implementation
+// The enumerator will hold an owning reference to each ELEMENT in
+// the array. This means that the nsCOMArray<T> can safely go away
+// without its objects going away.
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
+ const nsCOMArray_base& aArray);
+
+#endif
diff --git a/xpcom/glue/nsArrayUtils.cpp b/xpcom/glue/nsArrayUtils.cpp
new file mode 100644
index 000000000..480863737
--- /dev/null
+++ b/xpcom/glue/nsArrayUtils.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsArrayUtils.h"
+
+//
+// do_QueryElementAt helper stuff
+//
+nsresult
+nsQueryArrayElementAt::operator()(const nsIID& aIID, void** aResult) const
+{
+ nsresult status = mArray ? mArray->QueryElementAt(mIndex, aIID, aResult) :
+ NS_ERROR_NULL_POINTER;
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+
+ return status;
+}
diff --git a/xpcom/glue/nsArrayUtils.h b/xpcom/glue/nsArrayUtils.h
new file mode 100644
index 000000000..68032e8c0
--- /dev/null
+++ b/xpcom/glue/nsArrayUtils.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsArrayUtils_h__
+#define nsArrayUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsIArray.h"
+
+// helper class for do_QueryElementAt
+class MOZ_STACK_CLASS nsQueryArrayElementAt final : public nsCOMPtr_helper
+{
+public:
+ nsQueryArrayElementAt(nsIArray* aArray, uint32_t aIndex,
+ nsresult* aErrorPtr)
+ : mArray(aArray)
+ , mIndex(aIndex)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const
+ override;
+
+private:
+ nsIArray* MOZ_NON_OWNING_REF mArray;
+ uint32_t mIndex;
+ nsresult* mErrorPtr;
+};
+
+inline const nsQueryArrayElementAt
+do_QueryElementAt(nsIArray* aArray, uint32_t aIndex, nsresult* aErrorPtr = 0)
+{
+ return nsQueryArrayElementAt(aArray, aIndex, aErrorPtr);
+}
+
+#endif // nsArrayUtils_h__
diff --git a/xpcom/glue/nsBaseHashtable.h b/xpcom/glue/nsBaseHashtable.h
new file mode 100644
index 000000000..f52df3dd1
--- /dev/null
+++ b/xpcom/glue/nsBaseHashtable.h
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBaseHashtable_h__
+#define nsBaseHashtable_h__
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "nsTHashtable.h"
+#include "nsDebug.h"
+
+template<class KeyClass, class DataType, class UserDataType>
+class nsBaseHashtable; // forward declaration
+
+/**
+ * the private nsTHashtable::EntryType class used by nsBaseHashtable
+ * @see nsTHashtable for the specification of this class
+ * @see nsBaseHashtable for template parameters
+ */
+template<class KeyClass, class DataType>
+class nsBaseHashtableET : public KeyClass
+{
+public:
+ DataType mData;
+ friend class nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>;
+
+private:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef typename KeyClass::KeyTypePointer KeyTypePointer;
+
+ explicit nsBaseHashtableET(KeyTypePointer aKey);
+ nsBaseHashtableET(nsBaseHashtableET<KeyClass, DataType>&& aToMove);
+ ~nsBaseHashtableET();
+};
+
+/**
+ * templated hashtable for simple data types
+ * This class manages simple data types that do not need construction or
+ * destruction.
+ *
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the datatype stored in the hashtable,
+ * for example, uint32_t or nsCOMPtr. If UserDataType is not the same,
+ * DataType must implicitly cast to UserDataType
+ * @param UserDataType the user sees, for example uint32_t or nsISupports*
+ */
+template<class KeyClass, class DataType, class UserDataType>
+class nsBaseHashtable
+ : protected nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>
+{
+ typedef mozilla::fallible_t fallible_t;
+
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef nsBaseHashtableET<KeyClass, DataType> EntryType;
+
+ using nsTHashtable<EntryType>::Contains;
+
+ nsBaseHashtable() {}
+ explicit nsBaseHashtable(uint32_t aInitLength)
+ : nsTHashtable<EntryType>(aInitLength)
+ {
+ }
+
+ /**
+ * Return the number of entries in the table.
+ * @return number of entries
+ */
+ uint32_t Count() const { return nsTHashtable<EntryType>::Count(); }
+
+ /**
+ * retrieve the value for a key.
+ * @param aKey the key to retreive
+ * @param aData data associated with this key will be placed at this
+ * pointer. If you only need to check if the key exists, aData
+ * may be null.
+ * @return true if the key exists. If key does not exist, aData is not
+ * modified.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return false;
+ }
+
+ if (aData) {
+ *aData = ent->mData;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the value, returning a zero-initialized POD or a default-initialized
+ * object if the entry is not present in the table.
+ *
+ * @param aKey the key to retrieve
+ * @return The found value, or UserDataType{} if no entry was found with the
+ * given key.
+ * @note If zero/default-initialized values are stored in the table, it is
+ * not possible to distinguish between such a value and a missing entry.
+ */
+ UserDataType Get(KeyType aKey) const
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return UserDataType{};
+ }
+
+ return ent->mData;
+ }
+
+ /**
+ * Add key to the table if not already present, and return a reference to its
+ * value. If key is not already in the table then the value is default
+ * constructed.
+ */
+ DataType& GetOrInsert(const KeyType& aKey)
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ if (ent) {
+ return ent->mData;
+ }
+
+ ent = this->PutEntry(aKey);
+ return ent->mData;
+ }
+
+ /**
+ * put a new value for the associated key
+ * @param aKey the key to put
+ * @param aData the new data
+ * @return always true, unless memory allocation failed
+ */
+ void Put(KeyType aKey, const UserDataType& aData)
+ {
+ if (!Put(aKey, aData, mozilla::fallible)) {
+ NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
+ }
+ }
+
+ MOZ_MUST_USE bool Put(KeyType aKey, const UserDataType& aData,
+ const fallible_t&)
+ {
+ EntryType* ent = this->PutEntry(aKey, mozilla::fallible);
+ if (!ent) {
+ return false;
+ }
+
+ ent->mData = aData;
+
+ return true;
+ }
+
+ /**
+ * remove the data for the associated key
+ * @param aKey the key to remove from the hashtable
+ */
+ void Remove(KeyType aKey) { this->RemoveEntry(aKey); }
+
+ // This is an iterator that also allows entry removal. Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // const KeyType key = iter.Key();
+ // const UserDataType data = iter.UserData();
+ // // or
+ // const DataType& data = iter.Data();
+ // // ... do stuff with |key| and/or |data| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ class Iterator : public PLDHashTable::Iterator
+ {
+ public:
+ typedef PLDHashTable::Iterator Base;
+
+ explicit Iterator(nsBaseHashtable* aTable) : Base(&aTable->mTable) {}
+ Iterator(Iterator&& aOther) : Base(aOther.mTable) {}
+ ~Iterator() {}
+
+ KeyType Key() const { return static_cast<EntryType*>(Get())->GetKey(); }
+ UserDataType UserData() const
+ {
+ return static_cast<EntryType*>(Get())->mData;
+ }
+ DataType& Data() const { return static_cast<EntryType*>(Get())->mData; }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<nsBaseHashtable*>(this));
+ }
+
+ /**
+ * reset the hashtable, removing all entries
+ */
+ void Clear() { nsTHashtable<EntryType>::Clear(); }
+
+ /**
+ * Measure the size of the table's entry storage. The size of things pointed
+ * to by entries must be measured separately; hence the "Shallow" prefix.
+ *
+ * @param aMallocSizeOf the function used to measure heap-allocated blocks
+ * @return the summed size of the table's storage
+ */
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return this->mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Like ShallowSizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Swap the elements in this hashtable with the elements in aOther.
+ */
+ void SwapElements(nsBaseHashtable& aOther)
+ {
+ nsTHashtable<EntryType>::SwapElements(aOther);
+ }
+
+
+#ifdef DEBUG
+ using nsTHashtable<EntryType>::MarkImmutable;
+#endif
+};
+
+//
+// nsBaseHashtableET definitions
+//
+
+template<class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(KeyTypePointer aKey)
+ : KeyClass(aKey)
+ , mData()
+{
+}
+
+template<class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(
+ nsBaseHashtableET<KeyClass, DataType>&& aToMove)
+ : KeyClass(mozilla::Move(aToMove))
+ , mData(mozilla::Move(aToMove.mData))
+{
+}
+
+template<class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::~nsBaseHashtableET()
+{
+}
+
+#endif // nsBaseHashtable_h__
diff --git a/xpcom/glue/nsCOMArray.cpp b/xpcom/glue/nsCOMArray.cpp
new file mode 100644
index 000000000..3522f7b0d
--- /dev/null
+++ b/xpcom/glue/nsCOMArray.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCOMArray.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/OperatorNewExtensions.h"
+
+#include "nsCOMPtr.h"
+
+// This specialization is private to nsCOMArray.
+// It exists solely to automatically zero-out newly created array elements.
+template<>
+class nsTArrayElementTraits<nsISupports*>
+{
+ typedef nsISupports* E;
+public:
+ // Zero out the value
+ static inline void Construct(E* aE)
+ {
+ new (mozilla::KnownNotNull, static_cast<void*>(aE)) E();
+ }
+ // Invoke the copy-constructor in place.
+ template<class A>
+ static inline void Construct(E* aE, const A& aArg)
+ {
+ new (mozilla::KnownNotNull, static_cast<void*>(aE)) E(aArg);
+ }
+ // Invoke the destructor in place.
+ static inline void Destruct(E* aE)
+ {
+ aE->~E();
+ }
+};
+
+static void ReleaseObjects(nsTArray<nsISupports*>& aArray);
+
+// implementations of non-trivial methods in nsCOMArray_base
+
+nsCOMArray_base::nsCOMArray_base(const nsCOMArray_base& aOther)
+{
+ // make sure we do only one allocation
+ mArray.SetCapacity(aOther.Count());
+ AppendObjects(aOther);
+}
+
+nsCOMArray_base::~nsCOMArray_base()
+{
+ Clear();
+}
+
+int32_t
+nsCOMArray_base::IndexOf(nsISupports* aObject, uint32_t aStartIndex) const
+{
+ return mArray.IndexOf(aObject, aStartIndex);
+}
+
+int32_t
+nsCOMArray_base::IndexOfObject(nsISupports* aObject) const
+{
+ nsCOMPtr<nsISupports> supports = do_QueryInterface(aObject);
+ if (NS_WARN_IF(!supports)) {
+ return -1;
+ }
+
+ uint32_t i, count;
+ int32_t retval = -1;
+ count = mArray.Length();
+ for (i = 0; i < count; ++i) {
+ nsCOMPtr<nsISupports> arrayItem = do_QueryInterface(mArray[i]);
+ if (arrayItem == supports) {
+ retval = i;
+ break;
+ }
+ }
+ return retval;
+}
+
+bool
+nsCOMArray_base::EnumerateForwards(nsBaseArrayEnumFunc aFunc, void* aData) const
+{
+ for (uint32_t index = 0; index < mArray.Length(); ++index) {
+ if (!(*aFunc)(mArray[index], aData)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+nsCOMArray_base::EnumerateBackwards(nsBaseArrayEnumFunc aFunc, void* aData) const
+{
+ for (uint32_t index = mArray.Length(); index--; ) {
+ if (!(*aFunc)(mArray[index], aData)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int
+nsCOMArray_base::nsCOMArrayComparator(const void* aElement1,
+ const void* aElement2,
+ void* aData)
+{
+ nsCOMArrayComparatorContext* ctx =
+ static_cast<nsCOMArrayComparatorContext*>(aData);
+ return (*ctx->mComparatorFunc)(*static_cast<nsISupports* const*>(aElement1),
+ *static_cast<nsISupports* const*>(aElement2),
+ ctx->mData);
+}
+
+void
+nsCOMArray_base::Sort(nsBaseArrayComparatorFunc aFunc, void* aData)
+{
+ if (mArray.Length() > 1) {
+ nsCOMArrayComparatorContext ctx = {aFunc, aData};
+ NS_QuickSort(mArray.Elements(), mArray.Length(), sizeof(nsISupports*),
+ nsCOMArrayComparator, &ctx);
+ }
+}
+
+bool
+nsCOMArray_base::InsertObjectAt(nsISupports* aObject, int32_t aIndex)
+{
+ if ((uint32_t)aIndex > mArray.Length()) {
+ return false;
+ }
+
+ if (!mArray.InsertElementAt(aIndex, aObject)) {
+ return false;
+ }
+
+ NS_IF_ADDREF(aObject);
+ return true;
+}
+
+void
+nsCOMArray_base::InsertElementAt(uint32_t aIndex, nsISupports* aElement)
+{
+ mArray.InsertElementAt(aIndex, aElement);
+ NS_IF_ADDREF(aElement);
+}
+
+void
+nsCOMArray_base::InsertElementAt(uint32_t aIndex, already_AddRefed<nsISupports> aElement)
+{
+ mArray.InsertElementAt(aIndex, aElement.take());
+}
+
+bool
+nsCOMArray_base::InsertObjectsAt(const nsCOMArray_base& aObjects, int32_t aIndex)
+{
+ if ((uint32_t)aIndex > mArray.Length()) {
+ return false;
+ }
+
+ if (!mArray.InsertElementsAt(aIndex, aObjects.mArray)) {
+ return false;
+ }
+
+ // need to addref all these
+ uint32_t count = aObjects.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ NS_IF_ADDREF(aObjects[i]);
+ }
+
+ return true;
+}
+
+void
+nsCOMArray_base::InsertElementsAt(uint32_t aIndex,
+ const nsCOMArray_base& aElements)
+{
+ mArray.InsertElementsAt(aIndex, aElements.mArray);
+
+ // need to addref all these
+ uint32_t count = aElements.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ NS_IF_ADDREF(aElements[i]);
+ }
+}
+
+void
+nsCOMArray_base::InsertElementsAt(uint32_t aIndex,
+ nsISupports* const* aElements,
+ uint32_t aCount)
+{
+ mArray.InsertElementsAt(aIndex, aElements, aCount);
+
+ // need to addref all these
+ for (uint32_t i = 0; i < aCount; ++i) {
+ NS_IF_ADDREF(aElements[i]);
+ }
+}
+
+void
+nsCOMArray_base::ReplaceObjectAt(nsISupports* aObject, int32_t aIndex)
+{
+ mArray.EnsureLengthAtLeast(aIndex + 1);
+ nsISupports* oldObject = mArray[aIndex];
+ // Make sure to addref first, in case aObject == oldObject
+ NS_IF_ADDREF(mArray[aIndex] = aObject);
+ NS_IF_RELEASE(oldObject);
+}
+
+bool
+nsCOMArray_base::RemoveObject(nsISupports* aObject)
+{
+ bool result = mArray.RemoveElement(aObject);
+ if (result) {
+ NS_IF_RELEASE(aObject);
+ }
+ return result;
+}
+
+bool
+nsCOMArray_base::RemoveObjectAt(int32_t aIndex)
+{
+ if (uint32_t(aIndex) < mArray.Length()) {
+ nsISupports* element = mArray[aIndex];
+
+ mArray.RemoveElementAt(aIndex);
+ NS_IF_RELEASE(element);
+ return true;
+ }
+
+ return false;
+}
+
+void
+nsCOMArray_base::RemoveElementAt(uint32_t aIndex)
+{
+ nsISupports* element = mArray[aIndex];
+ mArray.RemoveElementAt(aIndex);
+ NS_IF_RELEASE(element);
+}
+
+bool
+nsCOMArray_base::RemoveObjectsAt(int32_t aIndex, int32_t aCount)
+{
+ if (uint32_t(aIndex) + uint32_t(aCount) <= mArray.Length()) {
+ nsTArray<nsISupports*> elementsToDestroy(aCount);
+ elementsToDestroy.AppendElements(mArray.Elements() + aIndex, aCount);
+ mArray.RemoveElementsAt(aIndex, aCount);
+ ReleaseObjects(elementsToDestroy);
+ return true;
+ }
+
+ return false;
+}
+
+void
+nsCOMArray_base::RemoveElementsAt(uint32_t aIndex, uint32_t aCount)
+{
+ nsTArray<nsISupports*> elementsToDestroy(aCount);
+ elementsToDestroy.AppendElements(mArray.Elements() + aIndex, aCount);
+ mArray.RemoveElementsAt(aIndex, aCount);
+ ReleaseObjects(elementsToDestroy);
+}
+
+// useful for destructors
+void
+ReleaseObjects(nsTArray<nsISupports*>& aArray)
+{
+ for (uint32_t i = 0; i < aArray.Length(); ++i) {
+ NS_IF_RELEASE(aArray[i]);
+ }
+}
+
+void
+nsCOMArray_base::Clear()
+{
+ nsTArray<nsISupports*> objects;
+ objects.SwapElements(mArray);
+ ReleaseObjects(objects);
+}
+
+bool
+nsCOMArray_base::SetCount(int32_t aNewCount)
+{
+ NS_ASSERTION(aNewCount >= 0, "SetCount(negative index)");
+ if (aNewCount < 0) {
+ return false;
+ }
+
+ int32_t count = mArray.Length();
+ if (count > aNewCount) {
+ RemoveObjectsAt(aNewCount, mArray.Length() - aNewCount);
+ }
+ mArray.SetLength(aNewCount);
+ return true;
+}
+
+void
+nsCOMArray_base::Adopt(nsISupports** aElements, uint32_t aSize)
+{
+ Clear();
+ mArray.AppendElements(aElements, aSize);
+
+ // Free the allocated array as well.
+ NS_Free(aElements);
+}
+
+uint32_t
+nsCOMArray_base::Forget(nsISupports*** aElements)
+{
+ uint32_t length = Length();
+ size_t array_size = sizeof(nsISupports*) * length;
+ nsISupports** array = static_cast<nsISupports**>(NS_Alloc(array_size));
+ memmove(array, Elements(), array_size);
+ *aElements = array;
+ // Don't Release the contained pointers; the caller of the method will
+ // do this eventually.
+ mArray.Clear();
+
+ return length;
+}
diff --git a/xpcom/glue/nsCOMArray.h b/xpcom/glue/nsCOMArray.h
new file mode 100644
index 000000000..56d077a2f
--- /dev/null
+++ b/xpcom/glue/nsCOMArray.h
@@ -0,0 +1,473 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCOMArray_h__
+#define nsCOMArray_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "nsCycleCollectionNoteChild.h"
+#include "nsTArray.h"
+#include "nsISupports.h"
+
+// See below for the definition of nsCOMArray<T>
+
+// a class that's nsISupports-specific, so that we can contain the
+// work of this class in the XPCOM dll
+class nsCOMArray_base
+{
+ friend class nsArrayBase;
+protected:
+ nsCOMArray_base() {}
+ explicit nsCOMArray_base(int32_t aCount) : mArray(aCount) {}
+ nsCOMArray_base(const nsCOMArray_base& aOther);
+ ~nsCOMArray_base();
+
+ int32_t IndexOf(nsISupports* aObject, uint32_t aStartIndex = 0) const;
+ bool Contains(nsISupports* aObject) const
+ {
+ return IndexOf(aObject) != -1;
+ }
+
+ int32_t IndexOfObject(nsISupports* aObject) const;
+ bool ContainsObject(nsISupports* aObject) const
+ {
+ return IndexOfObject(aObject) != -1;
+ }
+
+ typedef bool (*nsBaseArrayEnumFunc)(void* aElement, void* aData);
+
+ // enumerate through the array with a callback.
+ bool EnumerateForwards(nsBaseArrayEnumFunc aFunc, void* aData) const;
+
+ bool EnumerateBackwards(nsBaseArrayEnumFunc aFunc, void* aData) const;
+
+ typedef int (*nsBaseArrayComparatorFunc)(nsISupports* aElement1,
+ nsISupports* aElement2,
+ void* aData);
+
+ struct nsCOMArrayComparatorContext
+ {
+ nsBaseArrayComparatorFunc mComparatorFunc;
+ void* mData;
+ };
+
+ static int nsCOMArrayComparator(const void* aElement1, const void* aElement2,
+ void* aData);
+ void Sort(nsBaseArrayComparatorFunc aFunc, void* aData);
+
+ bool InsertObjectAt(nsISupports* aObject, int32_t aIndex);
+ void InsertElementAt(uint32_t aIndex, nsISupports* aElement);
+ void InsertElementAt(uint32_t aIndex, already_AddRefed<nsISupports> aElement);
+ bool InsertObjectsAt(const nsCOMArray_base& aObjects, int32_t aIndex);
+ void InsertElementsAt(uint32_t aIndex, const nsCOMArray_base& aElements);
+ void InsertElementsAt(uint32_t aIndex, nsISupports* const* aElements,
+ uint32_t aCount);
+ void ReplaceObjectAt(nsISupports* aObject, int32_t aIndex);
+ void ReplaceElementAt(uint32_t aIndex, nsISupports* aElement)
+ {
+ nsISupports* oldElement = mArray[aIndex];
+ NS_IF_ADDREF(mArray[aIndex] = aElement);
+ NS_IF_RELEASE(oldElement);
+ }
+ bool AppendObject(nsISupports* aObject)
+ {
+ return InsertObjectAt(aObject, Count());
+ }
+ void AppendElement(nsISupports* aElement)
+ {
+ InsertElementAt(Length(), aElement);
+ }
+ void AppendElement(already_AddRefed<nsISupports> aElement)
+ {
+ InsertElementAt(Length(), mozilla::Move(aElement));
+ }
+
+ bool AppendObjects(const nsCOMArray_base& aObjects)
+ {
+ return InsertObjectsAt(aObjects, Count());
+ }
+ void AppendElements(const nsCOMArray_base& aElements)
+ {
+ return InsertElementsAt(Length(), aElements);
+ }
+ void AppendElements(nsISupports* const* aElements, uint32_t aCount)
+ {
+ return InsertElementsAt(Length(), aElements, aCount);
+ }
+ bool RemoveObject(nsISupports* aObject);
+ nsISupports** Elements() { return mArray.Elements(); }
+ void SwapElements(nsCOMArray_base& aOther)
+ {
+ mArray.SwapElements(aOther.mArray);
+ }
+
+ void Adopt(nsISupports** aElements, uint32_t aCount);
+ uint32_t Forget(nsISupports*** aElements);
+public:
+ // elements in the array (including null elements!)
+ int32_t Count() const { return mArray.Length(); }
+ // nsTArray-compatible version
+ uint32_t Length() const { return mArray.Length(); }
+ bool IsEmpty() const { return mArray.IsEmpty(); }
+
+ // If the array grows, the newly created entries will all be null;
+ // if the array shrinks, the excess entries will all be released.
+ bool SetCount(int32_t aNewCount);
+ // nsTArray-compatible version
+ void TruncateLength(uint32_t aNewLength)
+ {
+ if (mArray.Length() > aNewLength) {
+ RemoveElementsAt(aNewLength, mArray.Length() - aNewLength);
+ }
+ }
+
+ // remove all elements in the array, and call NS_RELEASE on each one
+ void Clear();
+
+ nsISupports* ObjectAt(int32_t aIndex) const { return mArray[aIndex]; }
+ // nsTArray-compatible version
+ nsISupports* ElementAt(uint32_t aIndex) const { return mArray[aIndex]; }
+
+ nsISupports* SafeObjectAt(int32_t aIndex) const
+ {
+ return mArray.SafeElementAt(aIndex, nullptr);
+ }
+ // nsTArray-compatible version
+ nsISupports* SafeElementAt(uint32_t aIndex) const
+ {
+ return mArray.SafeElementAt(aIndex, nullptr);
+ }
+
+ nsISupports* operator[](int32_t aIndex) const { return mArray[aIndex]; }
+
+ // remove an element at a specific position, shrinking the array
+ // as necessary
+ bool RemoveObjectAt(int32_t aIndex);
+ // nsTArray-compatible version
+ void RemoveElementAt(uint32_t aIndex);
+
+ // remove a range of elements at a specific position, shrinking the array
+ // as necessary
+ bool RemoveObjectsAt(int32_t aIndex, int32_t aCount);
+ // nsTArray-compatible version
+ void RemoveElementsAt(uint32_t aIndex, uint32_t aCount);
+
+ void SwapElementsAt(uint32_t aIndex1, uint32_t aIndex2)
+ {
+ nsISupports* tmp = mArray[aIndex1];
+ mArray[aIndex1] = mArray[aIndex2];
+ mArray[aIndex2] = tmp;
+ }
+
+ // Ensures there is enough space to store a total of aCapacity objects.
+ // This method never deletes any objects.
+ void SetCapacity(uint32_t aCapacity) { mArray.SetCapacity(aCapacity); }
+ uint32_t Capacity() { return mArray.Capacity(); }
+
+ // Measures the size of the array's element storage. If you want to measure
+ // anything hanging off the array, you must iterate over the elements and
+ // measure them individually; hence the "Shallow" prefix. Note that because
+ // each element in an nsCOMArray<T> is actually a T* any such iteration
+ // should use a SizeOfIncludingThis() function on each element rather than a
+ // SizeOfExcludingThis() function, so that the memory taken by the T itself
+ // is included as well as anything it points to.
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+
+ // the actual storage
+ nsTArray<nsISupports*> mArray;
+
+ // don't implement these, defaults will muck with refcounts!
+ nsCOMArray_base& operator=(const nsCOMArray_base& aOther) = delete;
+};
+
+inline void
+ImplCycleCollectionUnlink(nsCOMArray_base& aField)
+{
+ aField.Clear();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsCOMArray_base& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ int32_t length = aField.Count();
+ for (int32_t i = 0; i < length; ++i) {
+ CycleCollectionNoteChild(aCallback, aField[i], aName, aFlags);
+ }
+}
+
+
+// a non-XPCOM, refcounting array of XPCOM objects
+// used as a member variable or stack variable - this object is NOT
+// refcounted, but the objects that it holds are
+//
+// most of the read-only accessors like ObjectAt()/etc do NOT refcount
+// on the way out. This means that you can do one of two things:
+//
+// * does an addref, but holds onto a reference
+// nsCOMPtr<T> foo = array[i];
+//
+// * avoids the refcount, but foo might go stale if array[i] is ever
+// * modified/removed. Be careful not to NS_RELEASE(foo)!
+// T* foo = array[i];
+//
+// This array will accept null as an argument for any object, and will store
+// null in the array. But that also means that methods like ObjectAt() may
+// return null when referring to an existing, but null entry in the array.
+template<class T>
+class nsCOMArray : public nsCOMArray_base
+{
+public:
+ nsCOMArray() {}
+ explicit nsCOMArray(int32_t aCount) : nsCOMArray_base(aCount) {}
+ explicit nsCOMArray(const nsCOMArray<T>& aOther) : nsCOMArray_base(aOther) {}
+ nsCOMArray(nsCOMArray<T>&& aOther) { SwapElements(aOther); }
+ ~nsCOMArray() {}
+
+ // We have a move assignment operator, but no copy assignment operator.
+ nsCOMArray<T>& operator=(nsCOMArray<T> && aOther)
+ {
+ SwapElements(aOther);
+ return *this;
+ }
+
+ // these do NOT refcount on the way out, for speed
+ T* ObjectAt(int32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::ObjectAt(aIndex));
+ }
+ // nsTArray-compatible version
+ T* ElementAt(uint32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::ElementAt(aIndex));
+ }
+
+ // these do NOT refcount on the way out, for speed
+ T* SafeObjectAt(int32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::SafeObjectAt(aIndex));
+ }
+ // nsTArray-compatible version
+ T* SafeElementAt(uint32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::SafeElementAt(aIndex));
+ }
+
+ // indexing operator for syntactic sugar
+ T* operator[](int32_t aIndex) const { return ObjectAt(aIndex); }
+
+ // index of the element in question.. does NOT refcount
+ // note: this does not check COM object identity. Use
+ // IndexOfObject() for that purpose
+ int32_t IndexOf(T* aObject, uint32_t aStartIndex = 0) const
+ {
+ return nsCOMArray_base::IndexOf(aObject, aStartIndex);
+ }
+ bool Contains(T* aObject) const
+ {
+ return nsCOMArray_base::Contains(aObject);
+ }
+
+ // index of the element in question.. be careful!
+ // this is much slower than IndexOf() because it uses
+ // QueryInterface to determine actual COM identity of the object
+ // if you need to do this frequently then consider enforcing
+ // COM object identity before adding/comparing elements
+ int32_t IndexOfObject(T* aObject) const
+ {
+ return nsCOMArray_base::IndexOfObject(aObject);
+ }
+ bool ContainsObject(nsISupports* aObject) const
+ {
+ return nsCOMArray_base::ContainsObject(aObject);
+ }
+
+ // inserts aObject at aIndex, shifting the objects at aIndex and
+ // later to make space
+ bool InsertObjectAt(T* aObject, int32_t aIndex)
+ {
+ return nsCOMArray_base::InsertObjectAt(aObject, aIndex);
+ }
+ // nsTArray-compatible version
+ void InsertElementAt(uint32_t aIndex, T* aElement)
+ {
+ nsCOMArray_base::InsertElementAt(aIndex, aElement);
+ }
+
+ // inserts the objects from aObject at aIndex, shifting the
+ // objects at aIndex and later to make space
+ bool InsertObjectsAt(const nsCOMArray<T>& aObjects, int32_t aIndex)
+ {
+ return nsCOMArray_base::InsertObjectsAt(aObjects, aIndex);
+ }
+ // nsTArray-compatible version
+ void InsertElementsAt(uint32_t aIndex, const nsCOMArray<T>& aElements)
+ {
+ nsCOMArray_base::InsertElementsAt(aIndex, aElements);
+ }
+ void InsertElementsAt(uint32_t aIndex, T* const* aElements, uint32_t aCount)
+ {
+ nsCOMArray_base::InsertElementsAt(
+ aIndex, reinterpret_cast<nsISupports* const*>(aElements), aCount);
+ }
+
+ // replaces an existing element. Warning: if the array grows,
+ // the newly created entries will all be null
+ void ReplaceObjectAt(T* aObject, int32_t aIndex)
+ {
+ nsCOMArray_base::ReplaceObjectAt(aObject, aIndex);
+ }
+ // nsTArray-compatible version
+ void ReplaceElementAt(uint32_t aIndex, T* aElement)
+ {
+ nsCOMArray_base::ReplaceElementAt(aIndex, aElement);
+ }
+
+ // Enumerator callback function. Return false to stop
+ // Here's a more readable form:
+ // bool enumerate(T* aElement, void* aData)
+ typedef bool (*nsCOMArrayEnumFunc)(T* aElement, void* aData);
+
+ // enumerate through the array with a callback.
+ bool EnumerateForwards(nsCOMArrayEnumFunc aFunc, void* aData)
+ {
+ return nsCOMArray_base::EnumerateForwards(nsBaseArrayEnumFunc(aFunc),
+ aData);
+ }
+
+ bool EnumerateBackwards(nsCOMArrayEnumFunc aFunc, void* aData)
+ {
+ return nsCOMArray_base::EnumerateBackwards(nsBaseArrayEnumFunc(aFunc),
+ aData);
+ }
+
+ typedef int (*nsCOMArrayComparatorFunc)(T* aElement1, T* aElement2,
+ void* aData);
+
+ void Sort(nsCOMArrayComparatorFunc aFunc, void* aData)
+ {
+ nsCOMArray_base::Sort(nsBaseArrayComparatorFunc(aFunc), aData);
+ }
+
+ // append an object, growing the array as necessary
+ bool AppendObject(T* aObject)
+ {
+ return nsCOMArray_base::AppendObject(aObject);
+ }
+ // nsTArray-compatible version
+ void AppendElement(T* aElement)
+ {
+ nsCOMArray_base::AppendElement(aElement);
+ }
+ void AppendElement(already_AddRefed<T> aElement)
+ {
+ nsCOMArray_base::AppendElement(mozilla::Move(aElement));
+ }
+
+ // append objects, growing the array as necessary
+ bool AppendObjects(const nsCOMArray<T>& aObjects)
+ {
+ return nsCOMArray_base::AppendObjects(aObjects);
+ }
+ // nsTArray-compatible version
+ void AppendElements(const nsCOMArray<T>& aElements)
+ {
+ return nsCOMArray_base::AppendElements(aElements);
+ }
+ void AppendElements(T* const* aElements, uint32_t aCount)
+ {
+ InsertElementsAt(Length(), aElements, aCount);
+ }
+
+ // remove the first instance of the given object and shrink the
+ // array as necessary
+ // Warning: if you pass null here, it will remove the first null element
+ bool RemoveObject(T* aObject)
+ {
+ return nsCOMArray_base::RemoveObject(aObject);
+ }
+ // nsTArray-compatible version
+ bool RemoveElement(T* aElement)
+ {
+ return nsCOMArray_base::RemoveObject(aElement);
+ }
+
+ T** Elements()
+ {
+ return reinterpret_cast<T**>(nsCOMArray_base::Elements());
+ }
+ void SwapElements(nsCOMArray<T>& aOther)
+ {
+ nsCOMArray_base::SwapElements(aOther);
+ }
+
+ /**
+ * Adopt parameters that resulted from an XPIDL outparam. The aElements
+ * parameter will be freed as a result of the call.
+ *
+ * Example usage:
+ * nsCOMArray<nsISomeInterface> array;
+ * nsISomeInterface** elements;
+ * uint32_t length;
+ * ptr->GetSomeArray(&elements, &length);
+ * array.Adopt(elements, length);
+ */
+ void Adopt(T** aElements, uint32_t aSize)
+ {
+ nsCOMArray_base::Adopt(reinterpret_cast<nsISupports**>(aElements), aSize);
+ }
+
+ /**
+ * Export the contents of this array to an XPIDL outparam. The array will be
+ * Clear()'d after this operation.
+ *
+ * Example usage:
+ * nsCOMArray<nsISomeInterface> array;
+ * *length = array.Forget(retval);
+ */
+ uint32_t Forget(T*** aElements)
+ {
+ return nsCOMArray_base::Forget(reinterpret_cast<nsISupports***>(aElements));
+ }
+
+private:
+
+ // don't implement these!
+ nsCOMArray<T>& operator=(const nsCOMArray<T>& aOther) = delete;
+};
+
+template<typename T>
+inline void
+ImplCycleCollectionUnlink(nsCOMArray<T>& aField)
+{
+ aField.Clear();
+}
+
+template<typename E>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsCOMArray<E>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ int32_t length = aField.Count();
+ for (int32_t i = 0; i < length; ++i) {
+ CycleCollectionNoteChild(aCallback, aField[i], aName, aFlags);
+ }
+}
+
+#endif
diff --git a/xpcom/glue/nsCOMPtr.cpp b/xpcom/glue/nsCOMPtr.cpp
new file mode 100644
index 000000000..263fa62c2
--- /dev/null
+++ b/xpcom/glue/nsCOMPtr.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCOMPtr.h"
+
+nsresult
+nsQueryInterface::operator()(const nsIID& aIID, void** aAnswer) const
+{
+ nsresult status;
+ if (mRawPtr) {
+ status = mRawPtr->QueryInterface(aIID, aAnswer);
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ return status;
+}
+
+nsresult
+nsQueryInterfaceWithError::operator()(const nsIID& aIID, void** aAnswer) const
+{
+ nsresult status;
+ if (mRawPtr) {
+ status = mRawPtr->QueryInterface(aIID, aAnswer);
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+void
+nsCOMPtr_base::assign_with_AddRef(nsISupports* aRawPtr)
+{
+ if (aRawPtr) {
+ NSCAP_ADDREF(this, aRawPtr);
+ }
+ assign_assuming_AddRef(aRawPtr);
+}
+
+void
+nsCOMPtr_base::assign_from_qi(const nsQueryInterface aQI, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_qi_with_error(const nsQueryInterfaceWithError& aQI,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_cid(const nsGetServiceByCID aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_cid_with_error(
+ const nsGetServiceByCIDWithError& aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_contractid(const nsGetServiceByContractID aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_contractid_with_error(
+ const nsGetServiceByContractIDWithError& aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_helper(const nsCOMPtr_helper& aHelper,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aHelper(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void**
+nsCOMPtr_base::begin_assignment()
+{
+ assign_assuming_AddRef(nullptr);
+ return reinterpret_cast<void**>(&mRawPtr);
+}
diff --git a/xpcom/glue/nsCOMPtr.h b/xpcom/glue/nsCOMPtr.h
new file mode 100644
index 000000000..373f55cbb
--- /dev/null
+++ b/xpcom/glue/nsCOMPtr.h
@@ -0,0 +1,1472 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCOMPtr_h___
+#define nsCOMPtr_h___
+
+/*
+ * Having problems?
+ *
+ * See the User Manual at:
+ * http://www.mozilla.org/projects/xpcom/nsCOMPtr.html
+ *
+ *
+ * nsCOMPtr
+ * better than a raw pointer
+ * for owning objects
+ * -- scc
+ */
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+
+#include "nsDebug.h" // for |NS_ASSERTION|
+#include "nsISupportsUtils.h" // for |nsresult|, |NS_ADDREF|, |NS_GET_TEMPLATE_IID| et al
+#include "mozilla/RefPtr.h"
+
+#include "nsCycleCollectionNoteChild.h"
+
+
+/*
+ * WARNING: This file defines several macros for internal use only. These
+ * macros begin with the prefix |NSCAP_|. Do not use these macros in your own
+ * code. They are for internal use only for cross-platform compatibility, and
+ * are subject to change without notice.
+ */
+
+
+#ifdef _MSC_VER
+ // Under VC++, we win by inlining StartAssignment.
+ #define NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+
+ // Also under VC++, at the highest warning level, we are overwhelmed with
+ // warnings about (unused) inline functions being removed. This is to be
+ // expected with templates, so we disable the warning.
+ #pragma warning( disable: 4514 )
+#endif
+
+#define NSCAP_FEATURE_USE_BASE
+
+#ifdef DEBUG
+ #define NSCAP_FEATURE_TEST_DONTQUERY_CASES
+ #undef NSCAP_FEATURE_USE_BASE
+#endif
+
+#ifdef __GNUC__
+ // Our use of nsCOMPtr_base::mRawPtr violates the C++ standard's aliasing
+ // rules. Mark it with the may_alias attribute so that gcc 3.3 and higher
+ // don't reorder instructions based on aliasing assumptions for
+ // this variable. Fortunately, gcc versions < 3.3 do not do any
+ // optimizations that break nsCOMPtr.
+
+ #define NS_MAY_ALIAS_PTR(t) t* __attribute__((__may_alias__))
+#else
+ #define NS_MAY_ALIAS_PTR(t) t*
+#endif
+
+#if defined(NSCAP_DISABLE_DEBUG_PTR_TYPES)
+ #define NSCAP_FEATURE_USE_BASE
+#endif
+
+/*
+ * The following three macros (NSCAP_ADDREF, NSCAP_RELEASE, and
+ * NSCAP_LOG_ASSIGNMENT) allow external clients the ability to add logging or
+ * other interesting debug facilities. In fact, if you want |nsCOMPtr| to
+ * participate in the standard logging facility, you provide
+ * (e.g., in "nsISupportsImpl.h") suitable definitions
+ *
+ * #define NSCAP_ADDREF(this, ptr) NS_ADDREF(ptr)
+ * #define NSCAP_RELEASE(this, ptr) NS_RELEASE(ptr)
+ */
+
+#ifndef NSCAP_ADDREF
+ #define NSCAP_ADDREF(this, ptr) (ptr)->AddRef()
+#endif
+
+#ifndef NSCAP_RELEASE
+ #define NSCAP_RELEASE(this, ptr) (ptr)->Release()
+#endif
+
+// Clients can define |NSCAP_LOG_ASSIGNMENT| to perform logging.
+#ifdef NSCAP_LOG_ASSIGNMENT
+ // Remember that |NSCAP_LOG_ASSIGNMENT| was defined by some client so that we
+ // know to instantiate |~nsGetterAddRefs| in turn to note the external
+ // assignment into the |nsCOMPtr|.
+ #define NSCAP_LOG_EXTERNAL_ASSIGNMENT
+#else
+ // ...otherwise, just strip it out of the code
+ #define NSCAP_LOG_ASSIGNMENT(this, ptr)
+#endif
+
+#ifndef NSCAP_LOG_RELEASE
+ #define NSCAP_LOG_RELEASE(this, ptr)
+#endif
+
+namespace mozilla {
+template<class T> class OwningNonNull;
+} // namespace mozilla
+
+template<class T>
+inline already_AddRefed<T>
+dont_AddRef(T* aRawPtr)
+{
+ return already_AddRefed<T>(aRawPtr);
+}
+
+template<class T>
+inline already_AddRefed<T>&&
+dont_AddRef(already_AddRefed<T>&& aAlreadyAddRefedPtr)
+{
+ return mozilla::Move(aAlreadyAddRefedPtr);
+}
+
+
+/*
+ * An nsCOMPtr_helper transforms commonly called getters into typesafe forms
+ * that are more convenient to call, and more efficient to use with |nsCOMPtr|s.
+ * Good candidates for helpers are |QueryInterface()|, |CreateInstance()|, etc.
+ *
+ * Here are the rules for a helper:
+ * - it implements |operator()| to produce an interface pointer
+ * - (except for its name) |operator()| is a valid [XP]COM `getter'
+ * - the interface pointer that it returns is already |AddRef()|ed (as from
+ * any good getter)
+ * - it matches the type requested with the supplied |nsIID| argument
+ * - its constructor provides an optional |nsresult*| that |operator()| can
+ * fill in with an error when it is executed
+ *
+ * See |class nsGetInterface| for an example.
+ */
+class MOZ_STACK_CLASS nsCOMPtr_helper
+{
+public:
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const = 0;
+};
+
+/*
+ * nsQueryInterface could have been implemented as an nsCOMPtr_helper to avoid
+ * adding specialized machinery in nsCOMPtr, but do_QueryInterface is called
+ * often enough that the codesize savings are big enough to warrant the
+ * specialcasing.
+ */
+class MOZ_STACK_CLASS nsQueryInterface final
+{
+public:
+ explicit
+ nsQueryInterface(nsISupports* aRawPtr) : mRawPtr(aRawPtr) {}
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const;
+
+private:
+ nsISupports* MOZ_OWNING_REF mRawPtr;
+};
+
+class nsQueryInterfaceWithError final
+{
+public:
+ nsQueryInterfaceWithError(nsISupports* aRawPtr, nsresult* aError)
+ : mRawPtr(aRawPtr)
+ , mErrorPtr(aError)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const;
+
+private:
+ nsISupports* MOZ_OWNING_REF mRawPtr;
+ nsresult* mErrorPtr;
+};
+
+inline nsQueryInterface
+do_QueryInterface(nsISupports* aRawPtr)
+{
+ return nsQueryInterface(aRawPtr);
+}
+
+inline nsQueryInterfaceWithError
+do_QueryInterface(nsISupports* aRawPtr, nsresult* aError)
+{
+ return nsQueryInterfaceWithError(aRawPtr, aError);
+}
+
+template<class T>
+inline void
+do_QueryInterface(already_AddRefed<T>&)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_QueryInterface()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See bug 8221.
+}
+
+template<class T>
+inline void
+do_QueryInterface(already_AddRefed<T>&, nsresult*)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_QueryInterface()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See bug 8221.
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Using servicemanager with COMPtrs
+class nsGetServiceByCID final
+{
+public:
+ explicit nsGetServiceByCID(const nsCID& aCID) : mCID(aCID) {}
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const nsCID& mCID;
+};
+
+class nsGetServiceByCIDWithError final
+{
+public:
+ nsGetServiceByCIDWithError(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class nsGetServiceByContractID final
+{
+public:
+ explicit nsGetServiceByContractID(const char* aContractID)
+ : mContractID(aContractID)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const char* mContractID;
+};
+
+class nsGetServiceByContractIDWithError final
+{
+public:
+ nsGetServiceByContractIDWithError(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+/**
+ * Factors implementation for all template versions of nsCOMPtr.
+ *
+ * Here's the way people normally do things like this:
+ *
+ * template<class T> class Foo { ... };
+ * template<> class Foo<void*> { ... };
+ * template<class T> class Foo<T*> : private Foo<void*> { ... };
+ */
+class nsCOMPtr_base
+{
+public:
+ explicit nsCOMPtr_base(nsISupports* aRawPtr = nullptr) : mRawPtr(aRawPtr) {}
+
+ NS_CONSTRUCTOR_FASTCALL ~nsCOMPtr_base()
+ {
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ if (mRawPtr) {
+ NSCAP_RELEASE(this, mRawPtr);
+ }
+ }
+
+ void NS_FASTCALL
+ assign_with_AddRef(nsISupports*);
+ void NS_FASTCALL
+ assign_from_qi(const nsQueryInterface, const nsIID&);
+ void NS_FASTCALL
+ assign_from_qi_with_error(const nsQueryInterfaceWithError&, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_cid(const nsGetServiceByCID, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError&, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_contractid(const nsGetServiceByContractID, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_contractid_with_error(const nsGetServiceByContractIDWithError&,
+ const nsIID&);
+ void NS_FASTCALL
+ assign_from_helper(const nsCOMPtr_helper&, const nsIID&);
+ void** NS_FASTCALL
+ begin_assignment();
+
+protected:
+ NS_MAY_ALIAS_PTR(nsISupports) MOZ_OWNING_REF mRawPtr;
+
+ void assign_assuming_AddRef(nsISupports* aNewPtr)
+ {
+ // |AddRef()|ing the new value (before entering this function) before
+ // |Release()|ing the old lets us safely ignore the self-assignment case.
+ // We must, however, be careful only to |Release()| _after_ doing the
+ // assignment, in case the |Release()| leads to our _own_ destruction,
+ // which would, in turn, cause an incorrect second |Release()| of our old
+ // pointer. Thank <waterson@netscape.com> for discovering this.
+ nsISupports* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ NSCAP_LOG_ASSIGNMENT(this, aNewPtr);
+ NSCAP_LOG_RELEASE(this, oldPtr);
+ if (oldPtr) {
+ NSCAP_RELEASE(this, oldPtr);
+ }
+ }
+};
+
+// template<class T> class nsGetterAddRefs;
+
+// Helper for assert_validity method
+template<class T>
+char (&TestForIID(decltype(&NS_GET_TEMPLATE_IID(T))))[2];
+template<class T>
+char TestForIID(...);
+
+template<class T>
+class nsCOMPtr final
+#ifdef NSCAP_FEATURE_USE_BASE
+ : private nsCOMPtr_base
+#endif
+{
+
+#ifdef NSCAP_FEATURE_USE_BASE
+ #define NSCAP_CTOR_BASE(x) nsCOMPtr_base(x)
+#else
+ #define NSCAP_CTOR_BASE(x) mRawPtr(x)
+
+private:
+ void assign_with_AddRef(nsISupports*);
+ void assign_from_qi(const nsQueryInterface, const nsIID&);
+ void assign_from_qi_with_error(const nsQueryInterfaceWithError&, const nsIID&);
+ void assign_from_gs_cid(const nsGetServiceByCID, const nsIID&);
+ void assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError&,
+ const nsIID&);
+ void assign_from_gs_contractid(const nsGetServiceByContractID, const nsIID&);
+ void assign_from_gs_contractid_with_error(
+ const nsGetServiceByContractIDWithError&, const nsIID&);
+ void assign_from_helper(const nsCOMPtr_helper&, const nsIID&);
+ void** begin_assignment();
+
+ void assign_assuming_AddRef(T* aNewPtr)
+ {
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ NSCAP_LOG_ASSIGNMENT(this, aNewPtr);
+ NSCAP_LOG_RELEASE(this, oldPtr);
+ if (oldPtr) {
+ NSCAP_RELEASE(this, oldPtr);
+ }
+ }
+
+private:
+ T* MOZ_OWNING_REF mRawPtr;
+#endif
+
+ void assert_validity()
+ {
+ static_assert(1 < sizeof(TestForIID<T>(nullptr)), "nsCOMPtr only works "
+ "for types with IIDs. Either use RefPtr; add an IID to "
+ "your type with NS_DECLARE_STATIC_IID_ACCESSOR/"
+ "NS_DEFINE_STATIC_IID_ACCESSOR; or make the nsCOMPtr point "
+ "to a base class with an IID.");
+ }
+
+public:
+ typedef T element_type;
+
+#ifndef NSCAP_FEATURE_USE_BASE
+ ~nsCOMPtr()
+ {
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ if (mRawPtr) {
+ NSCAP_RELEASE(this, mRawPtr);
+ }
+ }
+#endif
+
+#ifdef NSCAP_FEATURE_TEST_DONTQUERY_CASES
+ void Assert_NoQueryNeeded()
+ {
+ if (mRawPtr) {
+ nsCOMPtr<T> query_result(do_QueryInterface(mRawPtr));
+ NS_ASSERTION(query_result.get() == mRawPtr, "QueryInterface needed");
+ }
+ }
+
+ #define NSCAP_ASSERT_NO_QUERY_NEEDED() Assert_NoQueryNeeded();
+#else
+ #define NSCAP_ASSERT_NO_QUERY_NEEDED()
+#endif
+
+
+ // Constructors
+
+ nsCOMPtr()
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(decltype(nullptr))
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ nsCOMPtr(const nsCOMPtr<T>& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.mRawPtr)
+ {
+ assert_validity();
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aSmartPtr.mRawPtr);
+ }
+
+ nsCOMPtr(nsCOMPtr<T>&& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.mRawPtr)
+ {
+ assert_validity();
+ aSmartPtr.mRawPtr = nullptr;
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(T* aRawPtr)
+ : NSCAP_CTOR_BASE(aRawPtr)
+ {
+ assert_validity();
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T>& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.take())
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |otherComPtr.forget()|.
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T>&& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.take())
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |already_AddRefed|.
+ template<typename U>
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<U>& aSmartPtr)
+ : NSCAP_CTOR_BASE(static_cast<T*>(aSmartPtr.take()))
+ {
+ assert_validity();
+ // But make sure that U actually inherits from T.
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ NSCAP_LOG_ASSIGNMENT(this, static_cast<T*>(mRawPtr));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |otherComPtr.forget()|.
+ template<typename U>
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<U>&& aSmartPtr)
+ : NSCAP_CTOR_BASE(static_cast<T*>(aSmartPtr.take()))
+ {
+ assert_validity();
+ // But make sure that U actually inherits from T.
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ NSCAP_LOG_ASSIGNMENT(this, static_cast<T*>(mRawPtr));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |do_QueryInterface(expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterface aQI)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi(aQI, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_QueryInterface(expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterfaceWithError& aQI)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi_with_error(aQI, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(cid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCID aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(cid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCIDWithError& aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid_with_error(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(contractid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractID aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(contractid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractIDWithError& aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid_with_error(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // And finally, anything else we might need to construct from can exploit the
+ // nsCOMPtr_helper facility.
+ MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr_helper& aHelper)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_helper(aHelper, NS_GET_TEMPLATE_IID(T));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Defined in OwningNonNull.h
+ template<class U>
+ MOZ_IMPLICIT nsCOMPtr(const mozilla::OwningNonNull<U>& aOther);
+
+
+ // Assignment operators
+
+ nsCOMPtr<T>& operator=(const nsCOMPtr<T>& aRhs)
+ {
+ assign_with_AddRef(aRhs.mRawPtr);
+ return *this;
+ }
+
+ nsCOMPtr<T>& operator=(T* aRhs)
+ {
+ assign_with_AddRef(aRhs);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ nsCOMPtr<T>& operator=(decltype(nullptr))
+ {
+ assign_assuming_AddRef(nullptr);
+ return *this;
+ }
+
+ // Assign from |already_AddRefed|.
+ template<typename U>
+ nsCOMPtr<T>& operator=(already_AddRefed<U>& aRhs)
+ {
+ // Make sure that U actually inherits from T
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ assign_assuming_AddRef(static_cast<T*>(aRhs.take()));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ // Assign from |otherComPtr.forget()|.
+ template<typename U>
+ nsCOMPtr<T>& operator=(already_AddRefed<U>&& aRhs)
+ {
+ // Make sure that U actually inherits from T
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ assign_assuming_AddRef(static_cast<T*>(aRhs.take()));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr)|.
+ nsCOMPtr<T>& operator=(const nsQueryInterface aRhs)
+ {
+ assign_from_qi(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr, &rv)|.
+ nsCOMPtr<T>& operator=(const nsQueryInterfaceWithError& aRhs)
+ {
+ assign_from_qi_with_error(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByCID aRhs)
+ {
+ assign_from_gs_cid(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr, &rv)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByCIDWithError& aRhs)
+ {
+ assign_from_gs_cid_with_error(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByContractID aRhs)
+ {
+ assign_from_gs_contractid(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr, &rv)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByContractIDWithError& aRhs)
+ {
+ assign_from_gs_contractid_with_error(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // And finally, anything else we might need to assign from can exploit the
+ // nsCOMPtr_helper facility.
+ nsCOMPtr<T>& operator=(const nsCOMPtr_helper& aRhs)
+ {
+ assign_from_helper(aRhs, NS_GET_TEMPLATE_IID(T));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ // Defined in OwningNonNull.h
+ template<class U>
+ nsCOMPtr<T>& operator=(const mozilla::OwningNonNull<U>& aOther);
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(nsCOMPtr<T>& aRhs)
+ {
+#ifdef NSCAP_FEATURE_USE_BASE
+ nsISupports* temp = aRhs.mRawPtr;
+#else
+ T* temp = aRhs.mRawPtr;
+#endif
+ NSCAP_LOG_ASSIGNMENT(&aRhs, mRawPtr);
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ NSCAP_LOG_RELEASE(&aRhs, temp);
+ aRhs.mRawPtr = mRawPtr;
+ mRawPtr = temp;
+ // |aRhs| maintains the same invariants, so we don't need to |NSCAP_ASSERT_NO_QUERY_NEEDED|
+ }
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(T*& aRhs)
+ {
+#ifdef NSCAP_FEATURE_USE_BASE
+ nsISupports* temp = aRhs;
+#else
+ T* temp = aRhs;
+#endif
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ aRhs = reinterpret_cast<T*>(mRawPtr);
+ mRawPtr = temp;
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+
+ // Other pointer operators
+
+ // Return the value of mRawPtr and null out mRawPtr. Useful for
+ // already_AddRefed return values.
+ already_AddRefed<T> forget()
+ {
+ T* temp = nullptr;
+ swap(temp);
+ return already_AddRefed<T>(temp);
+ }
+
+ // Set the target of aRhs to the value of mRawPtr and null out mRawPtr.
+ // Useful to avoid unnecessary AddRef/Release pairs with "out" parameters
+ // where aRhs bay be a T** or an I** where I is a base class of T.
+ template<typename I>
+ void forget(I** aRhs)
+ {
+ NS_ASSERTION(aRhs, "Null pointer passed to forget!");
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ *aRhs = get();
+ mRawPtr = nullptr;
+ }
+
+ // Prefer the implicit conversion provided automatically by
+ // |operator T*() const|. Use |get()| to resolve ambiguity or to get a
+ // castable pointer.
+ T* get() const { return reinterpret_cast<T*>(mRawPtr); }
+
+ // Makes an nsCOMPtr act like its underlying raw pointer type whenever it is
+ // used in a context where a raw pointer is expected. It is this operator
+ // that makes an nsCOMPtr substitutable for a raw pointer.
+ //
+ // Prefer the implicit use of this operator to calling |get()|, except where
+ // necessary to resolve ambiguity.
+ operator T*() const
+#ifdef MOZ_HAVE_REF_QUALIFIERS
+ &
+#endif
+ { return get(); }
+
+#ifdef MOZ_HAVE_REF_QUALIFIERS
+ // Don't allow implicit conversion of temporary nsCOMPtr to raw pointer,
+ // because the refcount might be one and the pointer will immediately become
+ // invalid.
+ operator T*() const && = delete;
+
+ // Needed to avoid the deleted operator above
+ explicit operator bool() const { return !!mRawPtr; }
+#endif
+
+ T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator->().");
+ return get();
+ }
+
+ // These are not intended to be used by clients. See |address_of| below.
+ nsCOMPtr<T>* get_address() { return this; }
+ const nsCOMPtr<T>* get_address() const { return this; }
+
+public:
+ T& operator*() const
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator*().");
+ return *get();
+ }
+
+ T** StartAssignment()
+ {
+#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+ return reinterpret_cast<T**>(begin_assignment());
+#else
+ assign_assuming_AddRef(nullptr);
+ return reinterpret_cast<T**>(&mRawPtr);
+#endif
+ }
+};
+
+
+/*
+ * Specializing nsCOMPtr for nsISupports allows us to use nsCOMPtr<nsISupports>
+ * the same way people use nsISupports* and void*, i.e., as a `catch-all'
+ * pointing to any valid [XP]COM interface. Otherwise, an nsCOMPtr<nsISupports>
+ * would only be able to point to the single [XP]COM-correct nsISupports
+ * instance within an object; extra querying ensues. Clients need to be able to
+ * pass around arbitrary interface pointers, without hassles, through
+ * intermediary code that doesn't know the exact type.
+ */
+template<>
+class nsCOMPtr<nsISupports>
+ : private nsCOMPtr_base
+{
+public:
+ typedef nsISupports element_type;
+
+ // Constructors
+
+ nsCOMPtr()
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(decltype(nullptr))
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ nsCOMPtr(const nsCOMPtr<nsISupports>& aSmartPtr)
+ : nsCOMPtr_base(aSmartPtr.mRawPtr)
+ {
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aSmartPtr.mRawPtr);
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(nsISupports* aRawPtr)
+ : nsCOMPtr_base(aRawPtr)
+ {
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aRawPtr);
+ }
+
+ // Construct from |already_AddRefed|.
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<nsISupports>& aSmartPtr)
+ : nsCOMPtr_base(aSmartPtr.take())
+ {
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ }
+
+ // Construct from |otherComPtr.forget()|.
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<nsISupports>&& aSmartPtr)
+ : nsCOMPtr_base(aSmartPtr.take())
+ {
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ }
+
+ // Construct from |do_QueryInterface(expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterface aQI)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi(aQI, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_QueryInterface(expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterfaceWithError& aQI)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi_with_error(aQI, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(cid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCID aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(cid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCIDWithError& aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid_with_error(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(contractid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractID aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(contractid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractIDWithError& aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid_with_error(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // And finally, anything else we might need to construct from can exploit
+ // the |nsCOMPtr_helper| facility
+ MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr_helper& aHelper)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_helper(aHelper, NS_GET_IID(nsISupports));
+ }
+
+
+ // Assignment operators
+
+ nsCOMPtr<nsISupports>& operator=(const nsCOMPtr<nsISupports>& aRhs)
+ {
+ assign_with_AddRef(aRhs.mRawPtr);
+ return *this;
+ }
+
+ nsCOMPtr<nsISupports>& operator=(nsISupports* aRhs)
+ {
+ assign_with_AddRef(aRhs);
+ return *this;
+ }
+
+ nsCOMPtr<nsISupports>& operator=(decltype(nullptr))
+ {
+ assign_assuming_AddRef(nullptr);
+ return *this;
+ }
+
+ // Assign from |already_AddRefed|.
+ nsCOMPtr<nsISupports>& operator=(already_AddRefed<nsISupports>& aRhs)
+ {
+ assign_assuming_AddRef(aRhs.take());
+ return *this;
+ }
+
+ // Assign from |otherComPtr.forget()|.
+ nsCOMPtr<nsISupports>& operator=(already_AddRefed<nsISupports>&& aRhs)
+ {
+ assign_assuming_AddRef(aRhs.take());
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr)|.
+ nsCOMPtr<nsISupports>& operator=(const nsQueryInterface aRhs)
+ {
+ assign_from_qi(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr, &rv)|.
+ nsCOMPtr<nsISupports>& operator=(const nsQueryInterfaceWithError& aRhs)
+ {
+ assign_from_qi_with_error(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByCID aRhs)
+ {
+ assign_from_gs_cid(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr, &rv)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByCIDWithError& aRhs)
+ {
+ assign_from_gs_cid_with_error(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByContractID aRhs)
+ {
+ assign_from_gs_contractid(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr, &rv)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByContractIDWithError& aRhs)
+ {
+ assign_from_gs_contractid_with_error(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // And finally, anything else we might need to assign from can exploit the
+ // nsCOMPtr_helper facility
+ nsCOMPtr<nsISupports>& operator=(const nsCOMPtr_helper& aRhs)
+ {
+ assign_from_helper(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(nsCOMPtr<nsISupports>& aRhs)
+ {
+ nsISupports* temp = aRhs.mRawPtr;
+ NSCAP_LOG_ASSIGNMENT(&aRhs, mRawPtr);
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ NSCAP_LOG_RELEASE(&aRhs, temp);
+ aRhs.mRawPtr = mRawPtr;
+ mRawPtr = temp;
+ }
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(nsISupports*& aRhs)
+ {
+ nsISupports* temp = aRhs;
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ aRhs = mRawPtr;
+ mRawPtr = temp;
+ }
+
+ // Return the value of mRawPtr and null out mRawPtr. Useful for
+ // already_AddRefed return values.
+ already_AddRefed<nsISupports> forget()
+ {
+ nsISupports* temp = nullptr;
+ swap(temp);
+ return already_AddRefed<nsISupports>(temp);
+ }
+
+ // Set the target of aRhs to the value of mRawPtr and null out mRawPtr.
+ // Useful to avoid unnecessary AddRef/Release pairs with "out"
+ // parameters.
+ void forget(nsISupports** aRhs)
+ {
+ NS_ASSERTION(aRhs, "Null pointer passed to forget!");
+ *aRhs = nullptr;
+ swap(*aRhs);
+ }
+
+ // Other pointer operators
+
+ // Prefer the implicit conversion provided automatically by
+ // |operator nsISupports*() const|. Use |get()| to resolve ambiguity or to
+ // get a castable pointer.
+ nsISupports* get() const { return reinterpret_cast<nsISupports*>(mRawPtr); }
+
+ // Makes an nsCOMPtr act like its underlying raw pointer type whenever it is
+ // used in a context where a raw pointer is expected. It is this operator
+ // that makes an nsCOMPtr substitutable for a raw pointer.
+ //
+ // Prefer the implicit use of this operator to calling |get()|, except where
+ // necessary to resolve ambiguity/
+ operator nsISupports* () const { return get(); }
+
+ nsISupports* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator->().");
+ return get();
+ }
+
+ // These are not intended to be used by clients. See |address_of| below.
+ nsCOMPtr<nsISupports>* get_address() { return this; }
+ const nsCOMPtr<nsISupports>* get_address() const { return this; }
+
+public:
+
+ nsISupports& operator*() const
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator*().");
+ return *get();
+ }
+
+ nsISupports** StartAssignment()
+ {
+#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+ return reinterpret_cast<nsISupports**>(begin_assignment());
+#else
+ assign_assuming_AddRef(nullptr);
+ return reinterpret_cast<nsISupports**>(&mRawPtr);
+#endif
+ }
+};
+
+template<typename T>
+inline void
+ImplCycleCollectionUnlink(nsCOMPtr<T>& aField)
+{
+ aField = nullptr;
+}
+
+template<typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsCOMPtr<T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
+}
+
+#ifndef NSCAP_FEATURE_USE_BASE
+template<class T>
+void
+nsCOMPtr<T>::assign_with_AddRef(nsISupports* aRawPtr)
+{
+ if (aRawPtr) {
+ NSCAP_ADDREF(this, aRawPtr);
+ }
+ assign_assuming_AddRef(reinterpret_cast<T*>(aRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_qi(const nsQueryInterface aQI, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_qi_with_error(const nsQueryInterfaceWithError& aQI,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_cid(const nsGetServiceByCID aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError& aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_contractid(const nsGetServiceByContractID aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_contractid_with_error(
+ const nsGetServiceByContractIDWithError& aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_helper(const nsCOMPtr_helper& helper, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(helper(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void**
+nsCOMPtr<T>::begin_assignment()
+{
+ assign_assuming_AddRef(nullptr);
+ union
+ {
+ T** mT;
+ void** mVoid;
+ } result;
+ result.mT = &mRawPtr;
+ return result.mVoid;
+}
+#endif
+
+template<class T>
+inline nsCOMPtr<T>*
+address_of(nsCOMPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+template<class T>
+inline const nsCOMPtr<T>*
+address_of(const nsCOMPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+/**
+ * This class is designed to be used for anonymous temporary objects in the
+ * argument list of calls that return COM interface pointers, e.g.,
+ *
+ * nsCOMPtr<IFoo> fooP;
+ * ...->QueryInterface(iid, getter_AddRefs(fooP))
+ *
+ * DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead.
+ *
+ * When initialized with a |nsCOMPtr|, as in the example above, it returns
+ * a |void**|, a |T**|, or an |nsISupports**| as needed, that the outer call
+ * (|QueryInterface| in this case) can fill in.
+ *
+ * This type should be a nested class inside |nsCOMPtr<T>|.
+ */
+template<class T>
+class nsGetterAddRefs
+{
+public:
+ explicit nsGetterAddRefs(nsCOMPtr<T>& aSmartPtr)
+ : mTargetSmartPtr(aSmartPtr)
+ {
+ }
+
+#if defined(NSCAP_FEATURE_TEST_DONTQUERY_CASES) || defined(NSCAP_LOG_EXTERNAL_ASSIGNMENT)
+ ~nsGetterAddRefs()
+ {
+#ifdef NSCAP_LOG_EXTERNAL_ASSIGNMENT
+ NSCAP_LOG_ASSIGNMENT(reinterpret_cast<void*>(address_of(mTargetSmartPtr)),
+ mTargetSmartPtr.get());
+#endif
+
+#ifdef NSCAP_FEATURE_TEST_DONTQUERY_CASES
+ mTargetSmartPtr.Assert_NoQueryNeeded();
+#endif
+ }
+#endif
+
+ operator void**()
+ {
+ return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
+ }
+
+ operator T**() { return mTargetSmartPtr.StartAssignment(); }
+ T*& operator*() { return *(mTargetSmartPtr.StartAssignment()); }
+
+private:
+ nsCOMPtr<T>& mTargetSmartPtr;
+};
+
+
+template<>
+class nsGetterAddRefs<nsISupports>
+{
+public:
+ explicit nsGetterAddRefs(nsCOMPtr<nsISupports>& aSmartPtr)
+ : mTargetSmartPtr(aSmartPtr)
+ {
+ }
+
+#ifdef NSCAP_LOG_EXTERNAL_ASSIGNMENT
+ ~nsGetterAddRefs()
+ {
+ NSCAP_LOG_ASSIGNMENT(reinterpret_cast<void*>(address_of(mTargetSmartPtr)),
+ mTargetSmartPtr.get());
+ }
+#endif
+
+ operator void**()
+ {
+ return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
+ }
+
+ operator nsISupports**() { return mTargetSmartPtr.StartAssignment(); }
+ nsISupports*& operator*() { return *(mTargetSmartPtr.StartAssignment()); }
+
+private:
+ nsCOMPtr<nsISupports>& mTargetSmartPtr;
+};
+
+template<class T>
+inline nsGetterAddRefs<T>
+getter_AddRefs(nsCOMPtr<T>& aSmartPtr)
+{
+ return nsGetterAddRefs<T>(aSmartPtr);
+}
+
+template<class T, class DestinationType>
+inline nsresult
+CallQueryInterface(T* aSource, nsGetterAddRefs<DestinationType> aDestination)
+{
+ return CallQueryInterface(aSource,
+ static_cast<DestinationType**>(aDestination));
+}
+
+
+// Comparing two |nsCOMPtr|s
+
+template<class T, class U>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, const nsCOMPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs.get());
+}
+
+
+template<class T, class U>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, const nsCOMPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs.get());
+}
+
+
+// Comparing an |nsCOMPtr| to a raw pointer
+
+template<class T, class U>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == aRhs;
+}
+
+template<class T, class U>
+inline bool
+operator==(const U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return aLhs == static_cast<const T*>(aRhs.get());
+}
+
+template<class T, class U>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != aRhs;
+}
+
+template<class T, class U>
+inline bool
+operator!=(const U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return aLhs != static_cast<const T*>(aRhs.get());
+}
+
+template<class T, class U>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == const_cast<const U*>(aRhs);
+}
+
+template<class T, class U>
+inline bool
+operator==(U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
+}
+
+template<class T, class U>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != const_cast<const U*>(aRhs);
+}
+
+template<class T, class U>
+inline bool
+operator!=(U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
+}
+
+
+
+// Comparing an |nsCOMPtr| to |nullptr|
+
+template<class T>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() == nullptr;
+}
+
+template<class T>
+inline bool
+operator==(decltype(nullptr), const nsCOMPtr<T>& aRhs)
+{
+ return nullptr == aRhs.get();
+}
+
+template<class T>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() != nullptr;
+}
+
+template<class T>
+inline bool
+operator!=(decltype(nullptr), const nsCOMPtr<T>& aRhs)
+{
+ return nullptr != aRhs.get();
+}
+
+
+// Comparing any two [XP]COM objects for identity
+
+inline bool
+SameCOMIdentity(nsISupports* aLhs, nsISupports* aRhs)
+{
+ return nsCOMPtr<nsISupports>(do_QueryInterface(aLhs)) ==
+ nsCOMPtr<nsISupports>(do_QueryInterface(aRhs));
+}
+
+
+
+template<class SourceType, class DestinationType>
+inline nsresult
+CallQueryInterface(nsCOMPtr<SourceType>& aSourcePtr, DestinationType** aDestPtr)
+{
+ return CallQueryInterface(aSourcePtr.get(), aDestPtr);
+}
+
+template <class T>
+RefPtr<T>::RefPtr(const nsCOMPtr_helper& aHelper)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ mRawPtr = static_cast<T*>(newRawPtr);
+}
+
+template <class T>
+RefPtr<T>&
+RefPtr<T>::operator=(const nsCOMPtr_helper& aHelper)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+ return *this;
+}
+
+
+#endif // !defined(nsCOMPtr_h___)
diff --git a/xpcom/glue/nsCRTGlue.cpp b/xpcom/glue/nsCRTGlue.cpp
new file mode 100644
index 000000000..7a9f6db03
--- /dev/null
+++ b/xpcom/glue/nsCRTGlue.cpp
@@ -0,0 +1,441 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCRTGlue.h"
+#include "nsXPCOM.h"
+#include "nsDebug.h"
+#include "prtime.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "mozilla/Sprintf.h"
+
+#ifdef XP_WIN
+#include <io.h>
+#include <windows.h>
+#include "mozilla/UniquePtr.h"
+#endif
+
+#ifdef ANDROID
+#include <android/log.h>
+#include <unistd.h>
+#endif
+
+using namespace mozilla;
+
+const char*
+NS_strspnp(const char* aDelims, const char* aStr)
+{
+ const char* d;
+ do {
+ for (d = aDelims; *d != '\0'; ++d) {
+ if (*aStr == *d) {
+ ++aStr;
+ break;
+ }
+ }
+ } while (*d);
+
+ return aStr;
+}
+
+char*
+NS_strtok(const char* aDelims, char** aStr)
+{
+ if (!*aStr) {
+ return nullptr;
+ }
+
+ char* ret = (char*)NS_strspnp(aDelims, *aStr);
+
+ if (!*ret) {
+ *aStr = ret;
+ return nullptr;
+ }
+
+ char* i = ret;
+ do {
+ for (const char* d = aDelims; *d != '\0'; ++d) {
+ if (*i == *d) {
+ *i = '\0';
+ *aStr = ++i;
+ return ret;
+ }
+ }
+ ++i;
+ } while (*i);
+
+ *aStr = nullptr;
+ return ret;
+}
+
+uint32_t
+NS_strlen(const char16_t* aString)
+{
+ MOZ_ASSERT(aString);
+ const char16_t* end;
+
+ for (end = aString; *end; ++end) {
+ // empty loop
+ }
+
+ return end - aString;
+}
+
+int
+NS_strcmp(const char16_t* aStrA, const char16_t* aStrB)
+{
+ while (*aStrB) {
+ int r = *aStrA - *aStrB;
+ if (r) {
+ return r;
+ }
+
+ ++aStrA;
+ ++aStrB;
+ }
+
+ return *aStrA != '\0';
+}
+
+int
+NS_strncmp(const char16_t* aStrA, const char16_t* aStrB, size_t aLen)
+{
+ while (aLen && *aStrB) {
+ int r = *aStrA - *aStrB;
+ if (r) {
+ return r;
+ }
+
+ ++aStrA;
+ ++aStrB;
+ --aLen;
+ }
+
+ return aLen ? *aStrA != '\0' : *aStrA - *aStrB;
+}
+
+char16_t*
+NS_strdup(const char16_t* aString)
+{
+ uint32_t len = NS_strlen(aString);
+ return NS_strndup(aString, len);
+}
+
+template<typename CharT>
+CharT*
+NS_strndup(const CharT* aString, uint32_t aLen)
+{
+ auto newBuf = (CharT*)NS_Alloc((aLen + 1) * sizeof(CharT));
+ if (newBuf) {
+ memcpy(newBuf, aString, aLen * sizeof(CharT));
+ newBuf[aLen] = '\0';
+ }
+ return newBuf;
+}
+
+template char16_t* NS_strndup<char16_t>(const char16_t* aString, uint32_t aLen);
+template char* NS_strndup<char>(const char* aString, uint32_t aLen);
+
+char*
+NS_strdup(const char* aString)
+{
+ uint32_t len = strlen(aString);
+ char* str = (char*)NS_Alloc(len + 1);
+ if (str) {
+ memcpy(str, aString, len);
+ str[len] = '\0';
+ }
+ return str;
+}
+
+// This table maps uppercase characters to lower case characters;
+// characters that are neither upper nor lower case are unaffected.
+const unsigned char nsLowerUpperUtils::kUpper2Lower[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64,
+
+ // upper band mapped to lower [A-Z] => [a-z]
+ 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,
+
+ 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+const unsigned char nsLowerUpperUtils::kLower2Upper[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96,
+
+ // lower band mapped to upper [a-z] => [A-Z]
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+
+ 123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+bool
+NS_IsUpper(char aChar)
+{
+ return aChar != (char)nsLowerUpperUtils::kUpper2Lower[(unsigned char)aChar];
+}
+
+bool
+NS_IsLower(char aChar)
+{
+ return aChar != (char)nsLowerUpperUtils::kLower2Upper[(unsigned char)aChar];
+}
+
+bool
+NS_IsAscii(char16_t aChar)
+{
+ return (0x0080 > aChar);
+}
+
+bool
+NS_IsAscii(const char16_t* aString)
+{
+ while (*aString) {
+ if (0x0080 <= *aString) {
+ return false;
+ }
+ aString++;
+ }
+ return true;
+}
+
+bool
+NS_IsAscii(const char* aString)
+{
+ while (*aString) {
+ if (0x80 & *aString) {
+ return false;
+ }
+ aString++;
+ }
+ return true;
+}
+
+bool
+NS_IsAscii(const char* aString, uint32_t aLength)
+{
+ const char* end = aString + aLength;
+ while (aString < end) {
+ if (0x80 & *aString) {
+ return false;
+ }
+ ++aString;
+ }
+ return true;
+}
+
+bool
+NS_IsAsciiAlpha(char16_t aChar)
+{
+ return (aChar >= 'A' && aChar <= 'Z') ||
+ (aChar >= 'a' && aChar <= 'z');
+}
+
+bool
+NS_IsAsciiWhitespace(char16_t aChar)
+{
+ return aChar == ' ' ||
+ aChar == '\r' ||
+ aChar == '\n' ||
+ aChar == '\t';
+}
+
+bool
+NS_IsAsciiDigit(char16_t aChar)
+{
+ return aChar >= '0' && aChar <= '9';
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+void
+NS_MakeRandomString(char* aBuf, int32_t aBufLen)
+{
+#define TABLE_SIZE 36
+ static const char table[] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9'
+ };
+
+ // turn PR_Now() into milliseconds since epoch
+ // and salt rand with that.
+ static unsigned int seed = 0;
+ if (seed == 0) {
+ double fpTime = double(PR_Now());
+ seed = (unsigned int)(fpTime * 1e-6 + 0.5); // use 1e-6, granularity of PR_Now() on the mac is seconds
+ srand(seed);
+ }
+
+ int32_t i;
+ for (i = 0; i < aBufLen; ++i) {
+ *aBuf++ = table[rand() % TABLE_SIZE];
+ }
+ *aBuf = 0;
+}
+
+#endif
+
+static StderrCallback sStderrCallback = nullptr;
+
+void
+set_stderr_callback(StderrCallback aCallback)
+{
+ sStderrCallback = aCallback;
+}
+
+#if defined(ANDROID) && !defined(RELEASE_OR_BETA)
+static FILE* sStderrCopy = nullptr;
+
+void
+stderr_to_file(const char* aFmt, va_list aArgs)
+{
+ vfprintf(sStderrCopy, aFmt, aArgs);
+}
+
+void
+copy_stderr_to_file(const char* aFile)
+{
+ if (sStderrCopy) {
+ return;
+ }
+ size_t buflen = strlen(aFile) + 16;
+ char* buf = (char*)malloc(buflen);
+ snprintf(buf, buflen, "%s.%u", aFile, (uint32_t)getpid());
+ sStderrCopy = fopen(buf, "w");
+ free(buf);
+ set_stderr_callback(stderr_to_file);
+}
+#endif
+
+#ifdef HAVE_VA_COPY
+#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
+#elif defined(HAVE_VA_LIST_AS_ARRAY)
+#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
+#else
+#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
+#endif
+
+#if defined(XP_WIN)
+void
+vprintf_stderr(const char* aFmt, va_list aArgs)
+{
+ if (sStderrCallback) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ sStderrCallback(aFmt, aArgs);
+ va_end(argsCpy);
+ }
+
+ if (IsDebuggerPresent()) {
+ int lengthNeeded = _vscprintf(aFmt, aArgs);
+ if (lengthNeeded) {
+ lengthNeeded++;
+ auto buf = MakeUnique<char[]>(lengthNeeded);
+ if (buf) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ vsnprintf(buf.get(), lengthNeeded, aFmt, argsCpy);
+ buf[lengthNeeded - 1] = '\0';
+ va_end(argsCpy);
+ OutputDebugStringA(buf.get());
+ }
+ }
+ }
+
+ FILE* fp = _fdopen(_dup(2), "a");
+ if (!fp) {
+ return;
+ }
+
+ vfprintf(fp, aFmt, aArgs);
+
+ fclose(fp);
+}
+
+#elif defined(ANDROID)
+void
+vprintf_stderr(const char* aFmt, va_list aArgs)
+{
+ if (sStderrCallback) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ sStderrCallback(aFmt, aArgs);
+ va_end(argsCpy);
+ }
+
+ __android_log_vprint(ANDROID_LOG_INFO, "Gecko", aFmt, aArgs);
+}
+#else
+void
+vprintf_stderr(const char* aFmt, va_list aArgs)
+{
+ if (sStderrCallback) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ sStderrCallback(aFmt, aArgs);
+ va_end(argsCpy);
+ }
+
+ vfprintf(stderr, aFmt, aArgs);
+}
+#endif
+
+void
+printf_stderr(const char* aFmt, ...)
+{
+ va_list args;
+ va_start(args, aFmt);
+ vprintf_stderr(aFmt, args);
+ va_end(args);
+}
+
+void
+fprintf_stderr(FILE* aFile, const char* aFmt, ...)
+{
+ va_list args;
+ va_start(args, aFmt);
+ if (aFile == stderr) {
+ vprintf_stderr(aFmt, args);
+ } else {
+ vfprintf(aFile, aFmt, args);
+ }
+ va_end(args);
+}
diff --git a/xpcom/glue/nsCRTGlue.h b/xpcom/glue/nsCRTGlue.h
new file mode 100644
index 000000000..8caa1ae27
--- /dev/null
+++ b/xpcom/glue/nsCRTGlue.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCRTGlue_h__
+#define nsCRTGlue_h__
+
+#include "nscore.h"
+
+/**
+ * Scan a string for the first character that is *not* in a set of
+ * delimiters. If the string is only delimiter characters, the end of the
+ * string is returned.
+ *
+ * @param aDelims The set of delimiters (null-terminated)
+ * @param aStr The string to search (null-terminated)
+ */
+const char* NS_strspnp(const char* aDelims, const char* aStr);
+
+/**
+ * Tokenize a string. This function is similar to the strtok function in the
+ * C standard library, but it does not use static variables to maintain state
+ * and is therefore thread and reentrancy-safe.
+ *
+ * Any leading delimiters in str are skipped. Then the string is scanned
+ * until an additional delimiter or end-of-string is found. The final
+ * delimiter is set to '\0'.
+ *
+ * @param aDelims The set of delimiters.
+ * @param aStr The string to search. This is an in-out parameter; it is
+ * reset to the end of the found token + 1, or to the
+ * end-of-string if there are no more tokens.
+ * @return The token. If no token is found (the string is only
+ * delimiter characters), nullptr is returned.
+ */
+char* NS_strtok(const char* aDelims, char** aStr);
+
+/**
+ * "strlen" for char16_t strings
+ */
+uint32_t NS_strlen(const char16_t* aString);
+
+/**
+ * "strcmp" for char16_t strings
+ */
+int NS_strcmp(const char16_t* aStrA, const char16_t* aStrB);
+
+/**
+ * "strncmp" for char16_t strings
+ */
+int NS_strncmp(const char16_t* aStrA, const char16_t* aStrB, size_t aLen);
+
+/**
+ * "strdup" for char16_t strings, uses the NS_Alloc allocator.
+ */
+char16_t* NS_strdup(const char16_t* aString);
+
+/**
+ * "strdup", but using the NS_Alloc allocator.
+ */
+char* NS_strdup(const char* aString);
+
+/**
+ * strndup for char16_t or char strings (normal strndup is not available on
+ * windows). This function will ensure that the new string is
+ * null-terminated. Uses the NS_Alloc allocator.
+ *
+ * CharT may be either char16_t or char.
+ */
+template<typename CharT>
+CharT* NS_strndup(const CharT* aString, uint32_t aLen);
+
+// The following case-conversion methods only deal in the ascii repertoire
+// A-Z and a-z
+
+// semi-private data declarations... don't use these directly.
+class nsLowerUpperUtils
+{
+public:
+ static const unsigned char kLower2Upper[256];
+ static const unsigned char kUpper2Lower[256];
+};
+
+inline char
+NS_ToUpper(char aChar)
+{
+ return (char)nsLowerUpperUtils::kLower2Upper[(unsigned char)aChar];
+}
+
+inline char
+NS_ToLower(char aChar)
+{
+ return (char)nsLowerUpperUtils::kUpper2Lower[(unsigned char)aChar];
+}
+
+bool NS_IsUpper(char aChar);
+bool NS_IsLower(char aChar);
+
+bool NS_IsAscii(char16_t aChar);
+bool NS_IsAscii(const char16_t* aString);
+bool NS_IsAsciiAlpha(char16_t aChar);
+bool NS_IsAsciiDigit(char16_t aChar);
+bool NS_IsAsciiWhitespace(char16_t aChar);
+bool NS_IsAscii(const char* aString);
+bool NS_IsAscii(const char* aString, uint32_t aLength);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+void NS_MakeRandomString(char* aBuf, int32_t aBufLen);
+#endif
+
+#define FF '\f'
+#define TAB '\t'
+
+#define CRSTR "\015"
+#define LFSTR "\012"
+#define CRLF "\015\012" /* A CR LF equivalent string */
+
+// We use the most restrictive filesystem as our default set of illegal filename
+// characters. This is currently Windows.
+#define OS_FILE_ILLEGAL_CHARACTERS "/:*?\"<>|"
+// We also provide a list of all known file path separators for all filesystems.
+// This can be used in replacement of FILE_PATH_SEPARATOR when you need to
+// identify or replace all known path separators.
+#define KNOWN_PATH_SEPARATORS "\\/"
+
+#if defined(XP_MACOSX)
+ #define FILE_PATH_SEPARATOR "/"
+#elif defined(XP_WIN)
+ #define FILE_PATH_SEPARATOR "\\"
+#elif defined(XP_UNIX)
+ #define FILE_PATH_SEPARATOR "/"
+#else
+ #error need_to_define_your_file_path_separator_and_maybe_illegal_characters
+#endif
+
+// Not all these control characters are illegal in all OSs, but we don't really
+// want them appearing in filenames
+#define CONTROL_CHARACTERS "\001\002\003\004\005\006\007" \
+ "\010\011\012\013\014\015\016\017" \
+ "\020\021\022\023\024\025\026\027" \
+ "\030\031\032\033\034\035\036\037"
+
+#define FILE_ILLEGAL_CHARACTERS CONTROL_CHARACTERS OS_FILE_ILLEGAL_CHARACTERS
+
+#endif // nsCRTGlue_h__
diff --git a/xpcom/glue/nsCategoryCache.cpp b/xpcom/glue/nsCategoryCache.cpp
new file mode 100644
index 000000000..30501b8e3
--- /dev/null
+++ b/xpcom/glue/nsCategoryCache.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIStringEnumerator.h"
+
+#include "nsXPCOMCID.h"
+
+#include "nsCategoryCache.h"
+
+nsCategoryObserver::nsCategoryObserver(const char* aCategory)
+ : mCategory(aCategory)
+ , mObserversRemoved(false)
+{
+ // First, enumerate the currently existing entries
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv = catMan->EnumerateCategory(aCategory,
+ getter_AddRefs(enumerator));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> strings = do_QueryInterface(enumerator);
+ MOZ_ASSERT(strings);
+
+ bool more;
+ while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
+ nsAutoCString entryName;
+ strings->GetNext(entryName);
+
+ nsCString entryValue;
+ rv = catMan->GetCategoryEntry(aCategory,
+ entryName.get(),
+ getter_Copies(entryValue));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
+ if (service) {
+ mHash.Put(entryName, service);
+ }
+ }
+ }
+
+ // Now, listen for changes
+ nsCOMPtr<nsIObserverService> serv =
+ mozilla::services::GetObserverService();
+ if (serv) {
+ serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, false);
+ }
+}
+
+nsCategoryObserver::~nsCategoryObserver()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsCategoryObserver, nsIObserver)
+
+void
+nsCategoryObserver::ListenerDied()
+{
+ RemoveObservers();
+}
+
+void
+nsCategoryObserver::RemoveObservers()
+{
+ if (mObserversRemoved) {
+ return;
+ }
+
+ mObserversRemoved = true;
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID);
+ }
+}
+
+NS_IMETHODIMP
+nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ mHash.Clear();
+ RemoveObservers();
+
+ return NS_OK;
+ }
+
+ if (!aData ||
+ !nsDependentString(aData).Equals(NS_ConvertASCIItoUTF16(mCategory))) {
+ return NS_OK;
+ }
+
+ nsAutoCString str;
+ nsCOMPtr<nsISupportsCString> strWrapper(do_QueryInterface(aSubject));
+ if (strWrapper) {
+ strWrapper->GetData(str);
+ }
+
+ if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) {
+ // We may get an add notification even when we already have an entry. This
+ // is due to the notification happening asynchronously, so if the entry gets
+ // added and an nsCategoryObserver gets instantiated before events get
+ // processed, we'd get the notification for an existing entry.
+ // Do nothing in that case.
+ if (mHash.GetWeak(str)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return NS_OK;
+ }
+
+ nsCString entryValue;
+ catMan->GetCategoryEntry(mCategory.get(),
+ str.get(),
+ getter_Copies(entryValue));
+
+ nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
+
+ if (service) {
+ mHash.Put(str, service);
+ }
+ } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) {
+ mHash.Remove(str);
+ } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) {
+ mHash.Clear();
+ }
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsCategoryCache.h b/xpcom/glue/nsCategoryCache.h
new file mode 100644
index 000000000..023aa7a75
--- /dev/null
+++ b/xpcom/glue/nsCategoryCache.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCategoryCache_h_
+#define nsCategoryCache_h_
+
+#include "mozilla/Attributes.h"
+
+#include "nsICategoryManager.h"
+#include "nsIObserver.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsInterfaceHashtable.h"
+
+#include "nsXPCOM.h"
+
+class nsCategoryObserver final : public nsIObserver
+{
+ ~nsCategoryObserver();
+
+public:
+ explicit nsCategoryObserver(const char* aCategory);
+
+ void ListenerDied();
+ nsInterfaceHashtable<nsCStringHashKey, nsISupports>& GetHash()
+ {
+ return mHash;
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+private:
+ void RemoveObservers();
+
+ nsInterfaceHashtable<nsCStringHashKey, nsISupports> mHash;
+ nsCString mCategory;
+ bool mObserversRemoved;
+};
+
+/**
+ * This is a helper class that caches services that are registered in a certain
+ * category. The intended usage is that a service stores a variable of type
+ * nsCategoryCache<nsIFoo> in a member variable, where nsIFoo is the interface
+ * that these services should implement. The constructor of this class should
+ * then get the name of the category.
+ */
+template<class T>
+class nsCategoryCache final
+{
+public:
+ explicit nsCategoryCache(const char* aCategory)
+ : mCategoryName(aCategory)
+ {
+ }
+ ~nsCategoryCache()
+ {
+ if (mObserver) {
+ mObserver->ListenerDied();
+ }
+ }
+
+ void GetEntries(nsCOMArray<T>& aResult)
+ {
+ // Lazy initialization, so that services in this category can't
+ // cause reentrant getService (bug 386376)
+ if (!mObserver) {
+ mObserver = new nsCategoryObserver(mCategoryName.get());
+ }
+
+ for (auto iter = mObserver->GetHash().Iter(); !iter.Done(); iter.Next()) {
+ nsISupports* entry = iter.UserData();
+ nsCOMPtr<T> service = do_QueryInterface(entry);
+ if (service) {
+ aResult.AppendElement(service.forget());
+ }
+ }
+ }
+
+private:
+ // Not to be implemented
+ nsCategoryCache(const nsCategoryCache<T>&);
+
+ nsCString mCategoryName;
+ RefPtr<nsCategoryObserver> mObserver;
+};
+
+#endif
diff --git a/xpcom/glue/nsClassHashtable.h b/xpcom/glue/nsClassHashtable.h
new file mode 100644
index 000000000..53ca5676b
--- /dev/null
+++ b/xpcom/glue/nsClassHashtable.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsClassHashtable_h__
+#define nsClassHashtable_h__
+
+#include "mozilla/Move.h"
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
+
+/**
+ * templated hashtable class maps keys to C++ object pointers.
+ * See nsBaseHashtable for complete declaration.
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param Class the class-type being wrapped
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass, class T>
+class nsClassHashtable
+ : public nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*>
+{
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef T* UserDataType;
+ typedef nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*> base_type;
+
+ using base_type::IsEmpty;
+
+ nsClassHashtable() {}
+ explicit nsClassHashtable(uint32_t aInitLength)
+ : nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*>(aInitLength)
+ {
+ }
+
+ /**
+ * Looks up aKey in the hash table. If it doesn't exist a new object of
+ * KeyClass will be created (using the arguments provided) and then returned.
+ */
+ template<typename... Args>
+ UserDataType LookupOrAdd(KeyType aKey, Args&&... aConstructionArgs);
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @param aData if the key doesn't exist, pData will be set to nullptr.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const;
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @returns nullptr if the key is not present.
+ */
+ UserDataType Get(KeyType aKey) const;
+
+ /**
+ * Remove the entry for the given key from the hashtable and return it in
+ * aOut. If the key is not in the hashtable, aOut's pointer is set to
+ * nullptr.
+ *
+ * Normally, an entry is deleted when it's removed from an nsClassHashtable,
+ * but this function transfers ownership of the entry back to the caller
+ * through aOut -- the entry will be deleted when aOut goes out of scope.
+ *
+ * @param aKey the key to get and remove from the hashtable
+ */
+ void RemoveAndForget(KeyType aKey, nsAutoPtr<T>& aOut);
+};
+
+//
+// nsClassHashtable definitions
+//
+
+template<class KeyClass, class T>
+template<typename... Args>
+T*
+nsClassHashtable<KeyClass, T>::LookupOrAdd(KeyType aKey,
+ Args&&... aConstructionArgs)
+{
+ typename base_type::EntryType* ent = this->PutEntry(aKey);
+ if (!ent->mData) {
+ ent->mData = new T(mozilla::Forward<Args>(aConstructionArgs)...);
+ }
+ return ent->mData;
+}
+
+template<class KeyClass, class T>
+bool
+nsClassHashtable<KeyClass, T>::Get(KeyType aKey, T** aRetVal) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aRetVal) {
+ *aRetVal = ent->mData;
+ }
+
+ return true;
+ }
+
+ if (aRetVal) {
+ *aRetVal = nullptr;
+ }
+
+ return false;
+}
+
+template<class KeyClass, class T>
+T*
+nsClassHashtable<KeyClass, T>::Get(KeyType aKey) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return nullptr;
+ }
+
+ return ent->mData;
+}
+
+template<class KeyClass, class T>
+void
+nsClassHashtable<KeyClass, T>::RemoveAndForget(KeyType aKey, nsAutoPtr<T>& aOut)
+{
+ aOut = nullptr;
+
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return;
+ }
+
+ // Transfer ownership from ent->mData into aOut.
+ aOut = mozilla::Move(ent->mData);
+
+ this->Remove(aKey);
+}
+
+#endif // nsClassHashtable_h__
diff --git a/xpcom/glue/nsClassInfoImpl.cpp b/xpcom/glue/nsClassInfoImpl.cpp
new file mode 100644
index 000000000..6eb34f9f7
--- /dev/null
+++ b/xpcom/glue/nsClassInfoImpl.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIClassInfoImpl.h"
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+GenericClassInfo::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+GenericClassInfo::Release()
+{
+ return 1;
+}
+
+NS_IMPL_QUERY_INTERFACE(GenericClassInfo, nsIClassInfo)
+
+NS_IMETHODIMP
+GenericClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
+{
+ return mData->getinterfaces(aCount, aArray);
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetScriptableHelper(nsIXPCScriptable** aHelper)
+{
+ if (mData->getscriptablehelper) {
+ return mData->getscriptablehelper(aHelper);
+ }
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetContractID(char** aContractID)
+{
+ NS_ERROR("GetContractID not implemented");
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetClassDescription(char** aDescription)
+{
+ *aDescription = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetClassID(nsCID** aClassID)
+{
+ NS_ERROR("GetClassID not implemented");
+ *aClassID = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = mData->flags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ *aClassIDNoAlloc = mData->cid;
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsComponentManagerUtils.cpp b/xpcom/glue/nsComponentManagerUtils.cpp
new file mode 100644
index 000000000..d8a590fa7
--- /dev/null
+++ b/xpcom/glue/nsComponentManagerUtils.cpp
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOM_h__
+#include "nsXPCOM.h"
+#endif
+
+#ifndef nsCOMPtr_h__
+#include "nsCOMPtr.h"
+#endif
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIComponentManager.h"
+
+#ifndef MOZILLA_INTERNAL_API
+
+nsresult
+CallGetService(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIServiceManager> servMgr;
+ nsresult status = NS_GetServiceManager(getter_AddRefs(servMgr));
+ if (servMgr) {
+ status = servMgr->GetService(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult
+CallGetService(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIServiceManager> servMgr;
+ nsresult status = NS_GetServiceManager(getter_AddRefs(servMgr));
+ if (servMgr) {
+ status = servMgr->GetServiceByContractID(aContractID, aIID, aResult);
+ }
+ return status;
+}
+
+#else
+
+#include "nsComponentManager.h"
+
+nsresult
+CallGetService(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetService(aCID, aIID, aResult);
+}
+
+nsresult
+CallGetService(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetServiceByContractID(aContractID,
+ aIID,
+ aResult);
+}
+
+#endif
+
+#ifndef MOZILLA_INTERNAL_API
+
+nsresult
+CallCreateInstance(const nsCID& aCID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr) {
+ status = compMgr->CreateInstance(aCID, aDelegate, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult
+CallCreateInstance(const char* aContractID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr)
+ status = compMgr->CreateInstanceByContractID(aContractID, aDelegate,
+ aIID, aResult);
+ return status;
+}
+
+nsresult
+CallGetClassObject(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr) {
+ status = compMgr->GetClassObject(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult
+CallGetClassObject(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr)
+ status = compMgr->GetClassObjectByContractID(aContractID, aIID,
+ aResult);
+ return status;
+}
+
+#else
+
+#include "nsComponentManager.h"
+
+nsresult
+CallCreateInstance(const nsCID& aCID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::CreateInstance(aCID, aDelegate, aIID,
+ aResult);
+}
+
+nsresult
+CallCreateInstance(const char* aContractID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return
+ compMgr->nsComponentManagerImpl::CreateInstanceByContractID(aContractID,
+ aDelegate, aIID,
+ aResult);
+}
+
+nsresult
+CallGetClassObject(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetClassObject(aCID, aIID, aResult);
+}
+
+nsresult
+CallGetClassObject(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return
+ compMgr->nsComponentManagerImpl::GetClassObjectByContractID(aContractID,
+ aIID, aResult);
+}
+
+#endif
+
+nsresult
+nsCreateInstanceByCID::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status = CallCreateInstance(mCID, nullptr, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsCreateInstanceByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallCreateInstance(mContractID, nullptr, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsCreateInstanceFromFactory::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = mFactory->CreateInstance(nullptr, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+
+nsresult
+nsGetClassObjectByCID::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status = CallGetClassObject(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsGetClassObjectByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetClassObject(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+
+nsresult
+nsGetServiceByCID::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ return status;
+}
+
+nsresult
+nsGetServiceByCIDWithError::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsGetServiceByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ return status;
+}
+
+nsresult
+nsGetServiceByContractIDWithError::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
diff --git a/xpcom/glue/nsComponentManagerUtils.h b/xpcom/glue/nsComponentManagerUtils.h
new file mode 100644
index 000000000..2a7a4fbd7
--- /dev/null
+++ b/xpcom/glue/nsComponentManagerUtils.h
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsComponentManagerUtils_h__
+#define nsComponentManagerUtils_h__
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+#include "nsIFactory.h"
+
+
+nsresult CallCreateInstance(const nsCID& aClass, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult);
+
+nsresult CallCreateInstance(const char* aContractID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult);
+
+nsresult CallGetClassObject(const nsCID& aClass, const nsIID& aIID,
+ void** aResult);
+
+nsresult CallGetClassObject(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+
+class MOZ_STACK_CLASS nsCreateInstanceByCID final : public nsCOMPtr_helper
+{
+public:
+ nsCreateInstanceByCID(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
+ override;
+
+private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsCreateInstanceByContractID final : public nsCOMPtr_helper
+{
+public:
+ nsCreateInstanceByContractID(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsCreateInstanceFromFactory final : public nsCOMPtr_helper
+{
+public:
+ nsCreateInstanceFromFactory(nsIFactory* aFactory, nsresult* aErrorPtr)
+ : mFactory(aFactory)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ nsIFactory* MOZ_NON_OWNING_REF mFactory;
+ nsresult* mErrorPtr;
+};
+
+
+inline const nsCreateInstanceByCID
+do_CreateInstance(const nsCID& aCID, nsresult* aError = 0)
+{
+ return nsCreateInstanceByCID(aCID, aError);
+}
+
+inline const nsCreateInstanceByContractID
+do_CreateInstance(const char* aContractID, nsresult* aError = 0)
+{
+ return nsCreateInstanceByContractID(aContractID, aError);
+}
+
+inline const nsCreateInstanceFromFactory
+do_CreateInstance(nsIFactory* aFactory, nsresult* aError = 0)
+{
+ return nsCreateInstanceFromFactory(aFactory, aError);
+}
+
+
+class MOZ_STACK_CLASS nsGetClassObjectByCID final : public nsCOMPtr_helper
+{
+public:
+ nsGetClassObjectByCID(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsGetClassObjectByContractID final : public nsCOMPtr_helper
+{
+public:
+ nsGetClassObjectByContractID(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+/**
+ * do_GetClassObject can be used to improve performance of callers
+ * that call |CreateInstance| many times. They can cache the factory
+ * and call do_CreateInstance or CallCreateInstance with the cached
+ * factory rather than having the component manager retrieve it every
+ * time.
+ */
+inline const nsGetClassObjectByCID
+do_GetClassObject(const nsCID& aCID, nsresult* aError = 0)
+{
+ return nsGetClassObjectByCID(aCID, aError);
+}
+
+inline const nsGetClassObjectByContractID
+do_GetClassObject(const char* aContractID, nsresult* aError = 0)
+{
+ return nsGetClassObjectByContractID(aContractID, aError);
+}
+
+// type-safe shortcuts for calling |CreateInstance|
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const nsCID& aClass,
+ nsISupports* aDelegate,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aClass, aDelegate,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const nsCID& aClass, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aClass, nullptr,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const char* aContractID,
+ nsISupports* aDelegate,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aContractID, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aContractID,
+ aDelegate,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const char* aContractID, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aContractID, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aContractID, nullptr,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(nsIFactory* aFactory,
+ nsISupports* aDelegate,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aFactory, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aFactory->CreateInstance(aDelegate,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(nsIFactory* aFactory, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aFactory, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aFactory->CreateInstance(nullptr,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallGetClassObject(const nsCID& aClass, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetClassObject(aClass, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallGetClassObject(const char* aContractID, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetClassObject(aContractID, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+#endif /* nsComponentManagerUtils_h__ */
diff --git a/xpcom/glue/nsCycleCollectionNoteChild.h b/xpcom/glue/nsCycleCollectionNoteChild.h
new file mode 100644
index 000000000..5d47caefd
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionNoteChild.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This header will be included by headers that define refpointer and array classes
+// in order to specialize CC helpers such as ImplCycleCollectionTraverse for them.
+
+#ifndef nsCycleCollectionNoteChild_h__
+#define nsCycleCollectionNoteChild_h__
+
+#include "nsCycleCollectionTraversalCallback.h"
+#include "mozilla/Likely.h"
+#include "mozilla/TypeTraits.h"
+
+enum
+{
+ CycleCollectionEdgeNameArrayFlag = 1
+};
+
+// Just a helper for appending "[i]". Didn't want to pull in string headers here.
+void
+CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
+ const char* aName,
+ uint32_t aFlags = 0);
+
+// Should be inlined so that in the no-debug-info case this is just a simple if().
+MOZ_ALWAYS_INLINE void
+CycleCollectionNoteEdgeName(nsCycleCollectionTraversalCallback& aCallback,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
+ CycleCollectionNoteEdgeNameImpl(aCallback, aName, aFlags);
+ }
+}
+
+#define NS_CYCLE_COLLECTION_INNERCLASS \
+ cycleCollection
+
+#define NS_CYCLE_COLLECTION_INNERNAME \
+ _cycleCollectorGlobal
+
+#define NS_CYCLE_COLLECTION_PARTICIPANT(_class) \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()
+
+template<typename T>
+nsISupports*
+ToSupports(T* aPtr, typename T::NS_CYCLE_COLLECTION_INNERCLASS* aDummy = 0)
+{
+ return T::NS_CYCLE_COLLECTION_INNERCLASS::Upcast(aPtr);
+}
+
+// The default implementation of this class template is empty, because it
+// should never be used: see the partial specializations below.
+template<typename T,
+ bool IsXPCOM = mozilla::IsBaseOf<nsISupports, T>::value>
+struct CycleCollectionNoteChildImpl
+{
+};
+
+template<typename T>
+struct CycleCollectionNoteChildImpl<T, true>
+{
+ static void Run(nsCycleCollectionTraversalCallback& aCallback, T* aChild)
+ {
+ aCallback.NoteXPCOMChild(ToSupports(aChild));
+ }
+};
+
+template<typename T>
+struct CycleCollectionNoteChildImpl<T, false>
+{
+ static void Run(nsCycleCollectionTraversalCallback& aCallback, T* aChild)
+ {
+ aCallback.NoteNativeChild(aChild, NS_CYCLE_COLLECTION_PARTICIPANT(T));
+ }
+};
+
+// We declare CycleCollectionNoteChild in 3-argument and 4-argument variants,
+// rather than using default arguments, so that forward declarations work
+// regardless of header inclusion order.
+template<typename T>
+inline void
+CycleCollectionNoteChild(nsCycleCollectionTraversalCallback& aCallback,
+ T* aChild, const char* aName, uint32_t aFlags)
+{
+ CycleCollectionNoteEdgeName(aCallback, aName, aFlags);
+ CycleCollectionNoteChildImpl<T>::Run(aCallback, aChild);
+}
+
+template<typename T>
+inline void
+CycleCollectionNoteChild(nsCycleCollectionTraversalCallback& aCallback,
+ T* aChild, const char* aName)
+{
+ CycleCollectionNoteChild(aCallback, aChild, aName, 0);
+}
+
+#endif // nsCycleCollectionNoteChild_h__
diff --git a/xpcom/glue/nsCycleCollectionNoteRootCallback.h b/xpcom/glue/nsCycleCollectionNoteRootCallback.h
new file mode 100644
index 000000000..42c43f301
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionNoteRootCallback.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCycleCollectionNoteRootCallback_h__
+#define nsCycleCollectionNoteRootCallback_h__
+
+class nsCycleCollectionParticipant;
+class nsISupports;
+
+class nsCycleCollectionNoteRootCallback
+{
+public:
+ NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports* aRoot) = 0;
+ NS_IMETHOD_(void) NoteJSRoot(JSObject* aRoot) = 0;
+ NS_IMETHOD_(void) NoteNativeRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant) = 0;
+
+ NS_IMETHOD_(void) NoteWeakMapping(JSObject* aMap, JS::GCCellPtr aKey,
+ JSObject* aKeyDelegate, JS::GCCellPtr aVal) = 0;
+
+ bool WantAllTraces() const { return mWantAllTraces; }
+protected:
+ nsCycleCollectionNoteRootCallback() : mWantAllTraces(false) {}
+
+ bool mWantAllTraces;
+};
+
+#endif // nsCycleCollectionNoteRootCallback_h__
diff --git a/xpcom/glue/nsCycleCollectionParticipant.cpp b/xpcom/glue/nsCycleCollectionParticipant.cpp
new file mode 100644
index 000000000..973ef2ff5
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Root(void* aPtr)
+{
+ nsISupports* s = static_cast<nsISupports*>(aPtr);
+ NS_ADDREF(s);
+}
+
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Unroot(void* aPtr)
+{
+ nsISupports* s = static_cast<nsISupports*>(aPtr);
+ NS_RELEASE(s);
+}
+
+// We define a default trace function because some participants don't need
+// to trace anything, so it is okay for them not to define one.
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure)
+{
+}
+
+bool
+nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports* aSupports)
+{
+ nsISupports* foo;
+ aSupports->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+ reinterpret_cast<void**>(&foo));
+ return aSupports == foo;
+}
diff --git a/xpcom/glue/nsCycleCollectionParticipant.h b/xpcom/glue/nsCycleCollectionParticipant.h
new file mode 100644
index 000000000..2dfbb6750
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -0,0 +1,852 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCycleCollectionParticipant_h__
+#define nsCycleCollectionParticipant_h__
+
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "nsCycleCollectionNoteChild.h"
+#include "js/RootingAPI.h"
+
+#define NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID \
+{ \
+ 0x9674489b, \
+ 0x1f6f, \
+ 0x4550, \
+ { 0xa7, 0x30, 0xcc, 0xae, 0xdd, 0x10, 0x4c, 0xf9 } \
+}
+
+/**
+ * Special IID to get at the base nsISupports for a class. Usually this is the
+ * canonical nsISupports pointer, but in the case of tearoffs for example it is
+ * the base nsISupports pointer of the tearoff. This allow the cycle collector
+ * to have separate nsCycleCollectionParticipant's for tearoffs or aggregated
+ * classes.
+ */
+#define NS_CYCLECOLLECTIONISUPPORTS_IID \
+{ \
+ 0xc61eac14, \
+ 0x5f7a, \
+ 0x4481, \
+ { 0x96, 0x5e, 0x7e, 0xaa, 0x6e, 0xff, 0xa8, 0x5f } \
+}
+
+/**
+ * Just holds the IID so NS_GET_IID works.
+ */
+class nsCycleCollectionISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_CYCLECOLLECTIONISUPPORTS_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionISupports,
+ NS_CYCLECOLLECTIONISUPPORTS_IID)
+
+namespace JS {
+template<class T> class Heap;
+} /* namespace JS */
+
+/*
+ * A struct defining pure virtual methods which are called when tracing cycle
+ * collection paticipants. The appropriate method is called depending on the
+ * type of JS GC thing.
+ */
+struct TraceCallbacks
+{
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+};
+
+/*
+ * An implementation of TraceCallbacks that calls a single function for all JS
+ * GC thing types encountered. Implemented in nsCycleCollectorTraceJSHelpers.cpp.
+ */
+struct TraceCallbackFunc : public TraceCallbacks
+{
+ typedef void (*Func)(JS::GCCellPtr aPtr, const char* aName, void* aClosure);
+
+ explicit TraceCallbackFunc(Func aCb) : mCallback(aCb) {}
+
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const override;
+
+private:
+ Func mCallback;
+};
+
+/**
+ * Participant implementation classes
+ */
+class NS_NO_VTABLE nsCycleCollectionParticipant
+{
+public:
+ constexpr nsCycleCollectionParticipant() : mMightSkip(false) {}
+ constexpr explicit nsCycleCollectionParticipant(bool aSkip) : mMightSkip(aSkip) {}
+
+ NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb) = 0;
+
+ NS_IMETHOD_(void) Root(void* aPtr) = 0;
+ NS_IMETHOD_(void) Unlink(void* aPtr) = 0;
+ NS_IMETHOD_(void) Unroot(void* aPtr) = 0;
+ NS_IMETHOD_(const char*) ClassName() = 0;
+
+ NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure) {}
+
+ // If CanSkip returns true, p is removed from the purple buffer during
+ // a call to nsCycleCollector_forgetSkippable().
+ // Note, calling CanSkip may remove objects from the purple buffer!
+ // If aRemovingAllowed is true, p can be removed from the purple buffer.
+ bool CanSkip(void* aPtr, bool aRemovingAllowed)
+ {
+ return mMightSkip ? CanSkipReal(aPtr, aRemovingAllowed) : false;
+ }
+
+ // If CanSkipInCC returns true, p is skipped when selecting roots for the
+ // cycle collector graph.
+ // Note, calling CanSkipInCC may remove other objects from the purple buffer!
+ bool CanSkipInCC(void* aPtr)
+ {
+ return mMightSkip ? CanSkipInCCReal(aPtr) : false;
+ }
+
+ // If CanSkipThis returns true, p is not added to the graph.
+ // This method is called during cycle collection, so don't
+ // change the state of any objects!
+ bool CanSkipThis(void* aPtr)
+ {
+ return mMightSkip ? CanSkipThisReal(aPtr) : false;
+ }
+
+ NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) = 0;
+
+protected:
+ NS_IMETHOD_(bool) CanSkipReal(void* aPtr, bool aRemovingAllowed)
+ {
+ NS_ASSERTION(false, "Forgot to implement CanSkipReal?");
+ return false;
+ }
+ NS_IMETHOD_(bool) CanSkipInCCReal(void* aPtr)
+ {
+ NS_ASSERTION(false, "Forgot to implement CanSkipInCCReal?");
+ return false;
+ }
+ NS_IMETHOD_(bool) CanSkipThisReal(void* aPtr)
+ {
+ NS_ASSERTION(false, "Forgot to implement CanSkipThisReal?");
+ return false;
+ }
+
+private:
+ const bool mMightSkip;
+};
+
+class NS_NO_VTABLE nsScriptObjectTracer : public nsCycleCollectionParticipant
+{
+public:
+ constexpr nsScriptObjectTracer()
+ : nsCycleCollectionParticipant(false)
+ {
+ }
+ constexpr explicit nsScriptObjectTracer(bool aSkip)
+ : nsCycleCollectionParticipant(aSkip)
+ {
+ }
+
+ NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure) override = 0;
+
+ // Implemented in nsCycleCollectorTraceJSHelpers.cpp.
+ static void NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
+ void* aClosure);
+};
+
+class NS_NO_VTABLE nsXPCOMCycleCollectionParticipant : public nsScriptObjectTracer
+{
+public:
+ constexpr nsXPCOMCycleCollectionParticipant()
+ : nsScriptObjectTracer(false)
+ {
+ }
+ constexpr explicit nsXPCOMCycleCollectionParticipant(bool aSkip)
+ : nsScriptObjectTracer(aSkip)
+ {
+ }
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID)
+
+ NS_IMETHOD_(void) Root(void* aPtr) override;
+ NS_IMETHOD_(void) Unroot(void* aPtr) override;
+
+ NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure) override;
+
+ static bool CheckForRightISupports(nsISupports* aSupports);
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsXPCOMCycleCollectionParticipant,
+ NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID)
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing a QI to nsXPCOMCycleCollectionParticipant
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_CYCLE_COLLECTION_CLASSNAME(_class) \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS
+
+#define NS_IMPL_QUERY_CYCLE_COLLECTION(_class) \
+ if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { \
+ *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
+ return NS_OK; \
+ } else
+
+#define NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class) \
+ if ( aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ) { \
+ *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ return NS_OK; \
+ } else
+
+#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class) \
+ NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class) \
+ NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)
+
+#define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
+
+#define NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(_class) \
+ NS_INTERFACE_MAP_BEGIN(_class) \
+ NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(_class) \
+ NS_INTERFACE_MAP_BEGIN(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(_class) \
+ if (rv == NS_OK) return rv; \
+ nsISupports* foundInterface; \
+ NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(_class) \
+ NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+ { \
+ NS_PRECONDITION(aInstancePtr, "null out param"); \
+ \
+ if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { \
+ *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
+ return NS_OK; \
+ } \
+ nsresult rv;
+
+#define NS_CYCLE_COLLECTION_UPCAST(obj, clazz) \
+ NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj)
+
+#ifdef DEBUG
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT(_ptr) _ptr->CheckForRightParticipant()
+#else
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT(_ptr)
+#endif
+
+// The default implementation of this class template is empty, because it
+// should never be used: see the partial specializations below.
+template<typename T,
+ bool IsXPCOM = mozilla::IsBaseOf<nsISupports, T>::value>
+struct DowncastCCParticipantImpl
+{
+};
+
+// Specialization for XPCOM CC participants
+template<typename T>
+struct DowncastCCParticipantImpl<T, true>
+{
+ static T* Run(void* aPtr)
+ {
+ nsISupports* s = static_cast<nsISupports*>(aPtr);
+ MOZ_ASSERT(NS_CYCLE_COLLECTION_CLASSNAME(T)::CheckForRightISupports(s),
+ "not the nsISupports pointer we expect");
+ T* rval = NS_CYCLE_COLLECTION_CLASSNAME(T)::Downcast(s);
+ NS_CHECK_FOR_RIGHT_PARTICIPANT(rval);
+ return rval;
+ }
+};
+
+// Specialization for native CC participants
+template<typename T>
+struct DowncastCCParticipantImpl<T, false>
+{
+ static T* Run(void* aPtr) { return static_cast<T*>(aPtr); }
+};
+
+template<typename T>
+T*
+DowncastCCParticipant(void* aPtr)
+{
+ return DowncastCCParticipantImpl<T>::Run(aPtr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing CanSkip methods
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(_class) \
+ NS_IMETHODIMP_(bool) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipReal(void *p, \
+ bool aRemovingAllowed) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END \
+ (void)tmp; \
+ return false; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(_class) \
+ NS_IMETHODIMP_(bool) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipInCCReal(void *p) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END \
+ (void)tmp; \
+ return false; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(_class) \
+ NS_IMETHODIMP_(bool) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipThisReal(void *p) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END \
+ (void)tmp; \
+ return false; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsCycleCollectionParticipant::Unlink
+//
+// You need to use NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED if you want
+// the base class Unlink version to be called before your own implementation.
+// You can use NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED if you want the
+// base class Unlink to get called after your own implementation. You should
+// never use them together.
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMETHODIMP_(void) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Unlink(s);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_HELPER(_field) \
+ ImplCycleCollectionUnlink(tmp->_field);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK(...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ MOZ_FOR_EACH(NS_IMPL_CYCLE_COLLECTION_UNLINK_HELPER, (), (__VA_ARGS__))
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ (void)tmp; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(_base_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Unlink(s); \
+ (void)tmp; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_0(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsCycleCollectionParticipant::Traverse
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, _refcnt) \
+ cb.DescribeRefCountedNode(_refcnt, #_class);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(_class) \
+ NS_IMETHODIMP \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \
+ (void *p, nsCycleCollectionTraversalCallback &cb) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(_class) \
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, tmp->mRefCnt.get())
+
+// Base class' CC participant should return NS_SUCCESS_INTERRUPTED_TRAVERSE
+// from Traverse if it wants derived classes to not traverse anything from
+// their CC participant.
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ if (NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Traverse(s, cb) \
+ == NS_SUCCESS_INTERRUPTED_TRAVERSE) { \
+ return NS_SUCCESS_INTERRUPTED_TRAVERSE; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_HELPER(_field) \
+ ImplCycleCollectionTraverse(cb, tmp->_field, #_field, 0);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE(...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ MOZ_FOR_EACH(NS_IMPL_CYCLE_COLLECTION_TRAVERSE_HELPER, (), (__VA_ARGS__))
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(_field) \
+ CycleCollectionNoteChild(cb, tmp->_field, #_field);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS \
+ { \
+ TraceCallbackFunc noteJsChild(&nsScriptObjectTracer::NoteJSChild); \
+ Trace(p, noteJsChild, &cb); \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \
+ (void)tmp; \
+ return NS_OK; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsScriptObjectTracer::Trace
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \
+ void \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \
+ const TraceCallbacks &aCallbacks, \
+ void *aClosure) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(_class, _base_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Trace(s, aCallbacks, aClosure);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(_field) \
+ aCallbacks.Trace(&tmp->_field, #_field, aClosure);
+
+// NB: The (void)tmp; hack in the TRACE_END macro exists to support
+// implementations that don't need to do anything in their Trace method.
+// Without this hack, some compilers warn about the unused tmp local.
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_END \
+ (void)tmp; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing a concrete nsCycleCollectionParticipant
+///////////////////////////////////////////////////////////////////////////////
+
+// If a class defines a participant, then QIing an instance of that class to
+// nsXPCOMCycleCollectionParticipant should produce that participant.
+#ifdef DEBUG
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_BASE \
+ virtual void CheckForRightParticipant()
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_DERIVED \
+ virtual void CheckForRightParticipant() override
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_BODY(_class) \
+ { \
+ nsXPCOMCycleCollectionParticipant *p; \
+ CallQueryInterface(this, &p); \
+ MOZ_ASSERT(p == &NS_CYCLE_COLLECTION_INNERNAME, \
+ #_class " should QI to its own CC participant"); \
+ }
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_BASE \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_BODY(_class)
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_DERIVED \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_BODY(_class)
+#else
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class)
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class)
+#endif
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(const char*) ClassName() override { return #_class; };
+
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(_class, _base) \
+public: \
+ NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void *p) override \
+ { \
+ DowncastCCParticipant<_class>(p)->DeleteCycleCollectable(); \
+ } \
+ static _class* Downcast(nsISupports* s) \
+ { \
+ return static_cast<_class*>(static_cast<_base*>(s)); \
+ } \
+ static nsISupports* Upcast(_class *p) \
+ { \
+ return NS_ISUPPORTS_CAST(_base*, p); \
+ } \
+ template<typename T> \
+ friend nsISupports* \
+ ToSupports(T* p, NS_CYCLE_COLLECTION_INNERCLASS* dummy);
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(_class, _base) \
+ NS_IMETHOD_(void) Unlink(void *p) override;
+
+#define NS_PARTICIPANT_AS(type, participant) \
+ const_cast<type*>(reinterpret_cast<const type*>(participant))
+
+#define NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+ static constexpr nsXPCOMCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ }
+
+/**
+ * We use this macro to force that classes that inherit from a ccable class and
+ * declare their own participant declare themselves as inherited cc classes.
+ * To avoid possibly unnecessary vtables we only do this checking in debug
+ * builds.
+ */
+#ifdef DEBUG
+#define NOT_INHERITED_CANT_OVERRIDE virtual void BaseCycleCollectable() final {}
+#else
+#define NOT_INHERITED_CANT_OVERRIDE
+#endif
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+ NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class)
+
+// Cycle collector helper for ambiguous classes that can sometimes be skipped.
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsXPCOMCycleCollectionParticipant(true) {} \
+private: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _class)
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsXPCOMCycleCollectionParticipant(true) {} \
+private: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) override; \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _class)
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(_class, \
+ _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) override; \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _class)
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(_class, \
+ _base_class) \
+public: \
+ NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ static _class* Downcast(nsISupports* s) \
+ { \
+ return static_cast<_class*>(static_cast<_base_class*>( \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s))); \
+ }
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(_class, _base_class) \
+ NS_IMETHOD_(void) Unlink(void *p) override;
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(_class, _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+public: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(_class, \
+ _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+public: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(_class, _base_class) \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(_class, \
+ _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) \
+ override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+// Cycle collector participant declarations.
+
+#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ public: \
+ NS_IMETHOD_(void) Root(void *n) override; \
+ NS_IMETHOD_(void) Unlink(void *n) override; \
+ NS_IMETHOD_(void) Unroot(void *n) override; \
+ NS_IMETHOD Traverse(void *n, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void *n) override \
+ { \
+ DowncastCCParticipant<_class>(n)->DeleteCycleCollectable(); \
+ } \
+ static _class* Downcast(void* s) \
+ { \
+ return DowncastCCParticipant<_class>(s); \
+ } \
+ static void* Upcast(_class *p) \
+ { \
+ return static_cast<void*>(p); \
+ }
+
+#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class) \
+ void DeleteCycleCollectable(void) \
+ { \
+ delete this; \
+ } \
+ class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsCycleCollectionParticipant \
+ { \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ static constexpr nsCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+ }; \
+ static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_NATIVE_CLASS(_class) \
+ void DeleteCycleCollectable(void) \
+ { \
+ delete this; \
+ } \
+ class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsCycleCollectionParticipant \
+ { \
+ public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsCycleCollectionParticipant(true) {} \
+ private: \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ static nsCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+ }; \
+ static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_NATIVE_CLASS_WITH_CUSTOM_DELETE(_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsCycleCollectionParticipant \
+{ \
+public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsCycleCollectionParticipant(true) {} \
+private: \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ static nsCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+}; \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(_class) \
+ void DeleteCycleCollectable(void) \
+ { \
+ delete this; \
+ } \
+ class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsScriptObjectTracer \
+ { \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) \
+ override; \
+ static constexpr nsScriptObjectTracer* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+ }; \
+ static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(_class, _root_function) \
+ NS_IMETHODIMP_(void) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Root(void *p) \
+ { \
+ _class *tmp = static_cast<_class*>(p); \
+ tmp->_root_function(); \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(_class, _unroot_function) \
+ NS_IMETHODIMP_(void) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unroot(void *p) \
+ { \
+ _class *tmp = static_cast<_class*>(p); \
+ tmp->_unroot_function(); \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS _class::NS_CYCLE_COLLECTION_INNERNAME;
+
+// NB: This is not something you usually want to use. It is here to allow
+// adding things to the CC graph to help debugging via CC logs, but it does not
+// traverse or unlink anything, so it is useless for anything else.
+#define NS_IMPL_CYCLE_COLLECTION_0(_class) \
+ NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_IMPL_CYCLE_COLLECTION(_class, ...) \
+ NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// If you are looking for NS_IMPL_CYCLE_COLLECTION_INHERITED_0(_class, _base)
+// you should instead not declare any cycle collected stuff in _class, so it
+// will just inherit the CC declarations from _base.
+
+#define NS_IMPL_CYCLE_COLLECTION_INHERITED(_class, _base, ...) \
+ NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_CYCLE_COLLECTION_NOTE_EDGE_NAME CycleCollectionNoteEdgeName
+
+#endif // nsCycleCollectionParticipant_h__
diff --git a/xpcom/glue/nsCycleCollectionTraversalCallback.h b/xpcom/glue/nsCycleCollectionTraversalCallback.h
new file mode 100644
index 000000000..9e314af9b
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionTraversalCallback.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCycleCollectionTraversalCallback_h__
+#define nsCycleCollectionTraversalCallback_h__
+
+#include "jspubtd.h"
+#include "js/HeapAPI.h"
+#include "nsISupports.h"
+
+class nsCycleCollectionParticipant;
+
+class NS_NO_VTABLE nsCycleCollectionTraversalCallback
+{
+public:
+ // You must call DescribeRefCountedNode() with an accurate
+ // refcount, otherwise cycle collection will fail, and probably crash.
+ // If the callback cares about objname, it should put
+ // WANT_DEBUG_INFO in mFlags.
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefcount,
+ const char* aObjName) = 0;
+ // Note, aCompartmentAddress is 0 if it is unknown.
+ NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked,
+ const char* aObjName,
+ uint64_t aCompartmentAddress = 0) = 0;
+
+ NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild) = 0;
+ NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aThing) = 0;
+ NS_IMETHOD_(void) NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aHelper) = 0;
+
+ // Give a name to the edge associated with the next call to
+ // NoteXPCOMChild, NoteJSObject, NoteJSScript, or NoteNativeChild.
+ // Callbacks who care about this should set WANT_DEBUG_INFO in the
+ // flags.
+ NS_IMETHOD_(void) NoteNextEdgeName(const char* aName) = 0;
+
+ enum
+ {
+ // Values for flags:
+
+ // Caller should call NoteNextEdgeName and pass useful objName
+ // to DescribeRefCountedNode and DescribeGCedNode.
+ WANT_DEBUG_INFO = (1 << 0),
+
+ // Caller should not skip objects that we know will be
+ // uncollectable.
+ WANT_ALL_TRACES = (1 << 1)
+ };
+ uint32_t Flags() const { return mFlags; }
+ bool WantDebugInfo() const { return (mFlags & WANT_DEBUG_INFO) != 0; }
+ bool WantAllTraces() const { return (mFlags & WANT_ALL_TRACES) != 0; }
+protected:
+ nsCycleCollectionTraversalCallback() : mFlags(0) {}
+
+ uint32_t mFlags;
+};
+
+#endif // nsCycleCollectionTraversalCallback_h__
diff --git a/xpcom/glue/nsDataHashtable.h b/xpcom/glue/nsDataHashtable.h
new file mode 100644
index 000000000..19c0728b4
--- /dev/null
+++ b/xpcom/glue/nsDataHashtable.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDataHashtable_h__
+#define nsDataHashtable_h__
+
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+#include "mozilla/Maybe.h"
+
+/**
+ * templated hashtable class maps keys to simple datatypes.
+ * See nsBaseHashtable for complete declaration
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the simple datatype being wrapped
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass, class DataType>
+class nsDataHashtable
+ : public nsBaseHashtable<KeyClass, DataType, DataType>
+{
+private:
+ typedef nsBaseHashtable<KeyClass, DataType, DataType> BaseClass;
+
+public:
+ using typename BaseClass::KeyType;
+ using typename BaseClass::EntryType;
+
+ nsDataHashtable() {}
+ explicit nsDataHashtable(uint32_t aInitLength)
+ : BaseClass(aInitLength)
+ {
+ }
+
+ /**
+ * Retrieve the value for a key and remove the corresponding entry at
+ * the same time.
+ *
+ * @param aKey the key to retrieve and remove
+ * @return the found value, or Nothing if no entry was found with the
+ * given key.
+ */
+ mozilla::Maybe<DataType> GetAndRemove(KeyType aKey)
+ {
+ mozilla::Maybe<DataType> value;
+ if (EntryType* ent = this->GetEntry(aKey)) {
+ value.emplace(mozilla::Move(ent->mData));
+ this->RemoveEntry(ent);
+ }
+ return value;
+ }
+};
+
+#endif // nsDataHashtable_h__
diff --git a/xpcom/glue/nsDebug.h b/xpcom/glue/nsDebug.h
new file mode 100644
index 000000000..7365f9ce3
--- /dev/null
+++ b/xpcom/glue/nsDebug.h
@@ -0,0 +1,460 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDebug_h___
+#define nsDebug_h___
+
+#include "nscore.h"
+#include "nsError.h"
+
+#include "nsXPCOM.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Likely.h"
+#include <stdarg.h>
+
+#ifdef DEBUG
+#include "prprf.h"
+#endif
+
+/**
+ * Warn if the given condition is true. The condition is evaluated in both
+ * release and debug builds, and the result is an expression which can be
+ * used in subsequent expressions, such as:
+ *
+ * if (NS_WARN_IF(NS_FAILED(rv)) {
+ * return rv;
+ * }
+ *
+ * This explicit warning and return is preferred to the NS_ENSURE_* macros
+ * which hide the warning and the return control flow.
+ *
+ * This macro can also be used outside of conditions just to issue a warning,
+ * like so:
+ *
+ * Unused << NS_WARN_IF(NS_FAILED(FnWithSideEffects());
+ *
+ * (The |Unused <<| is necessary because of the MOZ_MUST_USE annotation.)
+ *
+ * However, note that the argument to this macro is evaluated in all builds. If
+ * you just want a warning assertion, it is better to use NS_WARNING_ASSERTION
+ * (which evaluates the condition only in debug builds) like so:
+ *
+ * NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "operation failed");
+ *
+ * @note This is C++-only
+ */
+#ifdef __cplusplus
+#ifdef DEBUG
+inline MOZ_MUST_USE bool NS_warn_if_impl(bool aCondition, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (MOZ_UNLIKELY(aCondition)) {
+ NS_DebugBreak(NS_DEBUG_WARNING, nullptr, aExpr, aFile, aLine);
+ }
+ return aCondition;
+}
+#define NS_WARN_IF(condition) \
+ NS_warn_if_impl(condition, #condition, __FILE__, __LINE__)
+#else
+#define NS_WARN_IF(condition) (bool)(condition)
+#endif
+#endif
+
+/**
+ * Test an assertion for truth. If the expression is not true then
+ * emit a warning.
+ *
+ * Program execution continues past the usage of this macro.
+ *
+ * Note also that the non-debug version of this macro does <b>not</b>
+ * evaluate the message argument.
+ */
+#ifdef DEBUG
+#define NS_WARNING_ASSERTION(_expr, _msg) \
+ do { \
+ if (!(_expr)) { \
+ NS_DebugBreak(NS_DEBUG_WARNING, _msg, #_expr, __FILE__, __LINE__); \
+ } \
+ } while(0)
+#else
+#define NS_WARNING_ASSERTION(_expr, _msg) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Test an assertion for truth. If the expression is not true then
+ * trigger a program failure.
+ *
+ * Note that the non-debug version of this macro does <b>not</b>
+ * evaluate the message argument.
+ */
+#ifdef DEBUG
+inline void MOZ_PretendNoReturn()
+ MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS {}
+#define NS_ASSERTION(expr, str) \
+ do { \
+ if (!(expr)) { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, #expr, __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } \
+ } while(0)
+#else
+#define NS_ASSERTION(expr, str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * NS_PRECONDITION/POSTCONDITION are synonyms for NS_ASSERTION.
+ */
+#define NS_PRECONDITION(expr, str) NS_ASSERTION(expr, str)
+#define NS_POSTCONDITION(expr, str) NS_ASSERTION(expr, str)
+
+/**
+ * This macros triggers a program failure if executed. It indicates that
+ * an attempt was made to execute some unimplemented functionality.
+ */
+#ifdef DEBUG
+#define NS_NOTYETIMPLEMENTED(str) \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, "NotYetImplemented", __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_NOTYETIMPLEMENTED(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * This macros triggers a program failure if executed. It indicates that
+ * an attempt was made to execute a codepath which should not be reachable.
+ */
+#ifdef DEBUG
+#define NS_NOTREACHED(str) \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, "Not Reached", __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_NOTREACHED(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Log an error message.
+ */
+#ifdef DEBUG
+#define NS_ERROR(str) \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, "Error", __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_ERROR(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Log a warning message.
+ */
+#ifdef DEBUG
+#define NS_WARNING(str) \
+ NS_DebugBreak(NS_DEBUG_WARNING, str, nullptr, __FILE__, __LINE__)
+#else
+#define NS_WARNING(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Trigger an debug-only abort.
+ *
+ * @see NS_RUNTIMEABORT for release-mode asserts.
+ */
+#ifdef DEBUG
+#define NS_ABORT() \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_ABORT() do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Trigger a debugger breakpoint, only in debug builds.
+ */
+#ifdef DEBUG
+#define NS_BREAK() \
+ do { \
+ NS_DebugBreak(NS_DEBUG_BREAK, nullptr, nullptr, __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_BREAK() do { /* nothing */ } while(0)
+#endif
+
+/******************************************************************************
+** Macros for static assertions. These are used by the sixgill tool.
+** When the tool is not running these macros are no-ops.
+******************************************************************************/
+
+/* Avoid name collision if included with other headers defining annotations. */
+#ifndef HAVE_STATIC_ANNOTATIONS
+#define HAVE_STATIC_ANNOTATIONS
+
+#ifdef XGILL_PLUGIN
+
+#define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND)))
+#define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND)))
+#define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND)))
+#define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND)))
+#define STATIC_INVARIANT(COND) __attribute__((invariant(#COND)))
+#define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND)))
+
+/* Used to make identifiers for assert/assume annotations in a function. */
+#define STATIC_PASTE2(X,Y) X ## Y
+#define STATIC_PASTE1(X,Y) STATIC_PASTE2(X,Y)
+
+#define STATIC_ASSUME(COND) \
+ do { \
+ __attribute__((assume_static(#COND), unused)) \
+ int STATIC_PASTE1(assume_static_, __COUNTER__); \
+ } while(0)
+
+#define STATIC_ASSERT_RUNTIME(COND) \
+ do { \
+ __attribute__((assert_static_runtime(#COND), unused)) \
+ int STATIC_PASTE1(assert_static_runtime_, __COUNTER__); \
+ } while(0)
+
+#else /* XGILL_PLUGIN */
+
+#define STATIC_PRECONDITION(COND) /* nothing */
+#define STATIC_PRECONDITION_ASSUME(COND) /* nothing */
+#define STATIC_POSTCONDITION(COND) /* nothing */
+#define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */
+#define STATIC_INVARIANT(COND) /* nothing */
+#define STATIC_INVARIANT_ASSUME(COND) /* nothing */
+
+#define STATIC_ASSUME(COND) do { /* nothing */ } while(0)
+#define STATIC_ASSERT_RUNTIME(COND) do { /* nothing */ } while(0)
+
+#endif /* XGILL_PLUGIN */
+
+#define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference())
+
+#endif /* HAVE_STATIC_ANNOTATIONS */
+
+/******************************************************************************
+** Macros for terminating execution when an unrecoverable condition is
+** reached. These need to be compiled regardless of the DEBUG flag.
+******************************************************************************/
+
+/**
+ * Terminate execution <i>immediately</i>, and if possible on the current
+ * platform, in such a way that execution can't be continued by other
+ * code (e.g., by intercepting a signal).
+ */
+#define NS_RUNTIMEABORT(msg) \
+ NS_DebugBreak(NS_DEBUG_ABORT, msg, nullptr, __FILE__, __LINE__)
+
+
+/* Macros for checking the trueness of an expression passed in within an
+ * interface implementation. These need to be compiled regardless of the
+ * DEBUG flag. New code should use NS_WARN_IF(condition) instead!
+ * @status deprecated
+ */
+
+#define NS_ENSURE_TRUE(x, ret) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
+ return ret; \
+ } \
+ } while(0)
+
+#define NS_ENSURE_FALSE(x, ret) \
+ NS_ENSURE_TRUE(!(x), ret)
+
+#define NS_ENSURE_TRUE_VOID(x) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
+ return; \
+ } \
+ } while(0)
+
+#define NS_ENSURE_FALSE_VOID(x) \
+ NS_ENSURE_TRUE_VOID(!(x))
+
+/******************************************************************************
+** Macros for checking results
+******************************************************************************/
+
+#if defined(DEBUG) && !defined(XPCOM_GLUE_AVOID_NSPR)
+
+#define NS_ENSURE_SUCCESS_BODY(res, ret) \
+ char *msg = PR_smprintf("NS_ENSURE_SUCCESS(%s, %s) failed with " \
+ "result 0x%X", #res, #ret, __rv); \
+ NS_WARNING(msg); \
+ PR_smprintf_free(msg);
+
+#define NS_ENSURE_SUCCESS_BODY_VOID(res) \
+ char *msg = PR_smprintf("NS_ENSURE_SUCCESS_VOID(%s) failed with " \
+ "result 0x%X", #res, __rv); \
+ NS_WARNING(msg); \
+ PR_smprintf_free(msg);
+
+#else
+
+#define NS_ENSURE_SUCCESS_BODY(res, ret) \
+ NS_WARNING("NS_ENSURE_SUCCESS(" #res ", " #ret ") failed");
+
+#define NS_ENSURE_SUCCESS_BODY_VOID(res) \
+ NS_WARNING("NS_ENSURE_SUCCESS_VOID(" #res ") failed");
+
+#endif
+
+#define NS_ENSURE_SUCCESS(res, ret) \
+ do { \
+ nsresult __rv = res; /* Don't evaluate |res| more than once */ \
+ if (NS_FAILED(__rv)) { \
+ NS_ENSURE_SUCCESS_BODY(res, ret) \
+ return ret; \
+ } \
+ } while(0)
+
+#define NS_ENSURE_SUCCESS_VOID(res) \
+ do { \
+ nsresult __rv = res; \
+ if (NS_FAILED(__rv)) { \
+ NS_ENSURE_SUCCESS_BODY_VOID(res) \
+ return; \
+ } \
+ } while(0)
+
+/******************************************************************************
+** Macros for checking state and arguments upon entering interface boundaries
+******************************************************************************/
+
+#define NS_ENSURE_ARG(arg) \
+ NS_ENSURE_TRUE(arg, NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_ARG_POINTER(arg) \
+ NS_ENSURE_TRUE(arg, NS_ERROR_INVALID_POINTER)
+
+#define NS_ENSURE_ARG_MIN(arg, min) \
+ NS_ENSURE_TRUE((arg) >= min, NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_ARG_MAX(arg, max) \
+ NS_ENSURE_TRUE((arg) <= max, NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_ARG_RANGE(arg, min, max) \
+ NS_ENSURE_TRUE(((arg) >= min) && ((arg) <= max), NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_STATE(state) \
+ NS_ENSURE_TRUE(state, NS_ERROR_UNEXPECTED)
+
+#define NS_ENSURE_NO_AGGREGATION(outer) \
+ NS_ENSURE_FALSE(outer, NS_ERROR_NO_AGGREGATION)
+
+/*****************************************************************************/
+
+#if (defined(DEBUG) || (defined(NIGHTLY_BUILD) && !defined(MOZ_PROFILING))) && !defined(XPCOM_GLUE_AVOID_NSPR)
+ #define MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED 1
+#endif
+
+#ifdef XPCOM_GLUE
+ #define NS_CheckThreadSafe(owningThread, msg)
+#else
+ #define NS_CheckThreadSafe(owningThread, msg) \
+ if (MOZ_UNLIKELY(owningThread != PR_GetCurrentThread())) { \
+ MOZ_CRASH(msg); \
+ }
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+void NS_ABORT_OOM(size_t aSize);
+#else
+inline void NS_ABORT_OOM(size_t)
+{
+ MOZ_CRASH();
+}
+#endif
+
+typedef void (*StderrCallback)(const char* aFmt, va_list aArgs);
+/* When compiling the XPCOM Glue on Windows, we pretend that it's going to
+ * be linked with a static CRT (-MT) even when it's not. This means that we
+ * cannot link to data exports from the CRT, only function exports. So,
+ * instead of referencing "stderr" directly, use fdopen.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * printf_stderr(...) is much like fprintf(stderr, ...), except that:
+ * - it calls the callback set through set_stderr_callback
+ * - on Android and Firefox OS, *instead* of printing to stderr, it
+ * prints to logcat. (Newlines in the string lead to multiple lines
+ * of logcat, but each function call implicitly completes a line even
+ * if the string does not end with a newline.)
+ * - on Windows, if a debugger is present, it calls OutputDebugString
+ * in *addition* to writing to stderr
+ */
+void printf_stderr(const char* aFmt, ...) MOZ_FORMAT_PRINTF(1, 2);
+
+/**
+ * Same as printf_stderr, but taking va_list instead of varargs
+ */
+void vprintf_stderr(const char* aFmt, va_list aArgs);
+
+/**
+ * fprintf_stderr is like fprintf, except that if its file argument
+ * is stderr, it invokes printf_stderr instead.
+ *
+ * This is useful for general debugging code that logs information to a
+ * file, but that you would like to be useful on Android and Firefox OS.
+ * If you use fprintf_stderr instead of fprintf in such debugging code,
+ * then callers can pass stderr to get logging that works on Android and
+ * Firefox OS (and also the other side-effects of using printf_stderr).
+ *
+ * Code that is structured this way needs to be careful not to split a
+ * line of output across multiple calls to fprintf_stderr, since doing
+ * so will cause it to appear in multiple lines in logcat output.
+ * (Producing multiple lines at once is fine.)
+ */
+void fprintf_stderr(FILE* aFile, const char* aFmt, ...) MOZ_FORMAT_PRINTF(2, 3);
+
+// used by the profiler to log stderr in the profiler for more
+// advanced performance debugging and display/layers visualization.
+void set_stderr_callback(StderrCallback aCallback);
+
+#if defined(ANDROID) && !defined(RELEASE_OR_BETA)
+// Call this if you want a copy of stderr logging sent to a file. This is
+// useful to get around logcat overflow problems on android devices, which use
+// a circular logcat buffer and can intermittently drop messages if there's too
+// much spew.
+//
+// This is intended for local debugging only, DO NOT USE IN PRODUCTION CODE.
+// (This is ifndef RELEASE_OR_BETA to catch uses of it that accidentally get
+// checked in). Using this will also prevent the profiler from getting a copy of
+// the stderr messages which it uses for various visualization features.
+//
+// This function can be called from any thread, but if it is called multiple
+// times all invocations must be on the same thread. Invocations after the
+// first one are ignored, so you can safely put it inside a loop, for example.
+// Once this is called there is no way to turn it off; all stderr output from
+// that point forward will go to the file. Note that the output is subject to
+// buffering so make sure you have enough output to flush the messages you care
+// about before you terminate the process.
+//
+// The file passed in should be writable, so on Android devices a path like
+// "/data/local/tmp/blah" is a good one to use as it is world-writable and will
+// work even in B2G child processes which have reduced privileges. Note that the
+// actual file created will have the PID appended to the path you pass in, so
+// that on B2G the output from each process goes to a separate file.
+void copy_stderr_to_file(const char* aFile);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* nsDebug_h___ */
diff --git a/xpcom/glue/nsDeque.cpp b/xpcom/glue/nsDeque.cpp
new file mode 100644
index 000000000..f9eb18b40
--- /dev/null
+++ b/xpcom/glue/nsDeque.cpp
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDeque.h"
+#include "nsISupportsImpl.h"
+#include <string.h>
+#ifdef DEBUG_rickg
+#include <stdio.h>
+#endif
+
+#include "mozilla/CheckedInt.h"
+
+#define modulus(x,y) ((x)%(y))
+
+/**
+ * Standard constructor
+ * @param deallocator, called by Erase and ~nsDeque
+ */
+nsDeque::nsDeque(nsDequeFunctor* aDeallocator)
+{
+ MOZ_COUNT_CTOR(nsDeque);
+ mDeallocator = aDeallocator;
+ mOrigin = mSize = 0;
+ mData = mBuffer; // don't allocate space until you must
+ mCapacity = sizeof(mBuffer) / sizeof(mBuffer[0]);
+ memset(mData, 0, mCapacity * sizeof(mBuffer[0]));
+}
+
+/**
+ * Destructor
+ */
+nsDeque::~nsDeque()
+{
+ MOZ_COUNT_DTOR(nsDeque);
+
+#ifdef DEBUG_rickg
+ char buffer[30];
+ printf("Capacity: %i\n", mCapacity);
+
+ static int mCaps[15] = {0};
+ switch (mCapacity) {
+ case 4: mCaps[0]++; break;
+ case 8: mCaps[1]++; break;
+ case 16: mCaps[2]++; break;
+ case 32: mCaps[3]++; break;
+ case 64: mCaps[4]++; break;
+ case 128: mCaps[5]++; break;
+ case 256: mCaps[6]++; break;
+ case 512: mCaps[7]++; break;
+ case 1024: mCaps[8]++; break;
+ case 2048: mCaps[9]++; break;
+ case 4096: mCaps[10]++; break;
+ default:
+ break;
+ }
+#endif
+
+ Erase();
+ if (mData && mData != mBuffer) {
+ free(mData);
+ }
+ mData = 0;
+ SetDeallocator(0);
+}
+
+size_t
+nsDeque::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ size_t size = 0;
+ if (mData != mBuffer) {
+ size += aMallocSizeOf(mData);
+ }
+
+ if (mDeallocator) {
+ size += aMallocSizeOf(mDeallocator);
+ }
+
+ return size;
+}
+
+size_t
+nsDeque::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+/**
+ * Set the functor to be called by Erase()
+ * The deque owns the functor.
+ *
+ * @param aDeallocator functor object for use by Erase()
+ */
+void
+nsDeque::SetDeallocator(nsDequeFunctor* aDeallocator)
+{
+ delete mDeallocator;
+ mDeallocator = aDeallocator;
+}
+
+/**
+ * Remove all items from container without destroying them.
+ */
+void
+nsDeque::Empty()
+{
+ if (mSize && mData) {
+ memset(mData, 0, mCapacity * sizeof(*mData));
+ }
+ mSize = 0;
+ mOrigin = 0;
+}
+
+/**
+ * Remove and delete all items from container
+ */
+void
+nsDeque::Erase()
+{
+ if (mDeallocator && mSize) {
+ ForEach(*mDeallocator);
+ }
+ Empty();
+}
+
+/**
+ * This method quadruples the size of the deque
+ * Elements in the deque are resequenced so that elements
+ * in the deque are stored sequentially
+ *
+ * @return whether growing succeeded
+ */
+bool
+nsDeque::GrowCapacity()
+{
+ mozilla::CheckedInt<size_t> newCapacity = mCapacity;
+ newCapacity *= 4;
+
+ NS_ASSERTION(newCapacity.isValid(), "Overflow");
+ if (!newCapacity.isValid()) {
+ return false;
+ }
+
+ // Sanity check the new byte size.
+ mozilla::CheckedInt<size_t> newByteSize = newCapacity;
+ newByteSize *= sizeof(void*);
+
+ NS_ASSERTION(newByteSize.isValid(), "Overflow");
+ if (!newByteSize.isValid()) {
+ return false;
+ }
+
+ void** temp = (void**)malloc(newByteSize.value());
+ if (!temp) {
+ return false;
+ }
+
+ //Here's the interesting part: You can't just move the elements
+ //directly (in situ) from the old buffer to the new one.
+ //Since capacity has changed, the old origin doesn't make
+ //sense anymore. It's better to resequence the elements now.
+
+ memcpy(temp, mData + mOrigin, sizeof(void*) * (mCapacity - mOrigin));
+ memcpy(temp + (mCapacity - mOrigin), mData, sizeof(void*) * mOrigin);
+
+ if (mData != mBuffer) {
+ free(mData);
+ }
+
+ mCapacity = newCapacity.value();
+ mOrigin = 0; //now realign the origin...
+ mData = temp;
+
+ return true;
+}
+
+/**
+ * This method adds an item to the end of the deque.
+ * This operation has the potential to cause the
+ * underlying buffer to resize.
+ *
+ * @param aItem: new item to be added to deque
+ */
+bool
+nsDeque::Push(void* aItem, const fallible_t&)
+{
+ if (mSize == mCapacity && !GrowCapacity()) {
+ return false;
+ }
+ mData[modulus(mOrigin + mSize, mCapacity)] = aItem;
+ mSize++;
+ return true;
+}
+
+/**
+ * This method adds an item to the front of the deque.
+ * This operation has the potential to cause the
+ * underlying buffer to resize.
+ *
+ * --Commments for GrowCapacity() case
+ * We've grown and shifted which means that the old
+ * final element in the deque is now the first element
+ * in the deque. This is temporary.
+ * We haven't inserted the new element at the front.
+ *
+ * To continue with the idea of having the front at zero
+ * after a grow, we move the old final item (which through
+ * the voodoo of mOrigin-- is now the first) to its final
+ * position which is conveniently the old length.
+ *
+ * Note that this case only happens when the deque is full.
+ * [And that pieces of this magic only work if the deque is full.]
+ * picture:
+ * [ABCDEFGH] @[mOrigin:3]:D.
+ * Task: PushFront("Z")
+ * shift mOrigin so, @[mOrigin:2]:C
+ * stretch and rearrange: (mOrigin:0)
+ * [CDEFGHAB ________ ________ ________]
+ * copy: (The second C is currently out of bounds)
+ * [CDEFGHAB C_______ ________ ________]
+ * later we will insert Z:
+ * [ZDEFGHAB C_______ ________ ________]
+ * and increment size: 9. (C is no longer out of bounds)
+ * --
+ * @param aItem: new item to be added to deque
+ */
+bool
+nsDeque::PushFront(void* aItem, const fallible_t&)
+{
+
+ if (mOrigin == 0) {
+ mOrigin = mCapacity - 1;
+ } else {
+ mOrigin--;
+ }
+
+ if (mSize == mCapacity) {
+ if (!GrowCapacity()) {
+ return false;
+ }
+ /* Comments explaining this are above*/
+ mData[mSize] = mData[mOrigin];
+ }
+ mData[mOrigin] = aItem;
+ mSize++;
+ return true;
+}
+
+/**
+ * Remove and return the last item in the container.
+ *
+ * @return ptr to last item in container
+ */
+void*
+nsDeque::Pop()
+{
+ void* result = 0;
+ if (mSize > 0) {
+ --mSize;
+ size_t offset = modulus(mSize + mOrigin, mCapacity);
+ result = mData[offset];
+ mData[offset] = 0;
+ if (!mSize) {
+ mOrigin = 0;
+ }
+ }
+ return result;
+}
+
+/**
+ * This method gets called you want to remove and return
+ * the first member in the container.
+ *
+ * @return last item in container
+ */
+void*
+nsDeque::PopFront()
+{
+ void* result = 0;
+ if (mSize > 0) {
+ NS_ASSERTION(mOrigin < mCapacity, "Error: Bad origin");
+ result = mData[mOrigin];
+ mData[mOrigin++] = 0; //zero it out for debugging purposes.
+ mSize--;
+ // Cycle around if we pop off the end
+ // and reset origin if when we pop the last element
+ if (mCapacity == mOrigin || !mSize) {
+ mOrigin = 0;
+ }
+ }
+ return result;
+}
+
+/**
+ * This method gets called you want to peek at the bottom
+ * member without removing it.
+ *
+ * @return last item in container
+ */
+void*
+nsDeque::Peek() const
+{
+ void* result = 0;
+ if (mSize > 0) {
+ result = mData[modulus(mSize - 1 + mOrigin, mCapacity)];
+ }
+ return result;
+}
+
+/**
+ * This method gets called you want to peek at the topmost
+ * member without removing it.
+ *
+ * @return last item in container
+ */
+void*
+nsDeque::PeekFront() const
+{
+ void* result = 0;
+ if (mSize > 0) {
+ result = mData[mOrigin];
+ }
+ return result;
+}
+
+/**
+ * Call this to retrieve the ith element from this container.
+ * Keep in mind that accessing the underlying elements is
+ * done in a relative fashion. Object 0 is not necessarily
+ * the first element (the first element is at mOrigin).
+ *
+ * @param aIndex : 0 relative offset of item you want
+ * @return void* or null
+ */
+void*
+nsDeque::ObjectAt(size_t aIndex) const
+{
+ void* result = 0;
+ if (aIndex < mSize) {
+ result = mData[modulus(mOrigin + aIndex, mCapacity)];
+ }
+ return result;
+}
+
+/**
+ * Call this method when you want to iterate all the
+ * members of the container, passing a functor along
+ * to call your code.
+ *
+ * @param aFunctor object to call for each member
+ * @return *this
+ */
+void
+nsDeque::ForEach(nsDequeFunctor& aFunctor) const
+{
+ for (size_t i = 0; i < mSize; ++i) {
+ aFunctor(ObjectAt(i));
+ }
+}
diff --git a/xpcom/glue/nsDeque.h b/xpcom/glue/nsDeque.h
new file mode 100644
index 000000000..ace7607d3
--- /dev/null
+++ b/xpcom/glue/nsDeque.h
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * MODULE NOTES:
+ *
+ * The Deque is a very small, very efficient container object
+ * than can hold elements of type void*, offering the following features:
+ * Its interface supports pushing and popping of elements.
+ * It can iterate (via an interator class) its elements.
+ * When full, it can efficiently resize dynamically.
+ *
+ *
+ * NOTE: The only bit of trickery here is that this deque is
+ * built upon a ring-buffer. Like all ring buffers, the first
+ * element may not be at index[0]. The mOrigin member determines
+ * where the first child is. This point is quietly hidden from
+ * customers of this class.
+ *
+ */
+
+#ifndef _NSDEQUE
+#define _NSDEQUE
+
+#include "nscore.h"
+#include "nsDebug.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/fallible.h"
+#include "mozilla/MemoryReporting.h"
+
+/**
+ * The nsDequeFunctor class is used when you want to create
+ * callbacks between the deque and your generic code.
+ * Use these objects in a call to ForEach();
+ *
+ */
+
+class nsDequeFunctor
+{
+public:
+ virtual void* operator()(void* aObject) = 0;
+ virtual ~nsDequeFunctor() {}
+};
+
+/******************************************************
+ * Here comes the nsDeque class itself...
+ ******************************************************/
+
+/**
+ * The deque (double-ended queue) class is a common container type,
+ * whose behavior mimics a line in your favorite checkout stand.
+ * Classic CS describes the common behavior of a queue as FIFO.
+ * A deque allows insertion and removal at both ends of
+ * the container.
+ *
+ * The deque stores pointers to items.
+ */
+
+class nsDequeIterator;
+
+class nsDeque
+{
+ typedef mozilla::fallible_t fallible_t;
+public:
+ explicit nsDeque(nsDequeFunctor* aDeallocator = nullptr);
+ ~nsDeque();
+
+ /**
+ * Returns the number of elements currently stored in
+ * this deque.
+ *
+ * @return number of elements currently in the deque
+ */
+ inline size_t GetSize() const { return mSize; }
+
+ /**
+ * Appends new member at the end of the deque.
+ *
+ * @param item to store in deque
+ */
+ void Push(void* aItem)
+ {
+ if (!Push(aItem, mozilla::fallible)) {
+ NS_ABORT_OOM(mSize * sizeof(void*));
+ }
+ }
+
+ MOZ_MUST_USE bool Push(void* aItem, const fallible_t&);
+
+ /**
+ * Inserts new member at the front of the deque.
+ *
+ * @param item to store in deque
+ */
+ void PushFront(void* aItem)
+ {
+ if (!PushFront(aItem, mozilla::fallible)) {
+ NS_ABORT_OOM(mSize * sizeof(void*));
+ }
+ }
+
+ MOZ_MUST_USE bool PushFront(void* aItem, const fallible_t&);
+
+ /**
+ * Remove and return the last item in the container.
+ *
+ * @return the item that was the last item in container
+ */
+ void* Pop();
+
+ /**
+ * Remove and return the first item in the container.
+ *
+ * @return the item that was first item in container
+ */
+ void* PopFront();
+
+ /**
+ * Retrieve the bottom item without removing it.
+ *
+ * @return the first item in container
+ */
+
+ void* Peek() const;
+ /**
+ * Return topmost item without removing it.
+ *
+ * @return the first item in container
+ */
+ void* PeekFront() const;
+
+ /**
+ * Retrieve a member from the deque without removing it.
+ *
+ * @param index of desired item
+ * @return element in list
+ */
+ void* ObjectAt(size_t aIndex) const;
+
+ /**
+ * Remove and delete all items from container.
+ * Deletes are handled by the deallocator nsDequeFunctor
+ * which is specified at deque construction.
+ */
+ void Erase();
+
+ /**
+ * Call this method when you want to iterate all the
+ * members of the container, passing a functor along
+ * to call your code.
+ *
+ * @param aFunctor object to call for each member
+ */
+ void ForEach(nsDequeFunctor& aFunctor) const;
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+protected:
+ size_t mSize;
+ size_t mCapacity;
+ size_t mOrigin;
+ nsDequeFunctor* mDeallocator;
+ void* mBuffer[8];
+ void** mData;
+
+private:
+
+ /**
+ * Copy constructor (PRIVATE)
+ *
+ * @param aOther another deque
+ */
+ nsDeque(const nsDeque& aOther);
+
+ /**
+ * Deque assignment operator (PRIVATE)
+ *
+ * @param aOther another deque
+ * @return *this
+ */
+ nsDeque& operator=(const nsDeque& aOther);
+
+ bool GrowCapacity();
+ void SetDeallocator(nsDequeFunctor* aDeallocator);
+
+ /**
+ * Remove all items from container without destroying them.
+ */
+ void Empty();
+};
+#endif
diff --git a/xpcom/glue/nsEnumeratorUtils.cpp b/xpcom/glue/nsEnumeratorUtils.cpp
new file mode 100644
index 000000000..d1843a78a
--- /dev/null
+++ b/xpcom/glue/nsEnumeratorUtils.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Attributes.h"
+
+#include "nsEnumeratorUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class EmptyEnumeratorImpl
+ : public nsISimpleEnumerator
+ , public nsIUTF8StringEnumerator
+ , public nsIStringEnumerator
+{
+public:
+ EmptyEnumeratorImpl() {}
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS_INHERITED // not really inherited, but no mRefCnt
+
+ // nsISimpleEnumerator
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+ // can't use NS_DECL_NSISTRINGENUMERATOR because they share the
+ // HasMore() signature
+ NS_IMETHOD GetNext(nsAString& aResult) override;
+
+ static EmptyEnumeratorImpl* GetInstance()
+ {
+ static const EmptyEnumeratorImpl kInstance;
+ return const_cast<EmptyEnumeratorImpl*>(&kInstance);
+ }
+};
+
+// nsISupports interface
+NS_IMETHODIMP_(MozExternalRefCountType)
+EmptyEnumeratorImpl::AddRef(void)
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+EmptyEnumeratorImpl::Release(void)
+{
+ return 1;
+}
+
+NS_IMPL_QUERY_INTERFACE(EmptyEnumeratorImpl, nsISimpleEnumerator,
+ nsIUTF8StringEnumerator, nsIStringEnumerator)
+
+// nsISimpleEnumerator interface
+NS_IMETHODIMP
+EmptyEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::HasMore(bool* aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::GetNext(nsACString& aResult)
+{
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::GetNext(nsAString& aResult)
+{
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+NS_NewEmptyEnumerator(nsISimpleEnumerator** aResult)
+{
+ *aResult = EmptyEnumeratorImpl::GetInstance();
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class nsSingletonEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods
+ NS_IMETHOD HasMoreElements(bool* aResult) override;
+ NS_IMETHOD GetNext(nsISupports** aResult) override;
+
+ explicit nsSingletonEnumerator(nsISupports* aValue);
+
+private:
+ ~nsSingletonEnumerator();
+
+protected:
+ nsCOMPtr<nsISupports> mValue;
+ bool mConsumed;
+};
+
+nsSingletonEnumerator::nsSingletonEnumerator(nsISupports* aValue)
+ : mValue(aValue)
+{
+ mConsumed = (mValue ? false : true);
+}
+
+nsSingletonEnumerator::~nsSingletonEnumerator()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsSingletonEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsSingletonEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aResult = !mConsumed;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsSingletonEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (mConsumed) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mConsumed = true;
+
+ *aResult = mValue;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewSingletonEnumerator(nsISimpleEnumerator** aResult,
+ nsISupports* aSingleton)
+{
+ RefPtr<nsSingletonEnumerator> enumer = new nsSingletonEnumerator(aSingleton);
+ enumer.forget(aResult);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class nsUnionEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods
+ NS_IMETHOD HasMoreElements(bool* aResult) override;
+ NS_IMETHOD GetNext(nsISupports** aResult) override;
+
+ nsUnionEnumerator(nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator);
+
+private:
+ ~nsUnionEnumerator();
+
+protected:
+ nsCOMPtr<nsISimpleEnumerator> mFirstEnumerator, mSecondEnumerator;
+ bool mConsumed;
+ bool mAtSecond;
+};
+
+nsUnionEnumerator::nsUnionEnumerator(nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator)
+ : mFirstEnumerator(aFirstEnumerator)
+ , mSecondEnumerator(aSecondEnumerator)
+ , mConsumed(false)
+ , mAtSecond(false)
+{
+}
+
+nsUnionEnumerator::~nsUnionEnumerator()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsUnionEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsUnionEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv;
+
+ if (mConsumed) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ if (!mAtSecond) {
+ rv = mFirstEnumerator->HasMoreElements(aResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ mAtSecond = true;
+ }
+
+ rv = mSecondEnumerator->HasMoreElements(aResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ *aResult = false;
+ mConsumed = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUnionEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (mConsumed) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!mAtSecond) {
+ return mFirstEnumerator->GetNext(aResult);
+ }
+
+ return mSecondEnumerator->GetNext(aResult);
+}
+
+nsresult
+NS_NewUnionEnumerator(nsISimpleEnumerator** aResult,
+ nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator)
+{
+ *aResult = nullptr;
+ if (!aFirstEnumerator) {
+ *aResult = aSecondEnumerator;
+ } else if (!aSecondEnumerator) {
+ *aResult = aFirstEnumerator;
+ } else {
+ nsUnionEnumerator* enumer = new nsUnionEnumerator(aFirstEnumerator,
+ aSecondEnumerator);
+ if (!enumer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *aResult = enumer;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsEnumeratorUtils.h b/xpcom/glue/nsEnumeratorUtils.h
new file mode 100644
index 000000000..f7a0db099
--- /dev/null
+++ b/xpcom/glue/nsEnumeratorUtils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsEnumeratorUtils_h__
+#define nsEnumeratorUtils_h__
+
+#include "nscore.h"
+
+class nsISupports;
+class nsISimpleEnumerator;
+
+nsresult NS_NewEmptyEnumerator(nsISimpleEnumerator** aResult);
+
+nsresult NS_NewSingletonEnumerator(nsISimpleEnumerator** aResult,
+ nsISupports* aSingleton);
+
+nsresult NS_NewUnionEnumerator(nsISimpleEnumerator** aResult,
+ nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator);
+
+#endif /* nsEnumeratorUtils_h__ */
diff --git a/xpcom/glue/nsHashKeys.h b/xpcom/glue/nsHashKeys.h
new file mode 100644
index 000000000..9c688691f
--- /dev/null
+++ b/xpcom/glue/nsHashKeys.h
@@ -0,0 +1,660 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTHashKeys_h__
+#define nsTHashKeys_h__
+
+#include "nsID.h"
+#include "nsISupports.h"
+#include "nsIHashable.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "PLDHashTable.h"
+#include <new>
+
+#include "nsStringGlue.h"
+#include "nsCRTGlue.h"
+#include "nsUnicharUtils.h"
+#include "nsPointerHashKeys.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+
+// These are defined analogously to the HashString overloads in mfbt.
+
+inline uint32_t
+HashString(const nsAString& aStr)
+{
+ return HashString(aStr.BeginReading(), aStr.Length());
+}
+
+inline uint32_t
+HashString(const nsACString& aStr)
+{
+ return HashString(aStr.BeginReading(), aStr.Length());
+}
+
+} // namespace mozilla
+
+/** @file nsHashKeys.h
+ * standard HashKey classes for nsBaseHashtable and relatives. Each of these
+ * classes follows the nsTHashtable::EntryType specification
+ *
+ * Lightweight keytypes provided here:
+ * nsStringHashKey
+ * nsCStringHashKey
+ * nsUint32HashKey
+ * nsUint64HashKey
+ * nsFloatHashKey
+ * nsPtrHashKey
+ * nsClearingPtrHashKey
+ * nsVoidPtrHashKey
+ * nsClearingVoidPtrHashKey
+ * nsISupportsHashKey
+ * nsIDHashKey
+ * nsDepCharHashKey
+ * nsCharPtrHashKey
+ * nsUnicharPtrHashKey
+ * nsHashableHashKey
+ * nsGenericHashKey
+ */
+
+/**
+ * hashkey wrapper using nsAString KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsStringHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsAString& KeyType;
+ typedef const nsAString* KeyTypePointer;
+
+ explicit nsStringHashKey(KeyTypePointer aStr) : mStr(*aStr) {}
+ nsStringHashKey(const nsStringHashKey& aToCopy) : mStr(aToCopy.mStr) {}
+ ~nsStringHashKey() {}
+
+ KeyType GetKey() const { return mStr; }
+ bool KeyEquals(const KeyTypePointer aKey) const
+ {
+ return mStr.Equals(*aKey);
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey)
+ {
+ return mozilla::HashString(*aKey);
+ }
+
+#ifdef MOZILLA_INTERNAL_API
+ // To avoid double-counting, only measure the string if it is unshared.
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+#endif
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const nsString mStr;
+};
+
+#ifdef MOZILLA_INTERNAL_API
+
+/**
+ * hashkey wrapper using nsAString KeyType
+ *
+ * This is internal-API only because nsCaseInsensitiveStringComparator is
+ * internal-only.
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsStringCaseInsensitiveHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsAString& KeyType;
+ typedef const nsAString* KeyTypePointer;
+
+ explicit nsStringCaseInsensitiveHashKey(KeyTypePointer aStr)
+ : mStr(*aStr)
+ {
+ // take it easy just deal HashKey
+ }
+ nsStringCaseInsensitiveHashKey(const nsStringCaseInsensitiveHashKey& aToCopy)
+ : mStr(aToCopy.mStr)
+ {
+ }
+ ~nsStringCaseInsensitiveHashKey() {}
+
+ KeyType GetKey() const { return mStr; }
+ bool KeyEquals(const KeyTypePointer aKey) const
+ {
+ return mStr.Equals(*aKey, nsCaseInsensitiveStringComparator());
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey)
+ {
+ nsAutoString tmKey(*aKey);
+ ToLowerCase(tmKey);
+ return mozilla::HashString(tmKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+ // To avoid double-counting, only measure the string if it is unshared.
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+private:
+ const nsString mStr;
+};
+
+#endif
+
+/**
+ * hashkey wrapper using nsACString KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsCStringHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsACString& KeyType;
+ typedef const nsACString* KeyTypePointer;
+
+ explicit nsCStringHashKey(const nsACString* aStr) : mStr(*aStr) {}
+ nsCStringHashKey(const nsCStringHashKey& aToCopy) : mStr(aToCopy.mStr) {}
+ ~nsCStringHashKey() {}
+
+ KeyType GetKey() const { return mStr; }
+ bool KeyEquals(KeyTypePointer aKey) const { return mStr.Equals(*aKey); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return mozilla::HashString(*aKey);
+ }
+
+#ifdef MOZILLA_INTERNAL_API
+ // To avoid double-counting, only measure the string if it is unshared.
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+#endif
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const nsCString mStr;
+};
+
+/**
+ * hashkey wrapper using uint32_t KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsUint32HashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const uint32_t& KeyType;
+ typedef const uint32_t* KeyTypePointer;
+
+ explicit nsUint32HashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ nsUint32HashKey(const nsUint32HashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~nsUint32HashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey) { return *aKey; }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const uint32_t mValue;
+};
+
+/**
+ * hashkey wrapper using uint64_t KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsUint64HashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const uint64_t& KeyType;
+ typedef const uint64_t* KeyTypePointer;
+
+ explicit nsUint64HashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ nsUint64HashKey(const nsUint64HashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~nsUint64HashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return PLDHashNumber(*aKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const uint64_t mValue;
+};
+
+/**
+ * hashkey wrapper using float KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsFloatHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const float& KeyType;
+ typedef const float* KeyTypePointer;
+
+ explicit nsFloatHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ nsFloatHashKey(const nsFloatHashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~nsFloatHashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return *reinterpret_cast<const uint32_t*>(aKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const float mValue;
+};
+
+/**
+ * hashkey wrapper using nsISupports* KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsISupportsHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef nsISupports* KeyType;
+ typedef const nsISupports* KeyTypePointer;
+
+ explicit nsISupportsHashKey(const nsISupports* aKey)
+ : mSupports(const_cast<nsISupports*>(aKey))
+ {
+ }
+ nsISupportsHashKey(const nsISupportsHashKey& aToCopy)
+ : mSupports(aToCopy.mSupports)
+ {
+ }
+ ~nsISupportsHashKey() {}
+
+ KeyType GetKey() const { return mSupports; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey == mSupports; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ nsCOMPtr<nsISupports> mSupports;
+};
+
+/**
+ * hashkey wrapper using refcounted * KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+template<class T>
+class nsRefPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef T* KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsRefPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
+ nsRefPtrHashKey(const nsRefPtrHashKey& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsRefPtrHashKey() {}
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ RefPtr<T> mKey;
+};
+
+template<class T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsRefPtrHashKey<T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.GetKey(), aName, aFlags);
+}
+
+/**
+ * hashkey wrapper using T* KeyType that sets key to nullptr upon
+ * destruction. Relevant only in cases where a memory pointer-scanner
+ * like valgrind might get confused about stale references.
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+
+template<class T>
+class nsClearingPtrHashKey : public nsPtrHashKey<T>
+{
+public:
+ explicit nsClearingPtrHashKey(const T* aKey) : nsPtrHashKey<T>(aKey) {}
+ nsClearingPtrHashKey(const nsClearingPtrHashKey<T>& aToCopy)
+ : nsPtrHashKey<T>(aToCopy)
+ {
+ }
+ ~nsClearingPtrHashKey() { nsPtrHashKey<T>::mKey = nullptr; }
+};
+
+typedef nsClearingPtrHashKey<const void> nsClearingVoidPtrHashKey;
+
+/**
+ * hashkey wrapper using a function pointer KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+template<class T>
+class nsFuncPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef T& KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsFuncPtrHashKey(const T* aKey) : mKey(*const_cast<T*>(aKey)) {}
+ nsFuncPtrHashKey(const nsFuncPtrHashKey<T>& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsFuncPtrHashKey() {}
+
+ KeyType GetKey() const { return const_cast<T&>(mKey); }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(*aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+protected:
+ T mKey;
+};
+
+/**
+ * hashkey wrapper using nsID KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsIDHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsID& KeyType;
+ typedef const nsID* KeyTypePointer;
+
+ explicit nsIDHashKey(const nsID* aInID) : mID(*aInID) {}
+ nsIDHashKey(const nsIDHashKey& aToCopy) : mID(aToCopy.mID) {}
+ ~nsIDHashKey() {}
+
+ KeyType GetKey() const { return mID; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey->Equals(mID); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ // Hash the nsID object's raw bytes.
+ return mozilla::HashBytes(aKey, sizeof(KeyType));
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const nsID mID;
+};
+
+/**
+ * hashkey wrapper for "dependent" const char*; this class does not "own"
+ * its string pointer.
+ *
+ * This class must only be used if the strings have a lifetime longer than
+ * the hashtable they occupy. This normally occurs only for static
+ * strings or strings that have been arena-allocated.
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsDepCharHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const char* KeyType;
+ typedef const char* KeyTypePointer;
+
+ explicit nsDepCharHashKey(const char* aKey) : mKey(aKey) {}
+ nsDepCharHashKey(const nsDepCharHashKey& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsDepCharHashKey() {}
+
+ const char* GetKey() const { return mKey; }
+ bool KeyEquals(const char* aKey) const { return !strcmp(mKey, aKey); }
+
+ static const char* KeyToPointer(const char* aKey) { return aKey; }
+ static PLDHashNumber HashKey(const char* aKey)
+ {
+ return mozilla::HashString(aKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const char* mKey;
+};
+
+/**
+ * hashkey wrapper for const char*; at construction, this class duplicates
+ * a string pointed to by the pointer so that it doesn't matter whether or not
+ * the string lives longer than the hash table.
+ */
+class nsCharPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const char* KeyType;
+ typedef const char* KeyTypePointer;
+
+ explicit nsCharPtrHashKey(const char* aKey) : mKey(strdup(aKey)) {}
+ nsCharPtrHashKey(const nsCharPtrHashKey& aToCopy)
+ : mKey(strdup(aToCopy.mKey))
+ {
+ }
+
+ nsCharPtrHashKey(nsCharPtrHashKey&& aOther)
+ : mKey(aOther.mKey)
+ {
+ aOther.mKey = nullptr;
+ }
+
+ ~nsCharPtrHashKey()
+ {
+ if (mKey) {
+ free(const_cast<char*>(mKey));
+ }
+ }
+
+ const char* GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return !strcmp(mKey, aKey); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return mozilla::HashString(aKey);
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(mKey);
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const char* mKey;
+};
+
+/**
+ * hashkey wrapper for const char16_t*; at construction, this class duplicates
+ * a string pointed to by the pointer so that it doesn't matter whether or not
+ * the string lives longer than the hash table.
+ */
+class nsUnicharPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const char16_t* KeyType;
+ typedef const char16_t* KeyTypePointer;
+
+ explicit nsUnicharPtrHashKey(const char16_t* aKey) : mKey(NS_strdup(aKey)) {}
+ nsUnicharPtrHashKey(const nsUnicharPtrHashKey& aToCopy)
+ : mKey(NS_strdup(aToCopy.mKey))
+ {
+ }
+
+ nsUnicharPtrHashKey(nsUnicharPtrHashKey&& aOther)
+ : mKey(aOther.mKey)
+ {
+ aOther.mKey = nullptr;
+ }
+
+ ~nsUnicharPtrHashKey()
+ {
+ if (mKey) {
+ NS_Free(const_cast<char16_t*>(mKey));
+ }
+ }
+
+ const char16_t* GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return !NS_strcmp(mKey, aKey); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return mozilla::HashString(aKey);
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(mKey);
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const char16_t* mKey;
+};
+
+/**
+ * Hashtable key class to use with objects that support nsIHashable
+ */
+class nsHashableHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef nsIHashable* KeyType;
+ typedef const nsIHashable* KeyTypePointer;
+
+ explicit nsHashableHashKey(const nsIHashable* aKey)
+ : mKey(const_cast<nsIHashable*>(aKey))
+ {
+ }
+ nsHashableHashKey(const nsHashableHashKey& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsHashableHashKey() {}
+
+ nsIHashable* GetKey() const { return mKey; }
+
+ bool KeyEquals(const nsIHashable* aKey) const
+ {
+ bool eq;
+ if (NS_SUCCEEDED(mKey->Equals(const_cast<nsIHashable*>(aKey), &eq))) {
+ return eq;
+ }
+ return false;
+ }
+
+ static const nsIHashable* KeyToPointer(nsIHashable* aKey) { return aKey; }
+ static PLDHashNumber HashKey(const nsIHashable* aKey)
+ {
+ uint32_t code = 8888; // magic number if GetHashCode fails :-(
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ const_cast<nsIHashable*>(aKey)->GetHashCode(&code);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "GetHashCode should not throw!");
+ return code;
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ nsCOMPtr<nsIHashable> mKey;
+};
+
+namespace mozilla {
+
+template <typename T>
+PLDHashNumber
+Hash(const T& aValue)
+{
+ return aValue.Hash();
+}
+
+} // namespace mozilla
+
+/**
+ * Hashtable key class to use with objects for which Hash() and operator==()
+ * are defined.
+ */
+template<typename T>
+class nsGenericHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const T& KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsGenericHashKey(KeyTypePointer aKey) : mKey(*aKey) {}
+ nsGenericHashKey(const nsGenericHashKey<T>& aOther) : mKey(aOther.mKey) {}
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey) { return ::mozilla::Hash(*aKey); }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ T mKey;
+};
+
+#endif // nsTHashKeys_h__
diff --git a/xpcom/glue/nsIClassInfoImpl.h b/xpcom/glue/nsIClassInfoImpl.h
new file mode 100644
index 000000000..44303f2be
--- /dev/null
+++ b/xpcom/glue/nsIClassInfoImpl.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIClassInfoImpl_h__
+#define nsIClassInfoImpl_h__
+
+#include "mozilla/Alignment.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "nsIClassInfo.h"
+#include "nsISupportsImpl.h"
+
+#include <new>
+
+/**
+ * This header file provides macros which help you make your class implement
+ * nsIClassInfo. Implementing nsIClassInfo is particularly helpful if you have
+ * a C++ class which implements multiple interfaces and which you access from
+ * JavaScript. If that class implements nsIClassInfo, the JavaScript code
+ * won't have to call QueryInterface on instances of the class; all methods
+ * from all interfaces returned by GetInterfaces() will be available
+ * automagically.
+ *
+ * Here's all you need to do. Given a class
+ *
+ * class nsFooBar : public nsIFoo, public nsIBar { };
+ *
+ * you should already have the following nsISupports implementation in its cpp
+ * file:
+ *
+ * NS_IMPL_ISUPPORTS(nsFooBar, nsIFoo, nsIBar).
+ *
+ * Change this to
+ *
+ * NS_IMPL_CLASSINFO(nsFooBar, nullptr, 0, NS_FOOBAR_CID)
+ * NS_IMPL_ISUPPORTS_CI(nsFooBar, nsIFoo, nsIBar)
+ *
+ * If nsFooBar is threadsafe, change the 0 above to nsIClassInfo::THREADSAFE.
+ * If it's a singleton, use nsIClassInfo::SINGLETON. The full list of flags is
+ * in nsIClassInfo.idl.
+ *
+ * The nullptr parameter is there so you can pass a function for converting
+ * from an XPCOM object to a scriptable helper. Unless you're doing
+ * specialized JS work, you can probably leave this as nullptr.
+ *
+ * This file also defines the NS_IMPL_QUERY_INTERFACE_CI macro, which you can
+ * use to replace NS_IMPL_QUERY_INTERFACE, if you use that instead of
+ * NS_IMPL_ISUPPORTS.
+ *
+ * That's it! The rest is gory details.
+ *
+ *
+ * Notice that nsFooBar didn't need to inherit from nsIClassInfo in order to
+ * "implement" it. However, after adding these macros to nsFooBar, you you can
+ * QueryInterface an instance of nsFooBar to nsIClassInfo. How can this be?
+ *
+ * The answer lies in the NS_IMPL_ISUPPORTS_CI macro. It modifies nsFooBar's
+ * QueryInterface implementation such that, if we ask to QI to nsIClassInfo, it
+ * returns a singleton object associated with the class. (That singleton is
+ * defined by NS_IMPL_CLASSINFO.) So all nsFooBar instances will return the
+ * same object when QI'ed to nsIClassInfo. (You can see this in
+ * NS_IMPL_QUERY_CLASSINFO below.)
+ *
+ * This hack breaks XPCOM's rules, since if you take an instance of nsFooBar,
+ * QI it to nsIClassInfo, and then try to QI to nsIFoo, that will fail. On the
+ * upside, implementing nsIClassInfo doesn't add a vtable pointer to instances
+ * of your class.
+ *
+ * In principal, you can also implement nsIClassInfo by inheriting from the
+ * interface. But some code expects that when it QI's an object to
+ * nsIClassInfo, it gets back a singleton which isn't attached to any
+ * particular object. If a class were to implement nsIClassInfo through
+ * inheritance, that code might QI to nsIClassInfo and keep the resulting
+ * object alive, thinking it was only keeping alive the classinfo singleton,
+ * but in fact keeping a whole instance of the class alive. See, e.g., bug
+ * 658632.
+ *
+ * Unless you specifically need to have a different nsIClassInfo instance for
+ * each instance of your class, you should probably just implement nsIClassInfo
+ * as a singleton.
+ */
+
+class GenericClassInfo : public nsIClassInfo
+{
+public:
+ struct ClassInfoData
+ {
+ // This function pointer uses NS_CALLBACK because it's always set to an
+ // NS_IMETHOD function, which uses __stdcall on Win32.
+ typedef NS_CALLBACK(GetInterfacesProc)(uint32_t* aCountP, nsIID*** aArray);
+ GetInterfacesProc getinterfaces;
+
+ // This function pointer doesn't use NS_CALLBACK because it's always set to
+ // a vanilla function.
+ typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper);
+ GetScriptableHelperProc getscriptablehelper;
+
+ uint32_t flags;
+ nsCID cid;
+ };
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSICLASSINFO
+
+ explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {}
+
+private:
+ const ClassInfoData* mData;
+};
+
+#define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal
+#define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper
+#define NS_DECL_CI_INTERFACE_GETTER(_class) \
+ extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class) \
+ (uint32_t *, nsIID ***);
+
+#define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid) \
+ NS_DECL_CI_INTERFACE_GETTER(_class) \
+ static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
+ NS_CI_INTERFACE_GETTER_NAME(_class), \
+ _getscriptablehelper, \
+ _flags | nsIClassInfo::SINGLETON_CLASSINFO, \
+ _cid, \
+ }; \
+ mozilla::AlignedStorage2<GenericClassInfo> k##_class##ClassInfoDataPlace; \
+ nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr;
+
+#define NS_IMPL_QUERY_CLASSINFO(_class) \
+ if ( aIID.Equals(NS_GET_IID(nsIClassInfo)) ) { \
+ if (!NS_CLASSINFO_NAME(_class)) \
+ NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \
+ GenericClassInfo(&k##_class##ClassInfoData); \
+ foundInterface = NS_CLASSINFO_NAME(_class); \
+ } else
+
+#define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \
+NS_IMETHODIMP \
+NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t *count, nsIID ***array) \
+{ \
+ *count = _c; \
+ *array = (nsIID **)moz_xmalloc(sizeof (nsIID *) * _c); \
+ uint32_t i = 0;
+
+#define NS_CLASSINFO_HELPER_ENTRY(_interface) \
+ (*array)[i++] = (nsIID*)nsMemory::Clone(&NS_GET_IID(_interface), \
+ sizeof(nsIID));
+
+#define NS_CLASSINFO_HELPER_END \
+ MOZ_ASSERT(i == *count, "Incorrent number of entries"); \
+ return NS_OK; \
+}
+
+#define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_CLASSINFO_HELPER_BEGIN(aClass, \
+ MOZ_PASTE_PREFIX_AND_ARG_COUNT(/* No prefix */, \
+ __VA_ARGS__)) \
+ MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \
+ NS_CLASSINFO_HELPER_END
+
+#define NS_IMPL_QUERY_INTERFACE_CI(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_MAP_BEGIN(aClass) \
+ MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__)) \
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, MOZ_ARG_1(__VA_ARGS__)) \
+ NS_IMPL_QUERY_CLASSINFO(aClass) \
+ NS_INTERFACE_MAP_END
+
+#define NS_IMPL_ISUPPORTS_CI(aClass, ...) \
+ NS_IMPL_ADDREF(aClass) \
+ NS_IMPL_RELEASE(aClass) \
+ NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \
+ NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__)
+
+#endif // nsIClassInfoImpl_h__
diff --git a/xpcom/glue/nsID.cpp b/xpcom/glue/nsID.cpp
new file mode 100644
index 000000000..0a73bf6c3
--- /dev/null
+++ b/xpcom/glue/nsID.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsID.h"
+#include "nsMemory.h"
+#include "mozilla/Sprintf.h"
+
+void nsID::Clear()
+{
+ m0 = 0;
+ m1 = 0;
+ m2 = 0;
+ for (int i = 0; i < 8; ++i) {
+ m3[i] = 0;
+ }
+}
+
+/**
+ * Multiplies the_int_var with 16 (0x10) and adds the value of the
+ * hexadecimal digit the_char. If it fails it returns false from
+ * the function it's used in.
+ */
+
+#define ADD_HEX_CHAR_TO_INT_OR_RETURN_FALSE(the_char, the_int_var) \
+ the_int_var = (the_int_var << 4) + the_char; \
+ if(the_char >= '0' && the_char <= '9') the_int_var -= '0'; \
+ else if(the_char >= 'a' && the_char <= 'f') the_int_var -= 'a'-10; \
+ else if(the_char >= 'A' && the_char <= 'F') the_int_var -= 'A'-10; \
+ else return false
+
+
+/**
+ * Parses number_of_chars characters from the char_pointer pointer and
+ * puts the number in the dest_variable. The pointer is moved to point
+ * at the first character after the parsed ones. If it fails it returns
+ * false from the function the macro is used in.
+ */
+
+#define PARSE_CHARS_TO_NUM(char_pointer, dest_variable, number_of_chars) \
+ do { int32_t _i=number_of_chars; \
+ dest_variable = 0; \
+ while(_i) { \
+ ADD_HEX_CHAR_TO_INT_OR_RETURN_FALSE(*char_pointer, dest_variable); \
+ char_pointer++; \
+ _i--; \
+ } } while(0)
+
+
+/**
+ * Parses a hyphen from the char_pointer string. If there is no hyphen there
+ * the function returns false from the function it's used in. The
+ * char_pointer is advanced one step.
+ */
+
+#define PARSE_HYPHEN(char_pointer) if (*(char_pointer++) != '-') return false
+
+/*
+ * Turns a {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} string into
+ * an nsID. It can also handle the old format without the { and }.
+ */
+
+bool
+nsID::Parse(const char* aIDStr)
+{
+ /* Optimized for speed */
+ if (!aIDStr) {
+ return false;
+ }
+
+ bool expectFormat1 = (aIDStr[0] == '{');
+ if (expectFormat1) {
+ ++aIDStr;
+ }
+
+ PARSE_CHARS_TO_NUM(aIDStr, m0, 8);
+ PARSE_HYPHEN(aIDStr);
+ PARSE_CHARS_TO_NUM(aIDStr, m1, 4);
+ PARSE_HYPHEN(aIDStr);
+ PARSE_CHARS_TO_NUM(aIDStr, m2, 4);
+ PARSE_HYPHEN(aIDStr);
+ int i;
+ for (i = 0; i < 2; ++i) {
+ PARSE_CHARS_TO_NUM(aIDStr, m3[i], 2);
+ }
+ PARSE_HYPHEN(aIDStr);
+ while (i < 8) {
+ PARSE_CHARS_TO_NUM(aIDStr, m3[i], 2);
+ i++;
+ }
+
+ return expectFormat1 ? *aIDStr == '}' : true;
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+static const char gIDFormat[] =
+ "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}";
+
+/*
+ * Returns an allocated string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ * format. The string is allocated with NS_Alloc and should be freed by
+ * the caller.
+ */
+
+char*
+nsID::ToString() const
+{
+ char* res = (char*)NS_Alloc(NSID_LENGTH);
+
+ if (res) {
+ snprintf(res, NSID_LENGTH, gIDFormat,
+ m0, (uint32_t)m1, (uint32_t)m2,
+ (uint32_t)m3[0], (uint32_t)m3[1], (uint32_t)m3[2],
+ (uint32_t)m3[3], (uint32_t)m3[4], (uint32_t)m3[5],
+ (uint32_t)m3[6], (uint32_t)m3[7]);
+ }
+ return res;
+}
+
+void
+nsID::ToProvidedString(char (&aDest)[NSID_LENGTH]) const
+{
+ SprintfLiteral(aDest, gIDFormat,
+ m0, (uint32_t)m1, (uint32_t)m2,
+ (uint32_t)m3[0], (uint32_t)m3[1], (uint32_t)m3[2],
+ (uint32_t)m3[3], (uint32_t)m3[4], (uint32_t)m3[5],
+ (uint32_t)m3[6], (uint32_t)m3[7]);
+}
+
+#endif // XPCOM_GLUE_AVOID_NSPR
diff --git a/xpcom/glue/nsID.h b/xpcom/glue/nsID.h
new file mode 100644
index 000000000..c04007d4e
--- /dev/null
+++ b/xpcom/glue/nsID.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsID_h__
+#define nsID_h__
+
+#include <string.h>
+
+#include "nscore.h"
+
+#define NSID_LENGTH 39
+
+/**
+ * A "unique identifier". This is modeled after OSF DCE UUIDs.
+ */
+
+struct nsID
+{
+ /**
+ * @name Identifier values
+ */
+
+ //@{
+ uint32_t m0;
+ uint16_t m1;
+ uint16_t m2;
+ uint8_t m3[8];
+ //@}
+
+ /**
+ * @name Methods
+ */
+
+ //@{
+ /**
+ * Ensures everything is zeroed out.
+ */
+ void Clear();
+
+ /**
+ * Equivalency method. Compares this nsID with another.
+ * @return <b>true</b> if they are the same, <b>false</b> if not.
+ */
+
+ inline bool Equals(const nsID& aOther) const
+ {
+ // Unfortunately memcmp isn't faster than this.
+ return
+ (((uint32_t*)&m0)[0] == ((uint32_t*)&aOther.m0)[0]) &&
+ (((uint32_t*)&m0)[1] == ((uint32_t*)&aOther.m0)[1]) &&
+ (((uint32_t*)&m0)[2] == ((uint32_t*)&aOther.m0)[2]) &&
+ (((uint32_t*)&m0)[3] == ((uint32_t*)&aOther.m0)[3]);
+ }
+
+ inline bool operator==(const nsID& aOther) const
+ {
+ return Equals(aOther);
+ }
+
+ /**
+ * nsID Parsing method. Turns a {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ * string into an nsID
+ */
+ bool Parse(const char* aIDStr);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ /**
+ * nsID string encoder. Returns an allocated string in
+ * {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format. Caller should free string.
+ * YOU SHOULD ONLY USE THIS IF YOU CANNOT USE ToProvidedString() BELOW.
+ */
+ char* ToString() const;
+
+ /**
+ * nsID string encoder. Builds a string in
+ * {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format, into a char[NSID_LENGTH]
+ * buffer provided by the caller (for instance, on the stack).
+ */
+ void ToProvidedString(char (&aDest)[NSID_LENGTH]) const;
+
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+ //@}
+};
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+/**
+ * A stack helper class to convert a nsID to a string. Useful
+ * for printing nsIDs. For example:
+ * nsID aID = ...;
+ * printf("%s", nsIDToCString(aID).get());
+ */
+class nsIDToCString
+{
+public:
+ explicit nsIDToCString(const nsID& aID)
+ {
+ aID.ToProvidedString(mStringBytes);
+ }
+
+ const char *get() const
+ {
+ return mStringBytes;
+ }
+
+protected:
+ char mStringBytes[NSID_LENGTH];
+};
+#endif
+
+/*
+ * Class IDs
+ */
+
+typedef nsID nsCID;
+
+// Define an CID
+#define NS_DEFINE_CID(_name, _cidspec) \
+ const nsCID _name = _cidspec
+
+#define NS_DEFINE_NAMED_CID(_name) \
+ static const nsCID k##_name = _name
+
+#define REFNSCID const nsCID&
+
+/**
+ * An "interface id" which can be used to uniquely identify a given
+ * interface.
+ */
+
+typedef nsID nsIID;
+
+/**
+ * A macro shorthand for <tt>const nsIID&<tt>
+ */
+
+#define REFNSIID const nsIID&
+
+/**
+ * Define an IID
+ * obsolete - do not use this macro
+ */
+
+#define NS_DEFINE_IID(_name, _iidspec) \
+ const nsIID _name = _iidspec
+
+/**
+ * A macro to build the static const IID accessor method. The Dummy
+ * template parameter only exists so that the kIID symbol will be linked
+ * properly (weak symbol on linux, gnu_linkonce on mac, multiple-definitions
+ * merged on windows). Dummy should always be instantiated as "void".
+ */
+
+#define NS_DECLARE_STATIC_IID_ACCESSOR(the_iid) \
+ template<typename T, typename U> \
+ struct COMTypeInfo;
+
+#define NS_DEFINE_STATIC_IID_ACCESSOR(the_interface, the_iid) \
+ template<typename T> \
+ struct the_interface::COMTypeInfo<the_interface, T> { \
+ static const nsIID kIID NS_HIDDEN; \
+ }; \
+ template<typename T> \
+ const nsIID the_interface::COMTypeInfo<the_interface, T>::kIID NS_HIDDEN = the_iid;
+
+/**
+ * A macro to build the static const CID accessor method
+ */
+
+#define NS_DEFINE_STATIC_CID_ACCESSOR(the_cid) \
+ static const nsID& GetCID() {static const nsID cid = the_cid; return cid;}
+
+#define NS_GET_IID(T) (T::COMTypeInfo<T, void>::kIID)
+#define NS_GET_TEMPLATE_IID(T) (T::template COMTypeInfo<T, void>::kIID)
+
+#endif
diff --git a/xpcom/glue/nsIInterfaceRequestorUtils.cpp b/xpcom/glue/nsIInterfaceRequestorUtils.cpp
new file mode 100644
index 000000000..8a2dfe3f8
--- /dev/null
+++ b/xpcom/glue/nsIInterfaceRequestorUtils.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+nsresult
+nsGetInterface::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status;
+
+ if (mSource) {
+ nsCOMPtr<nsIInterfaceRequestor> factoryPtr = do_QueryInterface(mSource);
+ if (factoryPtr) {
+ status = factoryPtr->GetInterface(aIID, aInstancePtr);
+ } else {
+ status = NS_ERROR_NO_INTERFACE;
+ }
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
diff --git a/xpcom/glue/nsIInterfaceRequestorUtils.h b/xpcom/glue/nsIInterfaceRequestorUtils.h
new file mode 100644
index 000000000..718cf387b
--- /dev/null
+++ b/xpcom/glue/nsIInterfaceRequestorUtils.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsInterfaceRequestorUtils_h
+#define __nsInterfaceRequestorUtils_h
+
+#include "nsCOMPtr.h"
+
+// a type-safe shortcut for calling the |GetInterface()| member function
+// T must inherit from nsIInterfaceRequestor, but the cast may be ambiguous.
+template<class T, class DestinationType>
+inline nsresult
+CallGetInterface(T* aSource, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aSource, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aSource->GetInterface(NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+class MOZ_STACK_CLASS nsGetInterface final : public nsCOMPtr_helper
+{
+public:
+ nsGetInterface(nsISupports* aSource, nsresult* aError)
+ : mSource(aSource)
+ , mErrorPtr(aError)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
+ override;
+
+private:
+ nsISupports* MOZ_NON_OWNING_REF mSource;
+ nsresult* mErrorPtr;
+};
+
+inline const nsGetInterface
+do_GetInterface(nsISupports* aSource, nsresult* aError = 0)
+{
+ return nsGetInterface(aSource, aError);
+}
+
+#endif // __nsInterfaceRequestorUtils_h
+
diff --git a/xpcom/glue/nsINIParser.cpp b/xpcom/glue/nsINIParser.cpp
new file mode 100644
index 000000000..53eae7292
--- /dev/null
+++ b/xpcom/glue/nsINIParser.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Moz headers (alphabetical)
+#include "nsCRTGlue.h"
+#include "nsError.h"
+#include "nsIFile.h"
+#include "nsINIParser.h"
+#include "mozilla/FileUtils.h" // AutoFILE
+
+// System headers (alphabetical)
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#if defined(XP_WIN)
+#define READ_BINARYMODE L"rb"
+#else
+#define READ_BINARYMODE "r"
+#endif
+
+using namespace mozilla;
+
+#ifdef XP_WIN
+inline FILE*
+TS_tfopen(const char* aPath, const wchar_t* aMode)
+{
+ wchar_t wPath[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH);
+ return _wfopen(wPath, aMode);
+}
+#else
+inline FILE*
+TS_tfopen(const char* aPath, const char* aMode)
+{
+ return fopen(aPath, aMode);
+}
+#endif
+
+// Stack based FILE wrapper to ensure that fclose is called, copied from
+// toolkit/mozapps/update/updater/readstrings.cpp
+
+class AutoFILE
+{
+public:
+ explicit AutoFILE(FILE* aFp = nullptr) : fp_(aFp) {}
+ ~AutoFILE()
+ {
+ if (fp_) {
+ fclose(fp_);
+ }
+ }
+ operator FILE*() { return fp_; }
+ FILE** operator&() { return &fp_; }
+ void operator=(FILE* aFp) { fp_ = aFp; }
+private:
+ FILE* fp_;
+};
+
+nsresult
+nsINIParser::Init(nsIFile* aFile)
+{
+ /* open the file. Don't use OpenANSIFileDesc, because you mustn't
+ pass FILE* across shared library boundaries, which may be using
+ different CRTs */
+
+ AutoFILE fd;
+
+#ifdef XP_WIN
+ nsAutoString path;
+ nsresult rv = aFile->GetPath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ fd = _wfopen(path.get(), READ_BINARYMODE);
+#else
+ nsAutoCString path;
+ aFile->GetNativePath(path);
+
+ fd = fopen(path.get(), READ_BINARYMODE);
+#endif
+
+ if (!fd) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return InitFromFILE(fd);
+}
+
+nsresult
+nsINIParser::Init(const char* aPath)
+{
+ /* open the file */
+ AutoFILE fd(TS_tfopen(aPath, READ_BINARYMODE));
+ if (!fd) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return InitFromFILE(fd);
+}
+
+static const char kNL[] = "\r\n";
+static const char kEquals[] = "=";
+static const char kWhitespace[] = " \t";
+static const char kRBracket[] = "]";
+
+nsresult
+nsINIParser::InitFromFILE(FILE* aFd)
+{
+ /* get file size */
+ if (fseek(aFd, 0, SEEK_END) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ long flen = ftell(aFd);
+ /* zero-sized file, or an error */
+ if (flen <= 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* malloc an internal buf the size of the file */
+ mFileContents = MakeUnique<char[]>(flen + 2);
+ if (!mFileContents) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* read the file in one swoop */
+ if (fseek(aFd, 0, SEEK_SET) != 0) {
+ return NS_BASE_STREAM_OSERROR;
+ }
+
+ int rd = fread(mFileContents.get(), sizeof(char), flen, aFd);
+ if (rd != flen) {
+ return NS_BASE_STREAM_OSERROR;
+ }
+
+ // We write a UTF16 null so that the file is easier to convert to UTF8
+ mFileContents[flen] = mFileContents[flen + 1] = '\0';
+
+ char* buffer = &mFileContents[0];
+
+ if (flen >= 3 &&
+ mFileContents[0] == '\xEF' &&
+ mFileContents[1] == '\xBB' &&
+ mFileContents[2] == '\xBF') {
+ // Someone set us up the Utf-8 BOM
+ // This case is easy, since we assume that BOM-less
+ // files are Utf-8 anyway. Just skip the BOM and process as usual.
+ buffer = &mFileContents[3];
+ }
+
+#ifdef XP_WIN
+ if (flen >= 2 &&
+ mFileContents[0] == '\xFF' &&
+ mFileContents[1] == '\xFE') {
+ // Someone set us up the Utf-16LE BOM
+ buffer = &mFileContents[2];
+ // Get the size required for our Utf8 buffer
+ flen = WideCharToMultiByte(CP_UTF8,
+ 0,
+ reinterpret_cast<LPWSTR>(buffer),
+ -1,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr);
+ if (flen == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ UniquePtr<char[]> utf8Buffer(new char[flen]);
+ if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1,
+ utf8Buffer.get(), flen, nullptr, nullptr) == 0) {
+ return NS_ERROR_FAILURE;
+ }
+ mFileContents = Move(utf8Buffer);
+ buffer = mFileContents.get();
+ }
+#endif
+
+ char* currSection = nullptr;
+
+ // outer loop tokenizes into lines
+ while (char* token = NS_strtok(kNL, &buffer)) {
+ if (token[0] == '#' || token[0] == ';') { // it's a comment
+ continue;
+ }
+
+ token = (char*)NS_strspnp(kWhitespace, token);
+ if (!*token) { // empty line
+ continue;
+ }
+
+ if (token[0] == '[') { // section header!
+ ++token;
+ currSection = token;
+
+ char* rb = NS_strtok(kRBracket, &token);
+ if (!rb || NS_strtok(kWhitespace, &token)) {
+ // there's either an unclosed [Section or a [Section]Moretext!
+ // we could frankly decide that this INI file is malformed right
+ // here and stop, but we won't... keep going, looking for
+ // a well-formed [section] to continue working with
+ currSection = nullptr;
+ }
+
+ continue;
+ }
+
+ if (!currSection) {
+ // If we haven't found a section header (or we found a malformed
+ // section header), don't bother parsing this line.
+ continue;
+ }
+
+ char* key = token;
+ char* e = NS_strtok(kEquals, &token);
+ if (!e || !token) {
+ continue;
+ }
+
+ INIValue* v;
+ if (!mSections.Get(currSection, &v)) {
+ v = new INIValue(key, token);
+ if (!v) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mSections.Put(currSection, v);
+ continue;
+ }
+
+ // Check whether this key has already been specified; overwrite
+ // if so, or append if not.
+ while (v) {
+ if (!strcmp(key, v->key)) {
+ v->value = token;
+ break;
+ }
+ if (!v->next) {
+ v->next = MakeUnique<INIValue>(key, token);
+ if (!v->next) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ break;
+ }
+ v = v->next.get();
+ }
+ NS_ASSERTION(v, "v should never be null coming out of this loop");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsINIParser::GetString(const char* aSection, const char* aKey,
+ nsACString& aResult)
+{
+ INIValue* val;
+ mSections.Get(aSection, &val);
+
+ while (val) {
+ if (strcmp(val->key, aKey) == 0) {
+ aResult.Assign(val->value);
+ return NS_OK;
+ }
+
+ val = val->next.get();
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsINIParser::GetString(const char* aSection, const char* aKey,
+ char* aResult, uint32_t aResultLen)
+{
+ INIValue* val;
+ mSections.Get(aSection, &val);
+
+ while (val) {
+ if (strcmp(val->key, aKey) == 0) {
+ strncpy(aResult, val->value, aResultLen);
+ aResult[aResultLen - 1] = '\0';
+ if (strlen(val->value) >= aResultLen) {
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+ }
+
+ return NS_OK;
+ }
+
+ val = val->next.get();
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsINIParser::GetSections(INISectionCallback aCB, void* aClosure)
+{
+ for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) {
+ if (!aCB(iter.Key(), aClosure)) {
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsINIParser::GetStrings(const char* aSection,
+ INIStringCallback aCB, void* aClosure)
+{
+ INIValue* val;
+
+ for (mSections.Get(aSection, &val);
+ val;
+ val = val->next.get()) {
+
+ if (!aCB(val->key, val->value, aClosure)) {
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsINIParser.h b/xpcom/glue/nsINIParser.h
new file mode 100644
index 000000000..d0f553d49
--- /dev/null
+++ b/xpcom/glue/nsINIParser.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file was shamelessly copied from mozilla/xpinstall/wizard/unix/src2
+
+#ifndef nsINIParser_h__
+#define nsINIParser_h__
+
+#ifdef MOZILLA_INTERNAL_API
+#define nsINIParser nsINIParser_internal
+#endif
+
+#include "nscore.h"
+#include "nsClassHashtable.h"
+#include "mozilla/UniquePtr.h"
+
+#include <stdio.h>
+
+class nsIFile;
+
+class nsINIParser
+{
+public:
+ nsINIParser() {}
+ ~nsINIParser() {}
+
+ /**
+ * Initialize the INIParser with a nsIFile. If this method fails, no
+ * other methods should be called. This method reads and parses the file,
+ * the class does not hold a file handle open. An instance must only be
+ * initialized once.
+ */
+ nsresult Init(nsIFile* aFile);
+
+ /**
+ * Initialize the INIParser with a file path. If this method fails, no
+ * other methods should be called. This method reads and parses the file,
+ * the class does not hold a file handle open. An instance must only
+ * be initialized once.
+ */
+ nsresult Init(const char* aPath);
+
+ /**
+ * Callback for GetSections
+ * @return false to stop enumeration, or true to continue.
+ */
+ typedef bool (*INISectionCallback)(const char* aSection, void* aClosure);
+
+ /**
+ * Enumerate the sections within the INI file.
+ */
+ nsresult GetSections(INISectionCallback aCB, void* aClosure);
+
+ /**
+ * Callback for GetStrings
+ * @return false to stop enumeration, or true to continue
+ */
+ typedef bool (*INIStringCallback)(const char* aString, const char* aValue,
+ void* aClosure);
+
+ /**
+ * Enumerate the strings within a section. If the section does
+ * not exist, this function will silently return.
+ */
+ nsresult GetStrings(const char* aSection,
+ INIStringCallback aCB, void* aClosure);
+
+ /**
+ * Get the value of the specified key in the specified section
+ * of the INI file represented by this instance.
+ *
+ * @param aSection section name
+ * @param aKey key name
+ * @param aResult the value found
+ * @throws NS_ERROR_FAILURE if the specified section/key could not be
+ * found.
+ */
+ nsresult GetString(const char* aSection, const char* aKey,
+ nsACString& aResult);
+
+ /**
+ * Alternate signature of GetString that uses a pre-allocated buffer
+ * instead of a nsACString (for use in the standalone glue before
+ * the glue is initialized).
+ *
+ * @throws NS_ERROR_LOSS_OF_SIGNIFICANT_DATA if the aResult buffer is not
+ * large enough for the data. aResult will be filled with as
+ * much data as possible.
+ *
+ * @see GetString [1]
+ */
+ nsresult GetString(const char* aSection, const char* aKey,
+ char* aResult, uint32_t aResultLen);
+
+private:
+ struct INIValue
+ {
+ INIValue(const char* aKey, const char* aValue)
+ : key(aKey)
+ , value(aValue)
+ {
+ }
+
+ const char* key;
+ const char* value;
+ mozilla::UniquePtr<INIValue> next;
+ };
+
+ nsClassHashtable<nsDepCharHashKey, INIValue> mSections;
+ mozilla::UniquePtr<char[]> mFileContents;
+
+ nsresult InitFromFILE(FILE* aFd);
+};
+
+#endif /* nsINIParser_h__ */
diff --git a/xpcom/glue/nsISupportsImpl.cpp b/xpcom/glue/nsISupportsImpl.cpp
new file mode 100644
index 000000000..c60c0bfa7
--- /dev/null
+++ b/xpcom/glue/nsISupportsImpl.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupportsImpl.h"
+
+nsresult NS_FASTCALL
+NS_TableDrivenQI(void* aThis, REFNSIID aIID, void** aInstancePtr,
+ const QITableEntry* aEntries)
+{
+ do {
+ if (aIID.Equals(*aEntries->iid)) {
+ nsISupports* r = reinterpret_cast<nsISupports*>(
+ reinterpret_cast<char*>(aThis) + aEntries->offset);
+ NS_ADDREF(r);
+ *aInstancePtr = r;
+ return NS_OK;
+ }
+
+ ++aEntries;
+ } while (aEntries->iid);
+
+ *aInstancePtr = nullptr;
+ return NS_ERROR_NO_INTERFACE;
+}
diff --git a/xpcom/glue/nsISupportsImpl.h b/xpcom/glue/nsISupportsImpl.h
new file mode 100644
index 000000000..26db3c525
--- /dev/null
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -0,0 +1,1090 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsISupports.h"
+
+
+#ifndef nsISupportsImpl_h__
+#define nsISupportsImpl_h__
+
+#include "nscore.h"
+#include "nsISupportsBase.h"
+#include "nsISupportsUtils.h"
+
+
+#if !defined(XPCOM_GLUE_AVOID_NSPR)
+#include "prthread.h" /* needed for thread-safety checks */
+#endif // !XPCOM_GLUE_AVOID_NSPR
+
+#include "nsDebug.h"
+#include "nsXPCOM.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/TypeTraits.h"
+
+#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) \
+ static_assert(!mozilla::IsDestructible<X>::value, \
+ "Reference-counted class " #X " should not have a public destructor. " \
+ "Make this class's destructor non-public");
+
+inline nsISupports*
+ToSupports(nsISupports* aSupports)
+{
+ return aSupports;
+}
+
+inline nsISupports*
+ToCanonicalSupports(nsISupports* aSupports)
+{
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Macros to help detect thread-safety:
+
+#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
+
+class nsAutoOwningThread
+{
+public:
+ nsAutoOwningThread() { mThread = PR_GetCurrentThread(); }
+ void* GetThread() const { return mThread; }
+
+private:
+ void* mThread;
+};
+
+#define NS_DECL_OWNINGTHREAD nsAutoOwningThread _mOwningThread;
+#define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) \
+ NS_CheckThreadSafe(agg->_mOwningThread.GetThread(), #_class " not thread-safe")
+#define NS_ASSERT_OWNINGTHREAD(_class) NS_ASSERT_OWNINGTHREAD_AGGREGATE(this, _class)
+#else // !DEBUG && !(NIGHTLY_BUILD && !MOZ_PROFILING)
+
+#define NS_DECL_OWNINGTHREAD /* nothing */
+#define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) ((void)0)
+#define NS_ASSERT_OWNINGTHREAD(_class) ((void)0)
+
+#endif // DEBUG || (NIGHTLY_BUILD && !MOZ_PROFILING)
+
+
+// Macros for reference-count and constructor logging
+
+#if defined(NS_BUILD_REFCNT_LOGGING)
+
+#define NS_LOG_ADDREF(_p, _rc, _type, _size) \
+ NS_LogAddRef((_p), (_rc), (_type), (uint32_t) (_size))
+
+#define NS_LOG_RELEASE(_p, _rc, _type) \
+ NS_LogRelease((_p), (_rc), (_type))
+
+#include "mozilla/TypeTraits.h"
+#define MOZ_ASSERT_CLASSNAME(_type) \
+ static_assert(mozilla::IsClass<_type>::value, \
+ "Token '" #_type "' is not a class type.")
+
+// Note that the following constructor/destructor logging macros are redundant
+// for refcounted objects that log via the NS_LOG_ADDREF/NS_LOG_RELEASE macros.
+// Refcount logging is preferred.
+#define MOZ_COUNT_CTOR(_type) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ NS_LogCtor((void*)this, #_type, sizeof(*this)); \
+} while (0)
+
+#define MOZ_COUNT_CTOR_INHERITED(_type, _base) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ MOZ_ASSERT_CLASSNAME(_base); \
+ NS_LogCtor((void*)this, #_type, sizeof(*this) - sizeof(_base)); \
+} while (0)
+
+#define MOZ_LOG_CTOR(_ptr, _name, _size) \
+do { \
+ NS_LogCtor((void*)_ptr, _name, _size); \
+} while (0)
+
+#define MOZ_COUNT_DTOR(_type) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ NS_LogDtor((void*)this, #_type, sizeof(*this)); \
+} while (0)
+
+#define MOZ_COUNT_DTOR_INHERITED(_type, _base) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ MOZ_ASSERT_CLASSNAME(_base); \
+ NS_LogDtor((void*)this, #_type, sizeof(*this) - sizeof(_base)); \
+} while (0)
+
+#define MOZ_LOG_DTOR(_ptr, _name, _size) \
+do { \
+ NS_LogDtor((void*)_ptr, _name, _size); \
+} while (0)
+
+/* nsCOMPtr.h allows these macros to be defined by clients
+ * These logging functions require dynamic_cast<void*>, so they don't
+ * do anything useful if we don't have dynamic_cast<void*>.
+ * Note: The explicit comparison to nullptr is needed to avoid warnings
+ * when _p is a nullptr itself. */
+#define NSCAP_LOG_ASSIGNMENT(_c, _p) \
+ if (_p != nullptr) \
+ NS_LogCOMPtrAddRef((_c),static_cast<nsISupports*>(_p))
+
+#define NSCAP_LOG_RELEASE(_c, _p) \
+ if (_p) \
+ NS_LogCOMPtrRelease((_c), static_cast<nsISupports*>(_p))
+
+#else /* !NS_BUILD_REFCNT_LOGGING */
+
+#define NS_LOG_ADDREF(_p, _rc, _type, _size)
+#define NS_LOG_RELEASE(_p, _rc, _type)
+#define MOZ_COUNT_CTOR(_type)
+#define MOZ_COUNT_CTOR_INHERITED(_type, _base)
+#define MOZ_LOG_CTOR(_ptr, _name, _size)
+#define MOZ_COUNT_DTOR(_type)
+#define MOZ_COUNT_DTOR_INHERITED(_type, _base)
+#define MOZ_LOG_DTOR(_ptr, _name, _size)
+
+#endif /* NS_BUILD_REFCNT_LOGGING */
+
+
+// Support for ISupports classes which interact with cycle collector.
+
+#define NS_NUMBER_OF_FLAGS_IN_REFCNT 2
+#define NS_IN_PURPLE_BUFFER (1 << 0)
+#define NS_IS_PURPLE (1 << 1)
+#define NS_REFCOUNT_CHANGE (1 << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+#define NS_REFCOUNT_VALUE(_val) (_val >> NS_NUMBER_OF_FLAGS_IN_REFCNT)
+
+class nsCycleCollectingAutoRefCnt
+{
+public:
+ nsCycleCollectingAutoRefCnt() : mRefCntAndFlags(0) {}
+
+ explicit nsCycleCollectingAutoRefCnt(uintptr_t aValue)
+ : mRefCntAndFlags(aValue << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+ {
+ }
+
+ nsCycleCollectingAutoRefCnt(const nsCycleCollectingAutoRefCnt&) = delete;
+ void operator=(const nsCycleCollectingAutoRefCnt&) = delete;
+
+ MOZ_ALWAYS_INLINE uintptr_t incr(nsISupports* aOwner)
+ {
+ return incr(aOwner, nullptr);
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t incr(void* aOwner,
+ nsCycleCollectionParticipant* aCp)
+ {
+ mRefCntAndFlags += NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags &= ~NS_IS_PURPLE;
+ // For incremental cycle collection, use the purple buffer to track objects
+ // that have been AddRef'd.
+ if (!IsInPurpleBuffer()) {
+ mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
+ // Refcount isn't zero, so Suspect won't delete anything.
+ MOZ_ASSERT(get() > 0);
+ NS_CycleCollectorSuspect3(aOwner, aCp, this, nullptr);
+ }
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE void stabilizeForDeletion()
+ {
+ // Set refcnt to 1 and mark us to be in the purple buffer.
+ // This way decr won't call suspect again.
+ mRefCntAndFlags = NS_REFCOUNT_CHANGE | NS_IN_PURPLE_BUFFER;
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t decr(nsISupports* aOwner,
+ bool* aShouldDelete = nullptr)
+ {
+ return decr(aOwner, nullptr, aShouldDelete);
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t decr(void* aOwner,
+ nsCycleCollectionParticipant* aCp,
+ bool* aShouldDelete = nullptr)
+ {
+ MOZ_ASSERT(get() > 0);
+ if (!IsInPurpleBuffer()) {
+ mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+ uintptr_t retval = NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ // Suspect may delete 'aOwner' and 'this'!
+ NS_CycleCollectorSuspect3(aOwner, aCp, this, aShouldDelete);
+ return retval;
+ }
+ mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE void RemovePurple()
+ {
+ MOZ_ASSERT(IsPurple(), "must be purple");
+ mRefCntAndFlags &= ~NS_IS_PURPLE;
+ }
+
+ MOZ_ALWAYS_INLINE void RemoveFromPurpleBuffer()
+ {
+ MOZ_ASSERT(IsInPurpleBuffer());
+ mRefCntAndFlags &= ~(NS_IS_PURPLE | NS_IN_PURPLE_BUFFER);
+ }
+
+ MOZ_ALWAYS_INLINE bool IsPurple() const
+ {
+ return !!(mRefCntAndFlags & NS_IS_PURPLE);
+ }
+
+ MOZ_ALWAYS_INLINE bool IsInPurpleBuffer() const
+ {
+ return !!(mRefCntAndFlags & NS_IN_PURPLE_BUFFER);
+ }
+
+ MOZ_ALWAYS_INLINE nsrefcnt get() const
+ {
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE operator nsrefcnt() const
+ {
+ return get();
+ }
+
+private:
+ uintptr_t mRefCntAndFlags;
+};
+
+class nsAutoRefCnt
+{
+public:
+ nsAutoRefCnt() : mValue(0) {}
+ explicit nsAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
+
+ nsAutoRefCnt(const nsAutoRefCnt&) = delete;
+ void operator=(const nsAutoRefCnt&) = delete;
+
+ // only support prefix increment/decrement
+ nsrefcnt operator++() { return ++mValue; }
+ nsrefcnt operator--() { return --mValue; }
+
+ nsrefcnt operator=(nsrefcnt aValue) { return (mValue = aValue); }
+ operator nsrefcnt() const { return mValue; }
+ nsrefcnt get() const { return mValue; }
+
+ static const bool isThreadSafe = false;
+private:
+ nsrefcnt operator++(int) = delete;
+ nsrefcnt operator--(int) = delete;
+ nsrefcnt mValue;
+};
+
+namespace mozilla {
+class ThreadSafeAutoRefCnt
+{
+public:
+ ThreadSafeAutoRefCnt() : mValue(0) {}
+ explicit ThreadSafeAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
+
+ ThreadSafeAutoRefCnt(const ThreadSafeAutoRefCnt&) = delete;
+ void operator=(const ThreadSafeAutoRefCnt&) = delete;
+
+ // only support prefix increment/decrement
+ MOZ_ALWAYS_INLINE nsrefcnt operator++() { return ++mValue; }
+ MOZ_ALWAYS_INLINE nsrefcnt operator--() { return --mValue; }
+
+ MOZ_ALWAYS_INLINE nsrefcnt operator=(nsrefcnt aValue)
+ {
+ return (mValue = aValue);
+ }
+ MOZ_ALWAYS_INLINE operator nsrefcnt() const { return mValue; }
+ MOZ_ALWAYS_INLINE nsrefcnt get() const { return mValue; }
+
+ static const bool isThreadSafe = true;
+private:
+ nsrefcnt operator++(int) = delete;
+ nsrefcnt operator--(int) = delete;
+ // In theory, RelaseAcquire consistency (but no weaker) is sufficient for
+ // the counter. Making it weaker could speed up builds on ARM (but not x86),
+ // but could break pre-existing code that assumes sequential consistency.
+ Atomic<nsrefcnt> mValue;
+};
+} // namespace mozilla
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Declare the reference count variable and the implementations of the
+ * AddRef and QueryInterface methods.
+ */
+
+#define NS_DECL_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+#define NS_DECL_THREADSAFE_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ typedef mozilla::TrueType HasThreadSafeRefCnt; \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+#define NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void); \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsCycleCollectingAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Implementation of AddRef and Release for non-nsISupports (ie "native")
+ * cycle-collected classes that use the purple buffer to avoid leaks.
+ */
+
+#define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = \
+ mRefCnt.incr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count;
+
+#define NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count;
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(_class) \
+NS_METHOD_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_METHOD_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ bool shouldDelete = false; \
+ nsrefcnt count = \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(), \
+ &shouldDelete); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt.incr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ _last; \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ if (shouldDelete) { \
+ mRefCnt.stabilizeForDeletion(); \
+ DeleteCycleCollectable(); \
+ } \
+ } \
+ return count; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(_class) \
+NS_METHOD_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+}
+
+#define NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(_class) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
+ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) { \
+ NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+ } \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsCycleCollectingAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i>.
+ *
+ * @param _class The name of the class implementing the method
+ * @param _destroy A statement that is executed when the object's
+ * refcount drops to zero.
+ * @param optional override Mark the AddRef & Release methods as overrides.
+ */
+#define NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, _destroy, ...) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ ++mRefCnt; \
+ NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
+ return mRefCnt; \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ --mRefCnt; \
+ NS_LOG_RELEASE(this, mRefCnt, #_class); \
+ if (mRefCnt == 0) { \
+ mRefCnt = 1; /* stabilize */ \
+ _destroy; \
+ return 0; \
+ } \
+ return mRefCnt; \
+ } \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i>.
+ *
+ * @param _class The name of the class implementing the method
+ * @param optional override Mark the AddRef & Release methods as overrides.
+ */
+#define NS_INLINE_DECL_REFCOUNTING(_class, ...) \
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, delete(this), __VA_ARGS__)
+
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, _decl, ...) \
+public: \
+ _decl(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return (nsrefcnt) count; \
+ } \
+ _decl(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ delete (this); \
+ return 0; \
+ } \
+ return count; \
+ } \
+ typedef mozilla::TrueType HasThreadSafeRefCnt; \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+public:
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i> in a threadsafe manner.
+ *
+ * DOES NOT DO REFCOUNT STABILIZATION!
+ *
+ * @param _class The name of the class implementing the method
+ */
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING(_class, ...) \
+NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_METHOD_, __VA_ARGS__)
+
+/**
+ * Like NS_INLINE_DECL_THREADSAFE_REFCOUNTING with AddRef & Release declared
+ * virtual.
+ */
+#define NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(_class, ...) \
+NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_IMETHOD_, __VA_ARGS__)
+
+/**
+ * Use this macro to implement the AddRef method for a given <i>_class</i>
+ * @param _class The name of the class implementing the method
+ */
+#define NS_IMPL_ADDREF(_class) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ if (!mRefCnt.isThreadSafe) \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count; \
+}
+
+/**
+ * Use this macro to implement the AddRef method for a given <i>_class</i>
+ * implemented as a wholly owned aggregated object intended to implement
+ * interface(s) for its owner
+ * @param _class The name of the class implementing the method
+ * @param _aggregator the owning/containing object
+ */
+#define NS_IMPL_ADDREF_USING_AGGREGATOR(_class, _aggregator) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ NS_PRECONDITION(_aggregator, "null aggregator"); \
+ return (_aggregator)->AddRef(); \
+}
+
+/**
+ * Use this macro to implement the Release method for a given
+ * <i>_class</i>.
+ * @param _class The name of the class implementing the method
+ * @param _destroy A statement that is executed when the object's
+ * refcount drops to zero.
+ *
+ * For example,
+ *
+ * NS_IMPL_RELEASE_WITH_DESTROY(Foo, Destroy(this))
+ *
+ * will cause
+ *
+ * Destroy(this);
+ *
+ * to be invoked when the object's refcount drops to zero. This
+ * allows for arbitrary teardown activity to occur (e.g., deallocation
+ * of object allocated with placement new).
+ */
+#define NS_IMPL_RELEASE_WITH_DESTROY(_class, _destroy) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ if (!mRefCnt.isThreadSafe) \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = --mRefCnt; \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt = 1; /* stabilize */ \
+ _destroy; \
+ return 0; \
+ } \
+ return count; \
+}
+
+/**
+ * Use this macro to implement the Release method for a given <i>_class</i>
+ * @param _class The name of the class implementing the method
+ *
+ * A note on the 'stabilization' of the refcnt to one. At that point,
+ * the object's refcount will have gone to zero. The object's
+ * destructor may trigger code that attempts to QueryInterface() and
+ * Release() 'this' again. Doing so will temporarily increment and
+ * decrement the refcount. (Only a logic error would make one try to
+ * keep a permanent hold on 'this'.) To prevent re-entering the
+ * destructor, we make sure that no balanced refcounting can return
+ * the refcount to |0|.
+ */
+#define NS_IMPL_RELEASE(_class) \
+ NS_IMPL_RELEASE_WITH_DESTROY(_class, delete (this))
+
+/**
+ * Use this macro to implement the Release method for a given <i>_class</i>
+ * implemented as a wholly owned aggregated object intended to implement
+ * interface(s) for its owner
+ * @param _class The name of the class implementing the method
+ * @param _aggregator the owning/containing object
+ */
+#define NS_IMPL_RELEASE_USING_AGGREGATOR(_class, _aggregator) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ NS_PRECONDITION(_aggregator, "null aggregator"); \
+ return (_aggregator)->Release(); \
+}
+
+
+#define NS_IMPL_CYCLE_COLLECTING_ADDREF(_class) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.incr(base); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.decr(base); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count; \
+} \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \
+{ \
+ _destroy; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class) \
+ NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, delete (this))
+
+// _LAST_RELEASE can be useful when certain resources should be released
+// as soon as we know the object will be deleted.
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ bool shouldDelete = false; \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.decr(base, &shouldDelete); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt.incr(base); \
+ _last; \
+ mRefCnt.decr(base); \
+ if (shouldDelete) { \
+ mRefCnt.stabilizeForDeletion(); \
+ DeleteCycleCollectable(); \
+ } \
+ } \
+ return count; \
+} \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \
+{ \
+ delete this; \
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * There are two ways of implementing QueryInterface, and we use both:
+ *
+ * Table-driven QueryInterface uses a static table of IID->offset mappings
+ * and a shared helper function. Using it tends to reduce codesize and improve
+ * runtime performance (due to processor cache hits).
+ *
+ * Macro-driven QueryInterface generates a QueryInterface function directly
+ * using common macros. This is necessary if special QueryInterface features
+ * are being used (such as tearoffs and conditional interfaces).
+ *
+ * These methods can be combined into a table-driven function call followed
+ * by custom code for tearoffs and conditionals.
+ */
+
+struct QITableEntry
+{
+ const nsIID* iid; // null indicates end of the QITableEntry array
+ int32_t offset;
+};
+
+nsresult NS_FASTCALL
+NS_TableDrivenQI(void* aThis, REFNSIID aIID,
+ void** aInstancePtr, const QITableEntry* aEntries);
+
+/**
+ * Implement table-driven queryinterface
+ */
+
+#define NS_INTERFACE_TABLE_HEAD(_class) \
+NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "QueryInterface requires a non-NULL destination!"); \
+ nsresult rv = NS_ERROR_FAILURE;
+
+#define NS_INTERFACE_TABLE_BEGIN \
+ static const QITableEntry table[] = {
+
+#define NS_INTERFACE_TABLE_ENTRY(_class, _interface) \
+ { &NS_GET_IID(_interface), \
+ int32_t(reinterpret_cast<char*>( \
+ static_cast<_interface*>((_class*) 0x1000)) - \
+ reinterpret_cast<char*>((_class*) 0x1000)) \
+ },
+
+#define NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, _interface, _implClass) \
+ { &NS_GET_IID(_interface), \
+ int32_t(reinterpret_cast<char*>( \
+ static_cast<_interface*>( \
+ static_cast<_implClass*>( \
+ (_class*) 0x1000))) - \
+ reinterpret_cast<char*>((_class*) 0x1000)) \
+ },
+
+/*
+ * XXX: we want to use mozilla::ArrayLength (or equivalent,
+ * MOZ_ARRAY_LENGTH) in this condition, but some versions of GCC don't
+ * see that the static_assert condition is actually constant in those
+ * cases, even with constexpr support (?).
+ */
+#define NS_INTERFACE_TABLE_END_WITH_PTR(_ptr) \
+ { nullptr, 0 } }; \
+ static_assert((sizeof(table)/sizeof(table[0])) > 1, "need at least 1 interface"); \
+ rv = NS_TableDrivenQI(static_cast<void*>(_ptr), \
+ aIID, aInstancePtr, table);
+
+#define NS_INTERFACE_TABLE_END \
+ NS_INTERFACE_TABLE_END_WITH_PTR(this)
+
+#define NS_INTERFACE_TABLE_TAIL \
+ return rv; \
+}
+
+#define NS_INTERFACE_TABLE_TAIL_INHERITING(_baseclass) \
+ if (NS_SUCCEEDED(rv)) \
+ return rv; \
+ return _baseclass::QueryInterface(aIID, aInstancePtr); \
+}
+
+#define NS_INTERFACE_TABLE_TAIL_USING_AGGREGATOR(_aggregator) \
+ if (NS_SUCCEEDED(rv)) \
+ return rv; \
+ NS_ASSERTION(_aggregator, "null aggregator"); \
+ return _aggregator->QueryInterface(aIID, aInstancePtr) \
+}
+
+/**
+ * This implements query interface with two assumptions: First, the
+ * class in question implements nsISupports and its own interface and
+ * nothing else. Second, the implementation of the class's primary
+ * inheritance chain leads to its own interface.
+ *
+ * @param _class The name of the class implementing the method
+ * @param _classiiddef The name of the #define symbol that defines the IID
+ * for the class (e.g. NS_ISUPPORTS_IID)
+ */
+
+#define NS_IMPL_QUERY_HEAD(_class) \
+NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "QueryInterface requires a non-NULL destination!"); \
+ nsISupports* foundInterface;
+
+#define NS_IMPL_QUERY_BODY(_interface) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>(this); \
+ else
+
+#define NS_IMPL_QUERY_BODY_CONDITIONAL(_interface, condition) \
+ if ( (condition) && aIID.Equals(NS_GET_IID(_interface))) \
+ foundInterface = static_cast<_interface*>(this); \
+ else
+
+#define NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>( \
+ static_cast<_implClass*>(this)); \
+ else
+
+#define NS_IMPL_QUERY_BODY_AGGREGATED(_interface, _aggregate) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>(_aggregate); \
+ else
+
+#define NS_IMPL_QUERY_TAIL_GUTS \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) \
+ { \
+ /* nsISupports should be handled by this point. If not, fail. */ \
+ MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports))); \
+ status = NS_NOINTERFACE; \
+ } \
+ else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL_INHERITING(_baseclass) \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) \
+ status = _baseclass::QueryInterface(aIID, (void**)&foundInterface); \
+ else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL_USING_AGGREGATOR(_aggregator) \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) { \
+ NS_ASSERTION(_aggregator, "null aggregator"); \
+ status = _aggregator->QueryInterface(aIID, (void**)&foundInterface); \
+ } else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL(_supports_interface) \
+ NS_IMPL_QUERY_BODY_AMBIGUOUS(nsISupports, _supports_interface) \
+ NS_IMPL_QUERY_TAIL_GUTS
+
+
+/*
+ This is the new scheme. Using this notation now will allow us to switch to
+ a table driven mechanism when it's ready. Note the difference between this
+ and the (currently) underlying NS_IMPL_QUERY_INTERFACE mechanism. You must
+ explicitly mention |nsISupports| when using the interface maps.
+*/
+#define NS_INTERFACE_MAP_BEGIN(_implClass) NS_IMPL_QUERY_HEAD(_implClass)
+#define NS_INTERFACE_MAP_ENTRY(_interface) NS_IMPL_QUERY_BODY(_interface)
+#define NS_INTERFACE_MAP_ENTRY_CONDITIONAL(_interface, condition) \
+ NS_IMPL_QUERY_BODY_CONDITIONAL(_interface, condition)
+#define NS_INTERFACE_MAP_ENTRY_AGGREGATED(_interface,_aggregate) \
+ NS_IMPL_QUERY_BODY_AGGREGATED(_interface,_aggregate)
+
+#define NS_INTERFACE_MAP_END NS_IMPL_QUERY_TAIL_GUTS
+#define NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(_interface, _implClass) \
+ NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass)
+#define NS_INTERFACE_MAP_END_INHERITING(_baseClass) \
+ NS_IMPL_QUERY_TAIL_INHERITING(_baseClass)
+#define NS_INTERFACE_MAP_END_AGGREGATED(_aggregator) \
+ NS_IMPL_QUERY_TAIL_USING_AGGREGATOR(_aggregator)
+
+#define NS_INTERFACE_TABLE0(_class) \
+ NS_INTERFACE_TABLE_BEGIN \
+ NS_INTERFACE_TABLE_ENTRY(_class, nsISupports) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_INTERFACE_TABLE(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_TABLE_BEGIN \
+ MOZ_FOR_EACH(NS_INTERFACE_TABLE_ENTRY, (aClass,), (__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(aClass, nsISupports, \
+ MOZ_ARG_1(__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_IMPL_QUERY_INTERFACE0(_class) \
+ NS_INTERFACE_TABLE_HEAD(_class) \
+ NS_INTERFACE_TABLE0(_class) \
+ NS_INTERFACE_TABLE_TAIL
+
+#define NS_IMPL_QUERY_INTERFACE(aClass, ...) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE(aClass, __VA_ARGS__) \
+ NS_INTERFACE_TABLE_TAIL
+
+/**
+ * Declare that you're going to inherit from something that already
+ * implements nsISupports, but also implements an additional interface, thus
+ * causing an ambiguity. In this case you don't need another mRefCnt, you
+ * just need to forward the definitions to the appropriate superclass. E.g.
+ *
+ * class Bar : public Foo, public nsIBar { // both provide nsISupports
+ * public:
+ * NS_DECL_ISUPPORTS_INHERITED
+ * ...other nsIBar and Bar methods...
+ * };
+ */
+#define NS_DECL_ISUPPORTS_INHERITED \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+
+/**
+ * These macros can be used in conjunction with NS_DECL_ISUPPORTS_INHERITED
+ * to implement the nsISupports methods, forwarding the invocations to a
+ * superclass that already implements nsISupports.
+ *
+ * Note that I didn't make these inlined because they're virtual methods.
+ */
+
+#define NS_IMPL_ADDREF_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(Class) \
+ nsrefcnt r = Super::AddRef(); \
+ NS_LOG_ADDREF(this, r, #Class, sizeof(*this)); \
+ return r; \
+}
+
+#define NS_IMPL_RELEASE_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::Release(void) \
+{ \
+ nsrefcnt r = Super::Release(); \
+ NS_LOG_RELEASE(this, r, #Class); \
+ return r; \
+}
+
+/**
+ * As above but not logging the addref/release; needed if the base
+ * class might be aggregated.
+ */
+#define NS_IMPL_NONLOGGING_ADDREF_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(Class) \
+ return Super::AddRef(); \
+}
+
+#define NS_IMPL_NONLOGGING_RELEASE_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::Release(void) \
+{ \
+ return Super::Release(); \
+}
+
+#define NS_INTERFACE_TABLE_INHERITED0(Class) /* Nothing to do here */
+
+#define NS_INTERFACE_TABLE_INHERITED(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_TABLE_BEGIN \
+ MOZ_FOR_EACH(NS_INTERFACE_TABLE_ENTRY, (aClass,), (__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_IMPL_QUERY_INTERFACE_INHERITED(aClass, aSuper, ...) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE_INHERITED(aClass, __VA_ARGS__) \
+ NS_INTERFACE_TABLE_TAIL_INHERITING(aSuper)
+
+/**
+ * Convenience macros for implementing all nsISupports methods for
+ * a simple class.
+ * @param _class The name of the class implementing the method
+ * @param _classiiddef The name of the #define symbol that defines the IID
+ * for the class (e.g. NS_ISUPPORTS_IID)
+ */
+
+#define NS_IMPL_ISUPPORTS0(_class) \
+ NS_IMPL_ADDREF(_class) \
+ NS_IMPL_RELEASE(_class) \
+ NS_IMPL_QUERY_INTERFACE0(_class)
+
+#define NS_IMPL_ISUPPORTS(aClass, ...) \
+ NS_IMPL_ADDREF(aClass) \
+ NS_IMPL_RELEASE(aClass) \
+ NS_IMPL_QUERY_INTERFACE(aClass, __VA_ARGS__)
+
+#define NS_IMPL_ISUPPORTS_INHERITED0(aClass, aSuper) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE_TAIL_INHERITING(aSuper) \
+ NS_IMPL_ADDREF_INHERITED(aClass, aSuper) \
+ NS_IMPL_RELEASE_INHERITED(aClass, aSuper) \
+
+#define NS_IMPL_ISUPPORTS_INHERITED(aClass, aSuper, ...) \
+ NS_IMPL_QUERY_INTERFACE_INHERITED(aClass, aSuper, __VA_ARGS__) \
+ NS_IMPL_ADDREF_INHERITED(aClass, aSuper) \
+ NS_IMPL_RELEASE_INHERITED(aClass, aSuper)
+
+/*
+ * Macro to glue together a QI that starts with an interface table
+ * and segues into an interface map (e.g. it uses singleton classinfo
+ * or tearoffs).
+ */
+#define NS_INTERFACE_TABLE_TO_MAP_SEGUE \
+ if (rv == NS_OK) return rv; \
+ nsISupports* foundInterface;
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ *
+ * Threadsafe implementations of the ISupports convenience macros.
+ *
+ * @note These are not available when linking against the standalone glue,
+ * because the implementation requires PR_ symbols.
+ */
+#define NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_QUERY_TAIL_GUTS
+
+/**
+ * Macro to generate nsIClassInfo methods for classes which do not have
+ * corresponding nsIFactory implementations.
+ */
+#define NS_IMPL_THREADSAFE_CI(_class) \
+NS_IMETHODIMP \
+_class::GetInterfaces(uint32_t* _count, nsIID*** _array) \
+{ \
+ return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetScriptableHelper(nsIXPCScriptable** _retval) \
+{ \
+ *_retval = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetContractID(char** _contractID) \
+{ \
+ *_contractID = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassDescription(char** _classDescription) \
+{ \
+ *_classDescription = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassID(nsCID** _classID) \
+{ \
+ *_classID = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetFlags(uint32_t* _flags) \
+{ \
+ *_flags = nsIClassInfo::THREADSAFE; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \
+{ \
+ return NS_ERROR_NOT_AVAILABLE; \
+}
+
+#endif
diff --git a/xpcom/glue/nsISupportsUtils.h b/xpcom/glue/nsISupportsUtils.h
new file mode 100644
index 000000000..4d306d3d1
--- /dev/null
+++ b/xpcom/glue/nsISupportsUtils.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsISupportsUtils_h__
+#define nsISupportsUtils_h__
+
+#include "nscore.h"
+#include "nsISupportsBase.h"
+#include "nsError.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TypeTraits.h"
+
+/**
+ * Macro for adding a reference to an interface.
+ * @param _ptr The interface pointer.
+ */
+#define NS_ADDREF(_ptr) \
+ (_ptr)->AddRef()
+
+/**
+ * Macro for adding a reference to this. This macro should be used
+ * because NS_ADDREF (when tracing) may require an ambiguous cast
+ * from the pointers primary type to nsISupports. This macro sidesteps
+ * that entire problem.
+ */
+#define NS_ADDREF_THIS() \
+ AddRef()
+
+
+// Making this a |inline| |template| allows |aExpr| to be evaluated only once,
+// yet still denies you the ability to |AddRef()| an |nsCOMPtr|.
+template<class T>
+inline void
+ns_if_addref(T aExpr)
+{
+ if (aExpr) {
+ aExpr->AddRef();
+ }
+}
+
+/**
+ * Macro for adding a reference to an interface that checks for nullptr.
+ * @param _expr The interface pointer.
+ */
+#define NS_IF_ADDREF(_expr) ns_if_addref(_expr)
+
+/*
+ * Given these declarations, it explicitly OK and efficient to end a `getter' with:
+ *
+ * NS_IF_ADDREF(*result = mThing);
+ *
+ * even if |mThing| is an |nsCOMPtr|. If |mThing| is an |nsCOMPtr|, however, it is still
+ * _illegal_ to say |NS_IF_ADDREF(mThing)|.
+ */
+
+/**
+ * Macro for releasing a reference to an interface.
+ * @param _ptr The interface pointer.
+ */
+#define NS_RELEASE(_ptr) \
+ do { \
+ (_ptr)->Release(); \
+ (_ptr) = 0; \
+ } while (0)
+
+/**
+ * Macro for releasing a reference to this interface.
+ */
+#define NS_RELEASE_THIS() \
+ Release()
+
+/**
+ * Macro for releasing a reference to an interface, except that this
+ * macro preserves the return value from the underlying Release call.
+ * The interface pointer argument will only be NULLed if the reference count
+ * goes to zero.
+ *
+ * @param _ptr The interface pointer.
+ * @param _rc The reference count.
+ */
+#define NS_RELEASE2(_ptr, _rc) \
+ do { \
+ _rc = (_ptr)->Release(); \
+ if (0 == (_rc)) (_ptr) = 0; \
+ } while (0)
+
+/**
+ * Macro for releasing a reference to an interface that checks for nullptr;
+ * @param _ptr The interface pointer.
+ */
+#define NS_IF_RELEASE(_ptr) \
+ do { \
+ if (_ptr) { \
+ (_ptr)->Release(); \
+ (_ptr) = 0; \
+ } \
+ } while (0)
+
+/*
+ * Often you have to cast an implementation pointer, e.g., |this|, to an
+ * |nsISupports*|, but because you have multiple inheritance, a simple cast
+ * is ambiguous. One could simply say, e.g., (given a base |nsIBase|),
+ * |static_cast<nsIBase*>(this)|; but that disguises the fact that what
+ * you are really doing is disambiguating the |nsISupports|. You could make
+ * that more obvious with a double cast, e.g., |static_cast<nsISupports*>
+ (* static_cast<nsIBase*>(this))|, but that is bulky and harder to read...
+ *
+ * The following macro is clean, short, and obvious. In the example above,
+ * you would use it like this: |NS_ISUPPORTS_CAST(nsIBase*, this)|.
+ */
+
+#define NS_ISUPPORTS_CAST(__unambiguousBase, __expr) \
+ static_cast<nsISupports*>(static_cast<__unambiguousBase>(__expr))
+
+// a type-safe shortcut for calling the |QueryInterface()| member function
+template<class T, class DestinationType>
+inline nsresult
+CallQueryInterface(T* aSource, DestinationType** aDestination)
+{
+ // We permit nsISupports-to-nsISupports here so that one can still obtain
+ // the canonical nsISupports pointer with CallQueryInterface.
+ static_assert(!mozilla::IsSame<T, DestinationType>::value ||
+ mozilla::IsSame<DestinationType, nsISupports>::value,
+ "don't use CallQueryInterface for compile-time-determinable casts");
+
+ NS_PRECONDITION(aSource, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aSource->QueryInterface(NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class SourceType, class DestinationType>
+inline nsresult
+CallQueryInterface(RefPtr<SourceType>& aSourcePtr, DestinationType** aDestPtr)
+{
+ return CallQueryInterface(aSourcePtr.get(), aDestPtr);
+}
+
+#endif /* __nsISupportsUtils_h */
diff --git a/xpcom/glue/nsIWeakReferenceUtils.h b/xpcom/glue/nsIWeakReferenceUtils.h
new file mode 100644
index 000000000..1c84e00df
--- /dev/null
+++ b/xpcom/glue/nsIWeakReferenceUtils.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIWeakReferenceUtils_h__
+#define nsIWeakReferenceUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsIWeakReference.h"
+
+typedef nsCOMPtr<nsIWeakReference> nsWeakPtr;
+
+/**
+ *
+ */
+
+// a type-safe shortcut for calling the |QueryReferent()| member function
+// T must inherit from nsIWeakReference, but the cast may be ambiguous.
+template<class T, class DestinationType>
+inline nsresult
+CallQueryReferent(T* aSource, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aSource, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aSource->QueryReferent(NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+
+class MOZ_STACK_CLASS nsQueryReferent final : public nsCOMPtr_helper
+{
+public:
+ nsQueryReferent(nsIWeakReference* aWeakPtr, nsresult* aError)
+ : mWeakPtr(aWeakPtr)
+ , mErrorPtr(aError)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const
+ override;
+
+private:
+ nsIWeakReference* MOZ_NON_OWNING_REF mWeakPtr;
+ nsresult* mErrorPtr;
+};
+
+inline const nsQueryReferent
+do_QueryReferent(nsIWeakReference* aRawPtr, nsresult* aError = 0)
+{
+ return nsQueryReferent(aRawPtr, aError);
+}
+
+
+/**
+ * Deprecated, use |do_GetWeakReference| instead.
+ */
+extern nsIWeakReference* NS_GetWeakReference(nsISupports*,
+ nsresult* aResult = 0);
+
+/**
+ * |do_GetWeakReference| is a convenience function that bundles up all the work needed
+ * to get a weak reference to an arbitrary object, i.e., the |QueryInterface|, test, and
+ * call through to |GetWeakReference|, and put it into your |nsCOMPtr|.
+ * It is specifically designed to cooperate with |nsCOMPtr| (or |nsWeakPtr|) like so:
+ * |nsWeakPtr myWeakPtr = do_GetWeakReference(aPtr);|.
+ */
+inline already_AddRefed<nsIWeakReference>
+do_GetWeakReference(nsISupports* aRawPtr, nsresult* aError = 0)
+{
+ return dont_AddRef(NS_GetWeakReference(aRawPtr, aError));
+}
+
+inline void
+do_GetWeakReference(nsIWeakReference* aRawPtr, nsresult* aError = 0)
+{
+ // This signature exists solely to _stop_ you from doing a bad thing.
+ // Saying |do_GetWeakReference()| on a weak reference itself,
+ // is very likely to be a programmer error.
+}
+
+template<class T>
+inline void
+do_GetWeakReference(already_AddRefed<T>&)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_GetWeakReference()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See <http://bugzilla.mozilla.org/show_bug.cgi?id=8221>.
+}
+
+template<class T>
+inline void
+do_GetWeakReference(already_AddRefed<T>&, nsresult*)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_GetWeakReference()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See <http://bugzilla.mozilla.org/show_bug.cgi?id=8221>.
+}
+
+#endif
diff --git a/xpcom/glue/nsInterfaceHashtable.h b/xpcom/glue/nsInterfaceHashtable.h
new file mode 100644
index 000000000..14368af63
--- /dev/null
+++ b/xpcom/glue/nsInterfaceHashtable.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsInterfaceHashtable_h__
+#define nsInterfaceHashtable_h__
+
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsCOMPtr.h"
+
+/**
+ * templated hashtable class maps keys to interface pointers.
+ * See nsBaseHashtable for complete declaration.
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param Interface the interface-type being wrapped
+ * @see nsDataHashtable, nsClassHashtable
+ */
+template<class KeyClass, class Interface>
+class nsInterfaceHashtable
+ : public nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*>
+{
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef Interface* UserDataType;
+ typedef nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*> base_type;
+
+ nsInterfaceHashtable() {}
+ explicit nsInterfaceHashtable(uint32_t aInitLength)
+ : nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*>(aInitLength)
+ {
+ }
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @param aData This is an XPCOM getter, so aData is already_addrefed.
+ * If the key doesn't exist, aData will be set to nullptr.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const;
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ */
+ already_AddRefed<Interface> Get(KeyType aKey) const;
+
+ /**
+ * Gets a weak reference to the hashtable entry.
+ * @param aFound If not nullptr, will be set to true if the entry is found,
+ * to false otherwise.
+ * @return The entry, or nullptr if not found. Do not release this pointer!
+ */
+ Interface* GetWeak(KeyType aKey, bool* aFound = nullptr) const;
+};
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionUnlink(nsInterfaceHashtable<K, T>& aField)
+{
+ aField.Clear();
+}
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ const nsInterfaceHashtable<K, T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ for (auto iter = aField.ConstIter(); !iter.Done(); iter.Next()) {
+ CycleCollectionNoteChild(aCallback, iter.UserData(), aName, aFlags);
+ }
+}
+
+//
+// nsInterfaceHashtable definitions
+//
+
+template<class KeyClass, class Interface>
+bool
+nsInterfaceHashtable<KeyClass, Interface>::Get(KeyType aKey,
+ UserDataType* aInterface) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aInterface) {
+ *aInterface = ent->mData;
+
+ NS_IF_ADDREF(*aInterface);
+ }
+
+ return true;
+ }
+
+ // if the key doesn't exist, set *aInterface to null
+ // so that it is a valid XPCOM getter
+ if (aInterface) {
+ *aInterface = nullptr;
+ }
+
+ return false;
+}
+
+template<class KeyClass, class Interface>
+already_AddRefed<Interface>
+nsInterfaceHashtable<KeyClass, Interface>::Get(KeyType aKey) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return nullptr;
+ }
+
+ nsCOMPtr<Interface> copy = ent->mData;
+ return copy.forget();
+}
+
+template<class KeyClass, class Interface>
+Interface*
+nsInterfaceHashtable<KeyClass, Interface>::GetWeak(KeyType aKey,
+ bool* aFound) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aFound) {
+ *aFound = true;
+ }
+
+ return ent->mData;
+ }
+
+ // Key does not exist, return nullptr and set aFound to false
+ if (aFound) {
+ *aFound = false;
+ }
+ return nullptr;
+}
+
+#endif // nsInterfaceHashtable_h__
diff --git a/xpcom/glue/nsJSThingHashtable.h b/xpcom/glue/nsJSThingHashtable.h
new file mode 100644
index 000000000..8c1b1447d
--- /dev/null
+++ b/xpcom/glue/nsJSThingHashtable.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsJSThingHashtable_h__
+#define nsJSThingHashtable_h__
+
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+
+namespace JS {
+template<class T>
+class Heap;
+} /* namespace JS */
+
+/**
+ * A wrapper for hash keys that sets ALLOW_MEMMOVE to false.
+ *
+ * This is used in the implementation of nsJSThingHashtable and is not intended
+ * to be used directly.
+ *
+ * It is necessary for hash tables containing JS::Heap<T> values as these must
+ * be copied rather than memmoved.
+ */
+template<class T>
+class nsHashKeyDisallowMemmove : public T
+{
+public:
+ explicit nsHashKeyDisallowMemmove(const typename T::KeyTypePointer aKey) : T(aKey) {}
+ enum { ALLOW_MEMMOVE = false };
+};
+
+
+/**
+ * Templated hashtable class for use on the heap where the values are JS GC things.
+ *
+ * Storing JS GC thing pointers on the heap requires wrapping them in a
+ * JS::Heap<T>, and this class takes care of that while presenting an interface
+ * in terms of the wrapped type T.
+ *
+ * For example, to store a hashtable mapping strings to JSObject pointers, you
+ * can declare a data member like this:
+ *
+ * nsJSThingHashtable<nsStringHashKey, JSObject*> mStringToObjectMap;
+ *
+ * See nsBaseHashtable for complete declaration
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the datatype being wrapped, must be a JS GC thing.
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass, class DataType>
+class nsJSThingHashtable
+ : public nsBaseHashtable<nsHashKeyDisallowMemmove<KeyClass>,
+ JS::Heap<DataType>, DataType>
+{
+};
+
+#endif // nsJSThingHashtable_h__
diff --git a/xpcom/glue/nsMemory.cpp b/xpcom/glue/nsMemory.cpp
new file mode 100644
index 000000000..3bf7c1f0f
--- /dev/null
+++ b/xpcom/glue/nsMemory.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsXPCOM.h"
+#include "nsMemory.h"
+#include "nsIMemory.h"
+#include "nsXPCOMPrivate.h"
+#include "nsDebug.h"
+#include "nsISupportsUtils.h"
+#include "nsCOMPtr.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// nsMemory static helper routines
+
+nsresult
+nsMemory::HeapMinimize(bool aImmediate)
+{
+ nsCOMPtr<nsIMemory> mem;
+ nsresult rv = NS_GetMemoryManager(getter_AddRefs(mem));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return mem->HeapMinimize(aImmediate);
+}
+
+void*
+nsMemory::Clone(const void* aPtr, size_t aSize)
+{
+ void* newPtr = NS_Alloc(aSize);
+ if (newPtr) {
+ memcpy(newPtr, aPtr, aSize);
+ }
+ return newPtr;
+}
+
+nsIMemory*
+nsMemory::GetGlobalMemoryService()
+{
+ nsIMemory* mem;
+ nsresult rv = NS_GetMemoryManager(&mem);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ return mem;
+}
+
+//----------------------------------------------------------------------
+
diff --git a/xpcom/glue/nsMemory.h b/xpcom/glue/nsMemory.h
new file mode 100644
index 000000000..6f19b8117
--- /dev/null
+++ b/xpcom/glue/nsMemory.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMemory_h__
+#define nsMemory_h__
+
+#include "nsXPCOM.h"
+
+class nsIMemory;
+
+#define NS_MEMORY_CONTRACTID "@mozilla.org/xpcom/memory-service;1"
+#define NS_MEMORY_CID \
+{ /* 30a04e40-38e7-11d4-8cf5-0060b0fc14a3 */ \
+ 0x30a04e40, \
+ 0x38e7, \
+ 0x11d4, \
+ {0x8c, 0xf5, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
+}
+
+
+/**
+ * Static helper routines to manage memory. These routines allow easy access
+ * to xpcom's built-in (global) nsIMemory implementation, without needing
+ * to go through the service manager to get it. However this requires clients
+ * to link with the xpcom DLL.
+ *
+ * This class is not threadsafe and is intented for use only on the main
+ * thread.
+ */
+class nsMemory
+{
+public:
+ static nsresult HeapMinimize(bool aImmediate);
+ static void* Clone(const void* aPtr, size_t aSize);
+ static nsIMemory* GetGlobalMemoryService(); // AddRefs
+};
+
+/**
+ * Macro to free all elements of an XPCOM array of a given size using
+ * freeFunc, then frees the array itself using free().
+ *
+ * Note that this macro (and its wrappers) can be used to deallocate a
+ * partially- or completely-built array while unwinding an error
+ * condition inside the XPCOM routine that was going to return the
+ * array. For this to work on a partially-built array, your code
+ * needs to be building the array from index 0 upwards, and simply
+ * pass the number of elements that have already been built (and thus
+ * need to be freed) as |size|.
+ *
+ * Thanks to <alecf@netscape.com> for suggesting this form, which
+ * allows the macro to be used with NS_RELEASE / NS_RELEASE_IF in
+ * addition to free.
+ *
+ * @param size Number of elements in the array. If not a constant, this
+ * should be a int32_t. Note that this means this macro
+ * will not work if size >= 2^31.
+ * @param array The array to be freed.
+ * @param freeFunc The function or macro to be used to free it.
+ * For arrays of nsISupports (or any class derived
+ * from it), NS_IF_RELEASE (or NS_RELEASE) should be
+ * passed as freeFunc. For most (all?) other pointer
+ * types (including XPCOM strings and wstrings),
+ * free should be used.
+ */
+#define NS_FREE_XPCOM_POINTER_ARRAY(size, array, freeFunc) \
+ PR_BEGIN_MACRO \
+ int32_t iter_ = int32_t(size); \
+ while (--iter_ >= 0) \
+ freeFunc((array)[iter_]); \
+ NS_Free((array)); \
+ PR_END_MACRO
+
+// convenience macros for commonly used calls. mmmmm. syntactic sugar.
+
+/**
+ * Macro to free arrays of non-refcounted objects allocated by the
+ * shared allocator (nsMemory) such as strings and wstrings. A
+ * convenience wrapper around NS_FREE_XPCOM_POINTER_ARRAY.
+ *
+ * @param size Number of elements in the array. If not a constant, this
+ * should be a int32_t. Note that this means this macro
+ * will not work if size >= 2^31.
+ * @param array The array to be freed.
+ */
+#define NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(size, array) \
+ NS_FREE_XPCOM_POINTER_ARRAY((size), (array), NS_Free)
+
+/**
+ * Macro to free an array of pointers to nsISupports (or classes
+ * derived from it). A convenience wrapper around
+ * NS_FREE_XPCOM_POINTER_ARRAY.
+ *
+ * Note that if you know that none of your nsISupports pointers are
+ * going to be 0, you can gain a bit of speed by calling
+ * NS_FREE_XPCOM_POINTER_ARRAY directly and using NS_RELEASE as your
+ * free function.
+ *
+ * @param size Number of elements in the array. If not a constant, this
+ * should be a int32_t. Note that this means this macro
+ * will not work if size >= 2^31.
+ * @param array The array to be freed.
+ */
+#define NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(size, array) \
+ NS_FREE_XPCOM_POINTER_ARRAY((size), (array), NS_IF_RELEASE)
+
+/**
+ * A macro, NS_ALIGNMENT_OF(t_) that determines the alignment
+ * requirements of a type.
+ */
+namespace mozilla {
+template<class T>
+struct AlignmentTestStruct
+{
+ char c;
+ T t;
+};
+} // namespace mozilla
+
+#define NS_ALIGNMENT_OF(t_) \
+ (sizeof(mozilla::AlignmentTestStruct<t_>) - sizeof(t_))
+
+/**
+ * An enumeration type used to represent a method of assignment.
+ */
+enum nsAssignmentType
+{
+ NS_ASSIGNMENT_COPY, // copy by value
+ NS_ASSIGNMENT_DEPEND, // copy by reference
+ NS_ASSIGNMENT_ADOPT // copy by reference (take ownership of resource)
+};
+
+#endif // nsMemory_h__
+
diff --git a/xpcom/glue/nsPointerHashKeys.h b/xpcom/glue/nsPointerHashKeys.h
new file mode 100644
index 000000000..a89843101
--- /dev/null
+++ b/xpcom/glue/nsPointerHashKeys.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Definitions for nsPtrHashKey<T> and nsVoidPtrHashKey. */
+
+#ifndef nsPointerHashKeys_h
+#define nsPointerHashKeys_h
+
+#include "nscore.h"
+
+#include "mozilla/Attributes.h"
+
+/**
+ * hashkey wrapper using T* KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+template<class T>
+class nsPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef T* KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
+ nsPtrHashKey(const nsPtrHashKey<T>& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsPtrHashKey() {}
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+protected:
+ T* MOZ_NON_OWNING_REF mKey;
+};
+
+typedef nsPtrHashKey<const void> nsVoidPtrHashKey;
+
+#endif // nsPointerHashKeys_h
diff --git a/xpcom/glue/nsProxyRelease.cpp b/xpcom/glue/nsProxyRelease.cpp
new file mode 100644
index 000000000..1a8150cc6
--- /dev/null
+++ b/xpcom/glue/nsProxyRelease.cpp
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+namespace detail {
+
+/* static */ void
+ProxyReleaseChooser<true>::ProxyReleaseISupports(nsIEventTarget* aTarget,
+ nsISupports* aDoomed,
+ bool aAlwaysProxy)
+{
+ ::detail::ProxyRelease<nsISupports>(aTarget, dont_AddRef(aDoomed),
+ aAlwaysProxy);
+}
+
+} // namespace detail
diff --git a/xpcom/glue/nsProxyRelease.h b/xpcom/glue/nsProxyRelease.h
new file mode 100644
index 000000000..d99f970b9
--- /dev/null
+++ b/xpcom/glue/nsProxyRelease.h
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsProxyRelease_h__
+#define nsProxyRelease_h__
+
+#include "nsIEventTarget.h"
+#include "nsIThread.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Unused.h"
+
+#ifdef XPCOM_GLUE_AVOID_NSPR
+#error NS_ProxyRelease implementation depends on NSPR.
+#endif
+
+namespace detail {
+
+template<typename T>
+class ProxyReleaseEvent : public mozilla::Runnable
+{
+public:
+ explicit ProxyReleaseEvent(already_AddRefed<T> aDoomed)
+ : mDoomed(aDoomed.take()) {}
+
+ NS_IMETHOD Run() override
+ {
+ NS_IF_RELEASE(mDoomed);
+ return NS_OK;
+ }
+
+private:
+ T* MOZ_OWNING_REF mDoomed;
+};
+
+template<typename T>
+void
+ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed, bool aAlwaysProxy)
+{
+ // Auto-managing release of the pointer.
+ RefPtr<T> doomed = aDoomed;
+ nsresult rv;
+
+ if (!doomed || !aTarget) {
+ return;
+ }
+
+ if (!aAlwaysProxy) {
+ bool onCurrentThread = false;
+ rv = aTarget->IsOnCurrentThread(&onCurrentThread);
+ if (NS_SUCCEEDED(rv) && onCurrentThread) {
+ return;
+ }
+ }
+
+ nsCOMPtr<nsIRunnable> ev = new ProxyReleaseEvent<T>(doomed.forget());
+
+ rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to post proxy release event, leaking!");
+ // It is better to leak the aDoomed object than risk crashing as
+ // a result of deleting it on the wrong thread.
+ }
+}
+
+template<bool nsISupportsBased>
+struct ProxyReleaseChooser
+{
+ template<typename T>
+ static void ProxyRelease(nsIEventTarget* aTarget,
+ already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy)
+ {
+ ::detail::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
+ }
+};
+
+template<>
+struct ProxyReleaseChooser<true>
+{
+ // We need an intermediate step for handling classes with ambiguous
+ // inheritance to nsISupports.
+ template<typename T>
+ static void ProxyRelease(nsIEventTarget* aTarget,
+ already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy)
+ {
+ ProxyReleaseISupports(aTarget, ToSupports(aDoomed.take()), aAlwaysProxy);
+ }
+
+ static void ProxyReleaseISupports(nsIEventTarget* aTarget,
+ nsISupports* aDoomed,
+ bool aAlwaysProxy);
+};
+
+} // namespace detail
+
+/**
+ * Ensures that the delete of a smart pointer occurs on the target thread.
+ *
+ * @param aTarget
+ * the target thread where the doomed object should be released.
+ * @param aDoomed
+ * the doomed object; the object to be released on the target thread.
+ * @param aAlwaysProxy
+ * normally, if NS_ProxyRelease is called on the target thread, then the
+ * doomed object will be released directly. However, if this parameter is
+ * true, then an event will always be posted to the target thread for
+ * asynchronous release.
+ */
+template<class T>
+inline NS_HIDDEN_(void)
+NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy = false)
+{
+ ::detail::ProxyReleaseChooser<mozilla::IsBaseOf<nsISupports, T>::value>
+ ::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
+}
+
+/**
+ * Ensures that the delete of a smart pointer occurs on the main thread.
+ *
+ * @param aDoomed
+ * the doomed object; the object to be released on the main thread.
+ * @param aAlwaysProxy
+ * normally, if NS_ReleaseOnMainThread is called on the main thread,
+ * then the doomed object will be released directly. However, if this
+ * parameter is true, then an event will always be posted to the main
+ * thread for asynchronous release.
+ */
+template<class T>
+inline NS_HIDDEN_(void)
+NS_ReleaseOnMainThread(already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy = false)
+{
+ // NS_ProxyRelease treats a null event target as "the current thread". So a
+ // handle on the main thread is only necessary when we're not already on the
+ // main thread or the release must happen asynchronously.
+ nsCOMPtr<nsIThread> mainThread;
+ if (!NS_IsMainThread() || aAlwaysProxy) {
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
+ mozilla::Unused << aDoomed.take();
+ return;
+ }
+ }
+
+ NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
+}
+
+/**
+ * Class to safely handle main-thread-only pointers off the main thread.
+ *
+ * Classes like XPCWrappedJS are main-thread-only, which means that it is
+ * forbidden to call methods on instances of these classes off the main thread.
+ * For various reasons (see bug 771074), this restriction recently began to
+ * apply to AddRef/Release as well.
+ *
+ * This presents a problem for consumers that wish to hold a callback alive
+ * on non-main-thread code. A common example of this is the proxy callback
+ * pattern, where non-main-thread code holds a strong-reference to the callback
+ * object, and dispatches new Runnables (also with a strong reference) to the
+ * main thread in order to execute the callback. This involves several AddRef
+ * and Release calls on the other thread, which is (now) verboten.
+ *
+ * The basic idea of this class is to introduce a layer of indirection.
+ * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
+ * maintains one strong reference to the main-thread-only object. It must be
+ * instantiated on the main thread (so that the AddRef of the underlying object
+ * happens on the main thread), but consumers may subsequently pass references
+ * to the holder anywhere they please. These references are meant to be opaque
+ * when accessed off-main-thread (assertions enforce this).
+ *
+ * The semantics of RefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
+ * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
+ * to the above (though it includes various convenience methods). The basic
+ * pattern is as follows.
+ *
+ * // On the main thread:
+ * nsCOMPtr<nsIFooCallback> callback = ...;
+ * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
+ * new nsMainThreadPtrHolder<nsIFooCallback>(callback);
+ * // Pass callbackHandle to structs/classes that might be accessed on other
+ * // threads.
+ *
+ * All structs and classes that might be accessed on other threads should store
+ * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
+ */
+template<class T>
+class nsMainThreadPtrHolder final
+{
+public:
+ // We can only acquire a pointer on the main thread. We to fail fast for
+ // threading bugs, so by default we assert if our pointer is used or acquired
+ // off-main-thread. But some consumers need to use the same pointer for
+ // multiple classes, some of which are main-thread-only and some of which
+ // aren't. So we allow them to explicitly disable this strict checking.
+ explicit nsMainThreadPtrHolder(T* aPtr, bool aStrict = true)
+ : mRawPtr(nullptr)
+ , mStrict(aStrict)
+ {
+ // We can only AddRef our pointer on the main thread, which means that the
+ // holder must be constructed on the main thread.
+ MOZ_ASSERT(!mStrict || NS_IsMainThread());
+ NS_IF_ADDREF(mRawPtr = aPtr);
+ }
+ explicit nsMainThreadPtrHolder(already_AddRefed<T> aPtr, bool aString = true)
+ : mRawPtr(aPtr.take())
+ , mStrict(aString)
+ {
+ // Since we don't need to AddRef the pointer, this constructor is safe to
+ // call on any thread.
+ }
+
+private:
+ // We can be released on any thread.
+ ~nsMainThreadPtrHolder()
+ {
+ if (NS_IsMainThread()) {
+ NS_IF_RELEASE(mRawPtr);
+ } else if (mRawPtr) {
+ NS_ReleaseOnMainThread(dont_AddRef(mRawPtr));
+ }
+ }
+
+public:
+ T* get()
+ {
+ // Nobody should be touching the raw pointer off-main-thread.
+ if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
+ NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
+ MOZ_CRASH();
+ }
+ return mRawPtr;
+ }
+
+ bool operator==(const nsMainThreadPtrHolder<T>& aOther) const
+ {
+ return mRawPtr == aOther.mRawPtr;
+ }
+ bool operator!() const
+ {
+ return !mRawPtr;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
+
+private:
+ // Our wrapped pointer.
+ T* mRawPtr;
+
+ // Whether to strictly enforce thread invariants in this class.
+ bool mStrict;
+
+ // Copy constructor and operator= not implemented. Once constructed, the
+ // holder is immutable.
+ T& operator=(nsMainThreadPtrHolder& aOther);
+ nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther);
+};
+
+template<class T>
+class nsMainThreadPtrHandle
+{
+ RefPtr<nsMainThreadPtrHolder<T>> mPtr;
+
+public:
+ nsMainThreadPtrHandle() : mPtr(nullptr) {}
+ MOZ_IMPLICIT nsMainThreadPtrHandle(decltype(nullptr)) : mPtr(nullptr) {}
+ explicit nsMainThreadPtrHandle(nsMainThreadPtrHolder<T>* aHolder)
+ : mPtr(aHolder)
+ {
+ }
+ explicit nsMainThreadPtrHandle(
+ already_AddRefed<nsMainThreadPtrHolder<T>> aHolder)
+ : mPtr(aHolder)
+ {
+ }
+ nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther)
+ : mPtr(aOther.mPtr)
+ {
+ }
+ nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther)
+ {
+ mPtr = aOther.mPtr;
+ return *this;
+ }
+ nsMainThreadPtrHandle& operator=(nsMainThreadPtrHolder<T>* aHolder)
+ {
+ mPtr = aHolder;
+ return *this;
+ }
+
+ // These all call through to nsMainThreadPtrHolder, and thus implicitly
+ // assert that we're on the main thread. Off-main-thread consumers must treat
+ // these handles as opaque.
+ T* get()
+ {
+ if (mPtr) {
+ return mPtr.get()->get();
+ }
+ return nullptr;
+ }
+ const T* get() const
+ {
+ if (mPtr) {
+ return mPtr.get()->get();
+ }
+ return nullptr;
+ }
+
+ operator T*() { return get(); }
+ T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
+
+ // These are safe to call on other threads with appropriate external locking.
+ bool operator==(const nsMainThreadPtrHandle<T>& aOther) const
+ {
+ if (!mPtr || !aOther.mPtr) {
+ return mPtr == aOther.mPtr;
+ }
+ return *mPtr == *aOther.mPtr;
+ }
+ bool operator!=(const nsMainThreadPtrHandle<T>& aOther) const
+ {
+ return !operator==(aOther);
+ }
+ bool operator==(decltype(nullptr)) const { return mPtr == nullptr; }
+ bool operator!=(decltype(nullptr)) const { return mPtr != nullptr; }
+ bool operator!() const {
+ return !mPtr || !*mPtr;
+ }
+};
+
+namespace mozilla {
+
+template<typename T>
+using PtrHolder = nsMainThreadPtrHolder<T>;
+
+template<typename T>
+using PtrHandle = nsMainThreadPtrHandle<T>;
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/nsQuickSort.cpp b/xpcom/glue/nsQuickSort.cpp
new file mode 100644
index 000000000..f409b875e
--- /dev/null
+++ b/xpcom/glue/nsQuickSort.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* We need this because Solaris' version of qsort is broken and
+ * causes array bounds reads.
+ */
+
+#include <stdlib.h>
+#include "nsAlgorithm.h"
+#include "nsQuickSort.h"
+
+extern "C" {
+
+#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc))
+# ifndef INLINE
+# define INLINE inline
+# endif
+#else
+# define INLINE
+#endif
+
+typedef int cmp_t(const void *, const void *, void *);
+static INLINE char *med3(char *, char *, char *, cmp_t *, void *);
+static INLINE void swapfunc(char *, char *, int, int);
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ TYPE *pi = (TYPE *) (parmi); \
+ TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static INLINE void
+swapfunc(char *a, char *b, int n, int swaptype)
+{
+ if(swaptype <= 1)
+ swapcode(long, a, b, n)
+ else
+ swapcode(char, a, b, n)
+}
+
+#define swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc((char *)a, (char*)b, (int)es, swaptype)
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc((char *)a, (char *)b, (int)n, swaptype)
+
+static INLINE char *
+med3(char *a, char *b, char *c, cmp_t* cmp, void *data)
+{
+ return cmp(a, b, data) < 0 ?
+ (cmp(b, c, data) < 0 ? b : (cmp(a, c, data) < 0 ? c : a ))
+ :(cmp(b, c, data) > 0 ? b : (cmp(a, c, data) < 0 ? a : c ));
+}
+
+void NS_QuickSort (
+ void *a,
+ unsigned int n,
+ unsigned int es,
+ cmp_t *cmp,
+ void *data
+ )
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype;
+
+loop: SWAPINIT(a, es);
+ /* Use insertion sort when input is small */
+ if (n < 7) {
+ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
+ for (pl = pm; pl > (char *)a && cmp(pl - es, pl, data) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+ /* Choose pivot */
+ pm = (char *)a + (n / 2) * es;
+ if (n > 7) {
+ pl = (char *)a;
+ pn = (char *)a + (n - 1) * es;
+ if (n > 40) {
+ d = (n / 8) * es;
+ pl = med3(pl, pl + d, pl + 2 * d, cmp, data);
+ pm = med3(pm - d, pm, pm + d, cmp, data);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp, data);
+ }
+ pm = med3(pl, pm, pn, cmp, data);
+ }
+ swap(a, pm);
+ pa = pb = (char *)a + es;
+
+ pc = pd = (char *)a + (n - 1) * es;
+ /* loop invariants:
+ * [a, pa) = pivot
+ * [pa, pb) < pivot
+ * [pb, pc + es) unprocessed
+ * [pc + es, pd + es) > pivot
+ * [pd + es, pn) = pivot
+ */
+ for (;;) {
+ while (pb <= pc && (r = cmp(pb, a, data)) <= 0) {
+ if (r == 0) {
+ swap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (r = cmp(pc, a, data)) >= 0) {
+ if (r == 0) {
+ swap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ pb += es;
+ pc -= es;
+ }
+ /* Move pivot values */
+ pn = (char *)a + n * es;
+ r = XPCOM_MIN(pa - (char *)a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = XPCOM_MIN<size_t>(pd - pc, pn - pd - es);
+ vecswap(pb, pn - r, r);
+ /* Recursively process partitioned items */
+ if ((r = pb - pa) > (int)es)
+ NS_QuickSort(a, r / es, es, cmp, data);
+ if ((r = pd - pc) > (int)es) {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+/* NS_QuickSort(pn - r, r / es, es, cmp, data);*/
+}
+
+}
+
+#undef INLINE
+#undef swapcode
+#undef SWAPINIT
+#undef swap
+#undef vecswap
diff --git a/xpcom/glue/nsQuickSort.h b/xpcom/glue/nsQuickSort.h
new file mode 100644
index 000000000..e8d8ed870
--- /dev/null
+++ b/xpcom/glue/nsQuickSort.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/* We need this because Solaris' version of qsort is broken and
+ * causes array bounds reads.
+ */
+
+#ifndef nsQuickSort_h___
+#define nsQuickSort_h___
+
+#include "nscore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Parameters:
+ * 1. the array to sort
+ * 2. the number of elements in the array
+ * 3. the size of each array element
+ * 4. comparison function taking two elements and parameter #5 and
+ * returning an integer:
+ * + less than zero if the first element should be before the second
+ * + 0 if the order of the elements does not matter
+ * + greater than zero if the second element should be before the first
+ * 5. extra data to pass to comparison function
+ */
+void NS_QuickSort(void*, unsigned int, unsigned int,
+ int (*)(const void*, const void*, void*),
+ void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* nsQuickSort_h___ */
diff --git a/xpcom/glue/nsRefPtrHashtable.h b/xpcom/glue/nsRefPtrHashtable.h
new file mode 100644
index 000000000..0ff1e0181
--- /dev/null
+++ b/xpcom/glue/nsRefPtrHashtable.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsRefPtrHashtable_h__
+#define nsRefPtrHashtable_h__
+
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
+
+/**
+ * templated hashtable class maps keys to reference pointers.
+ * See nsBaseHashtable for complete declaration.
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param PtrType the reference-type being wrapped
+ * @see nsDataHashtable, nsClassHashtable
+ */
+template<class KeyClass, class PtrType>
+class nsRefPtrHashtable
+ : public nsBaseHashtable<KeyClass, RefPtr<PtrType>, PtrType*>
+{
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef PtrType* UserDataType;
+ typedef nsBaseHashtable<KeyClass, RefPtr<PtrType>, PtrType*> base_type;
+
+ nsRefPtrHashtable() {}
+ explicit nsRefPtrHashtable(uint32_t aInitLength)
+ : nsBaseHashtable<KeyClass, RefPtr<PtrType>, PtrType*>(aInitLength)
+ {
+ }
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @param aData This is an XPCOM getter, so aData is already_addrefed.
+ * If the key doesn't exist, aData will be set to nullptr.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const;
+
+ /**
+ * Gets a weak reference to the hashtable entry.
+ * @param aFound If not nullptr, will be set to true if the entry is found,
+ * to false otherwise.
+ * @return The entry, or nullptr if not found. Do not release this pointer!
+ */
+ PtrType* GetWeak(KeyType aKey, bool* aFound = nullptr) const;
+
+ // Overload Put, rather than overriding it.
+ using base_type::Put;
+
+ void Put(KeyType aKey, already_AddRefed<PtrType> aData);
+
+ MOZ_MUST_USE bool Put(KeyType aKey, already_AddRefed<PtrType> aData,
+ const mozilla::fallible_t&);
+
+ // Overload Remove, rather than overriding it.
+ using base_type::Remove;
+
+ /**
+ * Remove the data for the associated key, swapping the current value into
+ * pData, thereby avoiding calls to AddRef and Release.
+ * @param aKey the key to remove from the hashtable
+ * @param aData This is an XPCOM getter, so aData is already_addrefed.
+ * If the key doesn't exist, aData will be set to nullptr. Must be non-null.
+ */
+ bool Remove(KeyType aKey, UserDataType* aData);
+};
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionUnlink(nsRefPtrHashtable<K, T>& aField)
+{
+ aField.Clear();
+}
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsRefPtrHashtable<K, T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ for (auto iter = aField.ConstIter(); !iter.Done(); iter.Next()) {
+ CycleCollectionNoteChild(aCallback, iter.UserData(), aName, aFlags);
+ }
+}
+
+//
+// nsRefPtrHashtable definitions
+//
+
+template<class KeyClass, class PtrType>
+bool
+nsRefPtrHashtable<KeyClass, PtrType>::Get(KeyType aKey,
+ UserDataType* aRefPtr) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aRefPtr) {
+ *aRefPtr = ent->mData;
+
+ NS_IF_ADDREF(*aRefPtr);
+ }
+
+ return true;
+ }
+
+ // if the key doesn't exist, set *aRefPtr to null
+ // so that it is a valid XPCOM getter
+ if (aRefPtr) {
+ *aRefPtr = nullptr;
+ }
+
+ return false;
+}
+
+template<class KeyClass, class PtrType>
+PtrType*
+nsRefPtrHashtable<KeyClass, PtrType>::GetWeak(KeyType aKey, bool* aFound) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aFound) {
+ *aFound = true;
+ }
+
+ return ent->mData;
+ }
+
+ // Key does not exist, return nullptr and set aFound to false
+ if (aFound) {
+ *aFound = false;
+ }
+
+ return nullptr;
+}
+
+template<class KeyClass, class PtrType>
+void
+nsRefPtrHashtable<KeyClass, PtrType>::Put(KeyType aKey,
+ already_AddRefed<PtrType> aData)
+{
+ if (!Put(aKey, mozilla::Move(aData), mozilla::fallible)) {
+ NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
+ }
+}
+
+template<class KeyClass, class PtrType>
+bool
+nsRefPtrHashtable<KeyClass, PtrType>::Put(KeyType aKey,
+ already_AddRefed<PtrType> aData,
+ const mozilla::fallible_t&)
+{
+ typename base_type::EntryType* ent = this->PutEntry(aKey);
+
+ if (!ent) {
+ return false;
+ }
+
+ ent->mData = aData;
+
+ return true;
+}
+
+template<class KeyClass, class PtrType>
+bool
+nsRefPtrHashtable<KeyClass, PtrType>::Remove(KeyType aKey,
+ UserDataType* aRefPtr)
+{
+ MOZ_ASSERT(aRefPtr);
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ ent->mData.forget(aRefPtr);
+ this->Remove(aKey);
+ return true;
+ }
+
+ // If the key doesn't exist, set *aRefPtr to null
+ // so that it is a valid XPCOM getter.
+ *aRefPtr = nullptr;
+ return false;
+}
+
+#endif // nsRefPtrHashtable_h__
diff --git a/xpcom/glue/nsServiceManagerUtils.h b/xpcom/glue/nsServiceManagerUtils.h
new file mode 100644
index 000000000..d1ea408a2
--- /dev/null
+++ b/xpcom/glue/nsServiceManagerUtils.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsServiceManagerUtils_h__
+#define nsServiceManagerUtils_h__
+
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+
+inline const nsGetServiceByCID
+do_GetService(const nsCID& aCID)
+{
+ return nsGetServiceByCID(aCID);
+}
+
+inline const nsGetServiceByCIDWithError
+do_GetService(const nsCID& aCID, nsresult* aError)
+{
+ return nsGetServiceByCIDWithError(aCID, aError);
+}
+
+inline const nsGetServiceByContractID
+do_GetService(const char* aContractID)
+{
+ return nsGetServiceByContractID(aContractID);
+}
+
+inline const nsGetServiceByContractIDWithError
+do_GetService(const char* aContractID, nsresult* aError)
+{
+ return nsGetServiceByContractIDWithError(aContractID, aError);
+}
+
+class MOZ_STACK_CLASS nsGetServiceFromCategory final : public nsCOMPtr_helper
+{
+public:
+ nsGetServiceFromCategory(const char* aCategory, const char* aEntry,
+ nsresult* aErrorPtr)
+ : mCategory(aCategory)
+ , mEntry(aEntry)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
+ override;
+protected:
+ const char* mCategory;
+ const char* mEntry;
+ nsresult* mErrorPtr;
+};
+
+inline const nsGetServiceFromCategory
+do_GetServiceFromCategory(const char* aCategory, const char* aEntry,
+ nsresult* aError = 0)
+{
+ return nsGetServiceFromCategory(aCategory, aEntry, aError);
+}
+
+nsresult CallGetService(const nsCID& aClass, const nsIID& aIID, void** aResult);
+
+nsresult CallGetService(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+// type-safe shortcuts for calling |GetService|
+template<class DestinationType>
+inline nsresult
+CallGetService(const nsCID& aClass,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetService(aClass,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallGetService(const char* aContractID,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aContractID, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetService(aContractID,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+#endif
diff --git a/xpcom/glue/nsStringAPI.cpp b/xpcom/glue/nsStringAPI.cpp
new file mode 100644
index 000000000..e5114a149
--- /dev/null
+++ b/xpcom/glue/nsStringAPI.cpp
@@ -0,0 +1,1304 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nscore.h"
+#include "nsCRTGlue.h"
+#include "prprf.h"
+#include "nsStringAPI.h"
+#include "nsXPCOMStrings.h"
+#include "nsDebug.h"
+
+#include "mozilla/Sprintf.h"
+
+#include <stdio.h>
+
+// nsAString
+
+uint32_t
+nsAString::BeginReading(const char_type** aBegin, const char_type** aEnd) const
+{
+ uint32_t len = NS_StringGetData(*this, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+const nsAString::char_type*
+nsAString::BeginReading() const
+{
+ const char_type* data;
+ NS_StringGetData(*this, &data);
+ return data;
+}
+
+const nsAString::char_type*
+nsAString::EndReading() const
+{
+ const char_type* data;
+ uint32_t len = NS_StringGetData(*this, &data);
+ return data + len;
+}
+
+uint32_t
+nsAString::BeginWriting(char_type** aBegin, char_type** aEnd, uint32_t aNewSize)
+{
+ uint32_t len = NS_StringGetMutableData(*this, aNewSize, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+nsAString::char_type*
+nsAString::BeginWriting(uint32_t aLen)
+{
+ char_type* data;
+ NS_StringGetMutableData(*this, aLen, &data);
+ return data;
+}
+
+nsAString::char_type*
+nsAString::EndWriting()
+{
+ char_type* data;
+ uint32_t len = NS_StringGetMutableData(*this, UINT32_MAX, &data);
+ return data + len;
+}
+
+bool
+nsAString::SetLength(uint32_t aLen)
+{
+ char_type* data;
+ NS_StringGetMutableData(*this, aLen, &data);
+ return data != nullptr;
+}
+
+void
+nsAString::AssignLiteral(const char* aStr)
+{
+ uint32_t len = strlen(aStr);
+ char16_t* buf = BeginWriting(len);
+ if (!buf) {
+ return;
+ }
+
+ for (; *aStr; ++aStr, ++buf) {
+ *buf = *aStr;
+ }
+}
+
+void
+nsAString::AppendLiteral(const char* aASCIIStr)
+{
+ uint32_t appendLen = strlen(aASCIIStr);
+
+ uint32_t thisLen = Length();
+ char16_t* begin;
+ char16_t* end;
+ BeginWriting(&begin, &end, appendLen + thisLen);
+ if (!begin) {
+ return;
+ }
+
+ for (begin += thisLen; begin < end; ++begin, ++aASCIIStr) {
+ *begin = *aASCIIStr;
+ }
+}
+
+void
+nsAString::StripChars(const char* aSet)
+{
+ nsString copy(*this);
+
+ const char_type* source;
+ const char_type* sourceEnd;
+ copy.BeginReading(&source, &sourceEnd);
+
+ char_type* dest;
+ BeginWriting(&dest);
+ if (!dest) {
+ return;
+ }
+
+ char_type* curDest = dest;
+
+ for (; source < sourceEnd; ++source) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*source == char_type(*test)) {
+ break;
+ }
+ }
+
+ if (!*test) {
+ // not stripped, copy this char
+ *curDest = *source;
+ ++curDest;
+ }
+ }
+
+ SetLength(curDest - dest);
+}
+
+void
+nsAString::Trim(const char* aSet, bool aLeading, bool aTrailing)
+{
+ NS_ASSERTION(aLeading || aTrailing, "Ineffective Trim");
+
+ const char16_t* start;
+ const char16_t* end;
+ uint32_t cutLen;
+
+ if (aLeading) {
+ BeginReading(&start, &end);
+ for (cutLen = 0; start < end; ++start, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *start) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_StringCutData(*this, 0, cutLen);
+ }
+ }
+ if (aTrailing) {
+ uint32_t len = BeginReading(&start, &end);
+ --end;
+ for (cutLen = 0; end >= start; --end, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *end) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_StringCutData(*this, len - cutLen, cutLen);
+ }
+ }
+}
+
+int32_t
+nsAString::DefaultComparator(const char_type* aStrA, const char_type* aStrB,
+ uint32_t aLen)
+{
+ for (const char_type* end = aStrA + aLen; aStrA < end; ++aStrA, ++aStrB) {
+ if (*aStrA == *aStrB) {
+ continue;
+ }
+
+ return *aStrA < *aStrB ? -1 : 1;
+ }
+
+ return 0;
+}
+
+int32_t
+nsAString::Compare(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_strlen(aOther);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, aOther, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+int32_t
+nsAString::Compare(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_StringGetData(aOther, &cother);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, cother, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+bool
+nsAString::Equals(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_strlen(aOther);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, aOther, selflen) == 0;
+}
+
+bool
+nsAString::Equals(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_StringGetData(aOther, &cother);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, cother, selflen) == 0;
+}
+
+bool
+nsAString::EqualsLiteral(const char* aASCIIString) const
+{
+ const char16_t* begin;
+ const char16_t* end;
+ BeginReading(&begin, &end);
+
+ for (; begin < end; ++begin, ++aASCIIString) {
+ if (!*aASCIIString || !NS_IsAscii(*begin) ||
+ (char)*begin != *aASCIIString) {
+ return false;
+ }
+ }
+
+ return *aASCIIString == '\0';
+}
+
+bool
+nsAString::LowerCaseEqualsLiteral(const char* aASCIIString) const
+{
+ const char16_t* begin;
+ const char16_t* end;
+ BeginReading(&begin, &end);
+
+ for (; begin < end; ++begin, ++aASCIIString) {
+ if (!*aASCIIString || !NS_IsAscii(*begin) ||
+ NS_ToLower((char)*begin) != *aASCIIString) {
+ return false;
+ }
+ }
+
+ return *aASCIIString == '\0';
+}
+
+int32_t
+nsAString::Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aOffset > selflen) {
+ return -1;
+ }
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (otherlen > selflen - aOffset) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= otherlen;
+
+ for (const char_type* cur = begin + aOffset; cur <= end; ++cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+static bool
+ns_strnmatch(const char16_t* aStr, const char* aSubstring, uint32_t aLen)
+{
+ for (; aLen; ++aStr, ++aSubstring, --aLen) {
+ if (!NS_IsAscii(*aStr)) {
+ return false;
+ }
+
+ if ((char)*aStr != *aSubstring) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+ns_strnimatch(const char16_t* aStr, const char* aSubstring, uint32_t aLen)
+{
+ for (; aLen; ++aStr, ++aSubstring, --aLen) {
+ if (!NS_IsAscii(*aStr)) {
+ return false;
+ }
+
+ if (NS_ToLower((char)*aStr) != NS_ToLower(*aSubstring)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int32_t
+nsAString::Find(const char* aStr, uint32_t aOffset, bool aIgnoreCase) const
+{
+ bool (*match)(const char16_t*, const char*, uint32_t) =
+ aIgnoreCase ? ns_strnimatch : ns_strnmatch;
+
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aOffset > selflen) {
+ return -1;
+ }
+
+ uint32_t otherlen = strlen(aStr);
+
+ if (otherlen > selflen - aOffset) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= otherlen;
+
+ for (const char_type* cur = begin + aOffset; cur <= end; ++cur) {
+ if (match(cur, aStr, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsAString::RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (selflen < otherlen) {
+ return -1;
+ }
+
+ if (aOffset < 0 || uint32_t(aOffset) > (selflen - otherlen)) {
+ end -= otherlen;
+ } else {
+ end = begin + aOffset;
+ }
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsAString::RFind(const char* aStr, int32_t aOffset, bool aIgnoreCase) const
+{
+ bool (*match)(const char16_t*, const char*, uint32_t) =
+ aIgnoreCase ? ns_strnimatch : ns_strnmatch;
+
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+ uint32_t otherlen = strlen(aStr);
+
+ if (selflen < otherlen) {
+ return -1;
+ }
+
+ if (aOffset < 0 || uint32_t(aOffset) > (selflen - otherlen)) {
+ end -= otherlen;
+ } else {
+ end = begin + aOffset;
+ }
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (match(cur, aStr, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsAString::FindChar(char_type aChar, uint32_t aOffset) const
+{
+ const char_type* start;
+ const char_type* end;
+ uint32_t len = BeginReading(&start, &end);
+ if (aOffset > len) {
+ return -1;
+ }
+
+ const char_type* cur;
+
+ for (cur = start + aOffset; cur < end; ++cur) {
+ if (*cur == aChar) {
+ return cur - start;
+ }
+ }
+
+ return -1;
+}
+
+int32_t
+nsAString::RFindChar(char_type aChar) const
+{
+ const char16_t* start;
+ const char16_t* end;
+ BeginReading(&start, &end);
+
+ do {
+ --end;
+
+ if (*end == aChar) {
+ return end - start;
+ }
+
+ } while (end >= start);
+
+ return -1;
+}
+
+void
+nsAString::AppendInt(int aInt, int32_t aRadix)
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 8:
+ fmt = "%o";
+ break;
+
+ case 10:
+ fmt = "%d";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix");
+ fmt = "";
+ }
+
+ char buf[20];
+ int len = SprintfLiteral(buf, fmt, aInt);
+ Append(NS_ConvertASCIItoUTF16(buf, len));
+}
+
+// Strings
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+int32_t
+nsAString::ToInteger(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ NS_ConvertUTF16toUTF8 narrow(*this);
+
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%i";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int32_t result = 0;
+ if (PR_sscanf(narrow.get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+
+int64_t
+nsAString::ToInteger64(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ NS_ConvertUTF16toUTF8 narrow(*this);
+
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%lli";
+ break;
+
+ case 16:
+ fmt = "%llx";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int64_t result = 0;
+ if (PR_sscanf(narrow.get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+// nsACString
+
+uint32_t
+nsACString::BeginReading(const char_type** aBegin, const char_type** aEnd) const
+{
+ uint32_t len = NS_CStringGetData(*this, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+const nsACString::char_type*
+nsACString::BeginReading() const
+{
+ const char_type* data;
+ NS_CStringGetData(*this, &data);
+ return data;
+}
+
+const nsACString::char_type*
+nsACString::EndReading() const
+{
+ const char_type* data;
+ uint32_t len = NS_CStringGetData(*this, &data);
+ return data + len;
+}
+
+uint32_t
+nsACString::BeginWriting(char_type** aBegin, char_type** aEnd,
+ uint32_t aNewSize)
+{
+ uint32_t len = NS_CStringGetMutableData(*this, aNewSize, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+nsACString::char_type*
+nsACString::BeginWriting(uint32_t aLen)
+{
+ char_type* data;
+ NS_CStringGetMutableData(*this, aLen, &data);
+ return data;
+}
+
+nsACString::char_type*
+nsACString::EndWriting()
+{
+ char_type* data;
+ uint32_t len = NS_CStringGetMutableData(*this, UINT32_MAX, &data);
+ return data + len;
+}
+
+bool
+nsACString::SetLength(uint32_t aLen)
+{
+ char_type* data;
+ NS_CStringGetMutableData(*this, aLen, &data);
+ return data != nullptr;
+}
+
+void
+nsACString::StripChars(const char* aSet)
+{
+ nsCString copy(*this);
+
+ const char_type* source;
+ const char_type* sourceEnd;
+ copy.BeginReading(&source, &sourceEnd);
+
+ char_type* dest;
+ BeginWriting(&dest);
+ if (!dest) {
+ return;
+ }
+
+ char_type* curDest = dest;
+
+ for (; source < sourceEnd; ++source) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*source == char_type(*test)) {
+ break;
+ }
+ }
+
+ if (!*test) {
+ // not stripped, copy this char
+ *curDest = *source;
+ ++curDest;
+ }
+ }
+
+ SetLength(curDest - dest);
+}
+
+void
+nsACString::Trim(const char* aSet, bool aLeading, bool aTrailing)
+{
+ NS_ASSERTION(aLeading || aTrailing, "Ineffective Trim");
+
+ const char* start;
+ const char* end;
+ uint32_t cutLen;
+
+ if (aLeading) {
+ BeginReading(&start, &end);
+ for (cutLen = 0; start < end; ++start, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *start) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_CStringCutData(*this, 0, cutLen);
+ }
+ }
+ if (aTrailing) {
+ uint32_t len = BeginReading(&start, &end);
+ --end;
+ for (cutLen = 0; end >= start; --end, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *end) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_CStringCutData(*this, len - cutLen, cutLen);
+ }
+ }
+}
+
+int32_t
+nsACString::DefaultComparator(const char_type* aStrA, const char_type* aStrB,
+ uint32_t aLen)
+{
+ return memcmp(aStrA, aStrB, aLen);
+}
+
+int32_t
+nsACString::Compare(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = strlen(aOther);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, aOther, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+int32_t
+nsACString::Compare(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = NS_CStringGetData(aOther, &cother);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, cother, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+bool
+nsACString::Equals(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = strlen(aOther);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, aOther, selflen) == 0;
+}
+
+bool
+nsACString::Equals(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = NS_CStringGetData(aOther, &cother);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, cother, selflen) == 0;
+}
+
+int32_t
+nsACString::Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aOffset > selflen) {
+ return -1;
+ }
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (otherlen > selflen - aOffset) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= otherlen;
+
+ for (const char_type* cur = begin + aOffset; cur <= end; ++cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::Find(const char_type* aStr, ComparatorFunc aComparator) const
+{
+ return Find(aStr, strlen(aStr), aComparator);
+}
+
+int32_t
+nsACString::Find(const char_type* aStr, uint32_t aLen,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aLen == 0) {
+ NS_WARNING("Searching for zero-length string.");
+ return -1;
+ }
+
+ if (aLen > selflen) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= aLen;
+
+ for (const char_type* cur = begin; cur <= end; ++cur) {
+ if (!aComparator(cur, aStr, aLen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (selflen < otherlen) {
+ return -1;
+ }
+
+ if (aOffset < 0 || uint32_t(aOffset) > (selflen - otherlen)) {
+ end -= otherlen;
+ } else {
+ end = begin + aOffset;
+ }
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::RFind(const char_type* aStr, ComparatorFunc aComparator) const
+{
+ return RFind(aStr, strlen(aStr), aComparator);
+}
+
+int32_t
+nsACString::RFind(const char_type* aStr, int32_t aLen,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aLen <= 0) {
+ NS_WARNING("Searching for zero-length string.");
+ return -1;
+ }
+
+ if (uint32_t(aLen) > selflen) {
+ return -1;
+ }
+
+ // We want to start searching otherlen characters before the end of the string
+ end -= aLen;
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (!aComparator(cur, aStr, aLen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::FindChar(char_type aChar, uint32_t aOffset) const
+{
+ const char_type* start;
+ const char_type* end;
+ uint32_t len = BeginReading(&start, &end);
+ if (aOffset > len) {
+ return -1;
+ }
+
+ const char_type* cur;
+
+ for (cur = start + aOffset; cur < end; ++cur) {
+ if (*cur == aChar) {
+ return cur - start;
+ }
+ }
+
+ return -1;
+}
+
+int32_t
+nsACString::RFindChar(char_type aChar) const
+{
+ const char* start;
+ const char* end;
+ BeginReading(&start, &end);
+
+ for (; end >= start; --end) {
+ if (*end == aChar) {
+ return end - start;
+ }
+ }
+
+ return -1;
+}
+
+void
+nsACString::AppendInt(int aInt, int32_t aRadix)
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 8:
+ fmt = "%o";
+ break;
+
+ case 10:
+ fmt = "%d";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix");
+ fmt = "";
+ }
+
+ char buf[20];
+ int len = SprintfLiteral(buf, fmt, aInt);
+ Append(buf, len);
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+int32_t
+nsACString::ToInteger(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%i";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int32_t result = 0;
+ if (PR_sscanf(nsCString(*this).get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+
+int64_t
+nsACString::ToInteger64(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%lli";
+ break;
+
+ case 16:
+ fmt = "%llx";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int64_t result = 0;
+ if (PR_sscanf(nsCString(*this).get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+// Substrings
+
+nsDependentSubstring::nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos)
+{
+ const char16_t* data;
+ uint32_t len = NS_StringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ NS_StringContainerInit2(*this, data + aStartPos, len - aStartPos,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+}
+
+nsDependentSubstring::nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos,
+ uint32_t aLength)
+{
+ const char16_t* data;
+ uint32_t len = NS_StringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ if (aStartPos + aLength > len) {
+ aLength = len - aStartPos;
+ }
+
+ NS_StringContainerInit2(*this, data + aStartPos, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+}
+
+nsDependentCSubstring::nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos)
+{
+ const char* data;
+ uint32_t len = NS_CStringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ NS_CStringContainerInit2(*this, data + aStartPos, len - aStartPos,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+}
+
+nsDependentCSubstring::nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos,
+ uint32_t aLength)
+{
+ const char* data;
+ uint32_t len = NS_CStringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ if (aStartPos + aLength > len) {
+ aLength = len - aStartPos;
+ }
+
+ NS_CStringContainerInit2(*this, data + aStartPos, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+}
+
+// Utils
+
+char*
+ToNewUTF8String(const nsAString& aSource)
+{
+ nsCString temp;
+ CopyUTF16toUTF8(aSource, temp);
+ return NS_CStringCloneData(temp);
+}
+
+void
+CompressWhitespace(nsAString& aString)
+{
+ char16_t* start;
+ uint32_t len = NS_StringGetMutableData(aString, UINT32_MAX, &start);
+ char16_t* end = start + len;
+ char16_t* from = start;
+ char16_t* to = start;
+
+ // Skip any leading whitespace
+ while (from < end && NS_IsAsciiWhitespace(*from)) {
+ from++;
+ }
+
+ while (from < end) {
+ char16_t theChar = *from++;
+
+ if (NS_IsAsciiWhitespace(theChar)) {
+ // We found a whitespace char, so skip over any more
+ while (from < end && NS_IsAsciiWhitespace(*from)) {
+ from++;
+ }
+
+ // Turn all whitespace into spaces
+ theChar = ' ';
+ }
+
+ *to++ = theChar;
+ }
+
+ // Drop any trailing space
+ if (to > start && to[-1] == ' ') {
+ to--;
+ }
+
+ // Re-terminate the string
+ *to = '\0';
+
+ // Set the new length
+ aString.SetLength(to - start);
+}
+
+uint32_t
+ToLowerCase(nsACString& aStr)
+{
+ char* begin;
+ char* end;
+ uint32_t len = aStr.BeginWriting(&begin, &end);
+
+ for (; begin < end; ++begin) {
+ *begin = NS_ToLower(*begin);
+ }
+
+ return len;
+}
+
+uint32_t
+ToUpperCase(nsACString& aStr)
+{
+ char* begin;
+ char* end;
+ uint32_t len = aStr.BeginWriting(&begin, &end);
+
+ for (; begin < end; ++begin) {
+ *begin = NS_ToUpper(*begin);
+ }
+
+ return len;
+}
+
+uint32_t
+ToLowerCase(const nsACString& aSrc, nsACString& aDest)
+{
+ const char* begin;
+ const char* end;
+ uint32_t len = aSrc.BeginReading(&begin, &end);
+
+ char* dest;
+ NS_CStringGetMutableData(aDest, len, &dest);
+
+ for (; begin < end; ++begin, ++dest) {
+ *dest = NS_ToLower(*begin);
+ }
+
+ return len;
+}
+
+uint32_t
+ToUpperCase(const nsACString& aSrc, nsACString& aDest)
+{
+ const char* begin;
+ const char* end;
+ uint32_t len = aSrc.BeginReading(&begin, &end);
+
+ char* dest;
+ NS_CStringGetMutableData(aDest, len, &dest);
+
+ for (; begin < end; ++begin, ++dest) {
+ *dest = NS_ToUpper(*begin);
+ }
+
+ return len;
+}
+
+int32_t
+CaseInsensitiveCompare(const char* aStrA, const char* aStrB,
+ uint32_t aLen)
+{
+ for (const char* aend = aStrA + aLen; aStrA < aend; ++aStrA, ++aStrB) {
+ char la = NS_ToLower(*aStrA);
+ char lb = NS_ToLower(*aStrB);
+
+ if (la == lb) {
+ continue;
+ }
+
+ return la < lb ? -1 : 1;
+ }
+
+ return 0;
+}
+
+bool
+ParseString(const nsACString& aSource, char aDelimiter,
+ nsTArray<nsCString>& aArray)
+{
+ int32_t start = 0;
+ int32_t end = aSource.Length();
+
+ uint32_t oldLength = aArray.Length();
+
+ for (;;) {
+ int32_t delimiter = aSource.FindChar(aDelimiter, start);
+ if (delimiter < 0) {
+ delimiter = end;
+ }
+
+ if (delimiter != start) {
+ if (!aArray.AppendElement(Substring(aSource, start, delimiter - start))) {
+ aArray.RemoveElementsAt(oldLength, aArray.Length() - oldLength);
+ return false;
+ }
+ }
+
+ if (delimiter == end) {
+ break;
+ }
+ start = ++delimiter;
+ if (start == end) {
+ break;
+ }
+ }
+
+ return true;
+}
diff --git a/xpcom/glue/nsStringAPI.h b/xpcom/glue/nsStringAPI.h
new file mode 100644
index 000000000..d5e368695
--- /dev/null
+++ b/xpcom/glue/nsStringAPI.h
@@ -0,0 +1,1596 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This header provides wrapper classes around the frozen string API
+ * which are roughly equivalent to the internal string classes.
+ */
+
+#ifdef MOZILLA_INTERNAL_API
+#error nsStringAPI.h is only usable from non-MOZILLA_INTERNAL_API code!
+#endif
+
+#ifndef nsStringAPI_h__
+#define nsStringAPI_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Char16.h"
+
+#include "nsXPCOMStrings.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/Logging.h"
+#include "nsTArray.h"
+
+/**
+ * Comparison function for use with nsACString::Equals
+ */
+NS_HIDDEN_(int32_t) CaseInsensitiveCompare(const char* aStrA, const char* aStrB,
+ uint32_t aLength);
+
+class nsAString
+{
+public:
+ typedef char16_t char_type;
+ typedef nsAString self_type;
+ typedef uint32_t size_type;
+ typedef uint32_t index_type;
+
+ /**
+ * Returns the length, beginning, and end of a string in one operation.
+ */
+ NS_HIDDEN_(uint32_t) BeginReading(const char_type** aBegin,
+ const char_type** aEnd = nullptr) const;
+
+ NS_HIDDEN_(const char_type*) BeginReading() const;
+ NS_HIDDEN_(const char_type*) EndReading() const;
+
+ NS_HIDDEN_(char_type) CharAt(uint32_t aPos) const
+ {
+ NS_ASSERTION(aPos < Length(), "Out of bounds");
+ return BeginReading()[aPos];
+ }
+ NS_HIDDEN_(char_type) operator [](uint32_t aPos) const
+ {
+ return CharAt(aPos);
+ }
+ NS_HIDDEN_(char_type) First() const
+ {
+ return CharAt(0);
+ }
+ NS_HIDDEN_(char_type) Last() const
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_StringGetData(*this, &data);
+ return data[dataLen - 1];
+ }
+
+ /**
+ * Get the length, begin writing, and optionally set the length of a
+ * string all in one operation.
+ *
+ * @param newSize Size the string to this length. Pass UINT32_MAX
+ * to leave the length unchanged.
+ * @return The new length of the string, or 0 if resizing failed.
+ */
+ NS_HIDDEN_(uint32_t) BeginWriting(char_type** aBegin,
+ char_type** aEnd = nullptr,
+ uint32_t aNewSize = UINT32_MAX);
+
+ NS_HIDDEN_(char_type*) BeginWriting(uint32_t = UINT32_MAX);
+ NS_HIDDEN_(char_type*) EndWriting();
+
+ NS_HIDDEN_(bool) SetLength(uint32_t aLen);
+
+ NS_HIDDEN_(size_type) Length() const
+ {
+ const char_type* data;
+ return NS_StringGetData(*this, &data);
+ }
+
+ NS_HIDDEN_(bool) IsEmpty() const { return Length() == 0; }
+
+ NS_HIDDEN_(void) SetIsVoid(bool aVal) { NS_StringSetIsVoid(*this, aVal); }
+ NS_HIDDEN_(bool) IsVoid() const { return NS_StringGetIsVoid(*this); }
+
+ NS_HIDDEN_(void) Assign(const self_type& aString)
+ {
+ NS_StringCopy(*this, aString);
+ }
+ NS_HIDDEN_(void) Assign(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringSetData(*this, aData, aLength);
+ }
+ NS_HIDDEN_(void) Assign(char_type aChar)
+ {
+ NS_StringSetData(*this, &aChar, 1);
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(void) Assign(char16ptr_t aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringSetData(*this, aData, aLength);
+ }
+#endif
+
+ NS_HIDDEN_(void) AssignLiteral(const char* aStr);
+ NS_HIDDEN_(void) AssignASCII(const char* aStr)
+ {
+ AssignLiteral(aStr);
+ }
+
+ NS_HIDDEN_(self_type&) operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(self_type&) operator=(char16ptr_t aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+#endif
+
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ NS_StringSetDataRange(*this, aCutStart, aCutLength, aData, aLength);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar)
+ {
+ Replace(aCutStart, aCutLength, &aChar, 1);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aReadable)
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_StringGetData(aReadable, &data);
+ NS_StringSetDataRange(*this, aCutStart, aCutLength, data, dataLen);
+ }
+ NS_HIDDEN_(void) SetCharAt(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 1, &aChar, 1);
+ }
+
+ NS_HIDDEN_(void) Append(char_type aChar)
+ {
+ Replace(size_type(-1), 0, aChar);
+ }
+ NS_HIDDEN_(void) Append(const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ Replace(size_type(-1), 0, aData, aLength);
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(void) Append(char16ptr_t aData, size_type aLength = size_type(-1))
+ {
+ Append(static_cast<const char16_t*>(aData), aLength);
+ }
+#endif
+ NS_HIDDEN_(void) Append(const self_type& aReadable)
+ {
+ Replace(size_type(-1), 0, aReadable);
+ }
+ NS_HIDDEN_(void) AppendLiteral(const char* aASCIIStr);
+ NS_HIDDEN_(void) AppendASCII(const char* aASCIIStr)
+ {
+ AppendLiteral(aASCIIStr);
+ }
+
+ NS_HIDDEN_(self_type&) operator+=(char_type aChar)
+ {
+ Append(aChar);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const char_type* aData)
+ {
+ Append(aData);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const self_type& aReadable)
+ {
+ Append(aReadable);
+ return *this;
+ }
+
+ NS_HIDDEN_(void) Insert(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 0, aChar);
+ }
+ NS_HIDDEN_(void) Insert(const char_type* aData, index_type aPos,
+ size_type aLength = size_type(-1))
+ {
+ Replace(aPos, 0, aData, aLength);
+ }
+ NS_HIDDEN_(void) Insert(const self_type& aReadable, index_type aPos)
+ {
+ Replace(aPos, 0, aReadable);
+ }
+
+ NS_HIDDEN_(void) Cut(index_type aCutStart, size_type aCutLength)
+ {
+ Replace(aCutStart, aCutLength, nullptr, 0);
+ }
+
+ NS_HIDDEN_(void) Truncate(size_type aNewLength = 0)
+ {
+ NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
+ SetLength(aNewLength);
+ }
+
+ /**
+ * Remove all occurences of characters in aSet from the string.
+ */
+ NS_HIDDEN_(void) StripChars(const char* aSet);
+
+ /**
+ * Strip whitespace characters from the string.
+ */
+ NS_HIDDEN_(void) StripWhitespace() { StripChars("\b\t\r\n "); }
+
+ NS_HIDDEN_(void) Trim(const char* aSet, bool aLeading = true,
+ bool aTrailing = true);
+
+ /**
+ * Compare strings of characters. Return 0 if the characters are equal,
+ */
+ typedef int32_t (*ComparatorFunc)(const char_type* aStrA,
+ const char_type* aStrB,
+ uint32_t aLength);
+
+ static NS_HIDDEN_(int32_t) DefaultComparator(const char_type* aStrA,
+ const char_type* aStrB,
+ uint32_t aLength);
+
+ NS_HIDDEN_(int32_t) Compare(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(int32_t) Compare(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) operator<(const self_type& aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+ NS_HIDDEN_(bool) operator<(const char_type* aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+
+ NS_HIDDEN_(bool) operator<=(const self_type& aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+ NS_HIDDEN_(bool) operator<=(const char_type* aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+
+ NS_HIDDEN_(bool) operator==(const self_type& aOther) const
+ {
+ return Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator==(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(bool) operator==(char16ptr_t aOther) const
+ {
+ return Equals(aOther);
+ }
+#endif
+
+ NS_HIDDEN_(bool) operator>=(const self_type& aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+ NS_HIDDEN_(bool) operator>=(const char_type* aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+
+ NS_HIDDEN_(bool) operator>(const self_type& aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+ NS_HIDDEN_(bool) operator>(const char_type* aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+
+ NS_HIDDEN_(bool) operator!=(const self_type& aOther) const
+ {
+ return !Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator!=(const char_type* aOther) const
+ {
+ return !Equals(aOther);
+ }
+
+ NS_HIDDEN_(bool) EqualsLiteral(const char* aASCIIString) const;
+ NS_HIDDEN_(bool) EqualsASCII(const char* aASCIIString) const
+ {
+ return EqualsLiteral(aASCIIString);
+ }
+
+ /**
+ * Case-insensitive match this string to a lowercase ASCII string.
+ */
+ NS_HIDDEN_(bool) LowerCaseEqualsLiteral(const char* aASCIIString) const;
+
+ /**
+ * Find the first occurrence of aStr in this string.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return Find(aStr, 0, aComparator);
+ }
+
+ /**
+ * Find the first occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find an ASCII string within this string.
+ *
+ * @return the offset of aStr, or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) Find(const char* aStr, bool aIgnoreCase = false) const
+ {
+ return Find(aStr, 0, aIgnoreCase);
+ }
+
+ NS_HIDDEN_(int32_t) Find(const char* aStr, uint32_t aOffset,
+ bool aIgnoreCase = false) const;
+
+ /**
+ * Find the last occurrence of aStr in this string.
+ *
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return RFind(aStr, -1, aComparator);
+ }
+
+ /**
+ * Find the last occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching. If aOffset < 0, search from end of this string.
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of an ASCII string within this string.
+ *
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char* aStr, bool aIgnoreCase = false) const
+ {
+ return RFind(aStr, -1, aIgnoreCase);
+ }
+
+ /**
+ * Find the last occurrence of an ASCII string beginning at aOffset.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching. If aOffset < 0, search from end of this string.
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char* aStr, int32_t aOffset,
+ bool aIgnoreCase) const;
+
+ /**
+ * Search for the offset of the first occurrence of a character in a
+ * string.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) FindChar(char_type aChar, uint32_t aOffset = 0) const;
+
+ /**
+ * Search for the offset of the last occurrence of a character in a
+ * string.
+ *
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFindChar(char_type aChar) const;
+
+ /**
+ * Append a string representation of a number.
+ */
+ NS_HIDDEN_(void) AppendInt(int aInt, int32_t aRadix = 10);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ /**
+ * Convert this string to an integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int32_t) ToInteger(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+ /**
+ * Convert this string to a 64-bit integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int64_t) ToInteger64(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+protected:
+ // Prevent people from allocating a nsAString directly.
+ ~nsAString() {}
+};
+
+class nsACString
+{
+public:
+ typedef char char_type;
+ typedef nsACString self_type;
+ typedef uint32_t size_type;
+ typedef uint32_t index_type;
+
+ /**
+ * Returns the length, beginning, and end of a string in one operation.
+ */
+ NS_HIDDEN_(uint32_t) BeginReading(const char_type** aBegin,
+ const char_type** aEnd = nullptr) const;
+
+ NS_HIDDEN_(const char_type*) BeginReading() const;
+ NS_HIDDEN_(const char_type*) EndReading() const;
+
+ NS_HIDDEN_(char_type) CharAt(uint32_t aPos) const
+ {
+ NS_ASSERTION(aPos < Length(), "Out of bounds");
+ return BeginReading()[aPos];
+ }
+ NS_HIDDEN_(char_type) operator [](uint32_t aPos) const
+ {
+ return CharAt(aPos);
+ }
+ NS_HIDDEN_(char_type) First() const
+ {
+ return CharAt(0);
+ }
+ NS_HIDDEN_(char_type) Last() const
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_CStringGetData(*this, &data);
+ return data[dataLen - 1];
+ }
+
+ /**
+ * Get the length, begin writing, and optionally set the length of a
+ * string all in one operation.
+ *
+ * @param newSize Size the string to this length. Pass UINT32_MAX
+ * to leave the length unchanged.
+ * @return The new length of the string, or 0 if resizing failed.
+ */
+ NS_HIDDEN_(uint32_t) BeginWriting(char_type** aBegin,
+ char_type** aEnd = nullptr,
+ uint32_t aNewSize = UINT32_MAX);
+
+ NS_HIDDEN_(char_type*) BeginWriting(uint32_t aLen = UINT32_MAX);
+ NS_HIDDEN_(char_type*) EndWriting();
+
+ NS_HIDDEN_(bool) SetLength(uint32_t aLen);
+
+ NS_HIDDEN_(size_type) Length() const
+ {
+ const char_type* data;
+ return NS_CStringGetData(*this, &data);
+ }
+
+ NS_HIDDEN_(bool) IsEmpty() const { return Length() == 0; }
+
+ NS_HIDDEN_(void) SetIsVoid(bool aVal) { NS_CStringSetIsVoid(*this, aVal); }
+ NS_HIDDEN_(bool) IsVoid() const { return NS_CStringGetIsVoid(*this); }
+
+ NS_HIDDEN_(void) Assign(const self_type& aString)
+ {
+ NS_CStringCopy(*this, aString);
+ }
+ NS_HIDDEN_(void) Assign(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringSetData(*this, aData, aLength);
+ }
+ NS_HIDDEN_(void) Assign(char_type aChar)
+ {
+ NS_CStringSetData(*this, &aChar, 1);
+ }
+ NS_HIDDEN_(void) AssignLiteral(const char_type* aData)
+ {
+ Assign(aData);
+ }
+ NS_HIDDEN_(void) AssignASCII(const char_type* aData)
+ {
+ Assign(aData);
+ }
+
+ NS_HIDDEN_(self_type&) operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ NS_CStringSetDataRange(*this, aCutStart, aCutLength, aData, aLength);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar)
+ {
+ Replace(aCutStart, aCutLength, &aChar, 1);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aReadable)
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_CStringGetData(aReadable, &data);
+ NS_CStringSetDataRange(*this, aCutStart, aCutLength, data, dataLen);
+ }
+ NS_HIDDEN_(void) SetCharAt(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 1, &aChar, 1);
+ }
+
+ NS_HIDDEN_(void) Append(char_type aChar)
+ {
+ Replace(size_type(-1), 0, aChar);
+ }
+ NS_HIDDEN_(void) Append(const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ Replace(size_type(-1), 0, aData, aLength);
+ }
+ NS_HIDDEN_(void) Append(const self_type& aReadable)
+ {
+ Replace(size_type(-1), 0, aReadable);
+ }
+ NS_HIDDEN_(void) AppendLiteral(const char* aASCIIStr)
+ {
+ Append(aASCIIStr);
+ }
+ NS_HIDDEN_(void) AppendASCII(const char* aASCIIStr)
+ {
+ Append(aASCIIStr);
+ }
+
+ NS_HIDDEN_(self_type&) operator+=(char_type aChar)
+ {
+ Append(aChar);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const char_type* aData)
+ {
+ Append(aData);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const self_type& aReadable)
+ {
+ Append(aReadable);
+ return *this;
+ }
+
+ NS_HIDDEN_(void) Insert(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 0, aChar);
+ }
+ NS_HIDDEN_(void) Insert(const char_type* aData, index_type aPos,
+ size_type aLength = size_type(-1))
+ {
+ Replace(aPos, 0, aData, aLength);
+ }
+ NS_HIDDEN_(void) Insert(const self_type& aReadable, index_type aPos)
+ {
+ Replace(aPos, 0, aReadable);
+ }
+
+ NS_HIDDEN_(void) Cut(index_type aCutStart, size_type aCutLength)
+ {
+ Replace(aCutStart, aCutLength, nullptr, 0);
+ }
+
+ NS_HIDDEN_(void) Truncate(size_type aNewLength = 0)
+ {
+ NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
+ SetLength(aNewLength);
+ }
+
+ /**
+ * Remove all occurences of characters in aSet from the string.
+ */
+ NS_HIDDEN_(void) StripChars(const char* aSet);
+
+ /**
+ * Strip whitespace characters from the string.
+ */
+ NS_HIDDEN_(void) StripWhitespace() { StripChars("\b\t\r\n "); }
+
+ NS_HIDDEN_(void) Trim(const char* aSet, bool aLeading = true,
+ bool aTrailing = true);
+
+ /**
+ * Compare strings of characters. Return 0 if the characters are equal,
+ */
+ typedef int32_t (*ComparatorFunc)(const char_type* a,
+ const char_type* b,
+ uint32_t length);
+
+ static NS_HIDDEN_(int32_t) DefaultComparator(const char_type* aStrA,
+ const char_type* aStrB,
+ uint32_t aLength);
+
+ NS_HIDDEN_(int32_t) Compare(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(int32_t) Compare(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) operator<(const self_type& aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+ NS_HIDDEN_(bool) operator<(const char_type* aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+
+ NS_HIDDEN_(bool) operator<=(const self_type& aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+ NS_HIDDEN_(bool) operator<=(const char_type* aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+
+ NS_HIDDEN_(bool) operator==(const self_type& aOther) const
+ {
+ return Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator==(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+
+ NS_HIDDEN_(bool) operator>=(const self_type& aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+ NS_HIDDEN_(bool) operator>=(const char_type* aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+
+ NS_HIDDEN_(bool) operator>(const self_type& aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+ NS_HIDDEN_(bool) operator>(const char_type* aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+
+ NS_HIDDEN_(bool) operator!=(const self_type& aOther) const
+ {
+ return !Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator!=(const char_type* aOther) const
+ {
+ return !Equals(aOther);
+ }
+
+ NS_HIDDEN_(bool) EqualsLiteral(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+ NS_HIDDEN_(bool) EqualsASCII(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+
+ /**
+ * Case-insensitive match this string to a lowercase ASCII string.
+ */
+ NS_HIDDEN_(bool) LowerCaseEqualsLiteral(const char* aASCIIString) const
+ {
+ return Equals(aASCIIString, CaseInsensitiveCompare);
+ }
+
+ /**
+ * Find the first occurrence of aStr in this string.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return Find(aStr, 0, aComparator);
+ }
+
+ /**
+ * Find the first occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the first occurrence of aStr in this string.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const char_type* aStr,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(int32_t) Find(const char_type* aStr, uint32_t aLen,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of aStr in this string.
+ *
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return RFind(aStr, -1, aComparator);
+ }
+
+ /**
+ * Find the last occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching. If aOffset < 0, search from end of this string.
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of aStr in this string.
+ *
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char_type* aStr,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of an ASCII string in this string,
+ * beginning at aOffset.
+ *
+ * @param aLen is the length of aStr
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char_type* aStr, int32_t aLen,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Search for the offset of the first occurrence of a character in a
+ * string.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) FindChar(char_type aChar, uint32_t aOffset = 0) const;
+
+ /**
+ * Search for the offset of the last occurrence of a character in a
+ * string.
+ *
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFindChar(char_type aChar) const;
+
+ /**
+ * Append a string representation of a number.
+ */
+ NS_HIDDEN_(void) AppendInt(int aInt, int32_t aRadix = 10);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ /**
+ * Convert this string to an integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int32_t) ToInteger(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+ /**
+ * Convert this string to a 64-bit integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int64_t) ToInteger64(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+protected:
+ // Prevent people from allocating a nsAString directly.
+ ~nsACString() {}
+};
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * Below we define nsStringContainer and nsCStringContainer. These classes
+ * have unspecified structure. In most cases, your code should use
+ * nsString/nsCString instead of these classes; if you prefer C-style
+ * programming, then look no further.
+ */
+
+class nsStringContainer
+ : public nsAString
+ , private nsStringContainer_base
+{
+};
+
+class nsCStringContainer
+ : public nsACString
+ , private nsStringContainer_base
+{
+};
+
+/**
+ * The following classes are C++ helper classes that make the frozen string
+ * API easier to use.
+ */
+
+/**
+ * Rename symbols to avoid conflicting with internal versions.
+ */
+#define nsString nsString_external
+#define nsCString nsCString_external
+#define nsDependentString nsDependentString_external
+#define nsDependentCString nsDependentCString_external
+#define NS_ConvertASCIItoUTF16 NS_ConvertASCIItoUTF16_external
+#define NS_ConvertUTF8toUTF16 NS_ConvertUTF8toUTF16_external
+#define NS_ConvertUTF16toUTF8 NS_ConvertUTF16toUTF8_external
+#define NS_LossyConvertUTF16toASCII NS_LossyConvertUTF16toASCII_external
+#define nsGetterCopies nsGetterCopies_external
+#define nsCGetterCopies nsCGetterCopies_external
+#define nsDependentSubstring nsDependentSubstring_external
+#define nsDependentCSubstring nsDependentCSubstring_external
+
+/**
+ * basic strings
+ */
+
+class nsString : public nsStringContainer
+{
+public:
+ typedef nsString self_type;
+ typedef nsAString abstract_string_type;
+
+ nsString()
+ {
+ NS_StringContainerInit(*this);
+ }
+
+ nsString(const self_type& aString)
+ {
+ NS_StringContainerInit(*this);
+ NS_StringCopy(*this, aString);
+ }
+
+ explicit nsString(const abstract_string_type& aReadable)
+ {
+ NS_StringContainerInit(*this);
+ NS_StringCopy(*this, aReadable);
+ }
+
+ explicit nsString(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringContainerInit2(*this, aData, aLength, 0);
+ }
+
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ explicit nsString(char16ptr_t aData, size_type aLength = UINT32_MAX)
+ : nsString(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+#endif
+
+ ~nsString()
+ {
+ NS_StringContainerFinish(*this);
+ }
+
+ char16ptr_t get() const
+ {
+ return char16ptr_t(BeginReading());
+ }
+
+ self_type& operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ self_type& operator=(const abstract_string_type& aReadable)
+ {
+ Assign(aReadable);
+ return *this;
+ }
+ self_type& operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+
+ void Adopt(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringContainerFinish(*this);
+ NS_StringContainerInit2(*this, aData, aLength,
+ NS_STRING_CONTAINER_INIT_ADOPT);
+ }
+
+protected:
+ nsString(const char_type* aData, size_type aLength, uint32_t aFlags)
+ {
+ NS_StringContainerInit2(*this, aData, aLength, aFlags);
+ }
+};
+
+class nsCString : public nsCStringContainer
+{
+public:
+ typedef nsCString self_type;
+ typedef nsACString abstract_string_type;
+
+ nsCString()
+ {
+ NS_CStringContainerInit(*this);
+ }
+
+ nsCString(const self_type& aString)
+ {
+ NS_CStringContainerInit(*this);
+ NS_CStringCopy(*this, aString);
+ }
+
+ explicit nsCString(const abstract_string_type& aReadable)
+ {
+ NS_CStringContainerInit(*this);
+ NS_CStringCopy(*this, aReadable);
+ }
+
+ explicit nsCString(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringContainerInit(*this);
+ NS_CStringSetData(*this, aData, aLength);
+ }
+
+ ~nsCString()
+ {
+ NS_CStringContainerFinish(*this);
+ }
+
+ const char_type* get() const
+ {
+ return BeginReading();
+ }
+
+ self_type& operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ self_type& operator=(const abstract_string_type& aReadable)
+ {
+ Assign(aReadable);
+ return *this;
+ }
+ self_type& operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+
+ void Adopt(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringContainerFinish(*this);
+ NS_CStringContainerInit2(*this, aData, aLength,
+ NS_CSTRING_CONTAINER_INIT_ADOPT);
+ }
+
+protected:
+ nsCString(const char_type* aData, size_type aLength, uint32_t aFlags)
+ {
+ NS_CStringContainerInit2(*this, aData, aLength, aFlags);
+ }
+};
+
+
+/**
+ * dependent strings
+ */
+
+class nsDependentString : public nsString
+{
+public:
+ typedef nsDependentString self_type;
+
+ nsDependentString() {}
+
+ explicit nsDependentString(const char_type* aData,
+ size_type aLength = UINT32_MAX)
+ : nsString(aData, aLength, NS_CSTRING_CONTAINER_INIT_DEPEND)
+ {
+ }
+
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ explicit nsDependentString(char16ptr_t aData, size_type aLength = UINT32_MAX)
+ : nsDependentString(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+#endif
+
+ void Rebind(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringContainerFinish(*this);
+ NS_StringContainerInit2(*this, aData, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class nsDependentCString : public nsCString
+{
+public:
+ typedef nsDependentCString self_type;
+
+ nsDependentCString() {}
+
+ explicit nsDependentCString(const char_type* aData,
+ size_type aLength = UINT32_MAX)
+ : nsCString(aData, aLength, NS_CSTRING_CONTAINER_INIT_DEPEND)
+ {
+ }
+
+ void Rebind(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringContainerFinish(*this);
+ NS_CStringContainerInit2(*this, aData, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+
+/**
+ * conversion classes
+ */
+
+inline void
+CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
+{
+ NS_UTF16ToCString(aSource, NS_CSTRING_ENCODING_UTF8, aDest);
+}
+
+inline void
+CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ NS_CStringToUTF16(aSource, NS_CSTRING_ENCODING_UTF8, aDest);
+}
+
+inline void
+LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest)
+{
+ NS_UTF16ToCString(aSource, NS_CSTRING_ENCODING_ASCII, aDest);
+}
+
+inline void
+CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ NS_CStringToUTF16(aSource, NS_CSTRING_ENCODING_ASCII, aDest);
+}
+
+char*
+ToNewUTF8String(const nsAString& aSource);
+
+class NS_ConvertASCIItoUTF16 : public nsString
+{
+public:
+ typedef NS_ConvertASCIItoUTF16 self_type;
+
+ explicit NS_ConvertASCIItoUTF16(const nsACString& aStr)
+ {
+ NS_CStringToUTF16(aStr, NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+ explicit NS_ConvertASCIItoUTF16(const char* aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_CStringToUTF16(nsDependentCString(aData, aLength),
+ NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class NS_ConvertUTF8toUTF16 : public nsString
+{
+public:
+ typedef NS_ConvertUTF8toUTF16 self_type;
+
+ explicit NS_ConvertUTF8toUTF16(const nsACString& aStr)
+ {
+ NS_CStringToUTF16(aStr, NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+ explicit NS_ConvertUTF8toUTF16(const char* aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_CStringToUTF16(nsDependentCString(aData, aLength),
+ NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class NS_ConvertUTF16toUTF8 : public nsCString
+{
+public:
+ typedef NS_ConvertUTF16toUTF8 self_type;
+
+ explicit NS_ConvertUTF16toUTF8(const nsAString& aStr)
+ {
+ NS_UTF16ToCString(aStr, NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+ explicit NS_ConvertUTF16toUTF8(const char16ptr_t aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_UTF16ToCString(nsDependentString(aData, aLength),
+ NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class NS_LossyConvertUTF16toASCII : public nsCString
+{
+public:
+ typedef NS_LossyConvertUTF16toASCII self_type;
+
+ explicit NS_LossyConvertUTF16toASCII(const nsAString& aStr)
+ {
+ NS_UTF16ToCString(aStr, NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+ explicit NS_LossyConvertUTF16toASCII(const char16ptr_t aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_UTF16ToCString(nsDependentString(aData, aLength),
+ NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+
+/**
+ * literal strings
+ */
+static_assert(sizeof(char16_t) == 2, "size of char16_t must be 2");
+static_assert(char16_t(-1) > char16_t(0), "char16_t must be unsigned");
+
+#define NS_MULTILINE_LITERAL_STRING(s) \
+ nsDependentString(reinterpret_cast<const nsAString::char_type*>(s), \
+ uint32_t((sizeof(s) / 2) - 1))
+#define NS_MULTILINE_LITERAL_STRING_INIT(n, s) \
+ n(reinterpret_cast<const nsAString::char_type*>(s), \
+ uint32_t((sizeof(s) / 2) - 1))
+#define NS_NAMED_MULTILINE_LITERAL_STRING(n,s) \
+ const nsDependentString n(reinterpret_cast<const nsAString::char_type*>(s), \
+ uint32_t((sizeof(s) / 2) - 1))
+typedef nsDependentString nsLiteralString;
+
+#define NS_LITERAL_STRING(s) \
+ static_cast<const nsString&>(NS_MULTILINE_LITERAL_STRING(u"" s))
+#define NS_LITERAL_STRING_INIT(n, s) \
+ NS_MULTILINE_LITERAL_STRING_INIT(n, (u"" s))
+#define NS_NAMED_LITERAL_STRING(n, s) \
+ NS_NAMED_MULTILINE_LITERAL_STRING(n, (u"" s))
+
+#define NS_LITERAL_CSTRING(s) \
+ static_cast<const nsDependentCString&>(nsDependentCString(s, uint32_t(sizeof(s) - 1)))
+#define NS_LITERAL_CSTRING_INIT(n, s) \
+ n(s, uint32_t(sizeof(s)-1))
+#define NS_NAMED_LITERAL_CSTRING(n, s) \
+ const nsDependentCString n(s, uint32_t(sizeof(s)-1))
+
+typedef nsDependentCString nsLiteralCString;
+
+
+/**
+ * getter_Copies support
+ *
+ * NS_IMETHOD GetBlah(char16_t**);
+ *
+ * void some_function()
+ * {
+ * nsString blah;
+ * GetBlah(getter_Copies(blah));
+ * // ...
+ * }
+ */
+
+class nsGetterCopies
+{
+public:
+ typedef char16_t char_type;
+
+ explicit nsGetterCopies(nsString& aStr)
+ : mString(aStr)
+ , mData(nullptr)
+ {
+ }
+
+ ~nsGetterCopies() { mString.Adopt(mData); }
+
+ operator char_type**() { return &mData; }
+
+private:
+ nsString& mString;
+ char_type* mData;
+};
+
+inline nsGetterCopies
+getter_Copies(nsString& aString)
+{
+ return nsGetterCopies(aString);
+}
+
+class nsCGetterCopies
+{
+public:
+ typedef char char_type;
+
+ explicit nsCGetterCopies(nsCString& aStr)
+ : mString(aStr)
+ , mData(nullptr)
+ {
+ }
+
+ ~nsCGetterCopies() { mString.Adopt(mData); }
+
+ operator char_type**() { return &mData; }
+
+private:
+ nsCString& mString;
+ char_type* mData;
+};
+
+inline nsCGetterCopies
+getter_Copies(nsCString& aString)
+{
+ return nsCGetterCopies(aString);
+}
+
+
+/**
+* substrings
+*/
+
+class nsDependentSubstring : public nsStringContainer
+{
+public:
+ typedef nsDependentSubstring self_type;
+ typedef nsAString abstract_string_type;
+
+ ~nsDependentSubstring() { NS_StringContainerFinish(*this); }
+ nsDependentSubstring() { NS_StringContainerInit(*this); }
+
+ nsDependentSubstring(const char_type* aStart, uint32_t aLength)
+ {
+ NS_StringContainerInit2(*this, aStart, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+ nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos);
+ nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos, uint32_t aLength);
+
+ void Rebind(const char_type* aStart, uint32_t aLength)
+ {
+ NS_StringContainerFinish(*this);
+ NS_StringContainerInit2(*this, aStart, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class nsDependentCSubstring : public nsCStringContainer
+{
+public:
+ typedef nsDependentCSubstring self_type;
+ typedef nsACString abstract_string_type;
+
+ ~nsDependentCSubstring() { NS_CStringContainerFinish(*this); }
+ nsDependentCSubstring() { NS_CStringContainerInit(*this); }
+
+ nsDependentCSubstring(const char_type* aStart, uint32_t aLength)
+ {
+ NS_CStringContainerInit2(*this, aStart, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+ nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos);
+ nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos, uint32_t aLength);
+
+ void Rebind(const char_type* aStart, uint32_t aLength)
+ {
+ NS_CStringContainerFinish(*this);
+ NS_CStringContainerInit2(*this, aStart, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+
+/**
+ * Various nsDependentC?Substring constructor functions
+ */
+
+// char16_t
+inline const nsDependentSubstring
+Substring(const nsAString& aStr, uint32_t aStartPos)
+{
+ return nsDependentSubstring(aStr, aStartPos);
+}
+
+inline const nsDependentSubstring
+Substring(const nsAString& aStr, uint32_t aStartPos, uint32_t aLength)
+{
+ return nsDependentSubstring(aStr, aStartPos, aLength);
+}
+
+inline const nsDependentSubstring
+Substring(const char16_t* aStart, const char16_t* aEnd)
+{
+ MOZ_ASSERT(uint32_t(aEnd - aStart) == uintptr_t(aEnd - aStart),
+ "string too long");
+ return nsDependentSubstring(aStart, uint32_t(aEnd - aStart));
+}
+
+inline const nsDependentSubstring
+Substring(const char16_t* aStart, uint32_t aLength)
+{
+ return nsDependentSubstring(aStart, aLength);
+}
+
+inline const nsDependentSubstring
+StringHead(const nsAString& aStr, uint32_t aCount)
+{
+ return nsDependentSubstring(aStr, 0, aCount);
+}
+
+inline const nsDependentSubstring
+StringTail(const nsAString& aStr, uint32_t aCount)
+{
+ return nsDependentSubstring(aStr, aStr.Length() - aCount, aCount);
+}
+
+// char
+inline const nsDependentCSubstring
+Substring(const nsACString& aStr, uint32_t aStartPos)
+{
+ return nsDependentCSubstring(aStr, aStartPos);
+}
+
+inline const nsDependentCSubstring
+Substring(const nsACString& aStr, uint32_t aStartPos, uint32_t aLength)
+{
+ return nsDependentCSubstring(aStr, aStartPos, aLength);
+}
+
+inline const nsDependentCSubstring
+Substring(const char* aStart, const char* aEnd)
+{
+ MOZ_ASSERT(uint32_t(aEnd - aStart) == uintptr_t(aEnd - aStart),
+ "string too long");
+ return nsDependentCSubstring(aStart, uint32_t(aEnd - aStart));
+}
+
+inline const nsDependentCSubstring
+Substring(const char* aStart, uint32_t aLength)
+{
+ return nsDependentCSubstring(aStart, aLength);
+}
+
+inline const nsDependentCSubstring
+StringHead(const nsACString& aStr, uint32_t aCount)
+{
+ return nsDependentCSubstring(aStr, 0, aCount);
+}
+
+inline const nsDependentCSubstring
+StringTail(const nsACString& aStr, uint32_t aCount)
+{
+ return nsDependentCSubstring(aStr, aStr.Length() - aCount, aCount);
+}
+
+
+inline bool
+StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
+ nsAString::ComparatorFunc aComparator = nsAString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringHead(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+inline bool
+StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
+ nsAString::ComparatorFunc aComparator = nsAString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringTail(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+inline bool
+StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
+ nsACString::ComparatorFunc aComparator = nsACString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringHead(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+inline bool
+StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
+ nsACString::ComparatorFunc aComparator = nsACString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringTail(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+/**
+ * Trim whitespace from the beginning and end of a string; then compress
+ * remaining runs of whitespace characters to a single space.
+ */
+NS_HIDDEN_(void) CompressWhitespace(nsAString& aString);
+
+#define EmptyCString() nsCString()
+#define EmptyString() nsString()
+
+/**
+ * Convert an ASCII string to all upper/lowercase (a-z,A-Z only). As a bonus,
+ * returns the string length.
+ */
+NS_HIDDEN_(uint32_t) ToLowerCase(nsACString& aStr);
+
+NS_HIDDEN_(uint32_t) ToUpperCase(nsACString& aStr);
+
+NS_HIDDEN_(uint32_t) ToLowerCase(const nsACString& aSrc, nsACString& aDest);
+
+NS_HIDDEN_(uint32_t) ToUpperCase(const nsACString& aSrc, nsACString& aDest);
+
+/**
+ * The following declarations are *deprecated*, and are included here only
+ * to make porting from existing code that doesn't use the frozen string API
+ * easier. They may disappear in the future.
+ */
+
+inline char*
+ToNewCString(const nsACString& aStr)
+{
+ return NS_CStringCloneData(aStr);
+}
+
+inline char16_t*
+ToNewUnicode(const nsAString& aStr)
+{
+ return NS_StringCloneData(aStr);
+}
+
+typedef nsString PromiseFlatString;
+typedef nsCString PromiseFlatCString;
+
+typedef nsCString nsAutoCString;
+typedef nsString nsAutoString;
+
+NS_HIDDEN_(bool) ParseString(const nsACString& aAstring, char aDelimiter,
+ nsTArray<nsCString>& aArray);
+
+#endif // nsStringAPI_h__
diff --git a/xpcom/glue/nsStringGlue.h b/xpcom/glue/nsStringGlue.h
new file mode 100644
index 000000000..a444eb8a1
--- /dev/null
+++ b/xpcom/glue/nsStringGlue.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+/**
+ * @file nsStringGlue.h
+ * This header exists solely to #include the proper internal/frozen string
+ * headers, depending on whether MOZILLA_INTERNAL_API is defined.
+ */
+
+#ifndef nsStringGlue_h__
+#define nsStringGlue_h__
+
+#ifdef MOZILLA_INTERNAL_API
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#else
+#include "nsStringAPI.h"
+#endif
+
+#endif // nsStringGlue_h__
diff --git a/xpcom/glue/nsTArray-inl.h b/xpcom/glue/nsTArray-inl.h
new file mode 100644
index 000000000..af57c9866
--- /dev/null
+++ b/xpcom/glue/nsTArray-inl.h
@@ -0,0 +1,463 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTArray_h__
+# error "Don't include this file directly"
+#endif
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::nsTArray_base()
+ : mHdr(EmptyHdr())
+{
+ MOZ_COUNT_CTOR(nsTArray_base);
+}
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::~nsTArray_base()
+{
+ if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
+ Alloc::Free(mHdr);
+ }
+ MOZ_COUNT_DTOR(nsTArray_base);
+}
+
+template<class Alloc, class Copy>
+const nsTArrayHeader*
+nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t aElemAlign) const
+{
+ // Assuming |this| points to an nsAutoArray, we want to get a pointer to
+ // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf!
+
+ const void* autoBuf =
+ &reinterpret_cast<const AutoTArray<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf;
+
+ // If we're on a 32-bit system and aElemAlign is 8, we need to adjust our
+ // pointer to take into account the extra alignment in the auto array.
+
+ static_assert(sizeof(void*) != 4 ||
+ (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
+ sizeof(AutoTArray<mozilla::AlignedElem<8>, 1>) ==
+ sizeof(void*) + sizeof(nsTArrayHeader) +
+ 4 + sizeof(mozilla::AlignedElem<8>)),
+ "auto array padding wasn't what we expected");
+
+ // We don't support alignments greater than 8 bytes.
+ MOZ_ASSERT(aElemAlign <= 4 || aElemAlign == 8,
+ "unsupported alignment.");
+ if (sizeof(void*) == 4 && aElemAlign == 8) {
+ autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
+ }
+
+ return reinterpret_cast<const Header*>(autoBuf);
+}
+
+template<class Alloc, class Copy>
+bool
+nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const
+{
+ if (!mHdr->mIsAutoArray) {
+ return false;
+ }
+
+ // This is nuts. If we were sane, we'd pass aElemAlign as a parameter to
+ // this function. Unfortunately this function is called in nsTArray_base's
+ // destructor, at which point we don't know elem_type's alignment.
+ //
+ // We'll fall on our face and return true when we should say false if
+ //
+ // * we're not using our auto buffer,
+ // * aElemAlign == 4, and
+ // * mHdr == GetAutoArrayBuffer(8).
+ //
+ // This could happen if |*this| lives on the heap and malloc allocated our
+ // buffer on the heap adjacent to |*this|.
+ //
+ // However, we can show that this can't happen. If |this| is an auto array
+ // (as we ensured at the beginning of the method), GetAutoArrayBuffer(8)
+ // always points to memory owned by |*this|, because (as we assert below)
+ //
+ // * GetAutoArrayBuffer(8) is at most 4 bytes past GetAutoArrayBuffer(4), and
+ // * sizeof(nsTArrayHeader) > 4.
+ //
+ // Since AutoTArray always contains an nsTArrayHeader,
+ // GetAutoArrayBuffer(8) will always point inside the auto array object,
+ // even if it doesn't point at the beginning of the header.
+ //
+ // Note that this means that we can't store elements with alignment 16 in an
+ // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
+ // owned by this AutoTArray. We statically assert that elem_type's
+ // alignment is 8 bytes or less in AutoTArray.
+
+ static_assert(sizeof(nsTArrayHeader) > 4,
+ "see comment above");
+
+#ifdef DEBUG
+ ptrdiff_t diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
+ reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
+ MOZ_ASSERT(diff >= 0 && diff <= 4,
+ "GetAutoArrayBuffer doesn't do what we expect.");
+#endif
+
+ return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
+}
+
+// defined in nsTArray.cpp
+bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
+ size_t aElemSize);
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+typename ActualAlloc::ResultTypeProxy
+nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type aCapacity,
+ size_type aElemSize)
+{
+ // This should be the most common case so test this first
+ if (aCapacity <= mHdr->mCapacity) {
+ return ActualAlloc::SuccessResult();
+ }
+
+ // If the requested memory allocation exceeds size_type(-1)/2, then
+ // our doubling algorithm may not be able to allocate it.
+ // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the
+ // Header::mCapacity member. Just bail out in cases like that. We don't want
+ // to be allocating 2 GB+ arrays anyway.
+ if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) {
+ ActualAlloc::SizeTooBig((size_t)aCapacity * aElemSize);
+ return ActualAlloc::FailureResult();
+ }
+
+ size_t reqSize = sizeof(Header) + aCapacity * aElemSize;
+
+ if (mHdr == EmptyHdr()) {
+ // Malloc() new data
+ Header* header = static_cast<Header*>(ActualAlloc::Malloc(reqSize));
+ if (!header) {
+ return ActualAlloc::FailureResult();
+ }
+ header->mLength = 0;
+ header->mCapacity = aCapacity;
+ header->mIsAutoArray = 0;
+ mHdr = header;
+
+ return ActualAlloc::SuccessResult();
+ }
+
+ // We increase our capacity so that the allocated buffer grows exponentially,
+ // which gives us amortized O(1) appending. Below the threshold, we use
+ // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up
+ // to the nearest MiB.
+ const size_t slowGrowthThreshold = 8 * 1024 * 1024;
+
+ size_t bytesToAlloc;
+ if (reqSize >= slowGrowthThreshold) {
+ size_t currSize = sizeof(Header) + Capacity() * aElemSize;
+ size_t minNewSize = currSize + (currSize >> 3); // multiply by 1.125
+ bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize;
+
+ // Round up to the next multiple of MiB.
+ const size_t MiB = 1 << 20;
+ bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB);
+ } else {
+ // Round up to the next power of two.
+ bytesToAlloc = mozilla::RoundUpPow2(reqSize);
+ }
+
+ Header* header;
+ if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
+ // Malloc() and copy
+ header = static_cast<Header*>(ActualAlloc::Malloc(bytesToAlloc));
+ if (!header) {
+ return ActualAlloc::FailureResult();
+ }
+
+ Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize);
+
+ if (!UsesAutoArrayBuffer()) {
+ ActualAlloc::Free(mHdr);
+ }
+ } else {
+ // Realloc() existing data
+ header = static_cast<Header*>(ActualAlloc::Realloc(mHdr, bytesToAlloc));
+ if (!header) {
+ return ActualAlloc::FailureResult();
+ }
+ }
+
+ // How many elements can we fit in bytesToAlloc?
+ size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize;
+ MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!");
+ header->mCapacity = newCapacity;
+
+ mHdr = header;
+
+ return ActualAlloc::SuccessResult();
+}
+
+// We don't need use Alloc template parameter specified here because failure to
+// shrink the capacity will leave the array unchanged.
+template<class Alloc, class Copy>
+void
+nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type aElemSize,
+ size_t aElemAlign)
+{
+ if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) {
+ return;
+ }
+
+ if (mHdr->mLength >= mHdr->mCapacity) { // should never be greater than...
+ return;
+ }
+
+ size_type length = Length();
+
+ if (IsAutoArray() && GetAutoArrayBuffer(aElemAlign)->mCapacity >= length) {
+ Header* header = GetAutoArrayBuffer(aElemAlign);
+
+ // Move the data, but don't copy the header to avoid overwriting mCapacity.
+ header->mLength = length;
+ Copy::MoveNonOverlappingRegion(header + 1, mHdr + 1, length, aElemSize);
+
+ nsTArrayFallibleAllocator::Free(mHdr);
+ mHdr = header;
+ return;
+ }
+
+ if (length == 0) {
+ MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
+ nsTArrayFallibleAllocator::Free(mHdr);
+ mHdr = EmptyHdr();
+ return;
+ }
+
+ size_type size = sizeof(Header) + length * aElemSize;
+ void* ptr = nsTArrayFallibleAllocator::Realloc(mHdr, size);
+ if (!ptr) {
+ return;
+ }
+ mHdr = static_cast<Header*>(ptr);
+ mHdr->mCapacity = length;
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+void
+nsTArray_base<Alloc, Copy>::ShiftData(index_type aStart,
+ size_type aOldLen, size_type aNewLen,
+ size_type aElemSize, size_t aElemAlign)
+{
+ if (aOldLen == aNewLen) {
+ return;
+ }
+
+ // Determine how many elements need to be shifted
+ size_type num = mHdr->mLength - (aStart + aOldLen);
+
+ // Compute the resulting length of the array
+ mHdr->mLength += aNewLen - aOldLen;
+ if (mHdr->mLength == 0) {
+ ShrinkCapacity(aElemSize, aElemAlign);
+ } else {
+ // Maybe nothing needs to be shifted
+ if (num == 0) {
+ return;
+ }
+ // Perform shift (change units to bytes first)
+ aStart *= aElemSize;
+ aNewLen *= aElemSize;
+ aOldLen *= aElemSize;
+ char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart;
+ Copy::MoveOverlappingRegion(baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize);
+ }
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+bool
+nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type aIndex, size_type aCount,
+ size_type aElemSize,
+ size_t aElemAlign)
+{
+ MOZ_ASSERT(aIndex <= Length(), "Bogus insertion index");
+ size_type newLen = Length() + aCount;
+
+ EnsureCapacity<ActualAlloc>(newLen, aElemSize);
+
+ // Check for out of memory conditions
+ if (Capacity() < newLen) {
+ return false;
+ }
+
+ // Move the existing elements as needed. Note that this will
+ // change our mLength, so no need to call IncrementLength.
+ ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign);
+
+ return true;
+}
+
+// nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
+// |nsTArray_base &array| in its constructor. When it's destructed, it ensures
+// that
+//
+// * array.mIsAutoArray has the same value as it did when we started, and
+// * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr,
+// array.mHdr points to array's auto buffer.
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer(
+ nsTArray_base<Alloc, Copy>& aArray,
+ size_t aElemAlign)
+ : mArray(aArray)
+ , mElemAlign(aElemAlign)
+ , mIsAuto(aArray.IsAutoArray())
+{
+}
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer()
+{
+ // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr.
+ if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) {
+ // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
+ // that mHdr->mIsAutoArray is true, which surely isn't the case here.
+ mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
+ mArray.mHdr->mLength = 0;
+ } else if (mArray.mHdr != mArray.EmptyHdr()) {
+ mArray.mHdr->mIsAutoArray = mIsAuto;
+ }
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc, class Allocator>
+typename ActualAlloc::ResultTypeProxy
+nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator,
+ Copy>& aOther,
+ size_type aElemSize,
+ size_t aElemAlign)
+{
+
+ // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an
+ // auto buffer. We need to point mHdr back to our auto buffer before we
+ // return, otherwise we'll forget that we have an auto buffer at all!
+ // IsAutoArrayRestorer takes care of this for us.
+
+ IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
+ typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer
+ otherAutoRestorer(aOther, aElemAlign);
+
+ // If neither array uses an auto buffer which is big enough to store the
+ // other array's elements, then ensure that both arrays use malloc'ed storage
+ // and swap their mHdr pointers.
+ if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) &&
+ (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) {
+
+ if (!EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize) ||
+ !aOther.template EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize)) {
+ return ActualAlloc::FailureResult();
+ }
+
+ Header* temp = mHdr;
+ mHdr = aOther.mHdr;
+ aOther.mHdr = temp;
+
+ return ActualAlloc::SuccessResult();
+ }
+
+ // Swap the two arrays by copying, since at least one is using an auto
+ // buffer which is large enough to hold all of the aOther's elements. We'll
+ // copy the shorter array into temporary storage.
+ //
+ // (We could do better than this in some circumstances. Suppose we're
+ // swapping arrays X and Y. X has space for 2 elements in its auto buffer,
+ // but currently has length 4, so it's using malloc'ed storage. Y has length
+ // 2. When we swap X and Y, we don't need to use a temporary buffer; we can
+ // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
+ // of Y, and then switch X to using its auto buffer.)
+
+ if (!ActualAlloc::Successful(EnsureCapacity<ActualAlloc>(aOther.Length(), aElemSize)) ||
+ !Allocator::Successful(aOther.template EnsureCapacity<Allocator>(Length(), aElemSize))) {
+ return ActualAlloc::FailureResult();
+ }
+
+ // The EnsureCapacity calls above shouldn't have caused *both* arrays to
+ // switch from their auto buffers to malloc'ed space.
+ MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
+ "One of the arrays should be using its auto buffer.");
+
+ size_type smallerLength = XPCOM_MIN(Length(), aOther.Length());
+ size_type largerLength = XPCOM_MAX(Length(), aOther.Length());
+ void* smallerElements;
+ void* largerElements;
+ if (Length() <= aOther.Length()) {
+ smallerElements = Hdr() + 1;
+ largerElements = aOther.Hdr() + 1;
+ } else {
+ smallerElements = aOther.Hdr() + 1;
+ largerElements = Hdr() + 1;
+ }
+
+ // Allocate temporary storage for the smaller of the two arrays. We want to
+ // allocate this space on the stack, if it's not too large. Sounds like a
+ // job for AutoTArray! (One of the two arrays we're swapping is using an
+ // auto buffer, so we're likely not allocating a lot of space here. But one
+ // could, in theory, allocate a huge AutoTArray on the heap.)
+ AutoTArray<nsTArray_Impl<uint8_t, ActualAlloc>, 64> temp;
+ if (!ActualAlloc::Successful(temp.template EnsureCapacity<ActualAlloc>(smallerLength,
+ aElemSize))) {
+ return ActualAlloc::FailureResult();
+ }
+
+ Copy::MoveNonOverlappingRegion(temp.Elements(), smallerElements, smallerLength, aElemSize);
+ Copy::MoveNonOverlappingRegion(smallerElements, largerElements, largerLength, aElemSize);
+ Copy::MoveNonOverlappingRegion(largerElements, temp.Elements(), smallerLength, aElemSize);
+
+ // Swap the arrays' lengths.
+ MOZ_ASSERT((aOther.Length() == 0 || mHdr != EmptyHdr()) &&
+ (Length() == 0 || aOther.mHdr != EmptyHdr()),
+ "Don't set sEmptyHdr's length.");
+ size_type tempLength = Length();
+
+ // Avoid writing to EmptyHdr, since it can trigger false
+ // positives with TSan.
+ if (mHdr != EmptyHdr()) {
+ mHdr->mLength = aOther.Length();
+ }
+ if (aOther.mHdr != EmptyHdr()) {
+ aOther.mHdr->mLength = tempLength;
+ }
+
+ return ActualAlloc::SuccessResult();
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+bool
+nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type aElemSize)
+{
+ if (UsesAutoArrayBuffer()) {
+
+ // If you call this on a 0-length array, we'll set that array's mHdr to
+ // sEmptyHdr, in flagrant violation of the AutoTArray invariants. It's
+ // up to you to set it back! (If you don't, the AutoTArray will forget
+ // that it has an auto buffer.)
+ if (Length() == 0) {
+ mHdr = EmptyHdr();
+ return true;
+ }
+
+ size_type size = sizeof(Header) + Length() * aElemSize;
+
+ Header* header = static_cast<Header*>(ActualAlloc::Malloc(size));
+ if (!header) {
+ return false;
+ }
+
+ Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize);
+ header->mCapacity = Length();
+ mHdr = header;
+ }
+
+ return true;
+}
diff --git a/xpcom/glue/nsTArray.cpp b/xpcom/glue/nsTArray.cpp
new file mode 100644
index 000000000..fd8422ec7
--- /dev/null
+++ b/xpcom/glue/nsTArray.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+#include "nsTArray.h"
+#include "nsXPCOM.h"
+#include "nsDebug.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/IntegerPrintfMacros.h"
+
+nsTArrayHeader nsTArrayHeader::sEmptyHdr = { 0, 0, 0 };
+
+bool
+IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity, size_t aElemSize)
+{
+ using mozilla::CheckedUint32;
+ return ((CheckedUint32(aCapacity) * aElemSize) * 2).isValid();
+}
+
+MOZ_NORETURN MOZ_COLD void
+InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength)
+{
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "ElementAt(aIndex = %" PRIu64 ", aLength = %" PRIu64 ")",
+ static_cast<uint64_t>(aIndex), static_cast<uint64_t>(aLength));
+}
diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h
new file mode 100644
index 000000000..ca74a41f7
--- /dev/null
+++ b/xpcom/glue/nsTArray.h
@@ -0,0 +1,2371 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTArray_h__
+#define nsTArray_h__
+
+#include "nsTArrayForwardDeclare.h"
+#include "mozilla/Alignment.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/fallible.h"
+#include "mozilla/Function.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "mozilla/ReverseIterator.h"
+#include "mozilla/TypeTraits.h"
+
+#include <string.h>
+
+#include "nsCycleCollectionNoteChild.h"
+#include "nsAlgorithm.h"
+#include "nscore.h"
+#include "nsQuickSort.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "nsRegionFwd.h"
+#include <initializer_list>
+#include <new>
+
+namespace JS {
+template<class T>
+class Heap;
+class ObjectPtr;
+} /* namespace JS */
+
+class nsRegion;
+namespace mozilla {
+namespace layers {
+struct TileClient;
+} // namespace layers
+} // namespace mozilla
+
+namespace mozilla {
+struct SerializedStructuredCloneBuffer;
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+class StructuredCloneData;
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+class ClonedMessageData;
+class MessagePortMessage;
+namespace indexedDB {
+struct StructuredCloneReadInfo;
+class SerializedStructuredCloneReadInfo;
+class ObjectStoreCursorResponse;
+} // namespace indexedDB
+} // namespace dom
+} // namespace mozilla
+
+class JSStructuredCloneData;
+
+//
+// nsTArray is a resizable array class, like std::vector.
+//
+// Unlike std::vector, which follows C++'s construction/destruction rules,
+// nsTArray assumes that your "T" can be memmoved()'ed safely.
+//
+// The public classes defined in this header are
+//
+// nsTArray<T>,
+// FallibleTArray<T>,
+// AutoTArray<T, N>, and
+//
+// nsTArray and AutoTArray are infallible by default. To opt-in to fallible
+// behaviour, use the `mozilla::fallible` parameter and check the return value.
+//
+// If you just want to declare the nsTArray types (e.g., if you're in a header
+// file and don't need the full nsTArray definitions) consider including
+// nsTArrayForwardDeclare.h instead of nsTArray.h.
+//
+// The template parameter (i.e., T in nsTArray<T>) specifies the type of the
+// elements and has the following requirements:
+//
+// T MUST be safely memmove()'able.
+// T MUST define a copy-constructor.
+// T MAY define operator< for sorting.
+// T MAY define operator== for searching.
+//
+// (Note that the memmove requirement may be relaxed for certain types - see
+// nsTArray_CopyChooser below.)
+//
+// For methods taking a Comparator instance, the Comparator must be a class
+// defining the following methods:
+//
+// class Comparator {
+// public:
+// /** @return True if the elements are equals; false otherwise. */
+// bool Equals(const elem_type& a, const Item& b) const;
+//
+// /** @return True if (a < b); false otherwise. */
+// bool LessThan(const elem_type& a, const Item& b) const;
+// };
+//
+// The Equals method is used for searching, and the LessThan method is used for
+// searching and sorting. The |Item| type above can be arbitrary, but must
+// match the Item type passed to the sort or search function.
+//
+
+
+//
+// nsTArrayFallibleResult and nsTArrayInfallibleResult types are proxy types
+// which are used because you cannot use a templated type which is bound to
+// void as an argument to a void function. In order to work around that, we
+// encode either a void or a boolean inside these proxy objects, and pass them
+// to the aforementioned function instead, and then use the type information to
+// decide what to do in the function.
+//
+// Note that public nsTArray methods should never return a proxy type. Such
+// types are only meant to be used in the internal nsTArray helper methods.
+// Public methods returning non-proxy types cannot be called from other
+// nsTArray members.
+//
+struct nsTArrayFallibleResult
+{
+ // Note: allows implicit conversions from and to bool
+ MOZ_IMPLICIT nsTArrayFallibleResult(bool aResult) : mResult(aResult) {}
+
+ MOZ_IMPLICIT operator bool() { return mResult; }
+
+private:
+ bool mResult;
+};
+
+struct nsTArrayInfallibleResult
+{
+};
+
+//
+// nsTArray*Allocators must all use the same |free()|, to allow swap()'ing
+// between fallible and infallible variants.
+//
+
+struct nsTArrayFallibleAllocatorBase
+{
+ typedef bool ResultType;
+ typedef nsTArrayFallibleResult ResultTypeProxy;
+
+ static ResultType Result(ResultTypeProxy aResult) { return aResult; }
+ static bool Successful(ResultTypeProxy aResult) { return aResult; }
+ static ResultTypeProxy SuccessResult() { return true; }
+ static ResultTypeProxy FailureResult() { return false; }
+ static ResultType ConvertBoolToResultType(bool aValue) { return aValue; }
+};
+
+struct nsTArrayInfallibleAllocatorBase
+{
+ typedef void ResultType;
+ typedef nsTArrayInfallibleResult ResultTypeProxy;
+
+ static ResultType Result(ResultTypeProxy aResult) {}
+ static bool Successful(ResultTypeProxy) { return true; }
+ static ResultTypeProxy SuccessResult() { return ResultTypeProxy(); }
+
+ static ResultTypeProxy FailureResult()
+ {
+ NS_RUNTIMEABORT("Infallible nsTArray should never fail");
+ return ResultTypeProxy();
+ }
+
+ static ResultType ConvertBoolToResultType(bool aValue)
+ {
+ if (!aValue) {
+ NS_RUNTIMEABORT("infallible nsTArray should never convert false to ResultType");
+ }
+ }
+};
+
+struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase
+{
+ static void* Malloc(size_t aSize) { return malloc(aSize); }
+ static void* Realloc(void* aPtr, size_t aSize)
+ {
+ return realloc(aPtr, aSize);
+ }
+
+ static void Free(void* aPtr) { free(aPtr); }
+ static void SizeTooBig(size_t) {}
+};
+
+#if defined(MOZALLOC_HAVE_XMALLOC)
+#include "mozilla/mozalloc_abort.h"
+
+struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
+{
+ static void* Malloc(size_t aSize) { return moz_xmalloc(aSize); }
+ static void* Realloc(void* aPtr, size_t aSize)
+ {
+ return moz_xrealloc(aPtr, aSize);
+ }
+
+ static void Free(void* aPtr) { free(aPtr); }
+ static void SizeTooBig(size_t aSize) { NS_ABORT_OOM(aSize); }
+};
+
+#else
+#include <stdlib.h>
+
+struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
+{
+ static void* Malloc(size_t aSize)
+ {
+ void* ptr = malloc(aSize);
+ if (MOZ_UNLIKELY(!ptr)) {
+ NS_ABORT_OOM(aSize);
+ }
+ return ptr;
+ }
+
+ static void* Realloc(void* aPtr, size_t aSize)
+ {
+ void* newptr = realloc(aPtr, aSize);
+ if (MOZ_UNLIKELY(!newptr && aSize)) {
+ NS_ABORT_OOM(aSize);
+ }
+ return newptr;
+ }
+
+ static void Free(void* aPtr) { free(aPtr); }
+ static void SizeTooBig(size_t aSize) { NS_ABORT_OOM(aSize); }
+};
+
+#endif
+
+// nsTArray_base stores elements into the space allocated beyond
+// sizeof(*this). This is done to minimize the size of the nsTArray
+// object when it is empty.
+struct nsTArrayHeader
+{
+ static nsTArrayHeader sEmptyHdr;
+
+ uint32_t mLength;
+ uint32_t mCapacity : 31;
+ uint32_t mIsAutoArray : 1;
+};
+
+// This class provides a SafeElementAt method to nsTArray<T*> which does
+// not take a second default value parameter.
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper
+{
+ typedef E* elem_type;
+ typedef size_t index_type;
+
+ // No implementation is provided for these two methods, and that is on
+ // purpose, since we don't support these functions on non-pointer type
+ // instantiations.
+ elem_type& SafeElementAt(index_type aIndex);
+ const elem_type& SafeElementAt(index_type aIndex) const;
+};
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<E*, Derived>
+{
+ typedef E* elem_type;
+ //typedef const E* const_elem_type; XXX: see below
+ typedef size_t index_type;
+
+ elem_type SafeElementAt(index_type aIndex)
+ {
+ return static_cast<Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+
+ // XXX: Probably should return const_elem_type, but callsites must be fixed.
+ // Also, the use of const_elem_type for nsTArray<xpcGCCallback> in
+ // xpcprivate.h causes build failures on Windows because xpcGCCallback is a
+ // function pointer and MSVC doesn't like qualifying it with |const|.
+ elem_type SafeElementAt(index_type aIndex) const
+ {
+ return static_cast<const Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+};
+
+// E is the base type that the smart pointer is templated over; the
+// smart pointer can act as E*.
+template<class E, class Derived>
+struct nsTArray_SafeElementAtSmartPtrHelper
+{
+ typedef E* elem_type;
+ typedef const E* const_elem_type;
+ typedef size_t index_type;
+
+ elem_type SafeElementAt(index_type aIndex)
+ {
+ return static_cast<Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+
+ // XXX: Probably should return const_elem_type, but callsites must be fixed.
+ elem_type SafeElementAt(index_type aIndex) const
+ {
+ return static_cast<const Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+};
+
+template<class T> class nsCOMPtr;
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<nsCOMPtr<E>, Derived>
+ : public nsTArray_SafeElementAtSmartPtrHelper<E, Derived>
+{
+};
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<RefPtr<E>, Derived>
+ : public nsTArray_SafeElementAtSmartPtrHelper<E, Derived>
+{
+};
+
+namespace mozilla {
+template<class T> class OwningNonNull;
+} // namespace mozilla
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<mozilla::OwningNonNull<E>, Derived>
+{
+ typedef E* elem_type;
+ typedef const E* const_elem_type;
+ typedef size_t index_type;
+
+ elem_type SafeElementAt(index_type aIndex)
+ {
+ if (aIndex < static_cast<Derived*>(this)->Length()) {
+ return static_cast<Derived*>(this)->ElementAt(aIndex);
+ }
+ return nullptr;
+ }
+
+ // XXX: Probably should return const_elem_type, but callsites must be fixed.
+ elem_type SafeElementAt(index_type aIndex) const
+ {
+ if (aIndex < static_cast<const Derived*>(this)->Length()) {
+ return static_cast<const Derived*>(this)->ElementAt(aIndex);
+ }
+ return nullptr;
+ }
+};
+
+// Servo bindings.
+extern "C" void Gecko_EnsureTArrayCapacity(void* aArray,
+ size_t aCapacity,
+ size_t aElementSize);
+extern "C" void Gecko_ClearPODTArray(void* aArray,
+ size_t aElementSize,
+ size_t aElementAlign);
+
+MOZ_NORETURN MOZ_COLD void
+InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength);
+
+//
+// This class serves as a base class for nsTArray. It shouldn't be used
+// directly. It holds common implementation code that does not depend on the
+// element type of the nsTArray.
+//
+template<class Alloc, class Copy>
+class nsTArray_base
+{
+ // Allow swapping elements with |nsTArray_base|s created using a
+ // different allocator. This is kosher because all allocators use
+ // the same free().
+ template<class Allocator, class Copier>
+ friend class nsTArray_base;
+ friend void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity,
+ size_t aElemSize);
+ friend void Gecko_ClearPODTArray(void* aTArray, size_t aElementSize,
+ size_t aElementAlign);
+
+protected:
+ typedef nsTArrayHeader Header;
+
+public:
+ typedef size_t size_type;
+ typedef size_t index_type;
+
+ // @return The number of elements in the array.
+ size_type Length() const { return mHdr->mLength; }
+
+ // @return True if the array is empty or false otherwise.
+ bool IsEmpty() const { return Length() == 0; }
+
+ // @return The number of elements that can fit in the array without forcing
+ // the array to be re-allocated. The length of an array is always less
+ // than or equal to its capacity.
+ size_type Capacity() const { return mHdr->mCapacity; }
+
+#ifdef DEBUG
+ void* DebugGetHeader() const { return mHdr; }
+#endif
+
+protected:
+ nsTArray_base();
+
+ ~nsTArray_base();
+
+ // Resize the storage if necessary to achieve the requested capacity.
+ // @param aCapacity The requested number of array elements.
+ // @param aElemSize The size of an array element.
+ // @return False if insufficient memory is available; true otherwise.
+ template<typename ActualAlloc>
+ typename ActualAlloc::ResultTypeProxy EnsureCapacity(size_type aCapacity,
+ size_type aElemSize);
+
+ // Tries to resize the storage to the minimum required amount. If this fails,
+ // the array is left as-is.
+ // @param aElemSize The size of an array element.
+ // @param aElemAlign The alignment in bytes of an array element.
+ void ShrinkCapacity(size_type aElemSize, size_t aElemAlign);
+
+ // This method may be called to resize a "gap" in the array by shifting
+ // elements around. It updates mLength appropriately. If the resulting
+ // array has zero elements, then the array's memory is free'd.
+ // @param aStart The starting index of the gap.
+ // @param aOldLen The current length of the gap.
+ // @param aNewLen The desired length of the gap.
+ // @param aElemSize The size of an array element.
+ // @param aElemAlign The alignment in bytes of an array element.
+ template<typename ActualAlloc>
+ void ShiftData(index_type aStart, size_type aOldLen, size_type aNewLen,
+ size_type aElemSize, size_t aElemAlign);
+
+ // This method increments the length member of the array's header.
+ // Note that mHdr may actually be sEmptyHdr in the case where a
+ // zero-length array is inserted into our array. But then aNum should
+ // always be 0.
+ void IncrementLength(size_t aNum)
+ {
+ if (mHdr == EmptyHdr()) {
+ if (MOZ_UNLIKELY(aNum != 0)) {
+ // Writing a non-zero length to the empty header would be extremely bad.
+ MOZ_CRASH();
+ }
+ } else {
+ mHdr->mLength += aNum;
+ }
+ }
+
+ // This method inserts blank slots into the array.
+ // @param aIndex the place to insert the new elements. This must be no
+ // greater than the current length of the array.
+ // @param aCount the number of slots to insert
+ // @param aElementSize the size of an array element.
+ // @param aElemAlign the alignment in bytes of an array element.
+ template<typename ActualAlloc>
+ bool InsertSlotsAt(index_type aIndex, size_type aCount,
+ size_type aElementSize, size_t aElemAlign);
+
+ template<typename ActualAlloc, class Allocator>
+ typename ActualAlloc::ResultTypeProxy
+ SwapArrayElements(nsTArray_base<Allocator, Copy>& aOther,
+ size_type aElemSize,
+ size_t aElemAlign);
+
+ // This is an RAII class used in SwapArrayElements.
+ class IsAutoArrayRestorer
+ {
+ public:
+ IsAutoArrayRestorer(nsTArray_base<Alloc, Copy>& aArray, size_t aElemAlign);
+ ~IsAutoArrayRestorer();
+
+ private:
+ nsTArray_base<Alloc, Copy>& mArray;
+ size_t mElemAlign;
+ bool mIsAuto;
+ };
+
+ // Helper function for SwapArrayElements. Ensures that if the array
+ // is an AutoTArray that it doesn't use the built-in buffer.
+ template<typename ActualAlloc>
+ bool EnsureNotUsingAutoArrayBuffer(size_type aElemSize);
+
+ // Returns true if this nsTArray is an AutoTArray with a built-in buffer.
+ bool IsAutoArray() const { return mHdr->mIsAutoArray; }
+
+ // Returns a Header for the built-in buffer of this AutoTArray.
+ Header* GetAutoArrayBuffer(size_t aElemAlign)
+ {
+ MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
+ return GetAutoArrayBufferUnsafe(aElemAlign);
+ }
+ const Header* GetAutoArrayBuffer(size_t aElemAlign) const
+ {
+ MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
+ return GetAutoArrayBufferUnsafe(aElemAlign);
+ }
+
+ // Returns a Header for the built-in buffer of this AutoTArray, but doesn't
+ // assert that we are an AutoTArray.
+ Header* GetAutoArrayBufferUnsafe(size_t aElemAlign)
+ {
+ return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(
+ this)->GetAutoArrayBufferUnsafe(aElemAlign));
+ }
+ const Header* GetAutoArrayBufferUnsafe(size_t aElemAlign) const;
+
+ // Returns true if this is an AutoTArray and it currently uses the
+ // built-in buffer to store its elements.
+ bool UsesAutoArrayBuffer() const;
+
+ // The array's elements (prefixed with a Header). This pointer is never
+ // null. If the array is empty, then this will point to sEmptyHdr.
+ Header* mHdr;
+
+ Header* Hdr() const { return mHdr; }
+ Header** PtrToHdr() { return &mHdr; }
+ static Header* EmptyHdr() { return &Header::sEmptyHdr; }
+};
+
+//
+// This class defines convenience functions for element specific operations.
+// Specialize this template if necessary.
+//
+template<class E>
+class nsTArrayElementTraits
+{
+public:
+ // Invoke the default constructor in place.
+ static inline void Construct(E* aE)
+ {
+ // Do NOT call "E()"! That triggers C++ "default initialization"
+ // which zeroes out POD ("plain old data") types such as regular
+ // ints. We don't want that because it can be a performance issue
+ // and people don't expect it; nsTArray should work like a regular
+ // C/C++ array in this respect.
+ new (static_cast<void*>(aE)) E;
+ }
+ // Invoke the copy-constructor in place.
+ template<class A>
+ static inline void Construct(E* aE, A&& aArg)
+ {
+ typedef typename mozilla::RemoveCV<E>::Type E_NoCV;
+ typedef typename mozilla::RemoveCV<A>::Type A_NoCV;
+ static_assert(!mozilla::IsSame<E_NoCV*, A_NoCV>::value,
+ "For safety, we disallow constructing nsTArray<E> elements "
+ "from E* pointers. See bug 960591.");
+ new (static_cast<void*>(aE)) E(mozilla::Forward<A>(aArg));
+ }
+ // Invoke the destructor in place.
+ static inline void Destruct(E* aE) { aE->~E(); }
+};
+
+// The default comparator used by nsTArray
+template<class A, class B>
+class nsDefaultComparator
+{
+public:
+ bool Equals(const A& aA, const B& aB) const { return aA == aB; }
+ bool LessThan(const A& aA, const B& aB) const { return aA < aB; }
+};
+
+template<bool IsPod, bool IsSameType>
+struct AssignRangeAlgorithm
+{
+ template<class Item, class ElemType, class IndexType, class SizeType>
+ static void implementation(ElemType* aElements, IndexType aStart,
+ SizeType aCount, const Item* aValues)
+ {
+ ElemType* iter = aElements + aStart;
+ ElemType* end = iter + aCount;
+ for (; iter != end; ++iter, ++aValues) {
+ nsTArrayElementTraits<ElemType>::Construct(iter, *aValues);
+ }
+ }
+};
+
+template<>
+struct AssignRangeAlgorithm<true, true>
+{
+ template<class Item, class ElemType, class IndexType, class SizeType>
+ static void implementation(ElemType* aElements, IndexType aStart,
+ SizeType aCount, const Item* aValues)
+ {
+ memcpy(aElements + aStart, aValues, aCount * sizeof(ElemType));
+ }
+};
+
+//
+// Normally elements are copied with memcpy and memmove, but for some element
+// types that is problematic. The nsTArray_CopyChooser template class can be
+// specialized to ensure that copying calls constructors and destructors
+// instead, as is done below for JS::Heap<E> elements.
+//
+
+//
+// A class that defines how to copy elements using memcpy/memmove.
+//
+struct nsTArray_CopyWithMemutils
+{
+ const static bool allowRealloc = true;
+
+ static void MoveNonOverlappingRegionWithHeader(void* aDest, const void* aSrc,
+ size_t aCount, size_t aElemSize)
+ {
+ memcpy(aDest, aSrc, sizeof(nsTArrayHeader) + aCount * aElemSize);
+ }
+
+ static void MoveOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ memmove(aDest, aSrc, aCount * aElemSize);
+ }
+
+ static void MoveNonOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ memcpy(aDest, aSrc, aCount * aElemSize);
+ }
+};
+
+//
+// A template class that defines how to copy elements calling their constructors
+// and destructors appropriately.
+//
+template<class ElemType>
+struct nsTArray_CopyWithConstructors
+{
+ typedef nsTArrayElementTraits<ElemType> traits;
+
+ const static bool allowRealloc = false;
+
+ static void MoveNonOverlappingRegionWithHeader(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(aDest);
+ nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(aSrc);
+ *destHeader = *srcHeader;
+ MoveNonOverlappingRegion(static_cast<uint8_t*>(aDest) + sizeof(nsTArrayHeader),
+ static_cast<uint8_t*>(aSrc) + sizeof(nsTArrayHeader),
+ aCount, aElemSize);
+ }
+
+ // These functions are defined by analogy with memmove and memcpy.
+ // What they actually do is slightly different: MoveOverlappingRegion
+ // checks to see which direction the movement needs to take place,
+ // whether from back-to-front of the range to be moved or from
+ // front-to-back. MoveNonOverlappingRegion assumes that moving
+ // front-to-back is always valid. So they're really more like
+ // std::move{_backward,} in that respect. We keep these names because
+ // we think they read slightly better, and MoveNonOverlappingRegion is
+ // only ever called on overlapping regions from MoveOverlappingRegion.
+ static void MoveOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ ElemType* destElem = static_cast<ElemType*>(aDest);
+ ElemType* srcElem = static_cast<ElemType*>(aSrc);
+ ElemType* destElemEnd = destElem + aCount;
+ ElemType* srcElemEnd = srcElem + aCount;
+ if (destElem == srcElem) {
+ return; // In practice, we don't do this.
+ }
+
+ // Figure out whether to copy back-to-front or front-to-back.
+ if (srcElemEnd > destElem && srcElemEnd < destElemEnd) {
+ while (destElemEnd != destElem) {
+ --destElemEnd;
+ --srcElemEnd;
+ traits::Construct(destElemEnd, mozilla::Move(*srcElemEnd));
+ traits::Destruct(srcElemEnd);
+ }
+ } else {
+ MoveNonOverlappingRegion(aDest, aSrc, aCount, aElemSize);
+ }
+ }
+
+ static void MoveNonOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ ElemType* destElem = static_cast<ElemType*>(aDest);
+ ElemType* srcElem = static_cast<ElemType*>(aSrc);
+ ElemType* destElemEnd = destElem + aCount;
+#ifdef DEBUG
+ ElemType* srcElemEnd = srcElem + aCount;
+ MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd);
+#endif
+ while (destElem != destElemEnd) {
+ traits::Construct(destElem, mozilla::Move(*srcElem));
+ traits::Destruct(srcElem);
+ ++destElem;
+ ++srcElem;
+ }
+ }
+};
+
+//
+// The default behaviour is to use memcpy/memmove for everything.
+//
+template<class E>
+struct MOZ_NEEDS_MEMMOVABLE_TYPE nsTArray_CopyChooser
+{
+ using Type = nsTArray_CopyWithMemutils;
+};
+
+//
+// Some classes require constructors/destructors to be called, so they are
+// specialized here.
+//
+#define DECLARE_USE_COPY_CONSTRUCTORS(T) \
+ template<> \
+ struct nsTArray_CopyChooser<T> \
+ { \
+ using Type = nsTArray_CopyWithConstructors<T>; \
+ };
+
+#define DECLARE_USE_COPY_CONSTRUCTORS_FOR_TEMPLATE(T) \
+ template<typename S> \
+ struct nsTArray_CopyChooser<T<S>> \
+ { \
+ using Type = nsTArray_CopyWithConstructors<T<S>>; \
+ };
+
+DECLARE_USE_COPY_CONSTRUCTORS_FOR_TEMPLATE(JS::Heap)
+
+DECLARE_USE_COPY_CONSTRUCTORS(nsRegion)
+DECLARE_USE_COPY_CONSTRUCTORS(nsIntRegion)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::layers::TileClient)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::SerializedStructuredCloneBuffer)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::ipc::StructuredCloneData)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::ClonedMessageData)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::indexedDB::StructuredCloneReadInfo);
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::indexedDB::ObjectStoreCursorResponse)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo);
+DECLARE_USE_COPY_CONSTRUCTORS(JSStructuredCloneData)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::MessagePortMessage)
+DECLARE_USE_COPY_CONSTRUCTORS(JS::ObjectPtr)
+
+
+//
+// Base class for nsTArray_Impl that is templated on element type and derived
+// nsTArray_Impl class, to allow extra conversions to be added for specific
+// types.
+//
+template<class E, class Derived>
+struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived>
+{
+};
+
+//
+// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E>
+// elements.
+//
+// These conversions are safe because JS::Heap<E> and E share the same
+// representation, and since the result of the conversions are const references
+// we won't miss any barriers.
+//
+// The static_cast is necessary to obtain the correct address for the derived
+// class since we are a base class used in multiple inheritance.
+//
+template<class E, class Derived>
+struct nsTArray_TypedBase<JS::Heap<E>, Derived>
+ : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived>
+{
+ operator const nsTArray<E>&()
+ {
+ static_assert(sizeof(E) == sizeof(JS::Heap<E>),
+ "JS::Heap<E> must be binary compatible with E.");
+ Derived* self = static_cast<Derived*>(this);
+ return *reinterpret_cast<nsTArray<E> *>(self);
+ }
+
+ operator const FallibleTArray<E>&()
+ {
+ Derived* self = static_cast<Derived*>(this);
+ return *reinterpret_cast<FallibleTArray<E> *>(self);
+ }
+};
+
+namespace detail {
+
+template<class Item, class Comparator>
+struct ItemComparatorEq
+{
+ const Item& mItem;
+ const Comparator& mComp;
+ ItemComparatorEq(const Item& aItem, const Comparator& aComp)
+ : mItem(aItem)
+ , mComp(aComp)
+ {}
+ template<class T>
+ int operator()(const T& aElement) const {
+ if (mComp.Equals(aElement, mItem)) {
+ return 0;
+ }
+
+ return mComp.LessThan(aElement, mItem) ? 1 : -1;
+ }
+};
+
+template<class Item, class Comparator>
+struct ItemComparatorFirstElementGT
+{
+ const Item& mItem;
+ const Comparator& mComp;
+ ItemComparatorFirstElementGT(const Item& aItem, const Comparator& aComp)
+ : mItem(aItem)
+ , mComp(aComp)
+ {}
+ template<class T>
+ int operator()(const T& aElement) const {
+ if (mComp.LessThan(aElement, mItem) ||
+ mComp.Equals(aElement, mItem)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+};
+
+} // namespace detail
+
+//
+// nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
+// AutoTArray.
+//
+// The only situation in which you might need to use nsTArray_Impl in your code
+// is if you're writing code which mutates a TArray which may or may not be
+// infallible.
+//
+// Code which merely reads from a TArray which may or may not be infallible can
+// simply cast the TArray to |const nsTArray&|; both fallible and infallible
+// TArrays can be cast to |const nsTArray&|.
+//
+template<class E, class Alloc>
+class nsTArray_Impl
+ : public nsTArray_base<Alloc, typename nsTArray_CopyChooser<E>::Type>
+ , public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc>>
+{
+private:
+ typedef nsTArrayFallibleAllocator FallibleAlloc;
+ typedef nsTArrayInfallibleAllocator InfallibleAlloc;
+
+public:
+ typedef typename nsTArray_CopyChooser<E>::Type copy_type;
+ typedef nsTArray_base<Alloc, copy_type> base_type;
+ typedef typename base_type::size_type size_type;
+ typedef typename base_type::index_type index_type;
+ typedef E elem_type;
+ typedef nsTArray_Impl<E, Alloc> self_type;
+ typedef nsTArrayElementTraits<E> elem_traits;
+ typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
+ typedef elem_type* iterator;
+ typedef const elem_type* const_iterator;
+ typedef mozilla::ReverseIterator<elem_type*> reverse_iterator;
+ typedef mozilla::ReverseIterator<const elem_type*> const_reverse_iterator;
+
+ using safeelementat_helper_type::SafeElementAt;
+ using base_type::EmptyHdr;
+
+ // A special value that is used to indicate an invalid or unknown index
+ // into the array.
+ static const index_type NoIndex = index_type(-1);
+
+ using base_type::Length;
+
+ //
+ // Finalization method
+ //
+
+ ~nsTArray_Impl() { Clear(); }
+
+ //
+ // Initialization methods
+ //
+
+ nsTArray_Impl() {}
+
+ // Initialize this array and pre-allocate some number of elements.
+ explicit nsTArray_Impl(size_type aCapacity) { SetCapacity(aCapacity); }
+
+ // Initialize this array with an r-value.
+ // Allow different types of allocators, since the allocator doesn't matter.
+ template<typename Allocator>
+ explicit nsTArray_Impl(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ SwapElements(aOther);
+ }
+
+ // The array's copy-constructor performs a 'deep' copy of the given array.
+ // @param aOther The array object to copy.
+ //
+ // It's very important that we declare this method as taking |const
+ // self_type&| as opposed to taking |const nsTArray_Impl<E, OtherAlloc>| for
+ // an arbitrary OtherAlloc.
+ //
+ // If we don't declare a constructor taking |const self_type&|, C++ generates
+ // a copy-constructor for this class which merely copies the object's
+ // members, which is obviously wrong.
+ //
+ // You can pass an nsTArray_Impl<E, OtherAlloc> to this method because
+ // nsTArray_Impl<E, X> can be cast to const nsTArray_Impl<E, Y>&. So the
+ // effect on the API is the same as if we'd declared this method as taking
+ // |const nsTArray_Impl<E, OtherAlloc>&|.
+ explicit nsTArray_Impl(const self_type& aOther) { AppendElements(aOther); }
+
+ explicit nsTArray_Impl(std::initializer_list<E> aIL) { AppendElements(aIL.begin(), aIL.size()); }
+ // Allow converting to a const array with a different kind of allocator,
+ // Since the allocator doesn't matter for const arrays
+ template<typename Allocator>
+ operator const nsTArray_Impl<E, Allocator>&() const
+ {
+ return *reinterpret_cast<const nsTArray_Impl<E, Allocator>*>(this);
+ }
+ // And we have to do this for our subclasses too
+ operator const nsTArray<E>&() const
+ {
+ return *reinterpret_cast<const InfallibleTArray<E>*>(this);
+ }
+ operator const FallibleTArray<E>&() const
+ {
+ return *reinterpret_cast<const FallibleTArray<E>*>(this);
+ }
+
+ // The array's assignment operator performs a 'deep' copy of the given
+ // array. It is optimized to reuse existing storage if possible.
+ // @param aOther The array object to copy.
+ self_type& operator=(const self_type& aOther)
+ {
+ if (this != &aOther) {
+ ReplaceElementsAt(0, Length(), aOther.Elements(), aOther.Length());
+ }
+ return *this;
+ }
+
+ // The array's move assignment operator steals the underlying data from
+ // the other array.
+ // @param other The array object to move from.
+ self_type& operator=(self_type&& aOther)
+ {
+ if (this != &aOther) {
+ Clear();
+ SwapElements(aOther);
+ }
+ return *this;
+ }
+
+ // Return true if this array has the same length and the same
+ // elements as |aOther|.
+ template<typename Allocator>
+ bool operator==(const nsTArray_Impl<E, Allocator>& aOther) const
+ {
+ size_type len = Length();
+ if (len != aOther.Length()) {
+ return false;
+ }
+
+ // XXX std::equal would be as fast or faster here
+ for (index_type i = 0; i < len; ++i) {
+ if (!(operator[](i) == aOther[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Return true if this array does not have the same length and the same
+ // elements as |aOther|.
+ bool operator!=(const self_type& aOther) const { return !operator==(aOther); }
+
+ template<typename Allocator>
+ self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ ReplaceElementsAt(0, Length(), aOther.Elements(), aOther.Length());
+ return *this;
+ }
+
+ template<typename Allocator>
+ self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ Clear();
+ SwapElements(aOther);
+ return *this;
+ }
+
+ // @return The amount of memory used by this nsTArray_Impl, excluding
+ // sizeof(*this). If you want to measure anything hanging off the array, you
+ // must iterate over the elements and measure them individually; hence the
+ // "Shallow" prefix.
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ if (this->UsesAutoArrayBuffer() || Hdr() == EmptyHdr()) {
+ return 0;
+ }
+ return aMallocSizeOf(this->Hdr());
+ }
+
+ // @return The amount of memory used by this nsTArray_Impl, including
+ // sizeof(*this). If you want to measure anything hanging off the array, you
+ // must iterate over the elements and measure them individually; hence the
+ // "Shallow" prefix.
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ //
+ // Accessor methods
+ //
+
+ // This method provides direct access to the array elements.
+ // @return A pointer to the first element of the array. If the array is
+ // empty, then this pointer must not be dereferenced.
+ elem_type* Elements() { return reinterpret_cast<elem_type*>(Hdr() + 1); }
+
+ // This method provides direct, readonly access to the array elements.
+ // @return A pointer to the first element of the array. If the array is
+ // empty, then this pointer must not be dereferenced.
+ const elem_type* Elements() const
+ {
+ return reinterpret_cast<const elem_type*>(Hdr() + 1);
+ }
+
+ // This method provides direct access to an element of the array. The given
+ // index must be within the array bounds.
+ // @param aIndex The index of an element in the array.
+ // @return A reference to the i'th element of the array.
+ elem_type& ElementAt(index_type aIndex)
+ {
+ if (MOZ_UNLIKELY(aIndex >= Length())) {
+ InvalidArrayIndex_CRASH(aIndex, Length());
+ }
+ return Elements()[aIndex];
+ }
+
+ // This method provides direct, readonly access to an element of the array
+ // The given index must be within the array bounds.
+ // @param aIndex The index of an element in the array.
+ // @return A const reference to the i'th element of the array.
+ const elem_type& ElementAt(index_type aIndex) const
+ {
+ if (MOZ_UNLIKELY(aIndex >= Length())) {
+ InvalidArrayIndex_CRASH(aIndex, Length());
+ }
+ return Elements()[aIndex];
+ }
+
+ // This method provides direct access to an element of the array in a bounds
+ // safe manner. If the requested index is out of bounds the provided default
+ // value is returned.
+ // @param aIndex The index of an element in the array.
+ // @param aDef The value to return if the index is out of bounds.
+ elem_type& SafeElementAt(index_type aIndex, elem_type& aDef)
+ {
+ return aIndex < Length() ? Elements()[aIndex] : aDef;
+ }
+
+ // This method provides direct access to an element of the array in a bounds
+ // safe manner. If the requested index is out of bounds the provided default
+ // value is returned.
+ // @param aIndex The index of an element in the array.
+ // @param aDef The value to return if the index is out of bounds.
+ const elem_type& SafeElementAt(index_type aIndex, const elem_type& aDef) const
+ {
+ return aIndex < Length() ? Elements()[aIndex] : aDef;
+ }
+
+ // Shorthand for ElementAt(aIndex)
+ elem_type& operator[](index_type aIndex) { return ElementAt(aIndex); }
+
+ // Shorthand for ElementAt(aIndex)
+ const elem_type& operator[](index_type aIndex) const { return ElementAt(aIndex); }
+
+ // Shorthand for ElementAt(length - 1)
+ elem_type& LastElement() { return ElementAt(Length() - 1); }
+
+ // Shorthand for ElementAt(length - 1)
+ const elem_type& LastElement() const { return ElementAt(Length() - 1); }
+
+ // Shorthand for SafeElementAt(length - 1, def)
+ elem_type& SafeLastElement(elem_type& aDef)
+ {
+ return SafeElementAt(Length() - 1, aDef);
+ }
+
+ // Shorthand for SafeElementAt(length - 1, def)
+ const elem_type& SafeLastElement(const elem_type& aDef) const
+ {
+ return SafeElementAt(Length() - 1, aDef);
+ }
+
+ // Methods for range-based for loops.
+ iterator begin() { return Elements(); }
+ const_iterator begin() const { return Elements(); }
+ const_iterator cbegin() const { return begin(); }
+ iterator end() { return Elements() + Length(); }
+ const_iterator end() const { return Elements() + Length(); }
+ const_iterator cend() const { return end(); }
+
+ // Methods for reverse iterating.
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
+ const_reverse_iterator crbegin() const { return rbegin(); }
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crend() const { return rend(); }
+
+ //
+ // Search methods
+ //
+
+ // This method searches for the first element in this array that is equal
+ // to the given element.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used to determine element equality.
+ // @return true if the element was found.
+ template<class Item, class Comparator>
+ bool Contains(const Item& aItem, const Comparator& aComp) const
+ {
+ return IndexOf(aItem, 0, aComp) != NoIndex;
+ }
+
+ // This method searches for the first element in this array that is equal
+ // to the given element. This method assumes that 'operator==' is defined
+ // for elem_type.
+ // @param aItem The item to search for.
+ // @return true if the element was found.
+ template<class Item>
+ bool Contains(const Item& aItem) const
+ {
+ return IndexOf(aItem) != NoIndex;
+ }
+
+ // This method searches for the offset of the first element in this
+ // array that is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from.
+ // @param aComp The Comparator used to determine element equality.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item, class Comparator>
+ index_type IndexOf(const Item& aItem, index_type aStart,
+ const Comparator& aComp) const
+ {
+ const elem_type* iter = Elements() + aStart;
+ const elem_type* iend = Elements() + Length();
+ for (; iter != iend; ++iter) {
+ if (aComp.Equals(*iter, aItem)) {
+ return index_type(iter - Elements());
+ }
+ }
+ return NoIndex;
+ }
+
+ // This method searches for the offset of the first element in this
+ // array that is equal to the given element. This method assumes
+ // that 'operator==' is defined for elem_type.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type IndexOf(const Item& aItem, index_type aStart = 0) const
+ {
+ return IndexOf(aItem, aStart, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This method searches for the offset of the last element in this
+ // array that is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from. If greater than or equal to the
+ // length of the array, then the entire array is searched.
+ // @param aComp The Comparator used to determine element equality.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item, class Comparator>
+ index_type LastIndexOf(const Item& aItem, index_type aStart,
+ const Comparator& aComp) const
+ {
+ size_type endOffset = aStart >= Length() ? Length() : aStart + 1;
+ const elem_type* iend = Elements() - 1;
+ const elem_type* iter = iend + endOffset;
+ for (; iter != iend; --iter) {
+ if (aComp.Equals(*iter, aItem)) {
+ return index_type(iter - Elements());
+ }
+ }
+ return NoIndex;
+ }
+
+ // This method searches for the offset of the last element in this
+ // array that is equal to the given element. This method assumes
+ // that 'operator==' is defined for elem_type.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from. If greater than or equal to the
+ // length of the array, then the entire array is searched.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type LastIndexOf(const Item& aItem,
+ index_type aStart = NoIndex) const
+ {
+ return LastIndexOf(aItem, aStart, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This method searches for the offset for the element in this array
+ // that is equal to the given element. The array is assumed to be sorted.
+ // If there is more than one equivalent element, there is no guarantee
+ // on which one will be returned.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item, class Comparator>
+ index_type BinaryIndexOf(const Item& aItem, const Comparator& aComp) const
+ {
+ using mozilla::BinarySearchIf;
+ typedef ::detail::ItemComparatorEq<Item, Comparator> Cmp;
+
+ size_t index;
+ bool found = BinarySearchIf(*this, 0, Length(), Cmp(aItem, aComp), &index);
+ return found ? index : NoIndex;
+ }
+
+ // This method searches for the offset for the element in this array
+ // that is equal to the given element. The array is assumed to be sorted.
+ // This method assumes that 'operator==' and 'operator<' are defined.
+ // @param aItem The item to search for.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type BinaryIndexOf(const Item& aItem) const
+ {
+ return BinaryIndexOf(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ //
+ // Mutation methods
+ //
+
+ template<class Allocator, typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType Assign(
+ const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ return ActualAlloc::ConvertBoolToResultType(
+ !!ReplaceElementsAt<E, ActualAlloc>(0, Length(),
+ aOther.Elements(), aOther.Length()));
+ }
+
+ template<class Allocator>
+ MOZ_MUST_USE
+ bool Assign(const nsTArray_Impl<E, Allocator>& aOther,
+ const mozilla::fallible_t&)
+ {
+ return Assign<Allocator, FallibleAlloc>(aOther);
+ }
+
+ template<class Allocator>
+ void Assign(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ Clear();
+ SwapElements(aOther);
+ }
+
+ // This method call the destructor on each element of the array, empties it,
+ // but does not shrink the array's capacity.
+ // See also SetLengthAndRetainStorage.
+ // Make sure to call Compact() if needed to avoid keeping a huge array
+ // around.
+ void ClearAndRetainStorage()
+ {
+ if (base_type::mHdr == EmptyHdr()) {
+ return;
+ }
+
+ DestructRange(0, Length());
+ base_type::mHdr->mLength = 0;
+ }
+
+ // This method modifies the length of the array, but unlike SetLength
+ // it doesn't deallocate/reallocate the current internal storage.
+ // The new length MUST be shorter than or equal to the current capacity.
+ // If the new length is larger than the existing length of the array,
+ // then new elements will be constructed using elem_type's default
+ // constructor. If shorter, elements will be destructed and removed.
+ // See also ClearAndRetainStorage.
+ // @param aNewLen The desired length of this array.
+ void SetLengthAndRetainStorage(size_type aNewLen)
+ {
+ MOZ_ASSERT(aNewLen <= base_type::Capacity());
+ size_type oldLen = Length();
+ if (aNewLen > oldLen) {
+ InsertElementsAt(oldLen, aNewLen - oldLen);
+ return;
+ }
+ if (aNewLen < oldLen) {
+ DestructRange(aNewLen, oldLen - aNewLen);
+ base_type::mHdr->mLength = aNewLen;
+ }
+ }
+
+ // This method replaces a range of elements in this array.
+ // @param aStart The starting index of the elements to replace.
+ // @param aCount The number of elements to replace. This may be zero to
+ // insert elements without removing any existing elements.
+ // @param aArray The values to copy into this array. Must be non-null,
+ // and these elements must not already exist in the array
+ // being modified.
+ // @param aArrayLen The number of values to copy into this array.
+ // @return A pointer to the new elements in the array, or null if
+ // the operation failed due to insufficient memory.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item* aArray, size_type aArrayLen);
+
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item* aArray, size_type aArrayLen,
+ const mozilla::fallible_t&)
+ {
+ return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount,
+ aArray, aArrayLen);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const nsTArray<Item>& aArray)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(
+ aStart, aCount, aArray.Elements(), aArray.Length());
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const nsTArray<Item>& aArray,
+ const mozilla::fallible_t&)
+ {
+ return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aArray);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item& aItem)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(aStart, aCount, &aItem, 1);
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item& aItem, const mozilla::fallible_t&)
+ {
+ return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aItem);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+ template<class Item>
+ elem_type* ReplaceElementAt(index_type aIndex, const Item& aItem)
+ {
+ return ReplaceElementsAt(aIndex, 1, &aItem, 1);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex, const Item* aArray,
+ size_type aArrayLen)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(aIndex, 0, aArray, aArrayLen);
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex, const Item* aArray,
+ size_type aArrayLen, const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<Item, FallibleAlloc>(aIndex, aArray, aArrayLen);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex,
+ const nsTArray_Impl<Item, Allocator>& aArray)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(
+ aIndex, 0, aArray.Elements(), aArray.Length());
+ }
+public:
+
+ template<class Item, class Allocator>
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex,
+ const nsTArray_Impl<Item, Allocator>& aArray,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<Item, Allocator, FallibleAlloc>(aIndex, aArray);
+ }
+
+ // Insert a new element without copy-constructing. This is useful to avoid
+ // temporaries.
+ // @return A pointer to the newly inserted element, or null on OOM.
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* InsertElementAt(index_type aIndex);
+
+public:
+
+ MOZ_MUST_USE
+ elem_type* InsertElementAt(index_type aIndex, const mozilla::fallible_t&)
+ {
+ return InsertElementAt<FallibleAlloc>(aIndex);
+ }
+
+ // Insert a new element, move constructing if possible.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementAt(index_type aIndex, Item&& aItem);
+
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementAt(index_type aIndex, Item&& aItem,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementAt<Item, FallibleAlloc>(aIndex,
+ mozilla::Forward<Item>(aItem));
+ }
+
+ // This method searches for the smallest index of an element that is strictly
+ // greater than |aItem|. If |aItem| is inserted at this index, the array will
+ // remain sorted and |aItem| would come after all elements that are equal to
+ // it. If |aItem| is greater than or equal to all elements in the array, the
+ // array length is returned.
+ //
+ // Note that consumers who want to know whether there are existing items equal
+ // to |aItem| in the array can just check that the return value here is > 0
+ // and indexing into the previous slot gives something equal to |aItem|.
+ //
+ //
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used.
+ // @return The index of greatest element <= to |aItem|
+ // @precondition The array is sorted
+ template<class Item, class Comparator>
+ index_type IndexOfFirstElementGt(const Item& aItem,
+ const Comparator& aComp) const
+ {
+ using mozilla::BinarySearchIf;
+ typedef ::detail::ItemComparatorFirstElementGT<Item, Comparator> Cmp;
+
+ size_t index;
+ BinarySearchIf(*this, 0, Length(), Cmp(aItem, aComp), &index);
+ return index;
+ }
+
+ // A variation on the IndexOfFirstElementGt method defined above.
+ template<class Item>
+ index_type
+ IndexOfFirstElementGt(const Item& aItem) const
+ {
+ return IndexOfFirstElementGt(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // Inserts |aItem| at such an index to guarantee that if the array
+ // was previously sorted, it will remain sorted after this
+ // insertion.
+protected:
+ template<class Item, class Comparator, typename ActualAlloc = Alloc>
+ elem_type* InsertElementSorted(Item&& aItem, const Comparator& aComp)
+ {
+ index_type index = IndexOfFirstElementGt<Item, Comparator>(aItem, aComp);
+ return InsertElementAt<Item, ActualAlloc>(
+ index, mozilla::Forward<Item>(aItem));
+ }
+public:
+
+ template<class Item, class Comparator>
+ MOZ_MUST_USE
+ elem_type* InsertElementSorted(Item&& aItem, const Comparator& aComp,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementSorted<Item, Comparator, FallibleAlloc>(
+ mozilla::Forward<Item>(aItem), aComp);
+ }
+
+ // A variation on the InsertElementSorted method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementSorted(Item&& aItem)
+ {
+ nsDefaultComparator<elem_type, Item> comp;
+ return InsertElementSorted<Item, decltype(comp), ActualAlloc>(
+ mozilla::Forward<Item>(aItem), comp);
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementSorted(Item&& aItem, const mozilla::fallible_t&)
+ {
+ return InsertElementSorted<Item, FallibleAlloc>(
+ mozilla::Forward<Item>(aItem));
+ }
+
+ // This method appends elements to the end of this array.
+ // @param aArray The elements to append to this array.
+ // @param aArrayLen The number of elements to append to this array.
+ // @return A pointer to the new elements in the array, or null if
+ // the operation failed due to insufficient memory.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* AppendElements(const Item* aArray, size_type aArrayLen);
+
+public:
+
+ template<class Item>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(const Item* aArray, size_type aArrayLen,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<Item, FallibleAlloc>(aArray, aArrayLen);
+ }
+
+ // A variation on the AppendElements method defined above.
+protected:
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ elem_type* AppendElements(const nsTArray_Impl<Item, Allocator>& aArray)
+ {
+ return AppendElements<Item, ActualAlloc>(aArray.Elements(), aArray.Length());
+ }
+public:
+
+ template<class Item, class Allocator>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(const nsTArray_Impl<Item, Allocator>& aArray,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<Item, Allocator, FallibleAlloc>(aArray);
+ }
+
+ // Move all elements from another array to the end of this array.
+ // @return A pointer to the newly appended elements, or null on OOM.
+protected:
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ elem_type* AppendElements(nsTArray_Impl<Item, Allocator>&& aArray);
+
+public:
+
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(nsTArray_Impl<Item, Allocator>&& aArray,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<Item, Allocator>(mozilla::Move(aArray));
+ }
+
+ // Append a new element, move constructing if possible.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* AppendElement(Item&& aItem);
+
+public:
+
+ template<class Item>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElement(Item&& aItem,
+ const mozilla::fallible_t&)
+ {
+ return AppendElement<Item, FallibleAlloc>(mozilla::Forward<Item>(aItem));
+ }
+
+ // Append new elements without copy-constructing. This is useful to avoid
+ // temporaries.
+ // @return A pointer to the newly appended elements, or null on OOM.
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* AppendElements(size_type aCount) {
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + aCount, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ elem_type* elems = Elements() + Length();
+ size_type i;
+ for (i = 0; i < aCount; ++i) {
+ elem_traits::Construct(elems + i);
+ }
+ this->IncrementLength(aCount);
+ return elems;
+ }
+public:
+
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(size_type aCount,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<FallibleAlloc>(aCount);
+ }
+
+ // Append a new element without copy-constructing. This is useful to avoid
+ // temporaries.
+ // @return A pointer to the newly appended element, or null on OOM.
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* AppendElement()
+ {
+ return AppendElements<ActualAlloc>(1);
+ }
+public:
+
+ /* MOZ_MUST_USE */
+ elem_type* AppendElement(const mozilla::fallible_t&)
+ {
+ return AppendElement<FallibleAlloc>();
+ }
+
+ // This method removes a range of elements from this array.
+ // @param aStart The starting index of the elements to remove.
+ // @param aCount The number of elements to remove.
+ void RemoveElementsAt(index_type aStart, size_type aCount);
+
+ // A variation on the RemoveElementsAt method defined above.
+ void RemoveElementAt(index_type aIndex) { RemoveElementsAt(aIndex, 1); }
+
+ // A variation on the RemoveElementsAt method defined above.
+ void Clear() { RemoveElementsAt(0, Length()); }
+
+ // This method removes elements based on the return value of the
+ // callback function aPredicate. If the function returns true for
+ // an element, the element is removed. aPredicate will be called
+ // for each element in order. It is not safe to access the array
+ // inside aPredicate.
+ template<typename Predicate>
+ void RemoveElementsBy(Predicate aPredicate);
+
+ // This helper function combines IndexOf with RemoveElementAt to "search
+ // and destroy" the first element that is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used to determine element equality.
+ // @return true if the element was found
+ template<class Item, class Comparator>
+ bool RemoveElement(const Item& aItem, const Comparator& aComp)
+ {
+ index_type i = IndexOf(aItem, 0, aComp);
+ if (i == NoIndex) {
+ return false;
+ }
+
+ RemoveElementAt(i);
+ return true;
+ }
+
+ // A variation on the RemoveElement method defined above that assumes
+ // that 'operator==' is defined for elem_type.
+ template<class Item>
+ bool RemoveElement(const Item& aItem)
+ {
+ return RemoveElement(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This helper function combines IndexOfFirstElementGt with
+ // RemoveElementAt to "search and destroy" the last element that
+ // is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used to determine element equality.
+ // @return true if the element was found
+ template<class Item, class Comparator>
+ bool RemoveElementSorted(const Item& aItem, const Comparator& aComp)
+ {
+ index_type index = IndexOfFirstElementGt(aItem, aComp);
+ if (index > 0 && aComp.Equals(ElementAt(index - 1), aItem)) {
+ RemoveElementAt(index - 1);
+ return true;
+ }
+ return false;
+ }
+
+ // A variation on the RemoveElementSorted method defined above.
+ template<class Item>
+ bool RemoveElementSorted(const Item& aItem)
+ {
+ return RemoveElementSorted(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This method causes the elements contained in this array and the given
+ // array to be swapped.
+ template<class Allocator>
+ typename Alloc::ResultType SwapElements(nsTArray_Impl<E, Allocator>& aOther)
+ {
+ return Alloc::Result(this->template SwapArrayElements<Alloc>(
+ aOther, sizeof(elem_type), MOZ_ALIGNOF(elem_type)));
+ }
+
+ //
+ // Allocation
+ //
+
+ // This method may increase the capacity of this array object by the
+ // specified amount. This method may be called in advance of several
+ // AppendElement operations to minimize heap re-allocations. This method
+ // will not reduce the number of elements in this array.
+ // @param aCapacity The desired capacity of this array.
+ // @return True if the operation succeeded; false if we ran out of memory
+protected:
+ template<typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType SetCapacity(size_type aCapacity)
+ {
+ return ActualAlloc::Result(this->template EnsureCapacity<ActualAlloc>(
+ aCapacity, sizeof(elem_type)));
+ }
+public:
+
+ MOZ_MUST_USE
+ bool SetCapacity(size_type aCapacity, const mozilla::fallible_t&)
+ {
+ return SetCapacity<FallibleAlloc>(aCapacity);
+ }
+
+ // This method modifies the length of the array. If the new length is
+ // larger than the existing length of the array, then new elements will be
+ // constructed using elem_type's default constructor. Otherwise, this call
+ // removes elements from the array (see also RemoveElementsAt).
+ // @param aNewLen The desired length of this array.
+ // @return True if the operation succeeded; false otherwise.
+ // See also TruncateLength if the new length is guaranteed to be smaller than
+ // the old.
+protected:
+ template<typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType SetLength(size_type aNewLen)
+ {
+ size_type oldLen = Length();
+ if (aNewLen > oldLen) {
+ return ActualAlloc::ConvertBoolToResultType(
+ InsertElementsAt<ActualAlloc>(oldLen, aNewLen - oldLen) != nullptr);
+ }
+
+ TruncateLength(aNewLen);
+ return ActualAlloc::ConvertBoolToResultType(true);
+ }
+public:
+
+ MOZ_MUST_USE
+ bool SetLength(size_type aNewLen, const mozilla::fallible_t&)
+ {
+ return SetLength<FallibleAlloc>(aNewLen);
+ }
+
+ // This method modifies the length of the array, but may only be
+ // called when the new length is shorter than the old. It can
+ // therefore be called when elem_type has no default constructor,
+ // unlike SetLength. It removes elements from the array (see also
+ // RemoveElementsAt).
+ // @param aNewLen The desired length of this array.
+ void TruncateLength(size_type aNewLen)
+ {
+ size_type oldLen = Length();
+ MOZ_ASSERT(aNewLen <= oldLen,
+ "caller should use SetLength instead");
+ RemoveElementsAt(aNewLen, oldLen - aNewLen);
+ }
+
+ // This method ensures that the array has length at least the given
+ // length. If the current length is shorter than the given length,
+ // then new elements will be constructed using elem_type's default
+ // constructor.
+ // @param aMinLen The desired minimum length of this array.
+ // @return True if the operation succeeded; false otherwise.
+protected:
+ template<typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType EnsureLengthAtLeast(size_type aMinLen)
+ {
+ size_type oldLen = Length();
+ if (aMinLen > oldLen) {
+ return ActualAlloc::ConvertBoolToResultType(
+ !!InsertElementsAt<ActualAlloc>(oldLen, aMinLen - oldLen));
+ }
+ return ActualAlloc::ConvertBoolToResultType(true);
+ }
+public:
+
+ MOZ_MUST_USE
+ bool EnsureLengthAtLeast(size_type aMinLen, const mozilla::fallible_t&)
+ {
+ return EnsureLengthAtLeast<FallibleAlloc>(aMinLen);
+ }
+
+ // This method inserts elements into the array, constructing
+ // them using elem_type's default constructor.
+ // @param aIndex the place to insert the new elements. This must be no
+ // greater than the current length of the array.
+ // @param aCount the number of elements to insert
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount)
+ {
+ if (!base_type::template InsertSlotsAt<ActualAlloc>(aIndex, aCount,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type))) {
+ return nullptr;
+ }
+
+ // Initialize the extra array elements
+ elem_type* iter = Elements() + aIndex;
+ elem_type* iend = iter + aCount;
+ for (; iter != iend; ++iter) {
+ elem_traits::Construct(iter);
+ }
+
+ return Elements() + aIndex;
+ }
+public:
+
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<FallibleAlloc>(aIndex, aCount);
+ }
+
+ // This method inserts elements into the array, constructing them
+ // elem_type's copy constructor (or whatever one-arg constructor
+ // happens to match the Item type).
+ // @param aIndex the place to insert the new elements. This must be no
+ // greater than the current length of the array.
+ // @param aCount the number of elements to insert.
+ // @param aItem the value to use when constructing the new elements.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount,
+ const Item& aItem);
+
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount,
+ const Item& aItem, const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<Item, FallibleAlloc>(aIndex, aCount, aItem);
+ }
+
+ // This method may be called to minimize the memory used by this array.
+ void Compact()
+ {
+ ShrinkCapacity(sizeof(elem_type), MOZ_ALIGNOF(elem_type));
+ }
+
+ //
+ // Sorting
+ //
+
+ // This function is meant to be used with the NS_QuickSort function. It
+ // maps the callback API expected by NS_QuickSort to the Comparator API
+ // used by nsTArray_Impl. See nsTArray_Impl::Sort.
+ template<class Comparator>
+ static int Compare(const void* aE1, const void* aE2, void* aData)
+ {
+ const Comparator* c = reinterpret_cast<const Comparator*>(aData);
+ const elem_type* a = static_cast<const elem_type*>(aE1);
+ const elem_type* b = static_cast<const elem_type*>(aE2);
+ return c->LessThan(*a, *b) ? -1 : (c->Equals(*a, *b) ? 0 : 1);
+ }
+
+ // This method sorts the elements of the array. It uses the LessThan
+ // method defined on the given Comparator object to collate elements.
+ // @param aComp The Comparator used to collate elements.
+ template<class Comparator>
+ void Sort(const Comparator& aComp)
+ {
+ NS_QuickSort(Elements(), Length(), sizeof(elem_type),
+ Compare<Comparator>, const_cast<Comparator*>(&aComp));
+ }
+
+ // A variation on the Sort method defined above that assumes that
+ // 'operator<' is defined for elem_type.
+ void Sort() { Sort(nsDefaultComparator<elem_type, elem_type>()); }
+
+protected:
+ using base_type::Hdr;
+ using base_type::ShrinkCapacity;
+
+ // This method invokes elem_type's destructor on a range of elements.
+ // @param aStart The index of the first element to destroy.
+ // @param aCount The number of elements to destroy.
+ void DestructRange(index_type aStart, size_type aCount)
+ {
+ elem_type* iter = Elements() + aStart;
+ elem_type *iend = iter + aCount;
+ for (; iter != iend; ++iter) {
+ elem_traits::Destruct(iter);
+ }
+ }
+
+ // This method invokes elem_type's copy-constructor on a range of elements.
+ // @param aStart The index of the first element to construct.
+ // @param aCount The number of elements to construct.
+ // @param aValues The array of elements to copy.
+ template<class Item>
+ void AssignRange(index_type aStart, size_type aCount, const Item* aValues)
+ {
+ AssignRangeAlgorithm<mozilla::IsPod<Item>::value,
+ mozilla::IsSame<Item, elem_type>::value>
+ ::implementation(Elements(), aStart, aCount, aValues);
+ }
+};
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item* aArray, size_type aArrayLen) -> elem_type*
+{
+ // Adjust memory allocation up-front to catch errors.
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + aArrayLen - aCount, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ DestructRange(aStart, aCount);
+ this->template ShiftData<ActualAlloc>(aStart, aCount, aArrayLen,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ AssignRange(aStart, aArrayLen, aArray);
+ return Elements() + aStart;
+}
+
+template<typename E, class Alloc>
+void
+nsTArray_Impl<E, Alloc>::RemoveElementsAt(index_type aStart, size_type aCount)
+{
+ MOZ_ASSERT(aCount == 0 || aStart < Length(), "Invalid aStart index");
+ MOZ_ASSERT(aStart + aCount <= Length(), "Invalid length");
+ // Check that the previous assert didn't overflow
+ MOZ_ASSERT(aStart <= aStart + aCount, "Start index plus length overflows");
+ DestructRange(aStart, aCount);
+ this->template ShiftData<InfallibleAlloc>(aStart, aCount, 0,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+}
+
+template<typename E, class Alloc>
+template<typename Predicate>
+void
+nsTArray_Impl<E, Alloc>::RemoveElementsBy(Predicate aPredicate)
+{
+ if (base_type::mHdr == EmptyHdr()) {
+ return;
+ }
+
+ index_type j = 0;
+ index_type len = Length();
+ for (index_type i = 0; i < len; ++i) {
+ if (aPredicate(Elements()[i])) {
+ elem_traits::Destruct(Elements() + i);
+ } else {
+ if (j < i) {
+ copy_type::MoveNonOverlappingRegion(Elements() + j, Elements() + i,
+ 1, sizeof(elem_type));
+ }
+ ++j;
+ }
+ }
+ base_type::mHdr->mLength = j;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::InsertElementsAt(index_type aIndex, size_type aCount,
+ const Item& aItem) -> elem_type*
+{
+ if (!base_type::template InsertSlotsAt<ActualAlloc>(aIndex, aCount,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type))) {
+ return nullptr;
+ }
+
+ // Initialize the extra array elements
+ elem_type* iter = Elements() + aIndex;
+ elem_type* iend = iter + aCount;
+ for (; iter != iend; ++iter) {
+ elem_traits::Construct(iter, aItem);
+ }
+
+ return Elements() + aIndex;
+}
+
+template<typename E, class Alloc>
+template<typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::InsertElementAt(index_type aIndex) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + 1, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ this->template ShiftData<ActualAlloc>(aIndex, 0, 1, sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ elem_type* elem = Elements() + aIndex;
+ elem_traits::Construct(elem);
+ return elem;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::InsertElementAt(index_type aIndex, Item&& aItem) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + 1, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ this->template ShiftData<ActualAlloc>(aIndex, 0, 1, sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ elem_type* elem = Elements() + aIndex;
+ elem_traits::Construct(elem, mozilla::Forward<Item>(aItem));
+ return elem;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::AppendElements(const Item* aArray, size_type aArrayLen) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + aArrayLen, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ index_type len = Length();
+ AssignRange(len, aArrayLen, aArray);
+ this->IncrementLength(aArrayLen);
+ return Elements() + len;
+}
+
+template<typename E, class Alloc>
+template<class Item, class Allocator, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::AppendElements(nsTArray_Impl<Item, Allocator>&& aArray) -> elem_type*
+{
+ MOZ_ASSERT(&aArray != this, "argument must be different aArray");
+ if (Length() == 0) {
+ SwapElements<ActualAlloc>(aArray);
+ return Elements();
+ }
+
+ index_type len = Length();
+ index_type otherLen = aArray.Length();
+ if (!Alloc::Successful(this->template EnsureCapacity<Alloc>(
+ len + otherLen, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ copy_type::MoveNonOverlappingRegion(Elements() + len, aArray.Elements(), otherLen,
+ sizeof(elem_type));
+ this->IncrementLength(otherLen);
+ aArray.template ShiftData<Alloc>(0, otherLen, 0, sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ return Elements() + len;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::AppendElement(Item&& aItem) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + 1, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ elem_type* elem = Elements() + Length();
+ elem_traits::Construct(elem, mozilla::Forward<Item>(aItem));
+ this->IncrementLength(1);
+ return elem;
+}
+
+template<typename E, typename Alloc>
+inline void
+ImplCycleCollectionUnlink(nsTArray_Impl<E, Alloc>& aField)
+{
+ aField.Clear();
+}
+
+template<typename E, typename Alloc>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsTArray_Impl<E, Alloc>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ size_t length = aField.Length();
+ for (size_t i = 0; i < length; ++i) {
+ ImplCycleCollectionTraverse(aCallback, aField[i], aName, aFlags);
+ }
+}
+
+//
+// nsTArray is an infallible vector class. See the comment at the top of this
+// file for more details.
+//
+template<class E>
+class nsTArray : public nsTArray_Impl<E, nsTArrayInfallibleAllocator>
+{
+public:
+ typedef nsTArray_Impl<E, nsTArrayInfallibleAllocator> base_type;
+ typedef nsTArray<E> self_type;
+ typedef typename base_type::size_type size_type;
+
+ nsTArray() {}
+ explicit nsTArray(size_type aCapacity) : base_type(aCapacity) {}
+ explicit nsTArray(const nsTArray& aOther) : base_type(aOther) {}
+ MOZ_IMPLICIT nsTArray(nsTArray&& aOther) : base_type(mozilla::Move(aOther)) {}
+ MOZ_IMPLICIT nsTArray(std::initializer_list<E> aIL) : base_type(aIL) {}
+
+ template<class Allocator>
+ explicit nsTArray(const nsTArray_Impl<E, Allocator>& aOther)
+ : base_type(aOther)
+ {
+ }
+ template<class Allocator>
+ MOZ_IMPLICIT nsTArray(nsTArray_Impl<E, Allocator>&& aOther)
+ : base_type(mozilla::Move(aOther))
+ {
+ }
+
+ self_type& operator=(const self_type& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ self_type& operator=(self_type&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+
+ using base_type::AppendElement;
+ using base_type::AppendElements;
+ using base_type::EnsureLengthAtLeast;
+ using base_type::InsertElementAt;
+ using base_type::InsertElementsAt;
+ using base_type::InsertElementSorted;
+ using base_type::ReplaceElementsAt;
+ using base_type::SetCapacity;
+ using base_type::SetLength;
+};
+
+//
+// FallibleTArray is a fallible vector class.
+//
+template<class E>
+class FallibleTArray : public nsTArray_Impl<E, nsTArrayFallibleAllocator>
+{
+public:
+ typedef nsTArray_Impl<E, nsTArrayFallibleAllocator> base_type;
+ typedef FallibleTArray<E> self_type;
+ typedef typename base_type::size_type size_type;
+
+ FallibleTArray() {}
+ explicit FallibleTArray(size_type aCapacity) : base_type(aCapacity) {}
+ explicit FallibleTArray(const FallibleTArray<E>& aOther) : base_type(aOther) {}
+ FallibleTArray(FallibleTArray<E>&& aOther)
+ : base_type(mozilla::Move(aOther))
+ {
+ }
+
+ template<class Allocator>
+ explicit FallibleTArray(const nsTArray_Impl<E, Allocator>& aOther)
+ : base_type(aOther)
+ {
+ }
+ template<class Allocator>
+ explicit FallibleTArray(nsTArray_Impl<E, Allocator>&& aOther)
+ : base_type(mozilla::Move(aOther))
+ {
+ }
+
+ self_type& operator=(const self_type& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ self_type& operator=(self_type&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+};
+
+//
+// AutoTArray<E, N> is like nsTArray<E>, but with N elements of inline storage.
+// Storing more than N elements is fine, but it will cause a heap allocation.
+//
+template<class E, size_t N>
+class MOZ_NON_MEMMOVABLE AutoTArray : public nsTArray<E>
+{
+ static_assert(N != 0, "AutoTArray<E, 0> should be specialized");
+public:
+ typedef AutoTArray<E, N> self_type;
+ typedef nsTArray<E> base_type;
+ typedef typename base_type::Header Header;
+ typedef typename base_type::elem_type elem_type;
+
+ AutoTArray()
+ {
+ Init();
+ }
+
+ AutoTArray(const self_type& aOther)
+ {
+ Init();
+ this->AppendElements(aOther);
+ }
+
+ explicit AutoTArray(const base_type& aOther)
+ {
+ Init();
+ this->AppendElements(aOther);
+ }
+
+ explicit AutoTArray(base_type&& aOther)
+ {
+ Init();
+ this->SwapElements(aOther);
+ }
+
+ template<typename Allocator>
+ explicit AutoTArray(nsTArray_Impl<elem_type, Allocator>&& aOther)
+ {
+ Init();
+ this->SwapElements(aOther);
+ }
+
+ MOZ_IMPLICIT AutoTArray(std::initializer_list<E> aIL)
+ {
+ Init();
+ this->AppendElements(aIL.begin(), aIL.size());
+ }
+
+ self_type& operator=(const self_type& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+
+ template<typename Allocator>
+ self_type& operator=(const nsTArray_Impl<elem_type, Allocator>& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+
+private:
+ // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
+ // to mAutoBuf.
+ template<class Allocator, class Copier>
+ friend class nsTArray_base;
+
+ void Init()
+ {
+ static_assert(MOZ_ALIGNOF(elem_type) <= 8,
+ "can't handle alignments greater than 8, "
+ "see nsTArray_base::UsesAutoArrayBuffer()");
+ // Temporary work around for VS2012 RC compiler crash
+ Header** phdr = base_type::PtrToHdr();
+ *phdr = reinterpret_cast<Header*>(&mAutoBuf);
+ (*phdr)->mLength = 0;
+ (*phdr)->mCapacity = N;
+ (*phdr)->mIsAutoArray = 1;
+
+ MOZ_ASSERT(base_type::GetAutoArrayBuffer(MOZ_ALIGNOF(elem_type)) ==
+ reinterpret_cast<Header*>(&mAutoBuf),
+ "GetAutoArrayBuffer needs to be fixed");
+ }
+
+ // Declare mAutoBuf aligned to the maximum of the header's alignment and
+ // elem_type's alignment. We need to use a union rather than
+ // MOZ_ALIGNED_DECL because GCC is picky about what goes into
+ // __attribute__((aligned(foo))).
+ union
+ {
+ char mAutoBuf[sizeof(nsTArrayHeader) + N * sizeof(elem_type)];
+ // Do the max operation inline to ensure that it is a compile-time constant.
+ mozilla::AlignedElem<(MOZ_ALIGNOF(Header) > MOZ_ALIGNOF(elem_type)) ?
+ MOZ_ALIGNOF(Header) : MOZ_ALIGNOF(elem_type)> mAlign;
+ };
+};
+
+//
+// Specialization of AutoTArray<E, N> for the case where N == 0.
+// AutoTArray<E, 0> behaves exactly like nsTArray<E>, but without this
+// specialization, it stores a useless inline header.
+//
+// We do have many AutoTArray<E, 0> objects in memory: about 2,000 per tab as
+// of May 2014. These are typically not explicitly AutoTArray<E, 0> but rather
+// AutoTArray<E, N> for some value N depending on template parameters, in
+// generic code.
+//
+// For that reason, we optimize this case with the below partial specialization,
+// which ensures that AutoTArray<E, 0> is just like nsTArray<E>, without any
+// inline header overhead.
+//
+template<class E>
+class AutoTArray<E, 0> : public nsTArray<E>
+{
+};
+
+template<class E, size_t N>
+struct nsTArray_CopyChooser<AutoTArray<E, N>>
+{
+ typedef nsTArray_CopyWithConstructors<AutoTArray<E, N>> Type;
+};
+
+// Assert that AutoTArray doesn't have any extra padding inside.
+//
+// It's important that the data stored in this auto array takes up a multiple of
+// 8 bytes; e.g. AutoTArray<uint32_t, 1> wouldn't work. Since AutoTArray
+// contains a pointer, its size must be a multiple of alignof(void*). (This is
+// because any type may be placed into an array, and there's no padding between
+// elements of an array.) The compiler pads the end of the structure to
+// enforce this rule.
+//
+// If we used AutoTArray<uint32_t, 1> below, this assertion would fail on a
+// 64-bit system, where the compiler inserts 4 bytes of padding at the end of
+// the auto array to make its size a multiple of alignof(void*) == 8 bytes.
+
+static_assert(sizeof(AutoTArray<uint32_t, 2>) ==
+ sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(uint32_t) * 2,
+ "AutoTArray shouldn't contain any extra padding, "
+ "see the comment");
+
+// Definitions of nsTArray_Impl methods
+#include "nsTArray-inl.h"
+
+#endif // nsTArray_h__
diff --git a/xpcom/glue/nsTArrayForwardDeclare.h b/xpcom/glue/nsTArrayForwardDeclare.h
new file mode 100644
index 000000000..f63ef5f59
--- /dev/null
+++ b/xpcom/glue/nsTArrayForwardDeclare.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTArrayForwardDeclare_h__
+#define nsTArrayForwardDeclare_h__
+
+//
+// This simple header file contains forward declarations for the TArray family
+// of classes.
+//
+// Including this header is preferable to writing
+//
+// template<class E> class nsTArray;
+//
+// yourself, since this header gives us flexibility to e.g. change the default
+// template parameters.
+//
+
+#include <stddef.h>
+
+template<class E>
+class nsTArray;
+
+template<class E>
+class FallibleTArray;
+
+template<class E, size_t N>
+class AutoTArray;
+
+template<class E>
+using InfallibleTArray = nsTArray<E>;
+
+#endif
diff --git a/xpcom/glue/nsTHashtable.h b/xpcom/glue/nsTHashtable.h
new file mode 100644
index 000000000..705b0294e
--- /dev/null
+++ b/xpcom/glue/nsTHashtable.h
@@ -0,0 +1,577 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTHashtable_h__
+#define nsTHashtable_h__
+
+#include "PLDHashTable.h"
+#include "nsPointerHashKeys.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/fallible.h"
+#include "mozilla/MemoryChecking.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "mozilla/OperatorNewExtensions.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/TypeTraits.h"
+
+#include <new>
+
+/**
+ * a base class for templated hashtables.
+ *
+ * Clients will rarely need to use this class directly. Check the derived
+ * classes first, to see if they will meet your needs.
+ *
+ * @param EntryType the templated entry-type class that is managed by the
+ * hashtable. <code>EntryType</code> must extend the following declaration,
+ * and <strong>must not declare any virtual functions or derive from classes
+ * with virtual functions.</strong> Any vtable pointer would break the
+ * PLDHashTable code.
+ *<pre> class EntryType : public PLDHashEntryHdr
+ * {
+ * public: or friend nsTHashtable<EntryType>;
+ * // KeyType is what we use when Get()ing or Put()ing this entry
+ * // this should either be a simple datatype (uint32_t, nsISupports*) or
+ * // a const reference (const nsAString&)
+ * typedef something KeyType;
+ * // KeyTypePointer is the pointer-version of KeyType, because
+ * // PLDHashTable.h requires keys to cast to <code>const void*</code>
+ * typedef const something* KeyTypePointer;
+ *
+ * EntryType(KeyTypePointer aKey);
+ *
+ * // A copy or C++11 Move constructor must be defined, even if
+ * // AllowMemMove() == true, otherwise you will cause link errors.
+ * EntryType(const EntryType& aEnt); // Either this...
+ * EntryType(EntryType&& aEnt); // ...or this
+ *
+ * // the destructor must be defined... or you will cause link errors!
+ * ~EntryType();
+ *
+ * // KeyEquals(): does this entry match this key?
+ * bool KeyEquals(KeyTypePointer aKey) const;
+ *
+ * // KeyToPointer(): Convert KeyType to KeyTypePointer
+ * static KeyTypePointer KeyToPointer(KeyType aKey);
+ *
+ * // HashKey(): calculate the hash number
+ * static PLDHashNumber HashKey(KeyTypePointer aKey);
+ *
+ * // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
+ * // to use the copy constructor?
+ * enum { ALLOW_MEMMOVE = true/false };
+ * }</pre>
+ *
+ * @see nsInterfaceHashtable
+ * @see nsDataHashtable
+ * @see nsClassHashtable
+ * @author "Benjamin Smedberg <bsmedberg@covad.net>"
+ */
+
+template<class EntryType>
+class MOZ_NEEDS_NO_VTABLE_TYPE nsTHashtable
+{
+ typedef mozilla::fallible_t fallible_t;
+ static_assert(mozilla::IsPointer<typename EntryType::KeyTypePointer>::value,
+ "KeyTypePointer should be a pointer");
+
+public:
+ // Separate constructors instead of default aInitLength parameter since
+ // otherwise the default no-arg constructor isn't found.
+ nsTHashtable()
+ : mTable(Ops(), sizeof(EntryType), PLDHashTable::kDefaultInitialLength)
+ {}
+ explicit nsTHashtable(uint32_t aInitLength)
+ : mTable(Ops(), sizeof(EntryType), aInitLength)
+ {}
+
+ /**
+ * destructor, cleans up and deallocates
+ */
+ ~nsTHashtable();
+
+ nsTHashtable(nsTHashtable<EntryType>&& aOther);
+
+ /**
+ * Return the generation number for the table. This increments whenever
+ * the table data items are moved.
+ */
+ uint32_t GetGeneration() const { return mTable.Generation(); }
+
+ /**
+ * KeyType is typedef'ed for ease of use.
+ */
+ typedef typename EntryType::KeyType KeyType;
+
+ /**
+ * KeyTypePointer is typedef'ed for ease of use.
+ */
+ typedef typename EntryType::KeyTypePointer KeyTypePointer;
+
+ /**
+ * Return the number of entries in the table.
+ * @return number of entries
+ */
+ uint32_t Count() const { return mTable.EntryCount(); }
+
+ /**
+ * Return true if the hashtable is empty.
+ */
+ bool IsEmpty() const { return Count() == 0; }
+
+ /**
+ * Get the entry associated with a key.
+ * @param aKey the key to retrieve
+ * @return pointer to the entry class, if the key exists; nullptr if the
+ * key doesn't exist
+ */
+ EntryType* GetEntry(KeyType aKey) const
+ {
+ return static_cast<EntryType*>(
+ const_cast<PLDHashTable*>(&mTable)->Search(EntryType::KeyToPointer(aKey)));
+ }
+
+ /**
+ * Return true if an entry for the given key exists, false otherwise.
+ * @param aKey the key to retrieve
+ * @return true if the key exists, false if the key doesn't exist
+ */
+ bool Contains(KeyType aKey) const { return !!GetEntry(aKey); }
+
+ /**
+ * Get the entry associated with a key, or create a new entry,
+ * @param aKey the key to retrieve
+ * @return pointer to the entry class retreived; nullptr only if memory
+ can't be allocated
+ */
+ EntryType* PutEntry(KeyType aKey)
+ {
+ // infallible add
+ return static_cast<EntryType*>(mTable.Add(EntryType::KeyToPointer(aKey)));
+ }
+
+ MOZ_MUST_USE
+ EntryType* PutEntry(KeyType aKey, const fallible_t&)
+ {
+ return static_cast<EntryType*>(mTable.Add(EntryType::KeyToPointer(aKey),
+ mozilla::fallible));
+ }
+
+ /**
+ * Remove the entry associated with a key.
+ * @param aKey of the entry to remove
+ */
+ void RemoveEntry(KeyType aKey)
+ {
+ mTable.Remove(EntryType::KeyToPointer(aKey));
+ }
+
+ /**
+ * Remove the entry associated with a key.
+ * @param aEntry the entry-pointer to remove (obtained from GetEntry)
+ */
+ void RemoveEntry(EntryType* aEntry)
+ {
+ mTable.RemoveEntry(aEntry);
+ }
+
+ /**
+ * Remove the entry associated with a key, but don't resize the hashtable.
+ * This is a low-level method, and is not recommended unless you know what
+ * you're doing. If you use it, please add a comment explaining why you
+ * didn't use RemoveEntry().
+ * @param aEntry the entry-pointer to remove (obtained from GetEntry)
+ */
+ void RawRemoveEntry(EntryType* aEntry)
+ {
+ mTable.RawRemove(aEntry);
+ }
+
+ // This is an iterator that also allows entry removal. Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // Entry* entry = iter.Get();
+ // // ... do stuff with |entry| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ class Iterator : public PLDHashTable::Iterator
+ {
+ public:
+ typedef PLDHashTable::Iterator Base;
+
+ explicit Iterator(nsTHashtable* aTable) : Base(&aTable->mTable) {}
+ Iterator(Iterator&& aOther) : Base(aOther.mTable) {}
+ ~Iterator() {}
+
+ EntryType* Get() const { return static_cast<EntryType*>(Base::Get()); }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<nsTHashtable*>(this));
+ }
+
+ /**
+ * Remove all entries, return hashtable to "pristine" state. It's
+ * conceptually the same as calling the destructor and then re-calling the
+ * constructor.
+ */
+ void Clear()
+ {
+ mTable.Clear();
+ }
+
+ /**
+ * Measure the size of the table's entry storage. Does *not* measure anything
+ * hanging off table entries; hence the "Shallow" prefix. To measure that,
+ * either use SizeOfExcludingThis() or iterate manually over the entries,
+ * calling SizeOfExcludingThis() on each one.
+ *
+ * @param aMallocSizeOf the function used to measure heap-allocated blocks
+ * @return the measured shallow size of the table
+ */
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Like ShallowSizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * This is a "deep" measurement of the table. To use it, |EntryType| must
+ * define SizeOfExcludingThis, and that method will be called on all live
+ * entries.
+ */
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
+ n += (*iter.Get()).SizeOfExcludingThis(aMallocSizeOf);
+ }
+ return n;
+ }
+
+ /**
+ * Like SizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Swap the elements in this hashtable with the elements in aOther.
+ */
+ void SwapElements(nsTHashtable<EntryType>& aOther)
+ {
+ MOZ_ASSERT_IF(this->mTable.Ops() && aOther.mTable.Ops(),
+ this->mTable.Ops() == aOther.mTable.Ops());
+ mozilla::Swap(this->mTable, aOther.mTable);
+ }
+
+#ifdef DEBUG
+ /**
+ * Mark the table as constant after initialization.
+ *
+ * This will prevent assertions when a read-only hash is accessed on multiple
+ * threads without synchronization.
+ */
+ void MarkImmutable()
+ {
+ mTable.MarkImmutable();
+ }
+#endif
+
+protected:
+ PLDHashTable mTable;
+
+ static PLDHashNumber s_HashKey(const void* aKey);
+
+ static bool s_MatchEntry(const PLDHashEntryHdr* aEntry,
+ const void* aKey);
+
+ static void s_CopyEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+
+ static void s_ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
+
+ static void s_InitEntry(PLDHashEntryHdr* aEntry, const void* aKey);
+
+private:
+ // copy constructor, not implemented
+ nsTHashtable(nsTHashtable<EntryType>& aToCopy) = delete;
+
+ /**
+ * Gets the table's ops.
+ */
+ static const PLDHashTableOps* Ops();
+
+ // assignment operator, not implemented
+ nsTHashtable<EntryType>& operator=(nsTHashtable<EntryType>& aToEqual) = delete;
+};
+
+//
+// template definitions
+//
+
+template<class EntryType>
+nsTHashtable<EntryType>::nsTHashtable(nsTHashtable<EntryType>&& aOther)
+ : mTable(mozilla::Move(aOther.mTable))
+{
+ // aOther shouldn't touch mTable after this, because we've stolen the table's
+ // pointers but not overwitten them.
+ MOZ_MAKE_MEM_UNDEFINED(&aOther.mTable, sizeof(aOther.mTable));
+}
+
+template<class EntryType>
+nsTHashtable<EntryType>::~nsTHashtable()
+{
+}
+
+template<class EntryType>
+/* static */ const PLDHashTableOps*
+nsTHashtable<EntryType>::Ops()
+{
+ // If this variable is a global variable, we get strange start-up failures on
+ // WindowsCrtPatch.h (see bug 1166598 comment 20). But putting it inside a
+ // function avoids that problem.
+ static const PLDHashTableOps sOps =
+ {
+ s_HashKey,
+ s_MatchEntry,
+ EntryType::ALLOW_MEMMOVE ? PLDHashTable::MoveEntryStub : s_CopyEntry,
+ s_ClearEntry,
+ s_InitEntry
+ };
+ return &sOps;
+}
+
+// static definitions
+
+template<class EntryType>
+PLDHashNumber
+nsTHashtable<EntryType>::s_HashKey(const void* aKey)
+{
+ return EntryType::HashKey(static_cast<const KeyTypePointer>(aKey));
+}
+
+template<class EntryType>
+bool
+nsTHashtable<EntryType>::s_MatchEntry(const PLDHashEntryHdr* aEntry,
+ const void* aKey)
+{
+ return ((const EntryType*)aEntry)->KeyEquals(
+ static_cast<const KeyTypePointer>(aKey));
+}
+
+template<class EntryType>
+void
+nsTHashtable<EntryType>::s_CopyEntry(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo)
+{
+ EntryType* fromEntry =
+ const_cast<EntryType*>(static_cast<const EntryType*>(aFrom));
+
+ new (mozilla::KnownNotNull, aTo) EntryType(mozilla::Move(*fromEntry));
+
+ fromEntry->~EntryType();
+}
+
+template<class EntryType>
+void
+nsTHashtable<EntryType>::s_ClearEntry(PLDHashTable* aTable,
+ PLDHashEntryHdr* aEntry)
+{
+ static_cast<EntryType*>(aEntry)->~EntryType();
+}
+
+template<class EntryType>
+void
+nsTHashtable<EntryType>::s_InitEntry(PLDHashEntryHdr* aEntry,
+ const void* aKey)
+{
+ new (mozilla::KnownNotNull, aEntry) EntryType(static_cast<KeyTypePointer>(aKey));
+}
+
+class nsCycleCollectionTraversalCallback;
+
+template<class EntryType>
+inline void
+ImplCycleCollectionUnlink(nsTHashtable<EntryType>& aField)
+{
+ aField.Clear();
+}
+
+template<class EntryType>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsTHashtable<EntryType>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ for (auto iter = aField.Iter(); !iter.Done(); iter.Next()) {
+ EntryType* entry = iter.Get();
+ ImplCycleCollectionTraverse(aCallback, *entry, aName, aFlags);
+ }
+}
+
+/**
+ * For nsTHashtable with pointer entries, we can have a template specialization
+ * that layers a typed T* interface on top of a common implementation that
+ * works internally with void pointers. This arrangement saves code size and
+ * might slightly improve performance as well.
+ */
+
+/**
+ * We need a separate entry type class for the inheritance structure of the
+ * nsTHashtable specialization below; nsVoidPtrHashKey is simply typedefed to a
+ * specialization of nsPtrHashKey, and the formulation:
+ *
+ * class nsTHashtable<nsPtrHashKey<T>> : protected nsTHashtable<nsPtrHashKey<const void>
+ *
+ * is not going to turn out very well, since we'd wind up with an nsTHashtable
+ * instantiation that is its own base class.
+ */
+namespace detail {
+
+class VoidPtrHashKey : public nsPtrHashKey<const void>
+{
+ typedef nsPtrHashKey<const void> Base;
+
+public:
+ explicit VoidPtrHashKey(const void* aKey) : Base(aKey) {}
+};
+
+} // namespace detail
+
+/**
+ * See the main nsTHashtable documentation for descriptions of this class's
+ * methods.
+ */
+template<typename T>
+class nsTHashtable<nsPtrHashKey<T>> : protected nsTHashtable<::detail::VoidPtrHashKey>
+{
+ typedef nsTHashtable<::detail::VoidPtrHashKey> Base;
+ typedef nsPtrHashKey<T> EntryType;
+
+ // We play games with reinterpret_cast'ing between these two classes, so
+ // try to ensure that playing said games is reasonable.
+ static_assert(sizeof(nsPtrHashKey<T>) == sizeof(::detail::VoidPtrHashKey),
+ "hash keys must be the same size");
+
+ nsTHashtable(const nsTHashtable& aOther) = delete;
+ nsTHashtable& operator=(const nsTHashtable& aOther) = delete;
+
+public:
+ nsTHashtable() = default;
+ explicit nsTHashtable(uint32_t aInitLength)
+ : Base(aInitLength)
+ {}
+
+ ~nsTHashtable() = default;
+
+ nsTHashtable(nsTHashtable&&) = default;
+
+ /* Wrapper functions */
+ using Base::GetGeneration;
+ using Base::Count;
+ using Base::IsEmpty;
+ using Base::Clear;
+
+ using Base::ShallowSizeOfExcludingThis;
+ using Base::ShallowSizeOfIncludingThis;
+
+#ifdef DEBUG
+ using Base::MarkImmutable;
+#endif
+
+ EntryType* GetEntry(T* aKey) const
+ {
+ return reinterpret_cast<EntryType*>(Base::GetEntry(aKey));
+ }
+
+ bool Contains(T* aKey) const
+ {
+ return Base::Contains(aKey);
+ }
+
+ EntryType* PutEntry(T* aKey)
+ {
+ return reinterpret_cast<EntryType*>(Base::PutEntry(aKey));
+ }
+
+ MOZ_MUST_USE
+ EntryType* PutEntry(T* aKey, const mozilla::fallible_t&)
+ {
+ return reinterpret_cast<EntryType*>(
+ Base::PutEntry(aKey, mozilla::fallible));
+ }
+
+ void RemoveEntry(T* aKey)
+ {
+ Base::RemoveEntry(aKey);
+ }
+
+ void RemoveEntry(EntryType* aEntry)
+ {
+ Base::RemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));
+ }
+
+ void RawRemoveEntry(EntryType* aEntry)
+ {
+ Base::RawRemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));
+ }
+
+ class Iterator : public Base::Iterator
+ {
+ public:
+ typedef nsTHashtable::Base::Iterator Base;
+
+ explicit Iterator(nsTHashtable* aTable) : Base(aTable) {}
+ Iterator(Iterator&& aOther) : Base(mozilla::Move(aOther)) {}
+ ~Iterator() = default;
+
+ EntryType* Get() const { return reinterpret_cast<EntryType*>(Base::Get()); }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<nsTHashtable*>(this));
+ }
+
+ void SwapElements(nsTHashtable& aOther)
+ {
+ Base::SwapElements(aOther);
+ }
+};
+
+#endif // nsTHashtable_h__
diff --git a/xpcom/glue/nsTObserverArray.cpp b/xpcom/glue/nsTObserverArray.cpp
new file mode 100644
index 000000000..dd68631f4
--- /dev/null
+++ b/xpcom/glue/nsTObserverArray.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTObserverArray.h"
+
+void
+nsTObserverArray_base::AdjustIterators(index_type aModPos,
+ diff_type aAdjustment)
+{
+ NS_PRECONDITION(aAdjustment == -1 || aAdjustment == 1, "invalid adjustment");
+ Iterator_base* iter = mIterators;
+ while (iter) {
+ if (iter->mPosition > aModPos) {
+ iter->mPosition += aAdjustment;
+ }
+ iter = iter->mNext;
+ }
+}
+
+void
+nsTObserverArray_base::ClearIterators()
+{
+ Iterator_base* iter = mIterators;
+ while (iter) {
+ iter->mPosition = 0;
+ iter = iter->mNext;
+ }
+}
diff --git a/xpcom/glue/nsTObserverArray.h b/xpcom/glue/nsTObserverArray.h
new file mode 100644
index 000000000..e9966d080
--- /dev/null
+++ b/xpcom/glue/nsTObserverArray.h
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTObserverArray_h___
+#define nsTObserverArray_h___
+
+#include "mozilla/MemoryReporting.h"
+#include "nsTArray.h"
+#include "nsCycleCollectionNoteChild.h"
+
+/**
+ * An array of observers. Like a normal array, but supports iterators that are
+ * stable even if the array is modified during iteration.
+ * The template parameter T is the observer type the array will hold;
+ * N is the number of built-in storage slots that come with the array.
+ * NOTE: You probably want to use nsTObserverArray, unless you specifically
+ * want built-in storage. See below.
+ * @see nsTObserverArray, nsTArray
+ */
+
+class nsTObserverArray_base
+{
+public:
+ typedef size_t index_type;
+ typedef size_t size_type;
+ typedef ptrdiff_t diff_type;
+
+protected:
+ class Iterator_base
+ {
+ protected:
+ friend class nsTObserverArray_base;
+
+ Iterator_base(index_type aPosition, Iterator_base* aNext)
+ : mPosition(aPosition)
+ , mNext(aNext)
+ {
+ }
+
+ // The current position of the iterator. Its exact meaning differs
+ // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
+ index_type mPosition;
+
+ // The next iterator currently iterating the same array
+ Iterator_base* mNext;
+ };
+
+ nsTObserverArray_base() : mIterators(nullptr) {}
+
+ ~nsTObserverArray_base()
+ {
+ NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
+ }
+
+ /**
+ * Adjusts iterators after an element has been inserted or removed
+ * from the array.
+ * @param aModPos Position where elements were added or removed.
+ * @param aAdjustment -1 if an element was removed, 1 if an element was
+ * added.
+ */
+ void AdjustIterators(index_type aModPos, diff_type aAdjustment);
+
+ /**
+ * Clears iterators when the array is destroyed.
+ */
+ void ClearIterators();
+
+ mutable Iterator_base* mIterators;
+};
+
+template<class T, size_t N>
+class nsAutoTObserverArray : protected nsTObserverArray_base
+{
+public:
+ typedef T elem_type;
+ typedef nsTArray<T> array_type;
+
+ nsAutoTObserverArray() {}
+
+ //
+ // Accessor methods
+ //
+
+ // @return The number of elements in the array.
+ size_type Length() const { return mArray.Length(); }
+
+ // @return True if the array is empty or false otherwise.
+ bool IsEmpty() const { return mArray.IsEmpty(); }
+
+ // This method provides direct access to an element of the array. The given
+ // index must be within the array bounds. If the underlying array may change
+ // during iteration, use an iterator instead of this function.
+ // @param aIndex The index of an element in the array.
+ // @return A reference to the i'th element of the array.
+ elem_type& ElementAt(index_type aIndex)
+ {
+ return mArray.ElementAt(aIndex);
+ }
+
+ // Same as above, but readonly.
+ const elem_type& ElementAt(index_type aIndex) const
+ {
+ return mArray.ElementAt(aIndex);
+ }
+
+ // This method provides direct access to an element of the array in a bounds
+ // safe manner. If the requested index is out of bounds the provided default
+ // value is returned.
+ // @param aIndex The index of an element in the array.
+ // @param aDef The value to return if the index is out of bounds.
+ elem_type& SafeElementAt(index_type aIndex, elem_type& aDef)
+ {
+ return mArray.SafeElementAt(aIndex, aDef);
+ }
+
+ // Same as above, but readonly.
+ const elem_type& SafeElementAt(index_type aIndex, const elem_type& aDef) const
+ {
+ return mArray.SafeElementAt(aIndex, aDef);
+ }
+
+ // No operator[] is provided because the point of this class is to support
+ // allow modifying the array during iteration, and ElementAt() is not safe
+ // in those conditions.
+
+ //
+ // Search methods
+ //
+
+ // This method searches, starting from the beginning of the array,
+ // for the first element in this array that is equal to the given element.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to search for.
+ // @return true if the element was found.
+ template<class Item>
+ bool Contains(const Item& aItem) const
+ {
+ return IndexOf(aItem) != array_type::NoIndex;
+ }
+
+ // This method searches for the offset of the first element in this
+ // array that is equal to the given element.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type IndexOf(const Item& aItem, index_type aStart = 0) const
+ {
+ return mArray.IndexOf(aItem, aStart);
+ }
+
+ //
+ // Mutation methods
+ //
+
+ // Insert a given element at the given index.
+ // @param aIndex The index at which to insert item.
+ // @param aItem The item to insert,
+ // @return A pointer to the newly inserted element, or a null on DOM
+ template<class Item>
+ elem_type* InsertElementAt(index_type aIndex, const Item& aItem)
+ {
+ elem_type* item = mArray.InsertElementAt(aIndex, aItem);
+ AdjustIterators(aIndex, 1);
+ return item;
+ }
+
+ // Same as above but without copy constructing.
+ // This is useful to avoid temporaries.
+ elem_type* InsertElementAt(index_type aIndex)
+ {
+ elem_type* item = mArray.InsertElementAt(aIndex);
+ AdjustIterators(aIndex, 1);
+ return item;
+ }
+
+ // Prepend an element to the array unless it already exists in the array.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to prepend.
+ // @return true if the element was found, or inserted successfully.
+ template<class Item>
+ bool PrependElementUnlessExists(const Item& aItem)
+ {
+ if (Contains(aItem)) {
+ return true;
+ }
+
+ bool inserted = mArray.InsertElementAt(0, aItem) != nullptr;
+ AdjustIterators(0, 1);
+ return inserted;
+ }
+
+ // Append an element to the array.
+ // @param aItem The item to append.
+ // @return A pointer to the newly appended element, or null on OOM.
+ template<class Item>
+ elem_type* AppendElement(const Item& aItem)
+ {
+ return mArray.AppendElement(aItem);
+ }
+
+ // Same as above, but without copy-constructing. This is useful to avoid
+ // temporaries.
+ elem_type* AppendElement()
+ {
+ return mArray.AppendElement();
+ }
+
+ // Append an element to the array unless it already exists in the array.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to append.
+ // @return true if the element was found, or inserted successfully.
+ template<class Item>
+ bool AppendElementUnlessExists(const Item& aItem)
+ {
+ return Contains(aItem) || AppendElement(aItem) != nullptr;
+ }
+
+ // Remove an element from the array.
+ // @param aIndex The index of the item to remove.
+ void RemoveElementAt(index_type aIndex)
+ {
+ NS_ASSERTION(aIndex < mArray.Length(), "invalid index");
+ mArray.RemoveElementAt(aIndex);
+ AdjustIterators(aIndex, -1);
+ }
+
+ // This helper function combines IndexOf with RemoveElementAt to "search
+ // and destroy" the first element that is equal to the given element.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to search for.
+ // @return true if the element was found and removed.
+ template<class Item>
+ bool RemoveElement(const Item& aItem)
+ {
+ index_type index = mArray.IndexOf(aItem, 0);
+ if (index == array_type::NoIndex) {
+ return false;
+ }
+
+ mArray.RemoveElementAt(index);
+ AdjustIterators(index, -1);
+ return true;
+ }
+
+ // See nsTArray::RemoveElementsBy.
+ void RemoveElementsBy(mozilla::function<bool(const elem_type&)> aPredicate)
+ {
+ index_type i = 0;
+ mArray.RemoveElementsBy([&](const elem_type& aItem) {
+ if (aPredicate(aItem)) {
+ // This element is going to be removed.
+ AdjustIterators(i, -1);
+ return true;
+ }
+ ++i;
+ return false;
+ });
+ }
+
+ // Removes all observers and collapses all iterators to the beginning of
+ // the array. The result is that forward iterators will see all elements
+ // in the array.
+ void Clear()
+ {
+ mArray.Clear();
+ ClearIterators();
+ }
+
+ // Compact the array to minimize the memory it uses
+ void Compact() { mArray.Compact(); }
+
+ // Returns the number of bytes on the heap taken up by this object, not
+ // including sizeof(*this). If you want to measure anything hanging off the
+ // array, you must iterate over the elements and measure them individually;
+ // hence the "Shallow" prefix.
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ //
+ // Iterators
+ //
+
+ // Base class for iterators. Do not use this directly.
+ class Iterator : public Iterator_base
+ {
+ protected:
+ friend class nsAutoTObserverArray;
+ typedef nsAutoTObserverArray<T, N> array_type;
+
+ Iterator(index_type aPosition, const array_type& aArray)
+ : Iterator_base(aPosition, aArray.mIterators)
+ , mArray(const_cast<array_type&>(aArray))
+ {
+ aArray.mIterators = this;
+ }
+
+ ~Iterator()
+ {
+ NS_ASSERTION(mArray.mIterators == this,
+ "Iterators must currently be destroyed in opposite order "
+ "from the construction order. It is suggested that you "
+ "simply put them on the stack");
+ mArray.mIterators = mNext;
+ }
+
+ // The array we're iterating
+ array_type& mArray;
+ };
+
+ // Iterates the array forward from beginning to end. mPosition points
+ // to the element that will be returned on next call to GetNext.
+ // Elements:
+ // - prepended to the array during iteration *will not* be traversed
+ // - appended during iteration *will* be traversed
+ // - removed during iteration *will not* be traversed.
+ // @see EndLimitedIterator
+ class ForwardIterator : protected Iterator
+ {
+ public:
+ typedef nsAutoTObserverArray<T, N> array_type;
+ typedef Iterator base_type;
+
+ explicit ForwardIterator(const array_type& aArray)
+ : Iterator(0, aArray)
+ {
+ }
+
+ ForwardIterator(const array_type& aArray, index_type aPos)
+ : Iterator(aPos, aArray)
+ {
+ }
+
+ bool operator<(const ForwardIterator& aOther) const
+ {
+ NS_ASSERTION(&this->mArray == &aOther.mArray,
+ "not iterating the same array");
+ return base_type::mPosition < aOther.mPosition;
+ }
+
+ // Returns true if there are more elements to iterate.
+ // This must precede a call to GetNext(). If false is
+ // returned, GetNext() must not be called.
+ bool HasMore() const
+ {
+ return base_type::mPosition < base_type::mArray.Length();
+ }
+
+ // Returns the next element and steps one step. This must
+ // be preceded by a call to HasMore().
+ // @return The next observer.
+ elem_type& GetNext()
+ {
+ NS_ASSERTION(HasMore(), "iterating beyond end of array");
+ return base_type::mArray.ElementAt(base_type::mPosition++);
+ }
+ };
+
+ // EndLimitedIterator works like ForwardIterator, but will not iterate new
+ // observers appended to the array after the iterator was created.
+ class EndLimitedIterator : protected ForwardIterator
+ {
+ public:
+ typedef nsAutoTObserverArray<T, N> array_type;
+ typedef Iterator base_type;
+
+ explicit EndLimitedIterator(const array_type& aArray)
+ : ForwardIterator(aArray)
+ , mEnd(aArray, aArray.Length())
+ {
+ }
+
+ // Returns true if there are more elements to iterate.
+ // This must precede a call to GetNext(). If false is
+ // returned, GetNext() must not be called.
+ bool HasMore() const { return *this < mEnd; }
+
+ // Returns the next element and steps one step. This must
+ // be preceded by a call to HasMore().
+ // @return The next observer.
+ elem_type& GetNext()
+ {
+ NS_ASSERTION(HasMore(), "iterating beyond end of array");
+ return base_type::mArray.ElementAt(base_type::mPosition++);
+ }
+
+ private:
+ ForwardIterator mEnd;
+ };
+
+ // Iterates the array backward from end to start. mPosition points
+ // to the element that was returned last.
+ // Elements:
+ // - prepended to the array during iteration *will* be traversed,
+ // unless the iteration already arrived at the first element
+ // - appended during iteration *will not* be traversed
+ // - removed during iteration *will not* be traversed.
+ class BackwardIterator : protected Iterator
+ {
+ public:
+ typedef nsAutoTObserverArray<T, N> array_type;
+ typedef Iterator base_type;
+
+ explicit BackwardIterator(const array_type& aArray)
+ : Iterator(aArray.Length(), aArray)
+ {
+ }
+
+ // Returns true if there are more elements to iterate.
+ // This must precede a call to GetNext(). If false is
+ // returned, GetNext() must not be called.
+ bool HasMore() const { return base_type::mPosition > 0; }
+
+ // Returns the next element and steps one step. This must
+ // be preceded by a call to HasMore().
+ // @return The next observer.
+ elem_type& GetNext()
+ {
+ NS_ASSERTION(HasMore(), "iterating beyond start of array");
+ return base_type::mArray.ElementAt(--base_type::mPosition);
+ }
+
+ // Removes the element at the current iterator position.
+ // (the last element returned from |GetNext()|)
+ // This will not affect the next call to |GetNext()|
+ void Remove()
+ {
+ return base_type::mArray.RemoveElementAt(base_type::mPosition);
+ }
+ };
+
+protected:
+ AutoTArray<T, N> mArray;
+};
+
+template<class T>
+class nsTObserverArray : public nsAutoTObserverArray<T, 0>
+{
+public:
+ typedef nsAutoTObserverArray<T, 0> base_type;
+ typedef nsTObserverArray_base::size_type size_type;
+
+ //
+ // Initialization methods
+ //
+
+ nsTObserverArray() {}
+
+ // Initialize this array and pre-allocate some number of elements.
+ explicit nsTObserverArray(size_type aCapacity)
+ {
+ base_type::mArray.SetCapacity(aCapacity);
+ }
+};
+
+template<typename T, size_t N>
+inline void
+ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField)
+{
+ aField.Clear();
+}
+
+template<typename T, size_t N>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsAutoTObserverArray<T, N>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ size_t length = aField.Length();
+ for (size_t i = 0; i < length; ++i) {
+ ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags);
+ }
+}
+
+// XXXbz I wish I didn't have to pass in the observer type, but I
+// don't see a way to get it out of array_.
+// Note that this macro only works if the array holds pointers to XPCOM objects.
+#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
+ RefPtr<obstype_> obs_; \
+ while (iter_.HasMore()) { \
+ obs_ = iter_.GetNext(); \
+ obs_ -> func_ params_ ; \
+ } \
+ PR_END_MACRO
+
+// Note that this macro only works if the array holds pointers to XPCOM objects.
+#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
+ obstype_* obs_; \
+ while (iter_.HasMore()) { \
+ obs_ = iter_.GetNext(); \
+ obs_ -> func_ params_ ; \
+ } \
+ PR_END_MACRO
+
+#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS_WITH_QI(array_, basetype_, obstype_, func_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<basetype_ *>::ForwardIterator iter_(array_); \
+ basetype_* obsbase_; \
+ while (iter_.HasMore()) { \
+ obsbase_ = iter_.GetNext(); \
+ nsCOMPtr<obstype_> obs_ = do_QueryInterface(obsbase_); \
+ if (obs_) { \
+ obs_ -> func_ params_ ; \
+ } \
+ } \
+ PR_END_MACRO
+#endif // nsTObserverArray_h___
diff --git a/xpcom/glue/nsTPriorityQueue.h b/xpcom/glue/nsTPriorityQueue.h
new file mode 100644
index 000000000..20a0fc8a5
--- /dev/null
+++ b/xpcom/glue/nsTPriorityQueue.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NS_TPRIORITY_QUEUE_H_
+#define NS_TPRIORITY_QUEUE_H_
+
+#include "nsTArray.h"
+#include "nsDebug.h"
+
+/**
+ * A templatized priority queue data structure that uses an nsTArray to serve as
+ * a binary heap. The default comparator causes this to act like a min-heap.
+ * Only the LessThan method of the comparator is used.
+ */
+template<class T, class Compare = nsDefaultComparator<T, T>>
+class nsTPriorityQueue
+{
+public:
+ typedef typename nsTArray<T>::size_type size_type;
+
+ /**
+ * Default constructor also creates a comparator object using the default
+ * constructor for type Compare.
+ */
+ nsTPriorityQueue() : mCompare(Compare()) {}
+
+ /**
+ * Constructor to allow a specific instance of a comparator object to be
+ * used.
+ */
+ explicit nsTPriorityQueue(const Compare& aComp) : mCompare(aComp) {}
+
+ /**
+ * Copy constructor
+ */
+ nsTPriorityQueue(const nsTPriorityQueue& aOther)
+ : mElements(aOther.mElements)
+ , mCompare(aOther.mCompare)
+ {
+ }
+
+ /**
+ * @return True if the queue is empty or false otherwise.
+ */
+ bool IsEmpty() const { return mElements.IsEmpty(); }
+
+ /**
+ * @return The number of elements in the queue.
+ */
+ size_type Length() const { return mElements.Length(); }
+
+ /**
+ * @return The topmost element in the queue without changing the queue. This
+ * is the element 'a' such that there is no other element 'b' in the queue for
+ * which Compare(b, a) returns true. (Since this container does not check
+ * for duplicate entries there may exist 'b' for which Compare(a, b) returns
+ * false.)
+ */
+ const T& Top() const
+ {
+ MOZ_ASSERT(!mElements.IsEmpty(), "Empty queue");
+ return mElements[0];
+ }
+
+ /**
+ * Adds an element to the queue
+ * @param aElement The element to add
+ * @return true on success, false on out of memory.
+ */
+ bool Push(const T& aElement)
+ {
+ T* elem = mElements.AppendElement(aElement);
+ if (!elem) {
+ return false; // Out of memory
+ }
+
+ // Sift up
+ size_type i = mElements.Length() - 1;
+ while (i) {
+ size_type parent = (size_type)((i - 1) / 2);
+ if (mCompare.LessThan(mElements[parent], mElements[i])) {
+ break;
+ }
+ Swap(i, parent);
+ i = parent;
+ }
+
+ return true;
+ }
+
+ /**
+ * Removes and returns the top-most element from the queue.
+ * @return The topmost element, that is, the element 'a' such that there is no
+ * other element 'b' in the queue for which Compare(b, a) returns true.
+ * @see Top()
+ */
+ T Pop()
+ {
+ MOZ_ASSERT(!mElements.IsEmpty(), "Empty queue");
+ T pop = mElements[0];
+
+ // Move last to front
+ mElements[0] = mElements[mElements.Length() - 1];
+ mElements.TruncateLength(mElements.Length() - 1);
+
+ // Sift down
+ size_type i = 0;
+ while (2 * i + 1 < mElements.Length()) {
+ size_type swap = i;
+ size_type l_child = 2 * i + 1;
+ if (mCompare.LessThan(mElements[l_child], mElements[i])) {
+ swap = l_child;
+ }
+ size_type r_child = l_child + 1;
+ if (r_child < mElements.Length() &&
+ mCompare.LessThan(mElements[r_child], mElements[swap])) {
+ swap = r_child;
+ }
+ if (swap == i) {
+ break;
+ }
+ Swap(i, swap);
+ i = swap;
+ }
+
+ return pop;
+ }
+
+ /**
+ * Removes all elements from the queue.
+ */
+ void Clear() { mElements.Clear(); }
+
+ /**
+ * Provides readonly access to the queue elements as an array. Generally this
+ * should be avoided but may be needed in some situations such as when the
+ * elements contained in the queue need to be enumerated for cycle-collection.
+ * @return A pointer to the first element of the array. If the array is
+ * empty, then this pointer must not be dereferenced.
+ */
+ const T* Elements() const { return mElements.Elements(); }
+
+protected:
+ /**
+ * Swaps the elements at the specified indices.
+ */
+ void Swap(size_type aIndexA, size_type aIndexB)
+ {
+ T temp = mElements[aIndexA];
+ mElements[aIndexA] = mElements[aIndexB];
+ mElements[aIndexB] = temp;
+ }
+
+ nsTArray<T> mElements;
+ Compare mCompare; // Comparator object
+};
+
+#endif // NS_TPRIORITY_QUEUE_H_
diff --git a/xpcom/glue/nsTWeakRef.h b/xpcom/glue/nsTWeakRef.h
new file mode 100644
index 000000000..6c9a5e8eb
--- /dev/null
+++ b/xpcom/glue/nsTWeakRef.h
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTWeakRef_h__
+#define nsTWeakRef_h__
+
+#ifndef nsDebug_h___
+#include "nsDebug.h"
+#endif
+
+/**
+ * A weak reference class for use with generic C++ objects. NOT THREADSAFE!
+ *
+ * Example usage:
+ *
+ * class A {
+ * public:
+ * A() : mWeakSelf(this) {
+ * }
+ * ~A() {
+ * mWeakSelf.forget();
+ * }
+ * void Bar() { printf("Bar!\n"); }
+ * const nsTWeakRef<A> &AsWeakRef() const { return mWeakSelf; }
+ * private:
+ * nsTWeakRef<A> mWeakSelf;
+ * };
+ *
+ * class B {
+ * public:
+ * void SetA(const nsTWeakRef<A> &a) {
+ * mA = a;
+ * }
+ * void Foo() {
+ * if (mA)
+ * mA->Bar();
+ * }
+ * private:
+ * nsTWeakRef<A> mA;
+ * };
+ *
+ * void Test() {
+ * B b;
+ * {
+ * A a;
+ * b.SetA(a.AsWeakRef());
+ * b.Foo(); // prints "Bar!"
+ * }
+ * b.Foo(); // prints nothing because |a| has already been destroyed
+ * }
+ *
+ * One can imagine much more complex examples, especially when asynchronous
+ * event processing is involved.
+ *
+ * Keep in mind that you should only ever need a class like this when you have
+ * multiple instances of B, such that it is not possible for A and B to simply
+ * have pointers to one another.
+ */
+template<class Type>
+class nsTWeakRef
+{
+public:
+ ~nsTWeakRef()
+ {}
+
+ /**
+ * Construct from an object pointer (may be null).
+ */
+ explicit nsTWeakRef(Type* aObj = nullptr)
+ {
+ if (aObj) {
+ mRef = new Inner(aObj);
+ } else {
+ mRef = nullptr;
+ }
+ }
+
+ /**
+ * Construct from another weak reference object.
+ */
+ explicit nsTWeakRef(const nsTWeakRef<Type>& aOther) : mRef(aOther.mRef)
+ {}
+
+ /**
+ * Assign from an object pointer.
+ */
+ nsTWeakRef<Type>& operator=(Type* aObj)
+ {
+ if (aObj) {
+ mRef = new Inner(aObj);
+ } else {
+ mRef = nullptr;
+ }
+ return *this;
+ }
+
+ /**
+ * Assign from another weak reference object.
+ */
+ nsTWeakRef<Type>& operator=(const nsTWeakRef<Type>& aOther)
+ {
+ mRef = aOther.mRef;
+ return *this;
+ }
+
+ /**
+ * Get the referenced object. This method may return null if the reference
+ * has been cleared or if an out-of-memory error occurred at assignment.
+ */
+ Type* get() const { return mRef ? mRef->mObj : nullptr; }
+
+ /**
+ * Called to "null out" the weak reference. Typically, the object referenced
+ * by this weak reference calls this method when it is being destroyed.
+ * @returns The former referenced object.
+ */
+ Type* forget()
+ {
+ Type* obj;
+ if (mRef) {
+ obj = mRef->mObj;
+ mRef->mObj = nullptr;
+ mRef = nullptr;
+ } else {
+ obj = nullptr;
+ }
+ return obj;
+ }
+
+ /**
+ * Allow |*this| to be treated as a |Type*| for convenience.
+ */
+ operator Type*() const { return get(); }
+
+ /**
+ * Allow |*this| to be treated as a |Type*| for convenience. Use with
+ * caution since this method will crash if the referenced object is null.
+ */
+ Type* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
+ {
+ NS_ASSERTION(mRef && mRef->mObj,
+ "You can't dereference a null weak reference with operator->().");
+ return get();
+ }
+
+private:
+
+ struct Inner
+ {
+ int mCnt;
+ Type* mObj;
+
+ explicit Inner(Type* aObj)
+ : mCnt(1)
+ , mObj(aObj)
+ {
+ }
+ void AddRef()
+ {
+ ++mCnt;
+ }
+ void Release()
+ {
+ if (--mCnt == 0) {
+ delete this;
+ }
+ }
+ };
+
+ RefPtr<Inner> mRef;
+};
+
+#endif // nsTWeakRef_h__
diff --git a/xpcom/glue/nsTextFormatter.cpp b/xpcom/glue/nsTextFormatter.cpp
new file mode 100644
index 000000000..ab9941d15
--- /dev/null
+++ b/xpcom/glue/nsTextFormatter.cpp
@@ -0,0 +1,1394 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Portable safe sprintf code.
+ *
+ * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
+ *
+ * Contributor(s):
+ * Kipp E.B. Hickman <kipp@netscape.com> (original author)
+ * Frank Yung-Fong Tang <ftang@netscape.com>
+ * Daniele Nicolodi <daniele@grinta.net>
+ */
+
+/*
+ * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
+ * Changed to use nsMemory and Frozen linkage
+ * -- Prasad <prasad@medhas.org>
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "prdtoa.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Sprintf.h"
+#include "prmem.h"
+#include "nsCRTGlue.h"
+#include "nsTextFormatter.h"
+#include "nsMemory.h"
+
+/*
+** Note: on some platforms va_list is defined as an array,
+** and requires array notation.
+*/
+
+#ifdef HAVE_VA_COPY
+#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
+#elif defined(HAVE_VA_LIST_AS_ARRAY)
+#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
+#else
+#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
+#endif
+
+typedef struct SprintfStateStr SprintfState;
+
+struct SprintfStateStr
+{
+ int (*stuff)(SprintfState* aState, const char16_t* aStr, uint32_t aLen);
+
+ char16_t* base;
+ char16_t* cur;
+ uint32_t maxlen;
+
+ void* stuffclosure;
+};
+
+/*
+** Numbered Arguement State
+*/
+struct NumArgState
+{
+ int type; /* type of the current ap */
+ va_list ap; /* point to the corresponding position on ap */
+
+ enum Type
+ {
+ INT16,
+ UINT16,
+ INTN,
+ UINTN,
+ INT32,
+ UINT32,
+ INT64,
+ UINT64,
+ STRING,
+ DOUBLE,
+ INTSTR,
+ UNISTRING,
+ UNKNOWN
+ };
+};
+
+#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
+
+#define _LEFT 0x1
+#define _SIGNED 0x2
+#define _SPACED 0x4
+#define _ZEROS 0x8
+#define _NEG 0x10
+
+#define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
+
+#define PR_CHECK_DELETE(nas) if (nas && (nas != nasArray)) { PR_DELETE(nas); }
+
+/*
+** Fill into the buffer using the data in src
+*/
+static int
+fill2(SprintfState* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
+ int aFlags)
+{
+ char16_t space = ' ';
+ int rv;
+
+ aWidth -= aSrcLen;
+ /* Right adjusting */
+ if ((aWidth > 0) && ((aFlags & _LEFT) == 0)) {
+ if (aFlags & _ZEROS) {
+ space = '0';
+ }
+ while (--aWidth >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Copy out the source data */
+ rv = (*aState->stuff)(aState, aSrc, aSrcLen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ /* Left adjusting */
+ if ((aWidth > 0) && ((aFlags & _LEFT) != 0)) {
+ while (--aWidth >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Fill a number. The order is: optional-sign zero-filling conversion-digits
+*/
+static int
+fill_n(SprintfState* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
+ int aPrec, int aType, int aFlags)
+{
+ int zerowidth = 0;
+ int precwidth = 0;
+ int signwidth = 0;
+ int leftspaces = 0;
+ int rightspaces = 0;
+ int cvtwidth;
+ int rv;
+ char16_t sign;
+ char16_t space = ' ';
+ char16_t zero = '0';
+
+ if ((aType & 1) == 0) {
+ if (aFlags & _NEG) {
+ sign = '-';
+ signwidth = 1;
+ } else if (aFlags & _SIGNED) {
+ sign = '+';
+ signwidth = 1;
+ } else if (aFlags & _SPACED) {
+ sign = ' ';
+ signwidth = 1;
+ }
+ }
+ cvtwidth = signwidth + aSrcLen;
+
+ if (aPrec > 0) {
+ if (aPrec > aSrcLen) {
+ /* Need zero filling */
+ precwidth = aPrec - aSrcLen;
+ cvtwidth += precwidth;
+ }
+ }
+
+ if ((aFlags & _ZEROS) && (aPrec < 0)) {
+ if (aWidth > cvtwidth) {
+ /* Zero filling */
+ zerowidth = aWidth - cvtwidth;
+ cvtwidth += zerowidth;
+ }
+ }
+
+ if (aFlags & _LEFT) {
+ if (aWidth > cvtwidth) {
+ /* Space filling on the right (i.e. left adjusting) */
+ rightspaces = aWidth - cvtwidth;
+ }
+ } else {
+ if (aWidth > cvtwidth) {
+ /* Space filling on the left (i.e. right adjusting) */
+ leftspaces = aWidth - cvtwidth;
+ }
+ }
+ while (--leftspaces >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ if (signwidth) {
+ rv = (*aState->stuff)(aState, &sign, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--precwidth >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--zerowidth >= 0) {
+ rv = (*aState->stuff)(aState, &zero, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ rv = (*aState->stuff)(aState, aSrc, aSrcLen);
+ if (rv < 0) {
+ return rv;
+ }
+ while (--rightspaces >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ return 0;
+}
+
+/*
+** Convert a long into its printable form
+*/
+static int
+cvt_l(SprintfState* aState, long aNum, int aWidth, int aPrec, int aRadix,
+ int aType, int aFlags, const char16_t* aHexStr)
+{
+ char16_t cvtbuf[100];
+ char16_t* cvt;
+ int digits;
+
+ /* according to the man page this needs to happen */
+ if ((aPrec == 0) && (aNum == 0)) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
+ digits = 0;
+ while (aNum) {
+ int digit = (((unsigned long)aNum) % aRadix) & 0xF;
+ *--cvt = aHexStr[digit];
+ digits++;
+ aNum = (long)(((unsigned long)aNum) / aRadix);
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(aState, cvt, digits, aWidth, aPrec, aType, aFlags);
+}
+
+/*
+** Convert a 64-bit integer into its printable form
+*/
+static int
+cvt_ll(SprintfState* aState, int64_t aNum, int aWidth, int aPrec, int aRadix,
+ int aType, int aFlags, const char16_t* aHexStr)
+{
+ char16_t cvtbuf[100];
+ char16_t* cvt;
+ int digits;
+ int64_t rad;
+
+ /* according to the man page this needs to happen */
+ if (aPrec == 0 && aNum == 0) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ rad = aRadix;
+ cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
+ digits = 0;
+ while (aNum != 0) {
+ *--cvt = aHexStr[int32_t(aNum % rad) & 0xf];
+ digits++;
+ aNum /= rad;
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(aState, cvt, digits, aWidth, aPrec, aType, aFlags);
+}
+
+/*
+** Convert a double precision floating point number into its printable
+** form.
+*/
+static int
+cvt_f(SprintfState* aState, double aDouble, int aWidth, int aPrec,
+ const char16_t aType, int aFlags)
+{
+ int mode = 2;
+ int decpt;
+ int sign;
+ char buf[256];
+ char* bufp = buf;
+ int bufsz = 256;
+ char num[256];
+ char* nump;
+ char* endnum;
+ int numdigits = 0;
+ char exp = 'e';
+
+ if (aPrec == -1) {
+ aPrec = 6;
+ } else if (aPrec > 50) {
+ // limit precision to avoid PR_dtoa bug 108335
+ // and to prevent buffers overflows
+ aPrec = 50;
+ }
+
+ switch (aType) {
+ case 'f':
+ numdigits = aPrec;
+ mode = 3;
+ break;
+ case 'E':
+ exp = 'E';
+ MOZ_FALLTHROUGH;
+ case 'e':
+ numdigits = aPrec + 1;
+ mode = 2;
+ break;
+ case 'G':
+ exp = 'E';
+ MOZ_FALLTHROUGH;
+ case 'g':
+ if (aPrec == 0) {
+ aPrec = 1;
+ }
+ numdigits = aPrec;
+ mode = 2;
+ break;
+ default:
+ NS_ERROR("invalid aType passed to cvt_f");
+ }
+
+ if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign,
+ &endnum, num, bufsz) == PR_FAILURE) {
+ buf[0] = '\0';
+ return -1;
+ }
+ numdigits = endnum - num;
+ nump = num;
+
+ if (sign) {
+ *bufp++ = '-';
+ } else if (aFlags & _SIGNED) {
+ *bufp++ = '+';
+ }
+
+ if (decpt == 9999) {
+ while ((*bufp++ = *nump++)) {
+ }
+ } else {
+
+ switch (aType) {
+
+ case 'E':
+ case 'e':
+
+ *bufp++ = *nump++;
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (*nump) {
+ *bufp++ = *nump++;
+ aPrec--;
+ }
+ while (aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ }
+ *bufp++ = exp;
+
+ snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
+ break;
+
+ case 'f':
+
+ if (decpt < 1) {
+ *bufp++ = '0';
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (decpt++ && aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ while (*nump && aPrec-- > 0) {
+ *bufp++ = *nump++;
+ }
+ while (aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ }
+ } else {
+ while (*nump && decpt-- > 0) {
+ *bufp++ = *nump++;
+ }
+ while (decpt-- > 0) {
+ *bufp++ = '0';
+ }
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (*nump && aPrec-- > 0) {
+ *bufp++ = *nump++;
+ }
+ while (aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ }
+ }
+ *bufp = '\0';
+ break;
+
+ case 'G':
+ case 'g':
+
+ if ((decpt < -3) || ((decpt - 1) >= aPrec)) {
+ *bufp++ = *nump++;
+ numdigits--;
+ if (numdigits > 0) {
+ *bufp++ = '.';
+ while (*nump) {
+ *bufp++ = *nump++;
+ }
+ }
+ *bufp++ = exp;
+ snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
+ } else {
+ if (decpt < 1) {
+ *bufp++ = '0';
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (decpt++) {
+ *bufp++ = '0';
+ }
+ while (*nump) {
+ *bufp++ = *nump++;
+ }
+ }
+ } else {
+ while (*nump && decpt-- > 0) {
+ *bufp++ = *nump++;
+ numdigits--;
+ }
+ while (decpt-- > 0) {
+ *bufp++ = '0';
+ }
+ if (numdigits > 0) {
+ *bufp++ = '.';
+ while (*nump) {
+ *bufp++ = *nump++;
+ }
+ }
+ }
+ *bufp = '\0';
+ }
+ }
+ }
+
+ char16_t rbuf[256];
+ char16_t* rbufp = rbuf;
+ bufp = buf;
+ // cast to char16_t
+ while ((*rbufp++ = *bufp++)) {
+ }
+ *rbufp = '\0';
+
+ return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags);
+}
+
+/*
+** Convert a string into its printable form. |aWidth| is the output
+** width. |aPrec| is the maximum number of characters of |aStr| to output,
+** where -1 means until NUL.
+*/
+static int
+cvt_S(SprintfState* aState, const char16_t* aStr, int aWidth, int aPrec,
+ int aFlags)
+{
+ int slen;
+
+ if (aPrec == 0) {
+ return 0;
+ }
+
+ /* Limit string length by precision value */
+ slen = aStr ? NS_strlen(aStr) : 6;
+ if (aPrec > 0) {
+ if (aPrec < slen) {
+ slen = aPrec;
+ }
+ }
+
+ /* and away we go */
+ return fill2(aState, aStr ? aStr : u"(null)", slen, aWidth, aFlags);
+}
+
+/*
+** Convert a string into its printable form. |aWidth| is the output
+** width. |aPrec| is the maximum number of characters of |aStr| to output,
+** where -1 means until NUL.
+*/
+static int
+cvt_s(SprintfState* aState, const char* aStr, int aWidth, int aPrec, int aFlags)
+{
+ NS_ConvertUTF8toUTF16 utf16Val(aStr);
+ return cvt_S(aState, utf16Val.get(), aWidth, aPrec, aFlags);
+}
+
+/*
+** BuildArgArray stands for Numbered Argument list Sprintf
+** for example,
+** fmp = "%4$i, %2$d, %3s, %1d";
+** the number must start from 1, and no gap among them
+*/
+
+static struct NumArgState*
+BuildArgArray(const char16_t* aFmt, va_list aAp, int* aRv,
+ struct NumArgState* aNasArray)
+{
+ int number = 0, cn = 0, i;
+ const char16_t* p;
+ char16_t c;
+ struct NumArgState* nas;
+
+ /*
+ ** first pass:
+ ** detemine how many legal % I have got, then allocate space
+ */
+ p = aFmt;
+ *aRv = 0;
+ i = 0;
+ while ((c = *p++) != 0) {
+ if (c != '%') {
+ continue;
+ }
+ /* skip %% case */
+ if ((c = *p++) == '%') {
+ continue;
+ }
+
+ while (c != 0) {
+ if (c > '9' || c < '0') {
+ /* numbered argument csae */
+ if (c == '$') {
+ if (i > 0) {
+ *aRv = -1;
+ return nullptr;
+ }
+ number++;
+ break;
+
+ } else {
+ /* non-numbered argument case */
+ if (number > 0) {
+ *aRv = -1;
+ return nullptr;
+ }
+ i = 1;
+ break;
+ }
+ }
+ c = *p++;
+ }
+ }
+
+ if (number == 0) {
+ return nullptr;
+ }
+
+ if (number > NAS_DEFAULT_NUM) {
+ nas = (struct NumArgState*)moz_xmalloc(number * sizeof(struct NumArgState));
+ if (!nas) {
+ *aRv = -1;
+ return nullptr;
+ }
+ } else {
+ nas = aNasArray;
+ }
+
+ for (i = 0; i < number; i++) {
+ nas[i].type = NumArgState::UNKNOWN;
+ }
+
+ /*
+ ** second pass:
+ ** set nas[].type
+ */
+ p = aFmt;
+ while ((c = *p++) != 0) {
+ if (c != '%') {
+ continue;
+ }
+ c = *p++;
+ if (c == '%') {
+ continue;
+ }
+ cn = 0;
+ /* should improve error check later */
+ while (c && c != '$') {
+ cn = cn * 10 + c - '0';
+ c = *p++;
+ }
+
+ if (!c || cn < 1 || cn > number) {
+ *aRv = -1;
+ break;
+ }
+
+ /* nas[cn] starts from 0, and make sure
+ nas[cn].type is not assigned */
+ cn--;
+ if (nas[cn].type != NumArgState::UNKNOWN) {
+ continue;
+ }
+
+ c = *p++;
+
+ /* width */
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *aRv = -1;
+ break;
+ } else {
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+
+ /* precision */
+ if (c == '.') {
+ c = *p++;
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *aRv = -1;
+ break;
+ } else {
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+ }
+
+ /* size */
+ nas[cn].type = NumArgState::INTN;
+ if (c == 'h') {
+ nas[cn].type = NumArgState::INT16;
+ c = *p++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ nas[cn].type = NumArgState::INT64;
+ c = *p++;
+ } else if (c == 'l') {
+ nas[cn].type = NumArgState::INT32;
+ c = *p++;
+ if (c == 'l') {
+ nas[cn].type = NumArgState::INT64;
+ c = *p++;
+ }
+ }
+
+ /* format */
+ switch (c) {
+ case 'd':
+ case 'c':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ nas[cn].type = NumArgState::DOUBLE;
+ break;
+
+ case 'p':
+ /* XXX should use cpp */
+ if (sizeof(void*) == sizeof(int32_t)) {
+ nas[cn].type = NumArgState::UINT32;
+ } else if (sizeof(void*) == sizeof(int64_t)) {
+ nas[cn].type = NumArgState::UINT64;
+ } else if (sizeof(void*) == sizeof(int)) {
+ nas[cn].type = NumArgState::UINTN;
+ } else {
+ nas[cn].type = NumArgState::UNKNOWN;
+ }
+ break;
+
+ case 'C':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ nas[cn].type = NumArgState::UNKNOWN;
+ break;
+
+ case 'S':
+ nas[cn].type = NumArgState::UNISTRING;
+ break;
+
+ case 's':
+ nas[cn].type = NumArgState::STRING;
+ break;
+
+ case 'n':
+ nas[cn].type = NumArgState::INTSTR;
+ break;
+
+ default:
+ PR_ASSERT(0);
+ nas[cn].type = NumArgState::UNKNOWN;
+ break;
+ }
+
+ /* get a legal para. */
+ if (nas[cn].type == NumArgState::UNKNOWN) {
+ *aRv = -1;
+ break;
+ }
+ }
+
+
+ /*
+ ** third pass
+ ** fill the nas[cn].ap
+ */
+ if (*aRv < 0) {
+ if (nas != aNasArray) {
+ PR_DELETE(nas);
+ }
+ return nullptr;
+ }
+
+ cn = 0;
+ while (cn < number) {
+ if (nas[cn].type == NumArgState::UNKNOWN) {
+ cn++;
+ continue;
+ }
+
+ VARARGS_ASSIGN(nas[cn].ap, aAp);
+
+ switch (nas[cn].type) {
+ case NumArgState::INT16:
+ case NumArgState::UINT16:
+ case NumArgState::INTN:
+ case NumArgState::UINTN: (void)va_arg(aAp, int); break;
+
+ case NumArgState::INT32: (void)va_arg(aAp, int32_t); break;
+
+ case NumArgState::UINT32: (void)va_arg(aAp, uint32_t); break;
+
+ case NumArgState::INT64: (void)va_arg(aAp, int64_t); break;
+
+ case NumArgState::UINT64: (void)va_arg(aAp, uint64_t); break;
+
+ case NumArgState::STRING: (void)va_arg(aAp, char*); break;
+
+ case NumArgState::INTSTR: (void)va_arg(aAp, int*); break;
+
+ case NumArgState::DOUBLE: (void)va_arg(aAp, double); break;
+
+ case NumArgState::UNISTRING: (void)va_arg(aAp, char16_t*); break;
+
+ default:
+ if (nas != aNasArray) {
+ PR_DELETE(nas);
+ }
+ *aRv = -1;
+ va_end(aAp);
+ return nullptr;
+ }
+ cn++;
+ }
+ va_end(aAp);
+ return nas;
+}
+
+
+/*
+** The workhorse sprintf code.
+*/
+static int
+dosprintf(SprintfState* aState, const char16_t* aFmt, va_list aAp)
+{
+ char16_t c;
+ int flags, width, prec, radix, type;
+ union
+ {
+ char16_t ch;
+ int i;
+ long l;
+ int64_t ll;
+ double d;
+ const char* s;
+ const char16_t* S;
+ int* ip;
+ } u;
+ char16_t space = ' ';
+
+ nsAutoString hex;
+ hex.AssignLiteral("0123456789abcdef");
+
+ nsAutoString HEX;
+ HEX.AssignLiteral("0123456789ABCDEF");
+
+ const char16_t* hexp;
+ int rv, i;
+ struct NumArgState* nas = nullptr;
+ struct NumArgState nasArray[NAS_DEFAULT_NUM];
+
+
+ /*
+ ** build an argument array, IF the aFmt is numbered argument
+ ** list style, to contain the Numbered Argument list pointers
+ */
+ nas = BuildArgArray(aFmt, aAp, &rv, nasArray);
+ if (rv < 0) {
+ /* the aFmt contains error Numbered Argument format, jliu@netscape.com */
+ PR_ASSERT(0);
+ return rv;
+ }
+
+ while ((c = *aFmt++) != 0) {
+ if (c != '%') {
+ rv = (*aState->stuff)(aState, aFmt - 1, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ continue;
+ }
+
+ /*
+ ** Gobble up the % format string. Hopefully we have handled all
+ ** of the strange cases!
+ */
+ flags = 0;
+ c = *aFmt++;
+ if (c == '%') {
+ /* quoting a % with %% */
+ rv = (*aState->stuff)(aState, aFmt - 1, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ continue;
+ }
+
+ if (nas) {
+ /* the aFmt contains the Numbered Arguments feature */
+ i = 0;
+ /* should improve error check later */
+ while (c && c != '$') {
+ i = (i * 10) + (c - '0');
+ c = *aFmt++;
+ }
+
+ if (nas[i - 1].type == NumArgState::UNKNOWN) {
+ if (nas != nasArray) {
+ PR_DELETE(nas);
+ }
+ va_end(aAp);
+ return -1;
+ }
+
+ VARARGS_ASSIGN(aAp, nas[i - 1].ap);
+ c = *aFmt++;
+ }
+
+ /*
+ * Examine optional flags. Note that we do not implement the
+ * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
+ * somewhat ambiguous and not ideal, which is perhaps why
+ * the various sprintf() implementations are inconsistent
+ * on this feature.
+ */
+ while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
+ if (c == '-') {
+ flags |= _LEFT;
+ }
+ if (c == '+') {
+ flags |= _SIGNED;
+ }
+ if (c == ' ') {
+ flags |= _SPACED;
+ }
+ if (c == '0') {
+ flags |= _ZEROS;
+ }
+ c = *aFmt++;
+ }
+ if (flags & _SIGNED) {
+ flags &= ~_SPACED;
+ }
+ if (flags & _LEFT) {
+ flags &= ~_ZEROS;
+ }
+
+ /* width */
+ if (c == '*') {
+ c = *aFmt++;
+ width = va_arg(aAp, int);
+ } else {
+ width = 0;
+ while ((c >= '0') && (c <= '9')) {
+ width = (width * 10) + (c - '0');
+ c = *aFmt++;
+ }
+ }
+
+ /* precision */
+ prec = -1;
+ if (c == '.') {
+ c = *aFmt++;
+ if (c == '*') {
+ c = *aFmt++;
+ prec = va_arg(aAp, int);
+ } else {
+ prec = 0;
+ while ((c >= '0') && (c <= '9')) {
+ prec = (prec * 10) + (c - '0');
+ c = *aFmt++;
+ }
+ }
+ }
+
+ /* size */
+ type = NumArgState::INTN;
+ if (c == 'h') {
+ type = NumArgState::INT16;
+ c = *aFmt++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ type = NumArgState::INT64;
+ c = *aFmt++;
+ } else if (c == 'l') {
+ type = NumArgState::INT32;
+ c = *aFmt++;
+ if (c == 'l') {
+ type = NumArgState::INT64;
+ c = *aFmt++;
+ }
+ }
+
+ /* format */
+ hexp = hex.get();
+ switch (c) {
+ case 'd':
+ case 'i': /* decimal/integer */
+ radix = 10;
+ goto fetch_and_convert;
+
+ case 'o': /* octal */
+ radix = 8;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'u': /* unsigned decimal */
+ radix = 10;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'x': /* unsigned hex */
+ radix = 16;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'X': /* unsigned HEX */
+ radix = 16;
+ hexp = HEX.get();
+ type |= 1;
+ goto fetch_and_convert;
+
+ fetch_and_convert:
+ switch (type) {
+ case NumArgState::INT16:
+ u.l = va_arg(aAp, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case NumArgState::UINT16:
+ u.l = va_arg(aAp, int) & 0xffff;
+ goto do_long;
+ case NumArgState::INTN:
+ u.l = va_arg(aAp, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case NumArgState::UINTN:
+ u.l = (long)va_arg(aAp, unsigned int);
+ goto do_long;
+
+ case NumArgState::INT32:
+ u.l = va_arg(aAp, int32_t);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case NumArgState::UINT32:
+ u.l = (long)va_arg(aAp, uint32_t);
+ do_long:
+ rv = cvt_l(aState, u.l, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+
+ case NumArgState::INT64:
+ u.ll = va_arg(aAp, int64_t);
+ if (u.ll < 0) {
+ u.ll = -u.ll;
+ flags |= _NEG;
+ }
+ goto do_longlong;
+ case NumArgState::UINT64:
+ u.ll = va_arg(aAp, uint64_t);
+ do_longlong:
+ rv = cvt_ll(aState, u.ll, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ u.d = va_arg(aAp, double);
+ rv = cvt_f(aState, u.d, width, prec, c, flags);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'c':
+ u.ch = va_arg(aAp, int);
+ if ((flags & _LEFT) == 0) {
+ while (width-- > 1) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ }
+ }
+ rv = (*aState->stuff)(aState, &u.ch, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ if (flags & _LEFT) {
+ while (width-- > 1) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ }
+ }
+ break;
+
+ case 'p':
+ if (sizeof(void*) == sizeof(int32_t)) {
+ type = NumArgState::UINT32;
+ } else if (sizeof(void*) == sizeof(int64_t)) {
+ type = NumArgState::UINT64;
+ } else if (sizeof(void*) == sizeof(int)) {
+ type = NumArgState::UINTN;
+ } else {
+ PR_ASSERT(0);
+ break;
+ }
+ radix = 16;
+ goto fetch_and_convert;
+
+#if 0
+ case 'C':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+ case 'S':
+ u.S = va_arg(aAp, const char16_t*);
+ rv = cvt_S(aState, u.S, width, prec, flags);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+
+ case 's':
+ u.s = va_arg(aAp, const char*);
+ rv = cvt_s(aState, u.s, width, prec, flags);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+
+ case 'n':
+ u.ip = va_arg(aAp, int*);
+ if (u.ip) {
+ *u.ip = aState->cur - aState->base;
+ }
+ break;
+
+ default:
+ /* Not a % token after all... skip it */
+#if 0
+ PR_ASSERT(0);
+#endif
+ char16_t perct = '%';
+ rv = (*aState->stuff)(aState, &perct, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ rv = (*aState->stuff)(aState, aFmt - 1, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ }
+ }
+
+ /* Stuff trailing NUL */
+ char16_t null = '\0';
+
+ rv = (*aState->stuff)(aState, &null, 1);
+
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+
+ return rv;
+}
+
+/************************************************************************/
+
+static int
+StringStuff(SprintfState* aState, const char16_t* aStr, uint32_t aLen)
+{
+ if (*aStr == '\0') {
+ return 0;
+ }
+
+ ptrdiff_t off = aState->cur - aState->base;
+
+ nsAString* str = static_cast<nsAString*>(aState->stuffclosure);
+ str->Append(aStr, aLen);
+
+ aState->base = str->BeginWriting();
+ aState->cur = aState->base + off;
+
+ return 0;
+}
+
+/*
+** Stuff routine that automatically grows the malloc'd output buffer
+** before it overflows.
+*/
+static int
+GrowStuff(SprintfState* aState, const char16_t* aStr, uint32_t aLen)
+{
+ ptrdiff_t off;
+ char16_t* newbase;
+ uint32_t newlen;
+
+ off = aState->cur - aState->base;
+ if (off + aLen >= aState->maxlen) {
+ /* Grow the buffer */
+ newlen = aState->maxlen + ((aLen > 32) ? aLen : 32);
+ if (aState->base) {
+ newbase = (char16_t*)moz_xrealloc(aState->base,
+ newlen * sizeof(char16_t));
+ } else {
+ newbase = (char16_t*)moz_xmalloc(newlen * sizeof(char16_t));
+ }
+ if (!newbase) {
+ /* Ran out of memory */
+ return -1;
+ }
+ aState->base = newbase;
+ aState->maxlen = newlen;
+ aState->cur = aState->base + off;
+ }
+
+ /* Copy data */
+ while (aLen) {
+ --aLen;
+ *aState->cur++ = *aStr++;
+ }
+ MOZ_ASSERT((uint32_t)(aState->cur - aState->base) <= aState->maxlen);
+ return 0;
+}
+
+/*
+** sprintf into a malloc'd buffer
+*/
+char16_t*
+nsTextFormatter::smprintf(const char16_t* aFmt, ...)
+{
+ va_list ap;
+ char16_t* rv;
+
+ va_start(ap, aFmt);
+ rv = nsTextFormatter::vsmprintf(aFmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+uint32_t
+nsTextFormatter::ssprintf(nsAString& aOut, const char16_t* aFmt, ...)
+{
+ va_list ap;
+ uint32_t rv;
+
+ va_start(ap, aFmt);
+ rv = nsTextFormatter::vssprintf(aOut, aFmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+uint32_t
+nsTextFormatter::vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp)
+{
+ SprintfState ss;
+ ss.stuff = StringStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ ss.stuffclosure = &aOut;
+
+ aOut.Truncate();
+ int n = dosprintf(&ss, aFmt, aAp);
+ return n ? n - 1 : n;
+}
+
+char16_t*
+nsTextFormatter::vsmprintf(const char16_t* aFmt, va_list aAp)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, aFmt, aAp);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+
+/*
+** Stuff routine that discards overflow data
+*/
+static int
+LimitStuff(SprintfState* aState, const char16_t* aStr, uint32_t aLen)
+{
+ uint32_t limit = aState->maxlen - (aState->cur - aState->base);
+
+ if (aLen > limit) {
+ aLen = limit;
+ }
+ while (aLen) {
+ --aLen;
+ *aState->cur++ = *aStr++;
+ }
+ return 0;
+}
+
+/*
+** sprintf into a fixed size buffer. Make sure there is a NUL at the end
+** when finished.
+*/
+uint32_t
+nsTextFormatter::snprintf(char16_t* aOut, uint32_t aOutLen,
+ const char16_t* aFmt, ...)
+{
+ va_list ap;
+ uint32_t rv;
+
+ MOZ_ASSERT((int32_t)aOutLen > 0);
+ if ((int32_t)aOutLen <= 0) {
+ return 0;
+ }
+
+ va_start(ap, aFmt);
+ rv = nsTextFormatter::vsnprintf(aOut, aOutLen, aFmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+uint32_t
+nsTextFormatter::vsnprintf(char16_t* aOut, uint32_t aOutLen,
+ const char16_t* aFmt, va_list aAp)
+{
+ SprintfState ss;
+ uint32_t n;
+
+ MOZ_ASSERT((int32_t)aOutLen > 0);
+ if ((int32_t)aOutLen <= 0) {
+ return 0;
+ }
+
+ ss.stuff = LimitStuff;
+ ss.base = aOut;
+ ss.cur = aOut;
+ ss.maxlen = aOutLen;
+ (void) dosprintf(&ss, aFmt, aAp);
+
+ /* If we added chars, and we didn't append a null, do it now. */
+ if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) {
+ *(--ss.cur) = '\0';
+ }
+
+ n = ss.cur - ss.base;
+ return n ? n - 1 : n;
+}
+
+/*
+ * Free memory allocated, for the caller, by smprintf
+ */
+void
+nsTextFormatter::smprintf_free(char16_t* aMem)
+{
+ free(aMem);
+}
+
diff --git a/xpcom/glue/nsTextFormatter.h b/xpcom/glue/nsTextFormatter.h
new file mode 100644
index 000000000..4cd44be32
--- /dev/null
+++ b/xpcom/glue/nsTextFormatter.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This code was copied from xpcom/ds/nsTextFormatter r1.3
+ * Memory model and Frozen linkage changes only.
+ * -- Prasad <prasad@medhas.org>
+ */
+
+#ifndef nsTextFormatter_h___
+#define nsTextFormatter_h___
+
+/*
+ ** API for PR printf like routines. Supports the following formats
+ ** %d - decimal
+ ** %u - unsigned decimal
+ ** %x - unsigned hex
+ ** %X - unsigned uppercase hex
+ ** %o - unsigned octal
+ ** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above
+ ** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above
+ ** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above
+ ** %s - utf8 string
+ ** %S - char16_t string
+ ** %c - character
+ ** %p - pointer (deals with machine dependent pointer size)
+ ** %f - float
+ ** %g - float
+ */
+#include "prio.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include "nscore.h"
+#include "nsStringGlue.h"
+
+#ifdef XPCOM_GLUE
+#error "nsTextFormatter is not available in the standalone glue due to NSPR dependencies."
+#endif
+
+class nsTextFormatter
+{
+public:
+
+ /*
+ * sprintf into a fixed size buffer. Guarantees that the buffer is null
+ * terminated. Returns the length of the written output, NOT including the
+ * null terminator, or (uint32_t)-1 if an error occurs.
+ */
+ static uint32_t snprintf(char16_t* aOut, uint32_t aOutLen,
+ const char16_t* aFmt, ...);
+
+ /*
+ * sprintf into a moz_xmalloc'd buffer. Return a pointer to
+ * buffer on success, nullptr on failure.
+ */
+ static char16_t* smprintf(const char16_t* aFmt, ...);
+
+ static uint32_t ssprintf(nsAString& aOut, const char16_t* aFmt, ...);
+
+ /*
+ * va_list forms of the above.
+ */
+ static uint32_t vsnprintf(char16_t* aOut, uint32_t aOutLen, const char16_t* aFmt,
+ va_list aAp);
+ static char16_t* vsmprintf(const char16_t* aFmt, va_list aAp);
+ static uint32_t vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp);
+
+ /*
+ * Free the memory allocated, for the caller, by smprintf.
+ * -- Deprecated --
+ * Callers can substitute calling smprintf_free with free
+ */
+ static void smprintf_free(char16_t* aMem);
+
+};
+
+#endif /* nsTextFormatter_h___ */
diff --git a/xpcom/glue/nsThreadUtils.cpp b/xpcom/glue/nsThreadUtils.cpp
new file mode 100644
index 000000000..287ada7be
--- /dev/null
+++ b/xpcom/glue/nsThreadUtils.cpp
@@ -0,0 +1,472 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+#include "mozilla/TimeStamp.h"
+#include "LeakRefPtr.h"
+
+#ifdef MOZILLA_INTERNAL_API
+# include "nsThreadManager.h"
+#else
+# include "nsXPCOMCIDInternal.h"
+# include "nsIThreadManager.h"
+# include "nsServiceManagerUtils.h"
+#endif
+
+#ifdef XP_WIN
+#include <windows.h>
+#include "mozilla/WindowsVersion.h"
+using mozilla::IsVistaOrLater;
+#elif defined(XP_MACOSX)
+#include <sys/resource.h>
+#endif
+
+#include <pratom.h>
+#include <prthread.h>
+
+using namespace mozilla;
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+NS_IMPL_ISUPPORTS(IdlePeriod, nsIIdlePeriod)
+
+NS_IMETHODIMP
+IdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
+{
+ *aIdleDeadline = TimeStamp();
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(Runnable, nsIRunnable)
+
+NS_IMETHODIMP
+Runnable::Run()
+{
+ // Do nothing
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(CancelableRunnable, Runnable,
+ nsICancelableRunnable)
+
+nsresult
+CancelableRunnable::Cancel()
+{
+ // Do nothing
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(IncrementalRunnable, CancelableRunnable,
+ nsIIncrementalRunnable)
+
+void
+IncrementalRunnable::SetDeadline(TimeStamp aDeadline)
+{
+ // Do nothing
+}
+
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_NewThread(nsIThread** aResult, nsIRunnable* aEvent, uint32_t aStackSize)
+{
+ nsCOMPtr<nsIThread> thread;
+#ifdef MOZILLA_INTERNAL_API
+ nsresult rv =
+ nsThreadManager::get().nsThreadManager::NewThread(0, aStackSize,
+ getter_AddRefs(thread));
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = mgr->NewThread(0, aStackSize, getter_AddRefs(thread));
+#endif
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aEvent) {
+ rv = thread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ *aResult = nullptr;
+ thread.swap(*aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_GetCurrentThread(nsIThread** aResult)
+{
+#ifdef MOZILLA_INTERNAL_API
+ return nsThreadManager::get().nsThreadManager::GetCurrentThread(aResult);
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return mgr->GetCurrentThread(aResult);
+#endif
+}
+
+nsresult
+NS_GetMainThread(nsIThread** aResult)
+{
+#ifdef MOZILLA_INTERNAL_API
+ return nsThreadManager::get().nsThreadManager::GetMainThread(aResult);
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return mgr->GetMainThread(aResult);
+#endif
+}
+
+#ifndef MOZILLA_INTERNAL_API
+bool
+NS_IsMainThread()
+{
+ bool result = false;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID);
+ if (mgr) {
+ mgr->GetIsMainThread(&result);
+ }
+ return bool(result);
+}
+#endif
+
+nsresult
+NS_DispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRunnable> event(aEvent);
+#ifdef MOZILLA_INTERNAL_API
+ nsIThread* thread = NS_GetCurrentThread();
+ if (!thread) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#else
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+ // To keep us from leaking the runnable if dispatch method fails,
+ // we grab the reference on failures and release it.
+ nsIRunnable* temp = event.get();
+ rv = thread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Dispatch() leaked the reference to the event, but due to caller's
+ // assumptions, we shouldn't leak here. And given we are on the same
+ // thread as the dispatch target, it's mostly safe to do it here.
+ NS_RELEASE(temp);
+ }
+ return rv;
+}
+
+// It is common to call NS_DispatchToCurrentThread with a newly
+// allocated runnable with a refcount of zero. To keep us from leaking
+// the runnable if the dispatch method fails, we take a death grip.
+nsresult
+NS_DispatchToCurrentThread(nsIRunnable* aEvent)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return NS_DispatchToCurrentThread(event.forget());
+}
+
+nsresult
+NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDispatchFlags)
+{
+ LeakRefPtr<nsIRunnable> event(Move(aEvent));
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ NS_ASSERTION(false, "Failed NS_DispatchToMainThread() in shutdown; leaking");
+ // NOTE: if you stop leaking here, adjust Promise::MaybeReportRejected(),
+ // which assumes a leak here, or split into leaks and no-leaks versions
+ return rv;
+ }
+ return thread->Dispatch(event.take(), aDispatchFlags);
+}
+
+// In the case of failure with a newly allocated runnable with a
+// refcount of zero, we intentionally leak the runnable, because it is
+// likely that the runnable is being dispatched to the main thread
+// because it owns main thread only objects, so it is not safe to
+// release them here.
+nsresult
+NS_DispatchToMainThread(nsIRunnable* aEvent, uint32_t aDispatchFlags)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return NS_DispatchToMainThread(event.forget(), aDispatchFlags);
+}
+
+nsresult
+NS_DelayedDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDelayMs)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+#ifdef MOZILLA_INTERNAL_API
+ nsIThread* thread = NS_GetCurrentThread();
+ if (!thread) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+ return thread->DelayedDispatch(event.forget(), aDelayMs);
+}
+
+nsresult
+NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRunnable> event(aEvent);
+#ifdef MOZILLA_INTERNAL_API
+ nsIThread* thread = NS_GetCurrentThread();
+ if (!thread) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#else
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+ // To keep us from leaking the runnable if dispatch method fails,
+ // we grab the reference on failures and release it.
+ nsIRunnable* temp = event.get();
+ rv = thread->IdleDispatch(event.forget());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Dispatch() leaked the reference to the event, but due to caller's
+ // assumptions, we shouldn't leak here. And given we are on the same
+ // thread as the dispatch target, it's mostly safe to do it here.
+ NS_RELEASE(temp);
+ }
+
+ return rv;
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+nsresult
+NS_ProcessPendingEvents(nsIThread* aThread, PRIntervalTime aTimeout)
+{
+ nsresult rv = NS_OK;
+
+#ifdef MOZILLA_INTERNAL_API
+ if (!aThread) {
+ aThread = NS_GetCurrentThread();
+ if (NS_WARN_IF(!aThread)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+#else
+ nsCOMPtr<nsIThread> current;
+ if (!aThread) {
+ rv = NS_GetCurrentThread(getter_AddRefs(current));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ aThread = current.get();
+ }
+#endif
+
+ PRIntervalTime start = PR_IntervalNow();
+ for (;;) {
+ bool processedEvent;
+ rv = aThread->ProcessNextEvent(false, &processedEvent);
+ if (NS_FAILED(rv) || !processedEvent) {
+ break;
+ }
+ if (PR_IntervalNow() - start > aTimeout) {
+ break;
+ }
+ }
+ return rv;
+}
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+inline bool
+hasPendingEvents(nsIThread* aThread)
+{
+ bool val;
+ return NS_SUCCEEDED(aThread->HasPendingEvents(&val)) && val;
+}
+
+bool
+NS_HasPendingEvents(nsIThread* aThread)
+{
+ if (!aThread) {
+#ifndef MOZILLA_INTERNAL_API
+ nsCOMPtr<nsIThread> current;
+ NS_GetCurrentThread(getter_AddRefs(current));
+ return hasPendingEvents(current);
+#else
+ aThread = NS_GetCurrentThread();
+ if (NS_WARN_IF(!aThread)) {
+ return false;
+ }
+#endif
+ }
+ return hasPendingEvents(aThread);
+}
+
+bool
+NS_ProcessNextEvent(nsIThread* aThread, bool aMayWait)
+{
+#ifdef MOZILLA_INTERNAL_API
+ if (!aThread) {
+ aThread = NS_GetCurrentThread();
+ if (NS_WARN_IF(!aThread)) {
+ return false;
+ }
+ }
+#else
+ nsCOMPtr<nsIThread> current;
+ if (!aThread) {
+ NS_GetCurrentThread(getter_AddRefs(current));
+ if (NS_WARN_IF(!current)) {
+ return false;
+ }
+ aThread = current.get();
+ }
+#endif
+ bool val;
+ return NS_SUCCEEDED(aThread->ProcessNextEvent(aMayWait, &val)) && val;
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+namespace {
+
+class nsNameThreadRunnable final : public nsIRunnable
+{
+ ~nsNameThreadRunnable() {}
+
+public:
+ explicit nsNameThreadRunnable(const nsACString& aName) : mName(aName) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+protected:
+ const nsCString mName;
+};
+
+NS_IMPL_ISUPPORTS(nsNameThreadRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+nsNameThreadRunnable::Run()
+{
+ PR_SetCurrentThreadName(mName.BeginReading());
+ return NS_OK;
+}
+
+} // namespace
+
+void
+NS_SetThreadName(nsIThread* aThread, const nsACString& aName)
+{
+ if (!aThread) {
+ return;
+ }
+
+ aThread->Dispatch(new nsNameThreadRunnable(aName),
+ nsIEventTarget::DISPATCH_NORMAL);
+}
+
+#else // !XPCOM_GLUE_AVOID_NSPR
+
+void
+NS_SetThreadName(nsIThread* aThread, const nsACString& aName)
+{
+ // No NSPR, no love.
+}
+
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+nsIThread*
+NS_GetCurrentThread()
+{
+ return nsThreadManager::get().GetCurrentThread();
+}
+#endif
+
+// nsThreadPoolNaming
+void
+nsThreadPoolNaming::SetThreadPoolName(const nsACString& aPoolName,
+ nsIThread* aThread)
+{
+ nsCString name(aPoolName);
+ name.AppendLiteral(" #");
+ name.AppendInt(++mCounter, 10); // The counter is declared as volatile
+
+ if (aThread) {
+ // Set on the target thread
+ NS_SetThreadName(aThread, name);
+ } else {
+ // Set on the current thread
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ PR_SetCurrentThreadName(name.BeginReading());
+#endif
+ }
+}
+
+// nsAutoLowPriorityIO
+nsAutoLowPriorityIO::nsAutoLowPriorityIO()
+{
+#if defined(XP_WIN)
+ lowIOPrioritySet = IsVistaOrLater() &&
+ SetThreadPriority(GetCurrentThread(),
+ THREAD_MODE_BACKGROUND_BEGIN);
+#elif defined(XP_MACOSX)
+ oldPriority = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD);
+ lowIOPrioritySet = oldPriority != -1 &&
+ setiopolicy_np(IOPOL_TYPE_DISK,
+ IOPOL_SCOPE_THREAD,
+ IOPOL_THROTTLE) != -1;
+#else
+ lowIOPrioritySet = false;
+#endif
+}
+
+nsAutoLowPriorityIO::~nsAutoLowPriorityIO()
+{
+#if defined(XP_WIN)
+ if (MOZ_LIKELY(lowIOPrioritySet)) {
+ // On Windows the old thread priority is automatically restored
+ SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
+ }
+#elif defined(XP_MACOSX)
+ if (MOZ_LIKELY(lowIOPrioritySet)) {
+ setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, oldPriority);
+ }
+#endif
+}
diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h
new file mode 100644
index 000000000..cbd7f7842
--- /dev/null
+++ b/xpcom/glue/nsThreadUtils.h
@@ -0,0 +1,1049 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsThreadUtils_h__
+#define nsThreadUtils_h__
+
+#include "prthread.h"
+#include "prinrval.h"
+#include "MainThreadUtils.h"
+#include "nsIThreadManager.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsICancelableRunnable.h"
+#include "nsIIdlePeriod.h"
+#include "nsIIncrementalRunnable.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/IndexSequence.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Move.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+
+//-----------------------------------------------------------------------------
+// These methods are alternatives to the methods on nsIThreadManager, provided
+// for convenience.
+
+/**
+ * Set name of the target thread. This operation is asynchronous.
+ */
+extern void NS_SetThreadName(nsIThread* aThread, const nsACString& aName);
+
+/**
+ * Static length version of the above function checking length of the
+ * name at compile time.
+ */
+template<size_t LEN>
+inline void
+NS_SetThreadName(nsIThread* aThread, const char (&aName)[LEN])
+{
+ static_assert(LEN <= 16,
+ "Thread name must be no more than 16 characters");
+ NS_SetThreadName(aThread, nsDependentCString(aName));
+}
+
+/**
+ * Create a new thread, and optionally provide an initial event for the thread.
+ *
+ * @param aResult
+ * The resulting nsIThread object.
+ * @param aInitialEvent
+ * The initial event to run on this thread. This parameter may be null.
+ * @param aStackSize
+ * The size in bytes to reserve for the thread's stack.
+ *
+ * @returns NS_ERROR_INVALID_ARG
+ * Indicates that the given name is not unique.
+ */
+extern nsresult
+NS_NewThread(nsIThread** aResult,
+ nsIRunnable* aInitialEvent = nullptr,
+ uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE);
+
+/**
+ * Creates a named thread, otherwise the same as NS_NewThread
+ */
+template<size_t LEN>
+inline nsresult
+NS_NewNamedThread(const char (&aName)[LEN],
+ nsIThread** aResult,
+ nsIRunnable* aInitialEvent = nullptr,
+ uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE)
+{
+ // Hold a ref while dispatching the initial event to match NS_NewThread()
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr, aStackSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ NS_SetThreadName<LEN>(thread, aName);
+ if (aInitialEvent) {
+ rv = thread->Dispatch(aInitialEvent, NS_DISPATCH_NORMAL);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Initial event dispatch failed");
+ }
+
+ *aResult = nullptr;
+ thread.swap(*aResult);
+ return rv;
+}
+
+/**
+ * Get a reference to the current thread.
+ *
+ * @param aResult
+ * The resulting nsIThread object.
+ */
+extern nsresult NS_GetCurrentThread(nsIThread** aResult);
+
+/**
+ * Dispatch the given event to the current thread.
+ *
+ * @param aEvent
+ * The event to dispatch.
+ *
+ * @returns NS_ERROR_INVALID_ARG
+ * If event is null.
+ */
+extern nsresult NS_DispatchToCurrentThread(nsIRunnable* aEvent);
+extern nsresult
+NS_DispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent);
+
+/**
+ * Dispatch the given event to the main thread.
+ *
+ * @param aEvent
+ * The event to dispatch.
+ * @param aDispatchFlags
+ * The flags to pass to the main thread's dispatch method.
+ *
+ * @returns NS_ERROR_INVALID_ARG
+ * If event is null.
+ */
+extern nsresult
+NS_DispatchToMainThread(nsIRunnable* aEvent,
+ uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
+extern nsresult
+NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent,
+ uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
+
+extern nsresult
+NS_DelayedDispatchToCurrentThread(
+ already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDelayMs);
+
+extern nsresult
+NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+/**
+ * Process all pending events for the given thread before returning. This
+ * method simply calls ProcessNextEvent on the thread while HasPendingEvents
+ * continues to return true and the time spent in NS_ProcessPendingEvents
+ * does not exceed the given timeout value.
+ *
+ * @param aThread
+ * The thread object for which to process pending events. If null, then
+ * events will be processed for the current thread.
+ * @param aTimeout
+ * The maximum number of milliseconds to spend processing pending events.
+ * Events are not pre-empted to honor this timeout. Rather, the timeout
+ * value is simply used to determine whether or not to process another event.
+ * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout.
+ */
+extern nsresult
+NS_ProcessPendingEvents(nsIThread* aThread,
+ PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT);
+#endif
+
+/**
+ * Shortcut for nsIThread::HasPendingEvents.
+ *
+ * It is an error to call this function when the given thread is not the
+ * current thread. This function will return false if called from some
+ * other thread.
+ *
+ * @param aThread
+ * The current thread or null.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that there are pending events
+ * in the current thread's event queue.
+ */
+extern bool NS_HasPendingEvents(nsIThread* aThread = nullptr);
+
+/**
+ * Shortcut for nsIThread::ProcessNextEvent.
+ *
+ * It is an error to call this function when the given thread is not the
+ * current thread. This function will simply return false if called
+ * from some other thread.
+ *
+ * @param aThread
+ * The current thread or null.
+ * @param aMayWait
+ * A boolean parameter that if "true" indicates that the method may block
+ * the calling thread to wait for a pending event.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that an event from the current
+ * thread's event queue was processed.
+ */
+extern bool NS_ProcessNextEvent(nsIThread* aThread = nullptr,
+ bool aMayWait = true);
+
+//-----------------------------------------------------------------------------
+// Helpers that work with nsCOMPtr:
+
+inline already_AddRefed<nsIThread>
+do_GetCurrentThread()
+{
+ nsIThread* thread = nullptr;
+ NS_GetCurrentThread(&thread);
+ return already_AddRefed<nsIThread>(thread);
+}
+
+inline already_AddRefed<nsIThread>
+do_GetMainThread()
+{
+ nsIThread* thread = nullptr;
+ NS_GetMainThread(&thread);
+ return already_AddRefed<nsIThread>(thread);
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef MOZILLA_INTERNAL_API
+// Fast access to the current thread. Do not release the returned pointer! If
+// you want to use this pointer from some other thread, then you will need to
+// AddRef it. Otherwise, you should only consider this pointer valid from code
+// running on the current thread.
+extern nsIThread* NS_GetCurrentThread();
+#endif
+
+//-----------------------------------------------------------------------------
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+namespace mozilla {
+
+// This class is designed to be subclassed.
+class IdlePeriod : public nsIIdlePeriod
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIDLEPERIOD
+
+ IdlePeriod() {}
+
+protected:
+ virtual ~IdlePeriod() {}
+private:
+ IdlePeriod(const IdlePeriod&) = delete;
+ IdlePeriod& operator=(const IdlePeriod&) = delete;
+ IdlePeriod& operator=(const IdlePeriod&&) = delete;
+};
+
+// This class is designed to be subclassed.
+class Runnable : public nsIRunnable
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ Runnable() {}
+
+protected:
+ virtual ~Runnable() {}
+private:
+ Runnable(const Runnable&) = delete;
+ Runnable& operator=(const Runnable&) = delete;
+ Runnable& operator=(const Runnable&&) = delete;
+};
+
+// This class is designed to be subclassed.
+class CancelableRunnable : public Runnable,
+ public nsICancelableRunnable
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ // nsICancelableRunnable
+ virtual nsresult Cancel() override;
+
+ CancelableRunnable() {}
+
+protected:
+ virtual ~CancelableRunnable() {}
+private:
+ CancelableRunnable(const CancelableRunnable&) = delete;
+ CancelableRunnable& operator=(const CancelableRunnable&) = delete;
+ CancelableRunnable& operator=(const CancelableRunnable&&) = delete;
+};
+
+// This class is designed to be subclassed.
+class IncrementalRunnable : public CancelableRunnable,
+ public nsIIncrementalRunnable
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ // nsIIncrementalRunnable
+ virtual void SetDeadline(TimeStamp aDeadline) override;
+
+ IncrementalRunnable() {}
+
+protected:
+ virtual ~IncrementalRunnable() {}
+private:
+ IncrementalRunnable(const IncrementalRunnable&) = delete;
+ IncrementalRunnable& operator=(const IncrementalRunnable&) = delete;
+ IncrementalRunnable& operator=(const IncrementalRunnable&&) = delete;
+};
+
+namespace detail {
+
+// An event that can be used to call a C++11 functions or function objects,
+// including lambdas. The function must have no required arguments, and must
+// return void.
+template<typename StoredFunction>
+class RunnableFunction : public Runnable
+{
+public:
+ template <typename F>
+ explicit RunnableFunction(F&& aFunction)
+ : mFunction(Forward<F>(aFunction))
+ { }
+
+ NS_IMETHOD Run() override {
+ static_assert(IsVoid<decltype(mFunction())>::value,
+ "The lambda must return void!");
+ mFunction();
+ return NS_OK;
+ }
+private:
+ StoredFunction mFunction;
+};
+
+} // namespace detail
+
+} // namespace mozilla
+
+template<typename Function>
+already_AddRefed<mozilla::Runnable>
+NS_NewRunnableFunction(Function&& aFunction)
+{
+ return do_AddRef(new mozilla::detail::RunnableFunction
+ // Make sure we store a non-reference in nsRunnableFunction.
+ <typename mozilla::RemoveReference<Function>::Type>
+ // But still forward aFunction to move if possible.
+ (mozilla::Forward<Function>(aFunction)));
+}
+
+// An event that can be used to call a method on a class. The class type must
+// support reference counting. This event supports Revoke for use
+// with nsRevocableEventPtr.
+template<class ClassType,
+ typename ReturnType = void,
+ bool Owning = true,
+ bool Cancelable = false>
+class nsRunnableMethod : public mozilla::Conditional<!Cancelable,
+ mozilla::Runnable,
+ mozilla::CancelableRunnable>::Type
+{
+public:
+ virtual void Revoke() = 0;
+
+ // These ReturnTypeEnforcer classes set up a blacklist for return types that
+ // we know are not safe. The default ReturnTypeEnforcer compiles just fine but
+ // already_AddRefed will not.
+ template<typename OtherReturnType>
+ class ReturnTypeEnforcer
+ {
+ public:
+ typedef int ReturnTypeIsSafe;
+ };
+
+ template<class T>
+ class ReturnTypeEnforcer<already_AddRefed<T>>
+ {
+ // No ReturnTypeIsSafe makes this illegal!
+ };
+
+ // Make sure this return type is safe.
+ typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
+};
+
+template<class ClassType, bool Owning>
+struct nsRunnableMethodReceiver
+{
+ RefPtr<ClassType> mObj;
+ explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
+ ~nsRunnableMethodReceiver() { Revoke(); }
+ ClassType* Get() const { return mObj.get(); }
+ void Revoke() { mObj = nullptr; }
+};
+
+template<class ClassType>
+struct nsRunnableMethodReceiver<ClassType, false>
+{
+ ClassType* MOZ_NON_OWNING_REF mObj;
+ explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
+ ClassType* Get() const { return mObj; }
+ void Revoke() { mObj = nullptr; }
+};
+
+template<typename Method, bool Owning, bool Cancelable> struct nsRunnableMethodTraits;
+
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(C::*)(As...), Owning, Cancelable>
+{
+ typedef C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(C::*)(As...) const, Owning, Cancelable>
+{
+ typedef const C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+#ifdef NS_HAVE_STDCALL
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(__stdcall C::*)(As...), Owning, Cancelable>
+{
+ typedef C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+template<class C, typename R, bool Owning, bool Cancelable>
+struct nsRunnableMethodTraits<R(NS_STDCALL C::*)(), Owning, Cancelable>
+{
+ typedef C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(__stdcall C::*)(As...) const, Owning, Cancelable>
+{
+ typedef const C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+template<class C, typename R, bool Owning, bool Cancelable>
+struct nsRunnableMethodTraits<R(NS_STDCALL C::*)() const, Owning, Cancelable>
+{
+ typedef const C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+#endif
+
+
+// IsParameterStorageClass<T>::value is true if T is a parameter-storage class
+// that will be recognized by NS_New[NonOwning]RunnableMethodWithArg[s] to
+// force a specific storage&passing strategy (instead of inferring one,
+// see ParameterStorage).
+// When creating a new storage class, add a specialization for it to be
+// recognized.
+template<typename T>
+struct IsParameterStorageClass : public mozilla::FalseType {};
+
+// StoreXPassByY structs used to inform nsRunnableMethodArguments how to
+// store arguments, and how to pass them to the target method.
+
+template<typename T>
+struct StoreCopyPassByValue
+{
+ typedef T stored_type;
+ typedef T passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByValue(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByValue<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByConstLRef
+{
+ typedef T stored_type;
+ typedef const T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByConstLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByLRef
+{
+ typedef T stored_type;
+ typedef T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByRRef
+{
+ typedef T stored_type;
+ typedef T&& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByRRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return mozilla::Move(m); }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByRRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreRefPassByLRef
+{
+ typedef T& stored_type;
+ typedef T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreRefPassByLRef(A& a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreRefPassByLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreConstRefPassByConstLRef
+{
+ typedef const T& stored_type;
+ typedef const T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreConstRefPassByConstLRef(const A& a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreConstRefPassByConstLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StorensRefPtrPassByPtr
+{
+ typedef RefPtr<T> stored_type;
+ typedef T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StorensRefPtrPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m.get(); }
+};
+template<typename S>
+struct IsParameterStorageClass<StorensRefPtrPassByPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StorePtrPassByPtr
+{
+ typedef T* stored_type;
+ typedef T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StorePtrPassByPtr(A a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StorePtrPassByPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreConstPtrPassByConstPtr
+{
+ typedef const T* stored_type;
+ typedef const T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreConstPtrPassByConstPtr(A a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreConstPtrPassByConstPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByConstPtr
+{
+ typedef T stored_type;
+ typedef const T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return &m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByConstPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByPtr
+{
+ typedef T stored_type;
+ typedef T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return &m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByPtr<S>>
+ : public mozilla::TrueType {};
+
+namespace detail {
+
+template<typename TWithoutPointer>
+struct NonnsISupportsPointerStorageClass
+ : mozilla::Conditional<mozilla::IsConst<TWithoutPointer>::value,
+ StoreConstPtrPassByConstPtr<
+ typename mozilla::RemoveConst<TWithoutPointer>::Type>,
+ StorePtrPassByPtr<TWithoutPointer>>
+{};
+
+template<typename>
+struct SFINAE1True : mozilla::TrueType
+{};
+
+template<class T>
+static auto HasRefCountMethodsTest(int)
+ -> SFINAE1True<decltype(mozilla::DeclVal<T>().AddRef(),
+ mozilla::DeclVal<T>().Release())>;
+template<class>
+static auto HasRefCountMethodsTest(long) -> mozilla::FalseType;
+
+template<class T>
+struct HasRefCountMethods : decltype(HasRefCountMethodsTest<T>(0))
+{};
+
+template<typename T>
+struct IsRefcountedSmartPointer : public mozilla::FalseType
+{};
+
+template<typename T>
+struct IsRefcountedSmartPointer<RefPtr<T>> : public mozilla::TrueType
+{};
+
+template<typename T>
+struct IsRefcountedSmartPointer<nsCOMPtr<T>> : public mozilla::TrueType
+{};
+
+template<typename T>
+struct StripSmartPointer
+{
+ typedef void Type;
+};
+
+template<typename T>
+struct StripSmartPointer<RefPtr<T>>
+{
+ typedef T Type;
+};
+
+template<typename T>
+struct StripSmartPointer<nsCOMPtr<T>>
+{
+ typedef T Type;
+};
+
+template<typename TWithoutPointer>
+struct PointerStorageClass
+ : mozilla::Conditional<HasRefCountMethods<TWithoutPointer>::value,
+ StorensRefPtrPassByPtr<TWithoutPointer>,
+ typename NonnsISupportsPointerStorageClass<
+ TWithoutPointer
+ >::Type>
+{};
+
+template<typename TWithoutRef>
+struct LValueReferenceStorageClass
+ : mozilla::Conditional<mozilla::IsConst<TWithoutRef>::value,
+ StoreConstRefPassByConstLRef<
+ typename mozilla::RemoveConst<TWithoutRef>::Type>,
+ StoreRefPassByLRef<TWithoutRef>>
+{};
+
+template<typename T>
+struct SmartPointerStorageClass
+ : mozilla::Conditional<IsRefcountedSmartPointer<T>::value,
+ StorensRefPtrPassByPtr<
+ typename StripSmartPointer<T>::Type>,
+ StoreCopyPassByValue<T>>
+{};
+
+template<typename T>
+struct NonLValueReferenceStorageClass
+ : mozilla::Conditional<mozilla::IsRvalueReference<T>::value,
+ StoreCopyPassByRRef<
+ typename mozilla::RemoveReference<T>::Type>,
+ typename SmartPointerStorageClass<T>::Type>
+{};
+
+template<typename T>
+struct NonPointerStorageClass
+ : mozilla::Conditional<mozilla::IsLvalueReference<T>::value,
+ typename LValueReferenceStorageClass<
+ typename mozilla::RemoveReference<T>::Type
+ >::Type,
+ typename NonLValueReferenceStorageClass<T>::Type>
+{};
+
+template<typename T>
+struct NonParameterStorageClass
+ : mozilla::Conditional<mozilla::IsPointer<T>::value,
+ typename PointerStorageClass<
+ typename mozilla::RemovePointer<T>::Type
+ >::Type,
+ typename NonPointerStorageClass<T>::Type>
+{};
+
+// Choose storage&passing strategy based on preferred storage type:
+// - If IsParameterStorageClass<T>::value is true, use as-is.
+// - RC* -> StorensRefPtrPassByPtr<RC> : Store RefPtr<RC>, pass RC*
+// ^^ RC quacks like a ref-counted type (i.e., has AddRef and Release methods)
+// - const T* -> StoreConstPtrPassByConstPtr<T> : Store const T*, pass const T*
+// - T* -> StorePtrPassByPtr<T> : Store T*, pass T*.
+// - const T& -> StoreConstRefPassByConstLRef<T>: Store const T&, pass const T&.
+// - T& -> StoreRefPassByLRef<T> : Store T&, pass T&.
+// - T&& -> StoreCopyPassByRRef<T> : Store T, pass Move(T).
+// - RefPtr<T>, nsCOMPtr<T>
+// -> StorensRefPtrPassByPtr<T> : Store RefPtr<T>, pass T*
+// - Other T -> StoreCopyPassByValue<T> : Store T, pass T.
+// Other available explicit options:
+// - StoreCopyPassByConstLRef<T> : Store T, pass const T&.
+// - StoreCopyPassByLRef<T> : Store T, pass T& (of copy!)
+// - StoreCopyPassByConstPtr<T> : Store T, pass const T*
+// - StoreCopyPassByPtr<T> : Store T, pass T* (of copy!)
+// Or create your own class with PassAsParameter() method, optional
+// clean-up in destructor, and with associated IsParameterStorageClass<>.
+template<typename T>
+struct ParameterStorage
+ : mozilla::Conditional<IsParameterStorageClass<T>::value,
+ T,
+ typename NonParameterStorageClass<T>::Type>
+{};
+
+} /* namespace detail */
+
+namespace mozilla {
+
+namespace detail {
+
+// struct used to store arguments and later apply them to a method.
+template <typename... Ts>
+struct RunnableMethodArguments final
+{
+ Tuple<typename ::detail::ParameterStorage<Ts>::Type...> mArguments;
+ template <typename... As>
+ explicit RunnableMethodArguments(As&&... aArguments)
+ : mArguments(Forward<As>(aArguments)...)
+ {}
+ template<typename C, typename M, typename... Args, size_t... Indices>
+ static auto
+ applyImpl(C* o, M m, Tuple<Args...>& args, IndexSequence<Indices...>)
+ -> decltype(((*o).*m)(Get<Indices>(args).PassAsParameter()...))
+ {
+ return ((*o).*m)(Get<Indices>(args).PassAsParameter()...);
+ }
+ template<class C, typename M> auto apply(C* o, M m)
+ -> decltype(applyImpl(o, m, mArguments,
+ typename IndexSequenceFor<Ts...>::Type()))
+ {
+ return applyImpl(o, m, mArguments,
+ typename IndexSequenceFor<Ts...>::Type());
+ }
+};
+
+template<typename Method, bool Owning, bool Cancelable, typename... Storages>
+class RunnableMethodImpl final
+ : public ::nsRunnableMethodTraits<Method, Owning, Cancelable>::base_type
+{
+ typedef typename ::nsRunnableMethodTraits<Method, Owning, Cancelable>::class_type
+ ClassType;
+ ::nsRunnableMethodReceiver<ClassType, Owning> mReceiver;
+ Method mMethod;
+ RunnableMethodArguments<Storages...> mArgs;
+private:
+ virtual ~RunnableMethodImpl() { Revoke(); };
+public:
+ template<typename... Args>
+ explicit RunnableMethodImpl(ClassType* aObj, Method aMethod,
+ Args&&... aArgs)
+ : mReceiver(aObj)
+ , mMethod(aMethod)
+ , mArgs(Forward<Args>(aArgs)...)
+ {
+ static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
+ }
+ NS_IMETHOD Run()
+ {
+ if (MOZ_LIKELY(mReceiver.Get())) {
+ mArgs.apply(mReceiver.Get(), mMethod);
+ }
+ return NS_OK;
+ }
+ nsresult Cancel() {
+ static_assert(Cancelable, "Don't use me!");
+ Revoke();
+ return NS_OK;
+ }
+ void Revoke() { mReceiver.Revoke(); }
+};
+
+} // namespace detail
+
+// Use this template function like so:
+//
+// nsCOMPtr<nsIRunnable> event =
+// mozilla::NewRunnableMethod(myObject, &MyClass::HandleEvent);
+// NS_DispatchToCurrentThread(event);
+//
+// Statically enforced constraints:
+// - myObject must be of (or implicitly convertible to) type MyClass
+// - MyClass must define AddRef and Release methods
+//
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, false>::base_type>
+NewRunnableMethod(PtrType aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, false>(aPtr, aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, true>::base_type>
+NewCancelableRunnableMethod(PtrType aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, true>(aPtr, aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, false>::base_type>
+NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, false>(aPtr, aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, true>::base_type>
+NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, true>(aPtr, aMethod));
+}
+
+// Similar to NewRunnableMethod. Call like so:
+// nsCOMPtr<nsIRunnable> event =
+// NewRunnableMethod<Types,...>(myObject, &MyClass::HandleEvent, myArg1,...);
+// 'Types' are the stored type for each argument, see ParameterStorage for details.
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, false>::base_type>
+NewRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, false, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, false>::base_type>
+NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, false, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, true>::base_type>
+NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, true, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, true>::base_type>
+NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod,
+ Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, true, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(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
+// pointer back to resource class R. If R goes away while E is still pending,
+// then it is important to "revoke" E so that it does not try use R after R has
+// been destroyed. nsRevocableEventPtr makes it easy for R to manage such
+// situations:
+//
+// class R;
+//
+// class E : public mozilla::Runnable {
+// public:
+// void Revoke() {
+// mResource = nullptr;
+// }
+// private:
+// R *mResource;
+// };
+//
+// class R {
+// public:
+// void EventHandled() {
+// mEvent.Forget();
+// }
+// private:
+// nsRevocableEventPtr<E> mEvent;
+// };
+//
+// void R::PostEvent() {
+// // Make sure any pending event is revoked.
+// mEvent->Revoke();
+//
+// nsCOMPtr<nsIRunnable> event = new E();
+// if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) {
+// // Keep pointer to event so we can revoke it.
+// mEvent = event;
+// }
+// }
+//
+// NS_IMETHODIMP E::Run() {
+// if (!mResource)
+// return NS_OK;
+// ...
+// mResource->EventHandled();
+// return NS_OK;
+// }
+//
+template<class T>
+class nsRevocableEventPtr
+{
+public:
+ nsRevocableEventPtr() : mEvent(nullptr) {}
+ ~nsRevocableEventPtr() { Revoke(); }
+
+ const nsRevocableEventPtr& operator=(T* aEvent)
+ {
+ if (mEvent != aEvent) {
+ Revoke();
+ mEvent = aEvent;
+ }
+ return *this;
+ }
+
+ const nsRevocableEventPtr& operator=(already_AddRefed<T> aEvent)
+ {
+ RefPtr<T> event = aEvent;
+ if (mEvent != event) {
+ Revoke();
+ mEvent = event.forget();
+ }
+ return *this;
+ }
+
+ void Revoke()
+ {
+ if (mEvent) {
+ mEvent->Revoke();
+ mEvent = nullptr;
+ }
+ }
+
+ void Forget() { mEvent = nullptr; }
+ bool IsPending() { return mEvent != nullptr; }
+ T* get() { return mEvent; }
+
+private:
+ // Not implemented
+ nsRevocableEventPtr(const nsRevocableEventPtr&);
+ nsRevocableEventPtr& operator=(const nsRevocableEventPtr&);
+
+ RefPtr<T> mEvent;
+};
+
+/**
+ * A simple helper to suffix thread pool name
+ * with incremental numbers.
+ */
+class nsThreadPoolNaming
+{
+public:
+ nsThreadPoolNaming() : mCounter(0) {}
+
+ /**
+ * Creates and sets next thread name as "<aPoolName> #<n>"
+ * on the specified thread. If no thread is specified (aThread
+ * is null) then the name is synchronously set on the current thread.
+ */
+ void SetThreadPoolName(const nsACString& aPoolName,
+ nsIThread* aThread = nullptr);
+
+private:
+ mozilla::Atomic<uint32_t> mCounter;
+
+ nsThreadPoolNaming(const nsThreadPoolNaming&) = delete;
+ void operator=(const nsThreadPoolNaming&) = delete;
+};
+
+/**
+ * Thread priority in most operating systems affect scheduling, not IO. This
+ * helper is used to set the current thread to low IO priority for the lifetime
+ * of the created object. You can only use this low priority IO setting within
+ * the context of the current thread.
+ */
+class MOZ_STACK_CLASS nsAutoLowPriorityIO
+{
+public:
+ nsAutoLowPriorityIO();
+ ~nsAutoLowPriorityIO();
+
+private:
+ bool lowIOPrioritySet;
+#if defined(XP_MACOSX)
+ int oldPriority;
+#endif
+};
+
+void
+NS_SetMainThread();
+
+#endif // nsThreadUtils_h__
diff --git a/xpcom/glue/nsVersionComparator.cpp b/xpcom/glue/nsVersionComparator.cpp
new file mode 100644
index 000000000..6be32b974
--- /dev/null
+++ b/xpcom/glue/nsVersionComparator.cpp
@@ -0,0 +1,379 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsVersionComparator.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
+#include <wchar.h>
+#include "nsStringGlue.h"
+#endif
+
+struct VersionPart
+{
+ int32_t numA;
+
+ const char* strB; // NOT null-terminated, can be a null pointer
+ uint32_t strBlen;
+
+ int32_t numC;
+
+ char* extraD; // null-terminated
+};
+
+#ifdef XP_WIN
+struct VersionPartW
+{
+ int32_t numA;
+
+ wchar_t* strB; // NOT null-terminated, can be a null pointer
+ uint32_t strBlen;
+
+ int32_t numC;
+
+ wchar_t* extraD; // null-terminated
+
+};
+#endif
+
+/**
+ * Parse a version part into a number and "extra text".
+ *
+ * @returns A pointer to the next versionpart, or null if none.
+ */
+static char*
+ParseVP(char* aPart, VersionPart& aResult)
+{
+ char* dot;
+
+ aResult.numA = 0;
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ aResult.numC = 0;
+ aResult.extraD = nullptr;
+
+ if (!aPart) {
+ return aPart;
+ }
+
+ dot = strchr(aPart, '.');
+ if (dot) {
+ *dot = '\0';
+ }
+
+ if (aPart[0] == '*' && aPart[1] == '\0') {
+ aResult.numA = INT32_MAX;
+ aResult.strB = "";
+ } else {
+ aResult.numA = strtol(aPart, const_cast<char**>(&aResult.strB), 10);
+ }
+
+ if (!*aResult.strB) {
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ } else {
+ if (aResult.strB[0] == '+') {
+ static const char kPre[] = "pre";
+
+ ++aResult.numA;
+ aResult.strB = kPre;
+ aResult.strBlen = sizeof(kPre) - 1;
+ } else {
+ const char* numstart = strpbrk(aResult.strB, "0123456789+-");
+ if (!numstart) {
+ aResult.strBlen = strlen(aResult.strB);
+ } else {
+ aResult.strBlen = numstart - aResult.strB;
+
+ aResult.numC = strtol(numstart, &aResult.extraD, 10);
+ if (!*aResult.extraD) {
+ aResult.extraD = nullptr;
+ }
+ }
+ }
+ }
+
+ if (dot) {
+ ++dot;
+
+ if (!*dot) {
+ dot = nullptr;
+ }
+ }
+
+ return dot;
+}
+
+
+/**
+ * Parse a version part into a number and "extra text".
+ *
+ * @returns A pointer to the next versionpart, or null if none.
+ */
+#ifdef XP_WIN
+static wchar_t*
+ParseVP(wchar_t* aPart, VersionPartW& aResult)
+{
+
+ wchar_t* dot;
+
+ aResult.numA = 0;
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ aResult.numC = 0;
+ aResult.extraD = nullptr;
+
+ if (!aPart) {
+ return aPart;
+ }
+
+ dot = wcschr(aPart, '.');
+ if (dot) {
+ *dot = '\0';
+ }
+
+ if (aPart[0] == '*' && aPart[1] == '\0') {
+ aResult.numA = INT32_MAX;
+ aResult.strB = L"";
+ } else {
+ aResult.numA = wcstol(aPart, const_cast<wchar_t**>(&aResult.strB), 10);
+ }
+
+ if (!*aResult.strB) {
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ } else {
+ if (aResult.strB[0] == '+') {
+ static wchar_t kPre[] = L"pre";
+
+ ++aResult.numA;
+ aResult.strB = kPre;
+ aResult.strBlen = sizeof(kPre) - 1;
+ } else {
+ const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-");
+ if (!numstart) {
+ aResult.strBlen = wcslen(aResult.strB);
+ } else {
+ aResult.strBlen = numstart - aResult.strB;
+
+ aResult.numC = wcstol(numstart, &aResult.extraD, 10);
+ if (!*aResult.extraD) {
+ aResult.extraD = nullptr;
+ }
+ }
+ }
+ }
+
+ if (dot) {
+ ++dot;
+
+ if (!*dot) {
+ dot = nullptr;
+ }
+ }
+
+ return dot;
+}
+#endif
+
+// compare two null-terminated strings, which may be null pointers
+static int32_t
+ns_strcmp(const char* aStr1, const char* aStr2)
+{
+ // any string is *before* no string
+ if (!aStr1) {
+ return aStr2 != 0;
+ }
+
+ if (!aStr2) {
+ return -1;
+ }
+
+ return strcmp(aStr1, aStr2);
+}
+
+// compare two length-specified string, which may be null pointers
+static int32_t
+ns_strnncmp(const char* aStr1, uint32_t aLen1,
+ const char* aStr2, uint32_t aLen2)
+{
+ // any string is *before* no string
+ if (!aStr1) {
+ return aStr2 != 0;
+ }
+
+ if (!aStr2) {
+ return -1;
+ }
+
+ for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
+ if (*aStr1 < *aStr2) {
+ return -1;
+ }
+
+ if (*aStr1 > *aStr2) {
+ return 1;
+ }
+ }
+
+ if (aLen1 == 0) {
+ return aLen2 == 0 ? 0 : -1;
+ }
+
+ return 1;
+}
+
+// compare two int32_t
+static int32_t
+ns_cmp(int32_t aNum1, int32_t aNum2)
+{
+ if (aNum1 < aNum2) {
+ return -1;
+ }
+
+ return aNum1 != aNum2;
+}
+
+/**
+ * Compares two VersionParts
+ */
+static int32_t
+CompareVP(VersionPart& aVer1, VersionPart& aVer2)
+{
+ int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
+ if (r) {
+ return r;
+ }
+
+ r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
+ if (r) {
+ return r;
+ }
+
+ r = ns_cmp(aVer1.numC, aVer2.numC);
+ if (r) {
+ return r;
+ }
+
+ return ns_strcmp(aVer1.extraD, aVer2.extraD);
+}
+
+/**
+ * Compares two VersionParts
+ */
+#ifdef XP_WIN
+static int32_t
+CompareVP(VersionPartW& aVer1, VersionPartW& aVer2)
+{
+ int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
+ if (r) {
+ return r;
+ }
+
+ r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen));
+ if (r) {
+ return r;
+ }
+
+ r = ns_cmp(aVer1.numC, aVer2.numC);
+ if (r) {
+ return r;
+ }
+
+ if (!aVer1.extraD) {
+ return aVer2.extraD != 0;
+ }
+
+ if (!aVer2.extraD) {
+ return -1;
+ }
+
+ return wcscmp(aVer1.extraD, aVer2.extraD);
+}
+#endif
+
+namespace mozilla {
+
+#ifdef XP_WIN
+int32_t
+CompareVersions(const char16_t* aStrA, const char16_t* aStrB)
+{
+ wchar_t* A2 = wcsdup(char16ptr_t(aStrA));
+ if (!A2) {
+ return 1;
+ }
+
+ wchar_t* B2 = wcsdup(char16ptr_t(aStrB));
+ if (!B2) {
+ free(A2);
+ return 1;
+ }
+
+ int32_t result;
+ wchar_t* a = A2;
+ wchar_t* b = B2;
+
+ do {
+ VersionPartW va, vb;
+
+ a = ParseVP(a, va);
+ b = ParseVP(b, vb);
+
+ result = CompareVP(va, vb);
+ if (result) {
+ break;
+ }
+
+ } while (a || b);
+
+ free(A2);
+ free(B2);
+
+ return result;
+}
+#endif
+
+int32_t
+CompareVersions(const char* aStrA, const char* aStrB)
+{
+ char* A2 = strdup(aStrA);
+ if (!A2) {
+ return 1;
+ }
+
+ char* B2 = strdup(aStrB);
+ if (!B2) {
+ free(A2);
+ return 1;
+ }
+
+ int32_t result;
+ char* a = A2;
+ char* b = B2;
+
+ do {
+ VersionPart va, vb;
+
+ a = ParseVP(a, va);
+ b = ParseVP(b, vb);
+
+ result = CompareVP(va, vb);
+ if (result) {
+ break;
+ }
+
+ } while (a || b);
+
+ free(A2);
+ free(B2);
+
+ return result;
+}
+
+} // namespace mozilla
+
diff --git a/xpcom/glue/nsVersionComparator.h b/xpcom/glue/nsVersionComparator.h
new file mode 100644
index 000000000..0dbf8532b
--- /dev/null
+++ b/xpcom/glue/nsVersionComparator.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsVersionComparator_h__
+#define nsVersionComparator_h__
+
+#include "nscore.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
+#include <wchar.h>
+#include "nsStringGlue.h"
+#endif
+
+/**
+ * In order to compare version numbers in Mozilla, you need to use the
+ * mozilla::Version class. You can construct an object of this type by passing
+ * in a string version number to the constructor. Objects of this type can be
+ * compared using the standard comparison operators.
+ *
+ * For example, let's say that you want to make sure that a given version
+ * number is not older than 15.a2. Here's how you would write a function to
+ * do that.
+ *
+ * bool IsVersionValid(const char* version) {
+ * return mozilla::Version("15.a2") <= mozilla::Version(version);
+ * }
+ *
+ * Or, since Version's constructor is implicit, you can simplify this code:
+ *
+ * bool IsVersionValid(const char* version) {
+ * return mozilla::Version("15.a2") <= version;
+ * }
+ *
+ * On Windows, if your version strings are wide characters, you should use the
+ * mozilla::VersionW variant instead. The semantics of that class is the same
+ * as Version.
+ */
+
+namespace mozilla {
+
+int32_t CompareVersions(const char* aStrA, const char* aStrB);
+
+#ifdef XP_WIN
+int32_t CompareVersions(const char16_t* aStrA, const char16_t* aStrB);
+#endif
+
+struct Version
+{
+ explicit Version(const char* aVersionString)
+ {
+ versionContent = strdup(aVersionString);
+ }
+
+ const char* ReadContent() const
+ {
+ return versionContent;
+ }
+
+ ~Version()
+ {
+ free(versionContent);
+ }
+
+ bool operator<(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) == -1;
+ }
+ bool operator<=(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) < 1;
+ }
+ bool operator>(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) == 1;
+ }
+ bool operator>=(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) > -1;
+ }
+ bool operator==(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) == 0;
+ }
+ bool operator!=(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) != 0;
+ }
+ bool operator<(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) == -1;
+ }
+ bool operator<=(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) < 1;
+ }
+ bool operator>(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) == 1;
+ }
+ bool operator>=(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) > -1;
+ }
+ bool operator==(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) == 0;
+ }
+ bool operator!=(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) != 0;
+ }
+
+private:
+ char* versionContent;
+};
+
+#ifdef XP_WIN
+struct VersionW
+{
+ VersionW(const char16_t* aVersionStringW)
+ {
+ versionContentW =
+ reinterpret_cast<char16_t*>(wcsdup(char16ptr_t(aVersionStringW)));
+ }
+
+ const char16_t* ReadContentW() const
+ {
+ return versionContentW;
+ }
+
+ ~VersionW()
+ {
+ free(versionContentW);
+ }
+
+ bool operator<(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) == -1;
+ }
+ bool operator<=(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) < 1;
+ }
+ bool operator>(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) == 1;
+ }
+ bool operator>=(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) > -1;
+ }
+ bool operator==(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) == 0;
+ }
+ bool operator!=(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) != 0;
+ }
+
+private:
+ char16_t* versionContentW;
+};
+#endif
+
+} // namespace mozilla
+
+#endif // nsVersionComparator_h__
+
diff --git a/xpcom/glue/nsWeakReference.cpp b/xpcom/glue/nsWeakReference.cpp
new file mode 100644
index 000000000..57f372641
--- /dev/null
+++ b/xpcom/glue/nsWeakReference.cpp
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// nsWeakReference.cpp
+
+#include "mozilla/Attributes.h"
+
+#include "nsWeakReference.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+
+#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
+
+#define MOZ_WEAKREF_DECL_OWNINGTHREAD nsAutoOwningThread _mWeakRefOwningThread;
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD \
+ NS_CheckThreadSafe(_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe")
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) \
+ NS_CheckThreadSafe((that)->_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe")
+
+#else
+
+#define MOZ_WEAKREF_DECL_OWNINGTHREAD
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD do { } while (false)
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) do { } while (false)
+
+#endif
+
+class nsWeakReference final : public nsIWeakReference
+{
+public:
+ // nsISupports...
+ NS_DECL_ISUPPORTS
+
+ // nsIWeakReference...
+ NS_DECL_NSIWEAKREFERENCE
+ virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
+private:
+ MOZ_WEAKREF_DECL_OWNINGTHREAD
+
+ friend class nsSupportsWeakReference;
+
+ explicit nsWeakReference(nsSupportsWeakReference* aReferent)
+ : mReferent(aReferent)
+ // ...I can only be constructed by an |nsSupportsWeakReference|
+ {
+ }
+
+ ~nsWeakReference()
+ // ...I will only be destroyed by calling |delete| myself.
+ {
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD;
+ if (mReferent) {
+ mReferent->NoticeProxyDestruction();
+ }
+ }
+
+ void
+ NoticeReferentDestruction()
+ // ...called (only) by an |nsSupportsWeakReference| from _its_ dtor.
+ {
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD;
+ mReferent = nullptr;
+ }
+
+ nsSupportsWeakReference* MOZ_NON_OWNING_REF mReferent;
+};
+
+nsresult
+nsQueryReferent::operator()(const nsIID& aIID, void** aAnswer) const
+{
+ nsresult status;
+ if (mWeakPtr) {
+ if (NS_FAILED(status = mWeakPtr->QueryReferent(aIID, aAnswer))) {
+ *aAnswer = 0;
+ }
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsIWeakReference* // or else |already_AddRefed<nsIWeakReference>|
+NS_GetWeakReference(nsISupports* aInstancePtr, nsresult* aErrorPtr)
+{
+ nsresult status;
+
+ nsIWeakReference* result = nullptr;
+
+ if (aInstancePtr) {
+ nsCOMPtr<nsISupportsWeakReference> factoryPtr =
+ do_QueryInterface(aInstancePtr, &status);
+ if (factoryPtr) {
+ status = factoryPtr->GetWeakReference(&result);
+ }
+ // else, |status| has already been set by |do_QueryInterface|
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (aErrorPtr) {
+ *aErrorPtr = status;
+ }
+ return result;
+}
+
+nsresult
+nsSupportsWeakReference::GetWeakReference(nsIWeakReference** aInstancePtr)
+{
+ if (!aInstancePtr) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mProxy) {
+ mProxy = new nsWeakReference(this);
+ } else {
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(mProxy);
+ }
+ *aInstancePtr = mProxy;
+
+ nsresult status;
+ if (!*aInstancePtr) {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ } else {
+ NS_ADDREF(*aInstancePtr);
+ status = NS_OK;
+ }
+
+ return status;
+}
+
+NS_IMPL_ISUPPORTS(nsWeakReference, nsIWeakReference)
+
+NS_IMETHODIMP
+nsWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
+{
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD;
+
+ return mReferent ? mReferent->QueryInterface(aIID, aInstancePtr) :
+ NS_ERROR_NULL_POINTER;
+}
+
+size_t
+nsWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this);
+}
+
+void
+nsSupportsWeakReference::ClearWeakReferences()
+{
+ if (mProxy) {
+ mProxy->NoticeReferentDestruction();
+ mProxy = nullptr;
+ }
+}
+
diff --git a/xpcom/glue/nsWeakReference.h b/xpcom/glue/nsWeakReference.h
new file mode 100644
index 000000000..fc875ba96
--- /dev/null
+++ b/xpcom/glue/nsWeakReference.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWeakReference_h__
+#define nsWeakReference_h__
+
+// nsWeakReference.h
+
+// See mfbt/WeakPtr.h for a more typesafe C++ implementation of weak references
+
+#include "nsIWeakReferenceUtils.h"
+
+class nsWeakReference;
+
+class nsSupportsWeakReference : public nsISupportsWeakReference
+{
+public:
+ nsSupportsWeakReference() : mProxy(0) {}
+
+ NS_DECL_NSISUPPORTSWEAKREFERENCE
+
+protected:
+ inline ~nsSupportsWeakReference();
+
+private:
+ friend class nsWeakReference;
+
+ // Called (only) by an |nsWeakReference| from _its_ dtor.
+ // The thread safety check is made by the caller.
+ void NoticeProxyDestruction() { mProxy = nullptr; }
+
+ nsWeakReference* MOZ_NON_OWNING_REF mProxy;
+
+protected:
+
+ void ClearWeakReferences();
+ bool HasWeakReferences() const { return !!mProxy; }
+};
+
+inline
+nsSupportsWeakReference::~nsSupportsWeakReference()
+{
+ ClearWeakReferences();
+}
+
+#endif
diff --git a/xpcom/glue/nsXPTCUtils.h b/xpcom/glue/nsXPTCUtils.h
new file mode 100644
index 000000000..8922aca16
--- /dev/null
+++ b/xpcom/glue/nsXPTCUtils.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPTCUtils_h__
+#define nsXPTCUtils_h__
+
+#include "xptcall.h"
+#include "mozilla/MemoryReporting.h"
+
+/**
+ * A helper class that initializes an xptcall helper at construction
+ * and releases it at destruction.
+ */
+class nsAutoXPTCStub : protected nsIXPTCProxy
+{
+public:
+ nsISomeInterface* mXPTCStub;
+
+protected:
+ nsAutoXPTCStub() : mXPTCStub(nullptr) {}
+
+ nsresult
+ InitStub(const nsIID& aIID)
+ {
+ return NS_GetXPTCallStub(aIID, this, &mXPTCStub);
+ }
+
+ ~nsAutoXPTCStub()
+ {
+ if (mXPTCStub) {
+ NS_DestroyXPTCallStub(mXPTCStub);
+ }
+ }
+
+ size_t
+ SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mXPTCStub ? NS_SizeOfIncludingThisXPTCallStub(mXPTCStub, aMallocSizeOf) : 0;
+ }
+};
+
+#endif // nsXPTCUtils_h__
diff --git a/xpcom/glue/objs.mozbuild b/xpcom/glue/objs.mozbuild
new file mode 100644
index 000000000..8161e1ebc
--- /dev/null
+++ b/xpcom/glue/objs.mozbuild
@@ -0,0 +1,48 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+xpcom_glue_src_lcppsrcs = [
+ 'AppData.cpp',
+ 'FileUtils.cpp',
+ 'nsArrayEnumerator.cpp',
+ 'nsArrayUtils.cpp',
+ 'nsCategoryCache.cpp',
+ 'nsClassInfoImpl.cpp',
+ 'nsCOMArray.cpp',
+ 'nsComponentManagerUtils.cpp',
+ 'nsCOMPtr.cpp',
+ 'nsCRTGlue.cpp',
+ 'nsCycleCollectionParticipant.cpp',
+ 'nsDeque.cpp',
+ 'nsEnumeratorUtils.cpp',
+ 'nsID.cpp',
+ 'nsIInterfaceRequestorUtils.cpp',
+ 'nsINIParser.cpp',
+ 'nsISupportsImpl.cpp',
+ 'nsMemory.cpp',
+ 'nsQuickSort.cpp',
+ 'nsTArray.cpp',
+ 'nsThreadUtils.cpp',
+ 'nsTObserverArray.cpp',
+ 'nsVersionComparator.cpp',
+ 'nsWeakReference.cpp',
+ 'PLDHashTable.cpp',
+]
+
+xpcom_glue_src_cppsrcs = [
+ '/xpcom/glue/%s' % s for s in xpcom_glue_src_lcppsrcs
+]
+
+xpcom_gluens_src_lcppsrcs = [
+ 'BlockingResourceBase.cpp',
+ 'GenericFactory.cpp',
+ 'nsProxyRelease.cpp',
+ 'nsTextFormatter.cpp',
+]
+
+xpcom_gluens_src_cppsrcs = [
+ '/xpcom/glue/%s' % s for s in xpcom_gluens_src_lcppsrcs
+]
diff --git a/xpcom/glue/standalone/moz.build b/xpcom/glue/standalone/moz.build
new file mode 100644
index 000000000..fd91fa0bb
--- /dev/null
+++ b/xpcom/glue/standalone/moz.build
@@ -0,0 +1,58 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# On win we build two glue libs - glue linked to crt dlls here and in staticruntime we build
+# a statically linked glue lib.
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DIRS += ['staticruntime']
+
+include('../objs.mozbuild')
+
+SOURCES += xpcom_glue_src_cppsrcs
+
+SOURCES += [
+ '../nsStringAPI.cpp',
+ 'nsXPCOMGlue.cpp',
+]
+
+Library('xpcomglue')
+
+EXPORTS += [
+ 'nsXPCOMGlue.h',
+]
+
+SDK_LIBRARY = True
+
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+DEFINES['XPCOM_GLUE'] = True
+
+LOCAL_INCLUDES += [
+ '../../build',
+ '../../threads',
+]
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DISABLE_STL_WRAPPING = True
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ CXXFLAGS += CONFIG['GLIB_CFLAGS']
diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp
new file mode 100644
index 000000000..b657d7050
--- /dev/null
+++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
@@ -0,0 +1,927 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsXPCOMGlue.h"
+
+#include "nspr.h"
+#include "nsDebug.h"
+#include "nsIServiceManager.h"
+#include "nsXPCOMPrivate.h"
+#include "nsCOMPtr.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mozilla/FileUtils.h"
+#include "mozilla/Sprintf.h"
+
+using namespace mozilla;
+
+#define XPCOM_DEPENDENT_LIBS_LIST "dependentlibs.list"
+
+static XPCOMFunctions xpcomFunctions;
+static bool do_preload = false;
+
+#if defined(XP_WIN)
+#define READ_TEXTMODE L"rt"
+#else
+#define READ_TEXTMODE "r"
+#endif
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include <mbstring.h>
+
+typedef HINSTANCE LibHandleType;
+
+static LibHandleType
+GetLibHandle(pathstr_t aDependentLib)
+{
+ LibHandleType libHandle =
+ LoadLibraryExW(aDependentLib, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
+
+#ifdef DEBUG
+ if (!libHandle) {
+ DWORD err = GetLastError();
+ LPWSTR lpMsgBuf;
+ FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&lpMsgBuf,
+ 0,
+ nullptr
+ );
+ wprintf(L"Error loading %ls: %s\n", aDependentLib, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ }
+#endif
+
+ return libHandle;
+}
+
+static NSFuncPtr
+GetSymbol(LibHandleType aLibHandle, const char* aSymbol)
+{
+ return (NSFuncPtr)GetProcAddress(aLibHandle, aSymbol);
+}
+
+static void
+CloseLibHandle(LibHandleType aLibHandle)
+{
+ FreeLibrary(aLibHandle);
+}
+
+#else
+#include <dlfcn.h>
+
+#if defined(MOZ_LINKER) && !defined(ANDROID)
+extern "C" {
+NS_HIDDEN __typeof(dlopen) __wrap_dlopen;
+NS_HIDDEN __typeof(dlsym) __wrap_dlsym;
+NS_HIDDEN __typeof(dlclose) __wrap_dlclose;
+}
+
+#define dlopen __wrap_dlopen
+#define dlsym __wrap_dlsym
+#define dlclose __wrap_dlclose
+#endif
+
+typedef void* LibHandleType;
+
+static LibHandleType
+GetLibHandle(pathstr_t aDependentLib)
+{
+ LibHandleType libHandle = dlopen(aDependentLib,
+ RTLD_GLOBAL | RTLD_LAZY
+#ifdef XP_MACOSX
+ | RTLD_FIRST
+#endif
+ );
+ if (!libHandle) {
+ fprintf(stderr, "XPCOMGlueLoad error for file %s:\n%s\n", aDependentLib,
+ dlerror());
+ }
+ return libHandle;
+}
+
+static NSFuncPtr
+GetSymbol(LibHandleType aLibHandle, const char* aSymbol)
+{
+ return (NSFuncPtr)dlsym(aLibHandle, aSymbol);
+}
+
+static void
+CloseLibHandle(LibHandleType aLibHandle)
+{
+ dlclose(aLibHandle);
+}
+#endif
+
+struct DependentLib
+{
+ LibHandleType libHandle;
+ DependentLib* next;
+};
+
+static DependentLib* sTop;
+
+static void
+AppendDependentLib(LibHandleType aLibHandle)
+{
+ DependentLib* d = new DependentLib;
+ if (!d) {
+ return;
+ }
+
+ d->next = sTop;
+ d->libHandle = aLibHandle;
+
+ sTop = d;
+}
+
+static bool
+ReadDependentCB(pathstr_t aDependentLib, bool aDoPreload)
+{
+ if (aDoPreload) {
+ ReadAheadLib(aDependentLib);
+ }
+ LibHandleType libHandle = GetLibHandle(aDependentLib);
+ if (libHandle) {
+ AppendDependentLib(libHandle);
+ }
+
+ return libHandle;
+}
+
+#ifdef XP_WIN
+static bool
+ReadDependentCB(const char* aDependentLib, bool do_preload)
+{
+ wchar_t wideDependentLib[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib, MAX_PATH);
+ return ReadDependentCB(wideDependentLib, do_preload);
+}
+
+inline FILE*
+TS_tfopen(const char* path, const wchar_t* mode)
+{
+ wchar_t wPath[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH);
+ return _wfopen(wPath, mode);
+}
+#else
+inline FILE*
+TS_tfopen(const char* aPath, const char* aMode)
+{
+ return fopen(aPath, aMode);
+}
+#endif
+
+/* RAII wrapper for FILE descriptors */
+struct ScopedCloseFileTraits
+{
+ typedef FILE* type;
+ static type empty() { return nullptr; }
+ static void release(type aFile)
+ {
+ if (aFile) {
+ fclose(aFile);
+ }
+ }
+};
+typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile;
+
+static void
+XPCOMGlueUnload()
+{
+ while (sTop) {
+ CloseLibHandle(sTop->libHandle);
+
+ DependentLib* temp = sTop;
+ sTop = sTop->next;
+
+ delete temp;
+ }
+}
+
+#if defined(XP_WIN)
+// like strpbrk but finds the *last* char, not the first
+static const char*
+ns_strrpbrk(const char* string, const char* strCharSet)
+{
+ const char* found = nullptr;
+ for (; *string; ++string) {
+ for (const char* search = strCharSet; *search; ++search) {
+ if (*search == *string) {
+ found = string;
+ // Since we're looking for the last char, we save "found"
+ // until we're at the end of the string.
+ }
+ }
+ }
+
+ return found;
+}
+#endif
+
+static GetFrozenFunctionsFunc
+XPCOMGlueLoad(const char* aXPCOMFile)
+{
+ char xpcomDir[MAXPATHLEN];
+#ifdef XP_WIN
+ const char* lastSlash = ns_strrpbrk(aXPCOMFile, "/\\");
+#elif XP_MACOSX
+ // On OSX, the dependentlibs.list file lives under Contents/Resources.
+ // However, the actual libraries listed in dependentlibs.list live under
+ // Contents/MacOS. We want to read the list from Contents/Resources, then
+ // load the libraries from Contents/MacOS.
+ const char *tempSlash = strrchr(aXPCOMFile, '/');
+ size_t tempLen = size_t(tempSlash - aXPCOMFile);
+ if (tempLen > MAXPATHLEN) {
+ return nullptr;
+ }
+ char tempBuffer[MAXPATHLEN];
+ memcpy(tempBuffer, aXPCOMFile, tempLen);
+ tempBuffer[tempLen] = '\0';
+ const char *slash = strrchr(tempBuffer, '/');
+ tempLen = size_t(slash - tempBuffer);
+ const char *lastSlash = aXPCOMFile + tempLen;
+#else
+ const char* lastSlash = strrchr(aXPCOMFile, '/');
+#endif
+ char* cursor;
+ if (lastSlash) {
+ size_t len = size_t(lastSlash - aXPCOMFile);
+
+ if (len > MAXPATHLEN - sizeof(XPCOM_FILE_PATH_SEPARATOR
+#ifdef XP_MACOSX
+ "Resources"
+ XPCOM_FILE_PATH_SEPARATOR
+#endif
+ XPCOM_DEPENDENT_LIBS_LIST)) {
+ return nullptr;
+ }
+ memcpy(xpcomDir, aXPCOMFile, len);
+ strcpy(xpcomDir + len, XPCOM_FILE_PATH_SEPARATOR
+#ifdef XP_MACOSX
+ "Resources"
+ XPCOM_FILE_PATH_SEPARATOR
+#endif
+ XPCOM_DEPENDENT_LIBS_LIST);
+ cursor = xpcomDir + len + 1;
+ } else {
+ strcpy(xpcomDir, XPCOM_DEPENDENT_LIBS_LIST);
+ cursor = xpcomDir;
+ }
+
+ if (getenv("MOZ_RUN_GTEST")) {
+ strcat(xpcomDir, ".gtest");
+ }
+
+ ScopedCloseFile flist;
+ flist = TS_tfopen(xpcomDir, READ_TEXTMODE);
+ if (!flist) {
+ return nullptr;
+ }
+
+#ifdef XP_MACOSX
+ tempLen = size_t(cursor - xpcomDir);
+ if (tempLen > MAXPATHLEN - sizeof("MacOS" XPCOM_FILE_PATH_SEPARATOR) - 1) {
+ return nullptr;
+ }
+ strcpy(cursor, "MacOS" XPCOM_FILE_PATH_SEPARATOR);
+ cursor += strlen(cursor);
+#endif
+ *cursor = '\0';
+
+ char buffer[MAXPATHLEN];
+
+ while (fgets(buffer, sizeof(buffer), flist)) {
+ int l = strlen(buffer);
+
+ // ignore empty lines and comments
+ if (l == 0 || *buffer == '#') {
+ continue;
+ }
+
+ // cut the trailing newline, if present
+ if (buffer[l - 1] == '\n') {
+ buffer[l - 1] = '\0';
+ }
+
+ if (l + size_t(cursor - xpcomDir) > MAXPATHLEN) {
+ return nullptr;
+ }
+
+ strcpy(cursor, buffer);
+ if (!ReadDependentCB(xpcomDir, do_preload)) {
+ XPCOMGlueUnload();
+ return nullptr;
+ }
+ }
+
+ GetFrozenFunctionsFunc sym =
+ (GetFrozenFunctionsFunc)GetSymbol(sTop->libHandle,
+ "NS_GetFrozenFunctions");
+
+ if (!sym) { // No symbol found.
+ XPCOMGlueUnload();
+ return nullptr;
+ }
+
+ return sym;
+}
+
+nsresult
+XPCOMGlueLoadXULFunctions(const nsDynamicFunctionLoad* aSymbols)
+{
+ // We don't null-check sXULLibHandle because this might work even
+ // if it is null (same as RTLD_DEFAULT)
+
+ nsresult rv = NS_OK;
+ while (aSymbols->functionName) {
+ char buffer[512];
+ SprintfLiteral(buffer, "%s", aSymbols->functionName);
+
+ *aSymbols->function = (NSFuncPtr)GetSymbol(sTop->libHandle, buffer);
+ if (!*aSymbols->function) {
+ rv = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+ }
+
+ ++aSymbols;
+ }
+ return rv;
+}
+
+void
+XPCOMGlueEnablePreload()
+{
+ do_preload = true;
+}
+
+#if defined(MOZ_WIDGET_GTK) && (defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__))
+#define MOZ_GSLICE_INIT
+#endif
+
+#ifdef MOZ_GSLICE_INIT
+#include <glib.h>
+
+class GSliceInit {
+public:
+ GSliceInit() {
+ mHadGSlice = bool(getenv("G_SLICE"));
+ if (!mHadGSlice) {
+ // Disable the slice allocator, since jemalloc already uses similar layout
+ // algorithms, and using a sub-allocator tends to increase fragmentation.
+ // This must be done before g_thread_init() is called.
+ // glib >= 2.36 initializes g_slice as a side effect of its various static
+ // initializers, so this needs to happen before glib is loaded, which is
+ // this is hooked in XPCOMGlueStartup before libxul is loaded. This
+ // relies on the main executable not depending on glib.
+ setenv("G_SLICE", "always-malloc", 1);
+ }
+ }
+
+ ~GSliceInit() {
+#if MOZ_WIDGET_GTK == 2
+ if (sTop) {
+ auto XRE_GlibInit = (void (*)(void)) GetSymbol(sTop->libHandle,
+ "XRE_GlibInit");
+ // Initialize glib enough for G_SLICE to have an effect before it is unset.
+ // unset.
+ XRE_GlibInit();
+ }
+#endif
+ if (!mHadGSlice) {
+ unsetenv("G_SLICE");
+ }
+ }
+
+private:
+ bool mHadGSlice;
+};
+#endif
+
+nsresult
+XPCOMGlueStartup(const char* aXPCOMFile)
+{
+#ifdef MOZ_GSLICE_INIT
+ GSliceInit gSliceInit;
+#endif
+ xpcomFunctions.version = XPCOM_GLUE_VERSION;
+ xpcomFunctions.size = sizeof(XPCOMFunctions);
+
+ if (!aXPCOMFile) {
+ aXPCOMFile = XPCOM_DLL;
+ }
+
+ GetFrozenFunctionsFunc func = XPCOMGlueLoad(aXPCOMFile);
+ if (!func) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv = (*func)(&xpcomFunctions, nullptr);
+ if (NS_FAILED(rv)) {
+ XPCOMGlueUnload();
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+XPCOM_API(nsresult)
+NS_InitXPCOM2(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider)
+{
+ if (!xpcomFunctions.init) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.init(aResult, aBinDirectory, aAppFileLocationProvider);
+}
+
+XPCOM_API(nsresult)
+NS_ShutdownXPCOM(nsIServiceManager* aServMgr)
+{
+ if (!xpcomFunctions.shutdown) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.shutdown(aServMgr);
+}
+
+XPCOM_API(nsresult)
+NS_GetServiceManager(nsIServiceManager** aResult)
+{
+ if (!xpcomFunctions.getServiceManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getServiceManager(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetComponentManager(nsIComponentManager** aResult)
+{
+ if (!xpcomFunctions.getComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getComponentManager(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetComponentRegistrar(nsIComponentRegistrar** aResult)
+{
+ if (!xpcomFunctions.getComponentRegistrar) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getComponentRegistrar(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetMemoryManager(nsIMemory** aResult)
+{
+ if (!xpcomFunctions.getMemoryManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getMemoryManager(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks, nsIFile** aResult)
+{
+ if (!xpcomFunctions.newLocalFile) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.newLocalFile(aPath, aFollowLinks, aResult);
+}
+
+XPCOM_API(nsresult)
+NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowLinks,
+ nsIFile** aResult)
+{
+ if (!xpcomFunctions.newNativeLocalFile) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.newNativeLocalFile(aPath, aFollowLinks, aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetDebug(nsIDebug2** aResult)
+{
+ if (!xpcomFunctions.getDebug) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getDebug(aResult);
+}
+
+
+XPCOM_API(nsresult)
+NS_StringContainerInit(nsStringContainer& aStr)
+{
+ if (!xpcomFunctions.stringContainerInit) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringContainerInit(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_StringContainerInit2(nsStringContainer& aStr, const char16_t* aData,
+ uint32_t aDataLength, uint32_t aFlags)
+{
+ if (!xpcomFunctions.stringContainerInit2) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringContainerInit2(aStr, aData, aDataLength, aFlags);
+}
+
+XPCOM_API(void)
+NS_StringContainerFinish(nsStringContainer& aStr)
+{
+ if (xpcomFunctions.stringContainerFinish) {
+ xpcomFunctions.stringContainerFinish(aStr);
+ }
+}
+
+XPCOM_API(uint32_t)
+NS_StringGetData(const nsAString& aStr, const char16_t** aBuf, bool* aTerm)
+{
+ if (!xpcomFunctions.stringGetData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.stringGetData(aStr, aBuf, aTerm);
+}
+
+XPCOM_API(uint32_t)
+NS_StringGetMutableData(nsAString& aStr, uint32_t aLen, char16_t** aBuf)
+{
+ if (!xpcomFunctions.stringGetMutableData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.stringGetMutableData(aStr, aLen, aBuf);
+}
+
+XPCOM_API(char16_t*)
+NS_StringCloneData(const nsAString& aStr)
+{
+ if (!xpcomFunctions.stringCloneData) {
+ return nullptr;
+ }
+ return xpcomFunctions.stringCloneData(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_StringSetData(nsAString& aStr, const char16_t* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.stringSetData) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return xpcomFunctions.stringSetData(aStr, aBuf, aCount);
+}
+
+XPCOM_API(nsresult)
+NS_StringSetDataRange(nsAString& aStr, uint32_t aCutStart, uint32_t aCutLength,
+ const char16_t* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.stringSetDataRange) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringSetDataRange(aStr, aCutStart, aCutLength, aBuf,
+ aCount);
+}
+
+XPCOM_API(nsresult)
+NS_StringCopy(nsAString& aDest, const nsAString& aSrc)
+{
+ if (!xpcomFunctions.stringCopy) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringCopy(aDest, aSrc);
+}
+
+XPCOM_API(void)
+NS_StringSetIsVoid(nsAString& aStr, const bool aIsVoid)
+{
+ if (xpcomFunctions.stringSetIsVoid) {
+ xpcomFunctions.stringSetIsVoid(aStr, aIsVoid);
+ }
+}
+
+XPCOM_API(bool)
+NS_StringGetIsVoid(const nsAString& aStr)
+{
+ if (!xpcomFunctions.stringGetIsVoid) {
+ return false;
+ }
+ return xpcomFunctions.stringGetIsVoid(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit(nsCStringContainer& aStr)
+{
+ if (!xpcomFunctions.cstringContainerInit) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringContainerInit(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit2(nsCStringContainer& aStr, const char* aData,
+ uint32_t aDataLength, uint32_t aFlags)
+{
+ if (!xpcomFunctions.cstringContainerInit2) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringContainerInit2(aStr, aData, aDataLength, aFlags);
+}
+
+XPCOM_API(void)
+NS_CStringContainerFinish(nsCStringContainer& aStr)
+{
+ if (xpcomFunctions.cstringContainerFinish) {
+ xpcomFunctions.cstringContainerFinish(aStr);
+ }
+}
+
+XPCOM_API(uint32_t)
+NS_CStringGetData(const nsACString& aStr, const char** aBuf, bool* aTerm)
+{
+ if (!xpcomFunctions.cstringGetData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.cstringGetData(aStr, aBuf, aTerm);
+}
+
+XPCOM_API(uint32_t)
+NS_CStringGetMutableData(nsACString& aStr, uint32_t aLen, char** aBuf)
+{
+ if (!xpcomFunctions.cstringGetMutableData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.cstringGetMutableData(aStr, aLen, aBuf);
+}
+
+XPCOM_API(char*)
+NS_CStringCloneData(const nsACString& aStr)
+{
+ if (!xpcomFunctions.cstringCloneData) {
+ return nullptr;
+ }
+ return xpcomFunctions.cstringCloneData(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetData(nsACString& aStr, const char* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.cstringSetData) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringSetData(aStr, aBuf, aCount);
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetDataRange(nsACString& aStr, uint32_t aCutStart,
+ uint32_t aCutLength, const char* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.cstringSetDataRange) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringSetDataRange(aStr, aCutStart, aCutLength, aBuf,
+ aCount);
+}
+
+XPCOM_API(nsresult)
+NS_CStringCopy(nsACString& aDest, const nsACString& aSrc)
+{
+ if (!xpcomFunctions.cstringCopy) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringCopy(aDest, aSrc);
+}
+
+XPCOM_API(void)
+NS_CStringSetIsVoid(nsACString& aStr, const bool aIsVoid)
+{
+ if (xpcomFunctions.cstringSetIsVoid) {
+ xpcomFunctions.cstringSetIsVoid(aStr, aIsVoid);
+ }
+}
+
+XPCOM_API(bool)
+NS_CStringGetIsVoid(const nsACString& aStr)
+{
+ if (!xpcomFunctions.cstringGetIsVoid) {
+ return false;
+ }
+ return xpcomFunctions.cstringGetIsVoid(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringToUTF16(const nsACString& aSrc, nsCStringEncoding aSrcEncoding,
+ nsAString& aDest)
+{
+ if (!xpcomFunctions.cstringToUTF16) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringToUTF16(aSrc, aSrcEncoding, aDest);
+}
+
+XPCOM_API(nsresult)
+NS_UTF16ToCString(const nsAString& aSrc, nsCStringEncoding aDestEncoding,
+ nsACString& aDest)
+{
+ if (!xpcomFunctions.utf16ToCString) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.utf16ToCString(aSrc, aDestEncoding, aDest);
+}
+
+XPCOM_API(void*)
+NS_Alloc(size_t aSize)
+{
+ if (!xpcomFunctions.allocFunc) {
+ return nullptr;
+ }
+ return xpcomFunctions.allocFunc(aSize);
+}
+
+XPCOM_API(void*)
+NS_Realloc(void* aPtr, size_t aSize)
+{
+ if (!xpcomFunctions.reallocFunc) {
+ return nullptr;
+ }
+ return xpcomFunctions.reallocFunc(aPtr, aSize);
+}
+
+XPCOM_API(void)
+NS_Free(void* aPtr)
+{
+ if (xpcomFunctions.freeFunc) {
+ xpcomFunctions.freeFunc(aPtr);
+ }
+}
+
+XPCOM_API(void)
+NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (xpcomFunctions.debugBreakFunc) {
+ xpcomFunctions.debugBreakFunc(aSeverity, aStr, aExpr, aFile, aLine);
+ }
+}
+
+XPCOM_API(void)
+NS_LogInit()
+{
+ if (xpcomFunctions.logInitFunc) {
+ xpcomFunctions.logInitFunc();
+ }
+}
+
+XPCOM_API(void)
+NS_LogTerm()
+{
+ if (xpcomFunctions.logTermFunc) {
+ xpcomFunctions.logTermFunc();
+ }
+}
+
+XPCOM_API(void)
+NS_LogAddRef(void* aPtr, nsrefcnt aNewRefCnt,
+ const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (xpcomFunctions.logAddRefFunc)
+ xpcomFunctions.logAddRefFunc(aPtr, aNewRefCnt,
+ aTypeName, aInstanceSize);
+}
+
+XPCOM_API(void)
+NS_LogRelease(void* aPtr, nsrefcnt aNewRefCnt, const char* aTypeName)
+{
+ if (xpcomFunctions.logReleaseFunc) {
+ xpcomFunctions.logReleaseFunc(aPtr, aNewRefCnt, aTypeName);
+ }
+}
+
+XPCOM_API(void)
+NS_LogCtor(void* aPtr, const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (xpcomFunctions.logCtorFunc) {
+ xpcomFunctions.logCtorFunc(aPtr, aTypeName, aInstanceSize);
+ }
+}
+
+XPCOM_API(void)
+NS_LogDtor(void* aPtr, const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (xpcomFunctions.logDtorFunc) {
+ xpcomFunctions.logDtorFunc(aPtr, aTypeName, aInstanceSize);
+ }
+}
+
+XPCOM_API(void)
+NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
+{
+ if (xpcomFunctions.logCOMPtrAddRefFunc) {
+ xpcomFunctions.logCOMPtrAddRefFunc(aCOMPtr, aObject);
+ }
+}
+
+XPCOM_API(void)
+NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
+{
+ if (xpcomFunctions.logCOMPtrReleaseFunc) {
+ xpcomFunctions.logCOMPtrReleaseFunc(aCOMPtr, aObject);
+ }
+}
+
+XPCOM_API(nsresult)
+NS_GetXPTCallStub(REFNSIID aIID, nsIXPTCProxy* aOuter,
+ nsISomeInterface** aStub)
+{
+ if (!xpcomFunctions.getXPTCallStubFunc) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return xpcomFunctions.getXPTCallStubFunc(aIID, aOuter, aStub);
+}
+
+XPCOM_API(void)
+NS_DestroyXPTCallStub(nsISomeInterface* aStub)
+{
+ if (xpcomFunctions.destroyXPTCallStubFunc) {
+ xpcomFunctions.destroyXPTCallStubFunc(aStub);
+ }
+}
+
+XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* aThat, uint32_t aMethodIndex,
+ uint32_t aParamCount, nsXPTCVariant* aParams)
+{
+ if (!xpcomFunctions.invokeByIndexFunc) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return xpcomFunctions.invokeByIndexFunc(aThat, aMethodIndex,
+ aParamCount, aParams);
+}
+
+XPCOM_API(bool)
+NS_CycleCollectorSuspect(nsISupports* aObj)
+{
+ if (!xpcomFunctions.cycleSuspectFunc) {
+ return false;
+ }
+
+ return xpcomFunctions.cycleSuspectFunc(aObj);
+}
+
+XPCOM_API(bool)
+NS_CycleCollectorForget(nsISupports* aObj)
+{
+ if (!xpcomFunctions.cycleForgetFunc) {
+ return false;
+ }
+
+ return xpcomFunctions.cycleForgetFunc(aObj);
+}
+
+XPCOM_API(nsPurpleBufferEntry*)
+NS_CycleCollectorSuspect2(void* aObj, nsCycleCollectionParticipant* aCp)
+{
+ if (!xpcomFunctions.cycleSuspect2Func) {
+ return nullptr;
+ }
+
+ return xpcomFunctions.cycleSuspect2Func(aObj, aCp);
+}
+
+XPCOM_API(void)
+NS_CycleCollectorSuspect3(void* aObj, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete)
+{
+ if (xpcomFunctions.cycleSuspect3Func) {
+ xpcomFunctions.cycleSuspect3Func(aObj, aCp, aRefCnt, aShouldDelete);
+ }
+}
+
+XPCOM_API(bool)
+NS_CycleCollectorForget2(nsPurpleBufferEntry* aEntry)
+{
+ if (!xpcomFunctions.cycleForget2Func) {
+ return false;
+ }
+
+ return xpcomFunctions.cycleForget2Func(aEntry);
+}
diff --git a/xpcom/glue/standalone/nsXPCOMGlue.h b/xpcom/glue/standalone/nsXPCOMGlue.h
new file mode 100644
index 000000000..e23bfa498
--- /dev/null
+++ b/xpcom/glue/standalone/nsXPCOMGlue.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOMGlue_h__
+#define nsXPCOMGlue_h__
+
+#include "nscore.h"
+
+#ifdef XPCOM_GLUE
+
+/**
+ * The following functions are only available in the standalone glue.
+ */
+
+/**
+ * Enabled preloading of dynamically loaded libraries
+ */
+extern "C" NS_HIDDEN_(void) XPCOMGlueEnablePreload();
+
+/**
+ * Initialize the XPCOM glue by dynamically linking against the XPCOM
+ * shared library indicated by xpcomFile.
+ */
+extern "C" NS_HIDDEN_(nsresult) XPCOMGlueStartup(const char* aXPCOMFile);
+
+typedef void (*NSFuncPtr)();
+
+struct nsDynamicFunctionLoad
+{
+ const char* functionName;
+ NSFuncPtr* function;
+};
+
+/**
+ * Dynamically load functions from libxul.
+ *
+ * @throws NS_ERROR_NOT_INITIALIZED if XPCOMGlueStartup() was not called or
+ * if the libxul DLL was not found.
+ * @throws NS_ERROR_LOSS_OF_SIGNIFICANT_DATA if only some of the required
+ * functions were found.
+ */
+extern "C" NS_HIDDEN_(nsresult)
+XPCOMGlueLoadXULFunctions(const nsDynamicFunctionLoad* aSymbols);
+
+#endif // XPCOM_GLUE
+#endif // nsXPCOMGlue_h__
diff --git a/xpcom/glue/standalone/staticruntime/moz.build b/xpcom/glue/standalone/staticruntime/moz.build
new file mode 100644
index 000000000..735086ab0
--- /dev/null
+++ b/xpcom/glue/standalone/staticruntime/moz.build
@@ -0,0 +1,50 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include('../../objs.mozbuild')
+
+SOURCES += xpcom_glue_src_cppsrcs
+
+SOURCES += [
+ '../../nsStringAPI.cpp',
+ '../nsXPCOMGlue.cpp',
+]
+
+Library('xpcomglue_staticruntime')
+
+SDK_LIBRARY = True
+
+# create a static lib
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+DEFINES['XPCOM_GLUE'] = True
+
+LOCAL_INCLUDES += [
+ '../../../build',
+ '../../../threads',
+]
+
+# Statically link to the CRT on Windows
+USE_STATIC_LIBS = True
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DISABLE_STL_WRAPPING = True
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
diff --git a/xpcom/glue/staticruntime/moz.build b/xpcom/glue/staticruntime/moz.build
new file mode 100644
index 000000000..384bc6878
--- /dev/null
+++ b/xpcom/glue/staticruntime/moz.build
@@ -0,0 +1,48 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include('../objs.mozbuild')
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ '../GenericModule.cpp',
+ '../nsStringAPI.cpp',
+]
+
+Library('xpcomglue_staticruntime_s')
+
+SDK_LIBRARY = True
+
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+LOCAL_INCLUDES += [
+ '../../build',
+ '../../threads',
+]
+
+# Statically link to the CRT on Windows
+USE_STATIC_LIBS = True
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DISABLE_STL_WRAPPING = True
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
diff --git a/xpcom/glue/tests/gtest/TestArray.cpp b/xpcom/glue/tests/gtest/TestArray.cpp
new file mode 100644
index 000000000..72d28b4df
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestArray.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "gtest/gtest.h"
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+#include "nsISupportsArray.h"
+
+// {9e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IFOO_IID \
+ {0x9e70a320, 0xbe02, 0x11d1, \
+ {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+namespace TestArray {
+
+class IFoo : public nsISupports {
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+ NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
+
+class Foo final : public IFoo {
+public:
+
+ explicit Foo(int32_t aID);
+
+ // nsISupports implementation
+ NS_DECL_ISUPPORTS
+
+ // IFoo implementation
+ NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return mID; }
+
+ static int32_t gCount;
+
+ int32_t mID;
+
+private:
+ ~Foo();
+};
+
+int32_t Foo::gCount;
+
+Foo::Foo(int32_t aID)
+{
+ mID = aID;
+ ++gCount;
+}
+
+Foo::~Foo()
+{
+ --gCount;
+}
+
+NS_IMPL_ISUPPORTS(Foo, IFoo)
+
+void CheckArray(nsISupportsArray* aArray, int32_t aExpectedCount, int32_t aElementIDs[], int32_t aExpectedTotal)
+{
+ uint32_t cnt = 0;
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ aArray->Count(&cnt);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
+ int32_t count = cnt;
+ int32_t index;
+
+ EXPECT_EQ(Foo::gCount, aExpectedTotal);
+ EXPECT_EQ(count, aExpectedCount);
+
+ for (index = 0; (index < count) && (index < aExpectedCount); index++) {
+ nsCOMPtr<IFoo> foo = do_QueryElementAt(aArray, index);
+ EXPECT_EQ(foo->ID(), aElementIDs[index]);
+ }
+}
+
+void FillArray(nsISupportsArray* aArray, int32_t aCount)
+{
+ int32_t index;
+ for (index = 0; index < aCount; index++) {
+ nsCOMPtr<IFoo> foo = new Foo(index);
+ aArray->AppendElement(foo);
+ }
+}
+
+} // namespace TestArray
+
+using namespace TestArray;
+
+TEST(Array, main)
+{
+ nsISupportsArray* array;
+ nsresult rv;
+
+ if (NS_OK == (rv = NS_NewISupportsArray(&array))) {
+ FillArray(array, 10);
+ int32_t fillResult[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ CheckArray(array, 10, fillResult, 10);
+
+ // test insert
+ nsCOMPtr<IFoo> foo = do_QueryElementAt(array, 3);
+ array->InsertElementAt(foo, 5);
+ int32_t insertResult[11] = {0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9};
+ CheckArray(array, 11, insertResult, 10);
+ array->InsertElementAt(foo, 0);
+ int32_t insertResult2[12] = {3, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9};
+ CheckArray(array, 12, insertResult2, 10);
+ array->AppendElement(foo);
+ int32_t appendResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9, 3};
+ CheckArray(array, 13, appendResult, 10);
+
+
+ // test IndexOf
+ int32_t expectedIndex = 0;
+ int32_t index = array->IndexOf(foo);
+ EXPECT_EQ(index, expectedIndex);
+
+ // test ReplaceElementAt
+ array->ReplaceElementAt(foo, 8);
+ int32_t replaceResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
+ CheckArray(array, 13, replaceResult, 9);
+
+ // test RemoveElementAt, RemoveElement
+ array->RemoveElementAt(0);
+ int32_t removeResult[12] = {0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
+ CheckArray(array, 12, removeResult, 9);
+ array->RemoveElementAt(7);
+ int32_t removeResult2[11] = {0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 3};
+ CheckArray(array, 11, removeResult2, 9);
+ array->RemoveElement(foo);
+ int32_t removeResult3[10] = {0, 1, 2, 4, 3, 5, 7, 8, 9, 3};
+ CheckArray(array, 10, removeResult3, 9);
+
+ foo = nullptr;
+
+ // test clear
+ array->Clear();
+ FillArray(array, 4);
+ CheckArray(array, 4, fillResult, 4);
+
+ // test delete
+ NS_RELEASE(array);
+ }
+}
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
diff --git a/xpcom/glue/tests/gtest/TestFileUtils.cpp b/xpcom/glue/tests/gtest/TestFileUtils.cpp
new file mode 100644
index 000000000..55106c6c5
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestFileUtils.cpp
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Test ReadSysFile() */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "FileUtils.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+
+#ifdef ReadSysFile_PRESENT
+
+/**
+ * Create a file with the specified contents.
+ */
+static bool
+WriteFile(
+ const char* aFilename,
+ const void* aContents,
+ size_t aContentsLen)
+{
+ int fd;
+ ssize_t ret;
+ size_t offt;
+
+ fd = MOZ_TEMP_FAILURE_RETRY(
+ open(aFilename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ fprintf(stderr, "open(): %s: %s\n", aFilename, strerror(errno));
+ return false;
+ }
+
+ offt = 0;
+ do {
+ ret = MOZ_TEMP_FAILURE_RETRY(
+ write(fd, (char*)aContents + offt, aContentsLen - offt));
+ if (ret == -1) {
+ fprintf(stderr, "write(): %s: %s\n", aFilename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ offt += ret;
+ } while (offt < aContentsLen);
+
+ ret = MOZ_TEMP_FAILURE_RETRY(close(fd));
+ if (ret == -1) {
+ fprintf(stderr, "close(): %s: %s\n", aFilename, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+TEST(ReadSysFile, Nonexistent) {
+ bool ret;
+ int errno_saved;
+
+ ret = ReadSysFile("/nonexistent", nullptr, 0);
+ errno_saved = errno;
+
+ ASSERT_FALSE(ret);
+ ASSERT_EQ(errno_saved, ENOENT);
+}
+
+TEST(ReadSysFile, Main) {
+ /* Use a different file name for each test since different tests could be
+ executed concurrently. */
+ static const char* fn = "TestReadSysFileMain";
+ /* If we have a file which contains "abcd" and we read it with ReadSysFile(),
+ providing a buffer of size 10 bytes, we would expect 5 bytes to be written
+ to that buffer: "abcd\0". */
+ struct
+ {
+ /* input (file contents), e.g. "abcd" */
+ const char* input;
+ /* pretended output buffer size, e.g. 10; the actual buffer is larger
+ and we check if anything was written past the end of the allowed length */
+ size_t output_size;
+ /* expected number of bytes written to the output buffer, including the
+ terminating '\0', e.g. 5 */
+ size_t output_len;
+ /* expected output buffer contents, e.g. "abcd\0", the first output_len
+ bytes of the output buffer should match the first 'output_len' bytes from
+ 'output', the rest of the output buffer should be untouched. */
+ const char* output;
+ } tests[] = {
+ /* No new lines */
+ {"", 0, 0, ""},
+ {"", 1, 1, "\0"}, /* \0 is redundant, but we write it for clarity */
+ {"", 9, 1, "\0"},
+
+ {"a", 0, 0, ""},
+ {"a", 1, 1, "\0"},
+ {"a", 2, 2, "a\0"},
+ {"a", 9, 2, "a\0"},
+
+ {"abcd", 0, 0, ""},
+ {"abcd", 1, 1, "\0"},
+ {"abcd", 2, 2, "a\0"},
+ {"abcd", 3, 3, "ab\0"},
+ {"abcd", 4, 4, "abc\0"},
+ {"abcd", 5, 5, "abcd\0"},
+ {"abcd", 9, 5, "abcd\0"},
+
+ /* A single trailing new line */
+ {"\n", 0, 0, ""},
+ {"\n", 1, 1, "\0"},
+ {"\n", 2, 1, "\0"},
+ {"\n", 9, 1, "\0"},
+
+ {"a\n", 0, 0, ""},
+ {"a\n", 1, 1, "\0"},
+ {"a\n", 2, 2, "a\0"},
+ {"a\n", 3, 2, "a\0"},
+ {"a\n", 9, 2, "a\0"},
+
+ {"abcd\n", 0, 0, ""},
+ {"abcd\n", 1, 1, "\0"},
+ {"abcd\n", 2, 2, "a\0"},
+ {"abcd\n", 3, 3, "ab\0"},
+ {"abcd\n", 4, 4, "abc\0"},
+ {"abcd\n", 5, 5, "abcd\0"},
+ {"abcd\n", 6, 5, "abcd\0"},
+ {"abcd\n", 9, 5, "abcd\0"},
+
+ /* Multiple trailing new lines */
+ {"\n\n", 0, 0, ""},
+ {"\n\n", 1, 1, "\0"},
+ {"\n\n", 2, 2, "\n\0"},
+ {"\n\n", 3, 2, "\n\0"},
+ {"\n\n", 9, 2, "\n\0"},
+
+ {"a\n\n", 0, 0, ""},
+ {"a\n\n", 1, 1, "\0"},
+ {"a\n\n", 2, 2, "a\0"},
+ {"a\n\n", 3, 3, "a\n\0"},
+ {"a\n\n", 4, 3, "a\n\0"},
+ {"a\n\n", 9, 3, "a\n\0"},
+
+ {"abcd\n\n", 0, 0, ""},
+ {"abcd\n\n", 1, 1, "\0"},
+ {"abcd\n\n", 2, 2, "a\0"},
+ {"abcd\n\n", 3, 3, "ab\0"},
+ {"abcd\n\n", 4, 4, "abc\0"},
+ {"abcd\n\n", 5, 5, "abcd\0"},
+ {"abcd\n\n", 6, 6, "abcd\n\0"},
+ {"abcd\n\n", 7, 6, "abcd\n\0"},
+ {"abcd\n\n", 9, 6, "abcd\n\0"},
+
+ /* New line in the middle */
+ {"ab\ncd", 9, 6, "ab\ncd\0"},
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+ /* Leave the file to exist if some of the assertions fail. */
+
+ char buf[128];
+ static const char unmodified = 'X';
+
+ memset(buf, unmodified, sizeof(buf));
+
+ ASSERT_TRUE(ReadSysFile(fn, buf, tests[i].output_size));
+
+ if (tests[i].output_size == 0) {
+ /* The buffer must be unmodified. We check only the first byte. */
+ ASSERT_EQ(unmodified, buf[0]);
+ } else {
+ ASSERT_EQ(tests[i].output_len, strlen(buf) + 1);
+ ASSERT_STREQ(tests[i].output, buf);
+ /* Check that the first byte after the trailing '\0' has not been
+ modified. */
+ ASSERT_EQ(unmodified, buf[tests[i].output_len]);
+ }
+ }
+
+ unlink(fn);
+}
+
+TEST(ReadSysFile, Int) {
+ static const char* fn = "TestReadSysFileInt";
+ struct
+ {
+ /* input (file contents), e.g. "5" */
+ const char* input;
+ /* expected return value, if false, then the output is not checked */
+ bool ret;
+ /* expected result */
+ int output;
+ } tests[] = {
+ {"0", true, 0},
+ {"00", true, 0},
+ {"1", true, 1},
+ {"5", true, 5},
+ {"55", true, 55},
+
+ {" 123", true, 123},
+ {"123 ", true, 123},
+ {" 123 ", true, 123},
+ {"123\n", true, 123},
+
+ {"", false, 0},
+ {" ", false, 0},
+ {"a", false, 0},
+
+ {"-1", true, -1},
+ {" -456 ", true, -456},
+ {" -78.9 ", true, -78},
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+ /* Leave the file to exist if some of the assertions fail. */
+
+ bool ret;
+ int output = 424242;
+
+ ret = ReadSysFile(fn, &output);
+
+ ASSERT_EQ(tests[i].ret, ret);
+
+ if (ret) {
+ ASSERT_EQ(tests[i].output, output);
+ }
+ }
+
+ unlink(fn);
+}
+
+TEST(ReadSysFile, Bool) {
+ static const char* fn = "TestReadSysFileBool";
+ struct
+ {
+ /* input (file contents), e.g. "1" */
+ const char* input;
+ /* expected return value */
+ bool ret;
+ /* expected result */
+ bool output;
+ } tests[] = {
+ {"0", true, false},
+ {"00", true, false},
+ {"1", true, true},
+ {"5", true, true},
+ {"23", true, true},
+ {"-1", true, true},
+
+ {"", false, true /* unused */},
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+ /* Leave the file to exist if some of the assertions fail. */
+
+ bool ret;
+ bool output;
+
+ ret = ReadSysFile(fn, &output);
+
+ ASSERT_EQ(tests[i].ret, ret);
+
+ if (ret) {
+ ASSERT_EQ(tests[i].output, output);
+ }
+ }
+
+ unlink(fn);
+}
+
+#endif /* ReadSysFile_PRESENT */
+
+} // namespace mozilla
diff --git a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
new file mode 100644
index 000000000..5bf10ab05
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Tests that generational garbage collection post-barriers are correctly
+ * implemented for nsTArrays that contain JavaScript Values.
+ */
+
+#include "jsapi.h"
+#include "nsTArray.h"
+
+#include "gtest/gtest.h"
+
+#include "js/TracingAPI.h"
+#include "js/HeapAPI.h"
+
+#include "mozilla/CycleCollectedJSContext.h"
+
+using namespace JS;
+using namespace mozilla;
+
+template<class ArrayT>
+static void
+TraceArray(JSTracer* trc, void* data)
+{
+ ArrayT* array = static_cast<ArrayT *>(data);
+ for (unsigned i = 0; i < array->Length(); ++i)
+ JS::TraceEdge(trc, &array->ElementAt(i), "array-element");
+}
+
+/*
+ * Use arrays with initial size much smaller than the final number of elements
+ * to test that moving Heap<T> elements works correctly.
+ */
+const size_t ElementCount = 100;
+const size_t InitialElements = ElementCount / 10;
+
+template<class ArrayT>
+static void
+RunTest(JSContext* cx, ArrayT* array)
+{
+ JS_GC(cx);
+
+ ASSERT_TRUE(array != nullptr);
+ JS_AddExtraGCRootsTracer(cx, TraceArray<ArrayT>, array);
+
+ /*
+ * Create the array and fill it with new JS objects. With GGC these will be
+ * allocated in the nursery.
+ */
+ RootedValue value(cx);
+ const char* property = "foo";
+ for (size_t i = 0; i < ElementCount; ++i) {
+ RootedObject obj(cx, JS_NewPlainObject(cx));
+ ASSERT_FALSE(JS::ObjectIsTenured(obj));
+ value = Int32Value(i);
+ ASSERT_TRUE(JS_SetProperty(cx, obj, property, value));
+ ASSERT_TRUE(array->AppendElement(obj, fallible));
+ }
+
+ /*
+ * If postbarriers are not working, we will crash here when we try to mark
+ * objects that have been moved to the tenured heap.
+ */
+ JS_GC(cx);
+
+ /*
+ * Sanity check that our array contains what we expect.
+ */
+ for (size_t i = 0; i < ElementCount; ++i) {
+ RootedObject obj(cx, array->ElementAt(i));
+ ASSERT_TRUE(JS::ObjectIsTenured(obj));
+ ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value));
+ ASSERT_TRUE(value.isInt32());
+ ASSERT_EQ(static_cast<int32_t>(i), value.toInt32());
+ }
+
+ JS_RemoveExtraGCRootsTracer(cx, TraceArray<ArrayT>, array);
+}
+
+static void
+CreateGlobalAndRunTest(JSContext* cx)
+{
+ static const JSClassOps GlobalClassOps = {
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr,
+ JS_GlobalObjectTraceHook
+ };
+
+ static const JSClass GlobalClass = {
+ "global", JSCLASS_GLOBAL_FLAGS,
+ &GlobalClassOps
+ };
+
+ JS::CompartmentOptions options;
+ options.behaviors().setVersion(JSVERSION_LATEST);
+ JS::PersistentRootedObject global(cx);
+ global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options);
+ ASSERT_TRUE(global != nullptr);
+
+ JSCompartment *oldCompartment = JS_EnterCompartment(cx, global);
+
+ typedef Heap<JSObject*> ElementT;
+
+ {
+ nsTArray<ElementT>* array = new nsTArray<ElementT>(InitialElements);
+ RunTest(cx, array);
+ delete array;
+ }
+
+ {
+ FallibleTArray<ElementT>* array = new FallibleTArray<ElementT>(InitialElements);
+ RunTest(cx, array);
+ delete array;
+ }
+
+ {
+ AutoTArray<ElementT, InitialElements> array;
+ RunTest(cx, &array);
+ }
+
+ JS_LeaveCompartment(cx, oldCompartment);
+}
+
+TEST(GCPostBarriers, nsTArray) {
+ CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
+ ASSERT_TRUE(ccjscx != nullptr);
+ JSContext* cx = ccjscx->Context();
+ ASSERT_TRUE(cx != nullptr);
+
+ JS_BeginRequest(cx);
+
+ CreateGlobalAndRunTest(cx);
+
+ JS_EndRequest(cx);
+}
diff --git a/xpcom/glue/tests/gtest/TestNsDeque.cpp b/xpcom/glue/tests/gtest/TestNsDeque.cpp
new file mode 100644
index 000000000..b84e1b781
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestNsDeque.cpp
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsDeque.h"
+#include "nsCRT.h"
+#include <stdio.h>
+
+/**************************************************************
+ Now define the token deallocator class...
+ **************************************************************/
+namespace TestNsDeque {
+
+ class _Dealloc: public nsDequeFunctor
+ {
+ virtual void* operator()(void* aObject) {
+ return 0;
+ }
+ };
+
+ static bool VerifyContents(const nsDeque& aDeque, const int* aContents, size_t aLength)
+ {
+ for (size_t i=0; i<aLength; ++i) {
+ if (*(int*)aDeque.ObjectAt(i) != aContents[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ class Deallocator: public nsDequeFunctor
+ {
+ virtual void* operator()(void* aObject)
+ {
+ if (aObject)
+ {
+ // Set value to -1, to use in test function.
+ *((int*)aObject) = -1;
+ }
+
+ return nullptr;
+ }
+ };
+
+ class ForEachAdder: public nsDequeFunctor
+ {
+ virtual void* operator()(void* aObject)
+ {
+ if (aObject)
+ {
+ sum += *(int*)aObject;
+ }
+
+ return aObject;
+ }
+
+ private:
+ int sum = 0;
+
+ public:
+ int GetSum() { return sum; }
+
+ };
+}
+
+using namespace TestNsDeque;
+
+TEST(NsDeque, OriginalTest)
+{
+ const size_t size = 200;
+ int ints[size];
+ size_t i=0;
+ int temp;
+ nsDeque theDeque(new _Dealloc); //construct a simple one...
+
+ // ints = [0...199]
+ for (i=0;i<size;i++) { //initialize'em
+ ints[i]=static_cast<int>(i);
+ }
+ // queue = [0...69]
+ for (i=0;i<70;i++) {
+ theDeque.Push(&ints[i]);
+ temp=*(int*)theDeque.Peek();
+ EXPECT_EQ(static_cast<int>(i), temp) << "Verify end after push #1";
+ EXPECT_EQ(i + 1, theDeque.GetSize()) << "Verify size after push #1";
+ }
+
+ EXPECT_EQ(70u,theDeque.GetSize()) << "Verify overall size after pushes #1";
+
+ // queue = [0...14]
+ for (i=1;i<=55;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(70-static_cast<int>(i),temp) << "Verify end after pop # 1";
+ EXPECT_EQ(70u - i,theDeque.GetSize()) << "Verify size after pop # 1";
+ }
+ EXPECT_EQ(15u,theDeque.GetSize()) << "Verify overall size after pops";
+
+ // queue = [0...14,0...54]
+ for (i=0;i<55;i++) {
+ theDeque.Push(&ints[i]);
+ temp=*(int*)theDeque.Peek();
+ EXPECT_EQ(static_cast<int>(i),temp) << "Verify end after push #2";
+ EXPECT_EQ(i + 15u + 1,theDeque.GetSize()) << "Verify size after push # 2";
+ }
+ EXPECT_EQ(70u,theDeque.GetSize()) << "Verify size after end of all pushes #2";
+
+ // queue = [0...14,0...19]
+ for (i=1;i<=35;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(55-static_cast<int>(i),temp ) << "Verify end after pop # 2";
+ EXPECT_EQ(70u - i,theDeque.GetSize()) << "Verify size after pop #2";
+ }
+ EXPECT_EQ(35u,theDeque.GetSize()) << "Verify overall size after end of all pops #2";
+
+ // queue = [0...14,0...19,0...34]
+ for (i=0;i<35;i++) {
+ theDeque.Push(&ints[i]);
+ temp = *(int*)theDeque.Peek();
+ EXPECT_EQ(static_cast<int>(i),temp) << "Verify end after push # 3";
+ EXPECT_EQ(35u + 1u + i,theDeque.GetSize()) << "Verify size after push #3";
+ }
+
+ // queue = [0...14,0...19]
+ for (i=0;i<35;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(34 - static_cast<int>(i), temp) << "Verify end after pop # 3";
+ }
+
+ // queue = [0...14]
+ for (i=0;i<20;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(19 - static_cast<int>(i),temp) << "Verify end after pop # 4";
+ }
+
+ // queue = []
+ for (i=0;i<15;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(14 - static_cast<int>(i),temp) << "Verify end after pop # 5";
+ }
+
+ EXPECT_EQ(0u,theDeque.GetSize()) << "Deque should finish empty.";
+}
+
+TEST(NsDeque, OriginalFlaw)
+{
+ int ints[200];
+ int i=0;
+ int temp;
+ nsDeque d(new _Dealloc);
+ /**
+ * Test 1. Origin near end, semi full, call Peek().
+ * you start, mCapacity is 8
+ */
+ for (i=0; i<30; i++)
+ ints[i]=i;
+
+ for (i=0; i<6; i++) {
+ d.Push(&ints[i]);
+ temp = *(int*)d.Peek();
+ EXPECT_EQ(i, temp) << "OriginalFlaw push #1";
+ }
+ EXPECT_EQ(6u, d.GetSize()) << "OriginalFlaw size check #1";
+
+ for (i=0; i<4; i++) {
+ temp=*(int*)d.PopFront();
+ EXPECT_EQ(i, temp) << "PopFront test";
+ }
+ // d = [4,5]
+ EXPECT_EQ(2u, d.GetSize()) << "OriginalFlaw size check #2";
+
+ for (i=0; i<4; i++) {
+ d.Push(&ints[6 + i]);
+ }
+
+ // d = [4...9]
+ for (i=4; i<=9; i++) {
+ temp=*(int*)d.PopFront();
+ EXPECT_EQ(i, temp) << "OriginalFlaw empty check";
+ }
+}
+
+TEST(NsDeque, TestObjectAt)
+{
+ nsDeque d;
+ const int count = 10;
+ int ints[count];
+ for (int i=0; i<count; i++) {
+ ints[i] = i;
+ }
+
+ for (int i=0; i<6; i++) {
+ d.Push(&ints[i]);
+ }
+ // d = [0...5]
+ d.PopFront();
+ d.PopFront();
+
+ // d = [2..5]
+ for (size_t i=2; i<=5; i++) {
+ int t = *(int*)d.ObjectAt(i-2);
+ EXPECT_EQ(static_cast<int>(i),t) << "Verify ObjectAt()";
+ }
+}
+
+TEST(NsDeque, TestPushFront)
+{
+ // PushFront has some interesting corner cases, primarily we're interested in whether:
+ // - wrapping around works properly
+ // - growing works properly
+
+ nsDeque d;
+
+ const int kPoolSize = 10;
+ const size_t kMaxSizeBeforeGrowth = 8;
+
+ int pool[kPoolSize];
+ for (int i = 0; i < kPoolSize; i++) {
+ pool[i] = i;
+ }
+
+ for (size_t i = 0; i < kMaxSizeBeforeGrowth; i++) {
+ d.PushFront(pool + i);
+ }
+
+ EXPECT_EQ(kMaxSizeBeforeGrowth, d.GetSize()) << "verify size";
+
+ static const int t1[] = {7,6,5,4,3,2,1,0};
+ EXPECT_TRUE(VerifyContents(d, t1, kMaxSizeBeforeGrowth)) << "verify pushfront 1";
+
+ // Now push one more so it grows
+ d.PushFront(pool + kMaxSizeBeforeGrowth);
+ EXPECT_EQ(kMaxSizeBeforeGrowth + 1, d.GetSize()) << "verify size";
+
+ static const int t2[] = {8,7,6,5,4,3,2,1,0};
+ EXPECT_TRUE(VerifyContents(d, t2, kMaxSizeBeforeGrowth + 1)) << "verify pushfront 2";
+
+ // And one more so that it wraps again
+ d.PushFront(pool + kMaxSizeBeforeGrowth + 1);
+ EXPECT_EQ(kMaxSizeBeforeGrowth + 2, d.GetSize()) << "verify size";
+
+ static const int t3[] = {9,8,7,6,5,4,3,2,1,0};
+ EXPECT_TRUE(VerifyContents(d, t3, kMaxSizeBeforeGrowth + 2)) <<"verify pushfront 3";
+}
+
+void CheckIfQueueEmpty(nsDeque& d)
+{
+ EXPECT_EQ(0u, d.GetSize()) << "Size should be 0";
+ EXPECT_EQ(nullptr, d.Pop()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.PopFront()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.Peek()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.PeekFront()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.ObjectAt(0u)) << "Invalid operation should return nullptr";
+}
+
+TEST(NsDeque,TestEmpty)
+{
+ // Make sure nsDeque gives sane results if it's empty.
+ nsDeque d;
+ size_t numberOfEntries = 8;
+
+ CheckIfQueueEmpty(d);
+
+ // Fill it up and drain it.
+ for (size_t i = 0; i < numberOfEntries; i++) {
+ d.Push((void*)0xAA);
+ }
+
+ EXPECT_EQ(numberOfEntries, d.GetSize());
+
+ for (size_t i = 0; i < numberOfEntries; i++) {
+ (void)d.Pop();
+ }
+
+ // Now check it again.
+ CheckIfQueueEmpty(d);
+}
+
+TEST(NsDeque,TestEraseMethod)
+{
+ nsDeque d;
+ const size_t numberOfEntries = 8;
+
+ // Fill it up before calling Erase
+ for (size_t i = 0; i < numberOfEntries; i++) {
+ d.Push((void*)0xAA);
+ }
+
+ // Call Erase
+ d.Erase();
+
+ // Now check it again.
+ CheckIfQueueEmpty(d);
+}
+
+TEST(NsDeque,TestEraseShouldCallDeallocator)
+{
+ nsDeque d(new Deallocator());
+ const size_t NumTestValues = 8;
+
+ int* testArray[NumTestValues];
+ for (size_t i=0; i < NumTestValues; i++)
+ {
+ testArray[i] = new int();
+ *(testArray[i]) = i;
+ d.Push((void*)testArray[i]);
+ }
+
+ d.Erase();
+
+ // Now check it again.
+ CheckIfQueueEmpty(d);
+
+ for (size_t i=0; i < NumTestValues; i++)
+ {
+ EXPECT_EQ(-1, *(testArray[i])) << "Erase should call deallocator: " << *(testArray[i]);
+ }
+}
+
+TEST(NsDeque, TestForEach)
+{
+ nsDeque d(new Deallocator());
+ const size_t NumTestValues = 8;
+ int sum = 0;
+
+ int* testArray[NumTestValues];
+ for (size_t i=0; i < NumTestValues; i++)
+ {
+ testArray[i] = new int();
+ *(testArray[i]) = i;
+ sum += i;
+ d.Push((void*)testArray[i]);
+ }
+
+ ForEachAdder adder;
+ d.ForEach(adder);
+ EXPECT_EQ(sum, adder.GetSum()) << "For each should iterate over values";
+
+ d.Erase();
+}
diff --git a/xpcom/glue/tests/gtest/TestThreadUtils.cpp b/xpcom/glue/tests/gtest/TestThreadUtils.cpp
new file mode 100644
index 000000000..728bae612
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestThreadUtils.cpp
@@ -0,0 +1,937 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nsThreadUtils.h"
+#include "gtest/gtest.h"
+
+// {9e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IFOO_IID \
+ {0x9e70a320, 0xbe02, 0x11d1, \
+ {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+namespace TestThreadUtils {
+
+static bool gDebug = false;
+static int gAlive, gZombies;
+static int gAllConstructions, gConstructions, gCopyConstructions,
+ gMoveConstructions, gDestructions, gAssignments, gMoves;
+struct Spy
+{
+ static void ClearActions()
+ {
+ gAllConstructions = gConstructions = gCopyConstructions
+ = gMoveConstructions = gDestructions = gAssignments = gMoves = 0;
+ }
+ static void ClearAll()
+ {
+ ClearActions();
+ gAlive = 0;
+ }
+
+ explicit Spy(int aID) : mID(aID)
+ {
+ ++gAlive; ++gAllConstructions; ++gConstructions;
+ if (gDebug) { printf("Spy[%d@%p]()\n", mID, this); }
+ }
+ Spy(const Spy& o) : mID(o.mID)
+ {
+ ++gAlive; ++gAllConstructions; ++gCopyConstructions;
+ if (gDebug) { printf("Spy[%d@%p](&[%d@%p])\n", mID, this, o.mID, &o); }
+ }
+ Spy(Spy&& o) : mID(o.mID)
+ {
+ o.mID = -o.mID;
+ ++gZombies; ++gAllConstructions; ++gMoveConstructions;
+ if (gDebug) { printf("Spy[%d@%p](&&[%d->%d@%p])\n", mID, this, -o.mID, o.mID, &o); }
+ }
+ ~Spy()
+ {
+ if (mID >= 0) { --gAlive; } else { --gZombies; } ++gDestructions;
+ if (gDebug) { printf("~Spy[%d@%p]()\n", mID, this); }
+ mID = 0;
+ }
+ Spy& operator=(const Spy& o)
+ {
+ ++gAssignments;
+ if (gDebug) { printf("Spy[%d->%d@%p] = &[%d@%p]\n", mID, o.mID, this, o.mID, &o); }
+ mID = o.mID;
+ return *this;
+ };
+ Spy& operator=(Spy&& o)
+ {
+ --gAlive; ++gZombies;
+ ++gMoves;
+ if (gDebug) { printf("Spy[%d->%d@%p] = &&[%d->%d@%p]\n", mID, o.mID, this, o.mID, -o.mID, &o); }
+ mID = o.mID; o.mID = -o.mID;
+ return *this;
+ };
+
+ int mID; // ID given at construction, or negation if was moved from; 0 when destroyed.
+};
+
+struct ISpyWithISupports : public nsISupports
+{
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+ NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(ISpyWithISupports, NS_IFOO_IID)
+struct SpyWithISupports : public ISpyWithISupports, public Spy
+{
+private:
+ virtual ~SpyWithISupports() = default;
+public:
+ explicit SpyWithISupports(int aID) : Spy(aID) {};
+ NS_DECL_ISUPPORTS
+ NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return mID; }
+};
+NS_IMPL_ISUPPORTS(SpyWithISupports, ISpyWithISupports)
+
+
+class IThreadUtilsObject : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+ NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IThreadUtilsObject, NS_IFOO_IID)
+
+struct ThreadUtilsObject : public IThreadUtilsObject
+{
+ // nsISupports implementation
+ NS_DECL_ISUPPORTS
+
+ // IThreadUtilsObject implementation
+ NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return 0; }
+
+ int mCount; // Number of calls + arguments processed.
+ int mA0, mA1, mA2, mA3;
+ Spy mSpy; const Spy* mSpyPtr;
+ ThreadUtilsObject()
+ : mCount(0)
+ , mA0(0), mA1(0), mA2(0), mA3(0)
+ , mSpy(1), mSpyPtr(nullptr)
+ {}
+private:
+ virtual ~ThreadUtilsObject() = default;
+public:
+ void Test0() { mCount += 1; }
+ void Test1i(int a0) { mCount += 2; mA0 = a0; }
+ void Test2i(int a0, int a1) { mCount += 3; mA0 = a0; mA1 = a1; }
+ void Test3i(int a0, int a1, int a2)
+ {
+ mCount += 4; mA0 = a0; mA1 = a1; mA2 = a2;
+ }
+ void Test4i(int a0, int a1, int a2, int a3)
+ {
+ mCount += 5; mA0 = a0; mA1 = a1; mA2 = a2; mA3 = a3;
+ }
+ void Test1pi(int* ap)
+ {
+ mCount += 2; mA0 = ap ? *ap : -1;
+ }
+ void Test1pci(const int* ap)
+ {
+ mCount += 2; mA0 = ap ? *ap : -1;
+ }
+ void Test1ri(int& ar)
+ {
+ mCount += 2; mA0 = ar;
+ }
+ void Test1rri(int&& arr)
+ {
+ mCount += 2; mA0 = arr;
+ }
+ void Test1upi(mozilla::UniquePtr<int> aup)
+ {
+ mCount += 2; mA0 = aup ? *aup : -1;
+ }
+ void Test1rupi(mozilla::UniquePtr<int>& aup)
+ {
+ mCount += 2; mA0 = aup ? *aup : -1;
+ }
+ void Test1rrupi(mozilla::UniquePtr<int>&& aup)
+ {
+ mCount += 2; mA0 = aup ? *aup : -1;
+ }
+
+ void Test1s(Spy) { mCount += 2; }
+ void Test1ps(Spy*) { mCount += 2; }
+ void Test1rs(Spy&) { mCount += 2; }
+ void Test1rrs(Spy&&) { mCount += 2; }
+ void Test1ups(mozilla::UniquePtr<Spy>) { mCount += 2; }
+ void Test1rups(mozilla::UniquePtr<Spy>&) { mCount += 2; }
+ void Test1rrups(mozilla::UniquePtr<Spy>&&) { mCount += 2; }
+
+ // Possible parameter passing styles:
+ void TestByValue(Spy s)
+ {
+ if (gDebug) { printf("TestByValue(Spy[%d@%p])\n", s.mID, &s); }
+ mSpy = s;
+ };
+ void TestByConstLRef(const Spy& s)
+ {
+ if (gDebug) { printf("TestByConstLRef(Spy[%d@%p]&)\n", s.mID, &s); }
+ mSpy = s;
+ };
+ void TestByRRef(Spy&& s)
+ {
+ if (gDebug) { printf("TestByRRef(Spy[%d@%p]&&)\n", s.mID, &s); }
+ mSpy = mozilla::Move(s);
+ };
+ void TestByLRef(Spy& s)
+ {
+ if (gDebug) { printf("TestByLRef(Spy[%d@%p]&)\n", s.mID, &s); }
+ mSpy = s;
+ mSpyPtr = &s;
+ };
+ void TestByPointer(Spy* p)
+ {
+ if (p) {
+ if (gDebug) { printf("TestByPointer(&Spy[%d@%p])\n", p->mID, p); }
+ mSpy = *p;
+ } else {
+ if (gDebug) { printf("TestByPointer(nullptr)\n"); }
+ }
+ mSpyPtr = p;
+ };
+ void TestByPointerToConst(const Spy* p)
+ {
+ if (p) {
+ if (gDebug) { printf("TestByPointerToConst(&Spy[%d@%p])\n", p->mID, p); }
+ mSpy = *p;
+ } else {
+ if (gDebug) { printf("TestByPointerToConst(nullptr)\n"); }
+ }
+ mSpyPtr = p;
+ };
+};
+
+NS_IMPL_ISUPPORTS(ThreadUtilsObject, IThreadUtilsObject)
+
+class ThreadUtilsRefCountedFinal final
+{
+public:
+ ThreadUtilsRefCountedFinal() : m_refCount(0) {}
+ ~ThreadUtilsRefCountedFinal() {}
+ // 'AddRef' and 'Release' methods with different return types, to verify
+ // that the return type doesn't influence storage selection.
+ long AddRef(void) { return ++m_refCount; }
+ void Release(void) { --m_refCount; }
+private:
+ long m_refCount;
+};
+
+class ThreadUtilsRefCountedBase
+{
+public:
+ ThreadUtilsRefCountedBase() : m_refCount(0) {}
+ virtual ~ThreadUtilsRefCountedBase() {}
+ // 'AddRef' and 'Release' methods with different return types, to verify
+ // that the return type doesn't influence storage selection.
+ virtual void AddRef(void) { ++m_refCount; }
+ virtual MozExternalRefCountType Release(void) { return --m_refCount; }
+private:
+ MozExternalRefCountType m_refCount;
+};
+
+class ThreadUtilsRefCountedDerived
+ : public ThreadUtilsRefCountedBase
+{};
+
+class ThreadUtilsNonRefCounted
+{};
+
+} // namespace TestThreadUtils
+
+TEST(ThreadUtils, main)
+{
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ using namespace TestThreadUtils;
+
+ static_assert(!IsParameterStorageClass<int>::value,
+ "'int' should not be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByValue<int>>::value,
+ "StoreCopyPassByValue<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByConstLRef<int>>::value,
+ "StoreCopyPassByConstLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByLRef<int>>::value,
+ "StoreCopyPassByLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByRRef<int>>::value,
+ "StoreCopyPassByRRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreRefPassByLRef<int>>::value,
+ "StoreRefPassByLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreConstRefPassByConstLRef<int>>::value,
+ "StoreConstRefPassByConstLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StorensRefPtrPassByPtr<int>>::value,
+ "StorensRefPtrPassByPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StorePtrPassByPtr<int>>::value,
+ "StorePtrPassByPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreConstPtrPassByConstPtr<int>>::value,
+ "StoreConstPtrPassByConstPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByConstPtr<int>>::value,
+ "StoreCopyPassByConstPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByPtr<int>>::value,
+ "StoreCopyPassByPtr<int> should be recognized as Storage Class");
+
+ RefPtr<ThreadUtilsObject> rpt(new ThreadUtilsObject);
+ int count = 0;
+
+ // Test legacy functions.
+
+ nsCOMPtr<nsIRunnable> r1 =
+ NewRunnableMethod(rpt, &ThreadUtilsObject::Test0);
+ r1->Run();
+ EXPECT_EQ(count += 1, rpt->mCount);
+
+ r1 = NewRunnableMethod<int>(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 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test0);
+ r1->Run();
+ EXPECT_EQ(count += 1, rpt->mCount);
+
+ static_assert(
+ mozilla::IsSame< ::detail::ParameterStorage<int>::Type,
+ StoreCopyPassByValue<int>>::value,
+ "detail::ParameterStorage<int>::Type should be StoreCopyPassByValue<int>");
+ static_assert(
+ mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type,
+ StoreCopyPassByValue<int>>::value,
+ "detail::ParameterStorage<StoreCopyPassByValue<int>>::Type should be StoreCopyPassByValue<int>");
+
+ r1 = NewRunnableMethod<int>(rpt, &ThreadUtilsObject::Test1i, 12);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(12, rpt->mA0);
+
+ r1 = NewRunnableMethod<int, int>(
+ rpt, &ThreadUtilsObject::Test2i, 21, 22);
+ r1->Run();
+ EXPECT_EQ(count += 3, rpt->mCount);
+ EXPECT_EQ(21, rpt->mA0);
+ EXPECT_EQ(22, rpt->mA1);
+
+ r1 = NewRunnableMethod<int, int, int>(
+ rpt, &ThreadUtilsObject::Test3i, 31, 32, 33);
+ r1->Run();
+ EXPECT_EQ(count += 4, rpt->mCount);
+ EXPECT_EQ(31, rpt->mA0);
+ EXPECT_EQ(32, rpt->mA1);
+ EXPECT_EQ(33, rpt->mA2);
+
+ r1 = NewRunnableMethod<int, int, int, int>(
+ rpt, &ThreadUtilsObject::Test4i, 41, 42, 43, 44);
+ r1->Run();
+ EXPECT_EQ(count += 5, rpt->mCount);
+ EXPECT_EQ(41, rpt->mA0);
+ EXPECT_EQ(42, rpt->mA1);
+ EXPECT_EQ(43, rpt->mA2);
+ EXPECT_EQ(44, rpt->mA3);
+
+ // More interesting types of arguments.
+
+ // Passing a short to make sure forwarding works with an inexact type match.
+ short int si = 11;
+ r1 = NewRunnableMethod<int>(rpt, &ThreadUtilsObject::Test1i, si);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(si, rpt->mA0);
+
+ // Raw pointer, possible cv-qualified.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int*>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int* const>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* volatile>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int* volatile>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const volatile>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int* const volatile>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::stored_type,
+ int*>::value,
+ "detail::ParameterStorage<int*>::Type::stored_type should be int*");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::passed_type,
+ int*>::value,
+ "detail::ParameterStorage<int*>::Type::passed_type should be int*");
+ {
+ int i = 12;
+ r1 = NewRunnableMethod<int*>(rpt, &ThreadUtilsObject::Test1pi, &i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Raw pointer to const.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int*>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int* const>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* volatile>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int* volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const volatile>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int* const volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::stored_type,
+ const int*>::value,
+ "detail::ParameterStorage<const int*>::Type::stored_type should be const int*");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::passed_type,
+ const int*>::value,
+ "detail::ParameterStorage<const int*>::Type::passed_type should be const int*");
+ {
+ int i = 1201;
+ r1 = NewRunnableMethod<const int*>(rpt, &ThreadUtilsObject::Test1pci, &i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Raw pointer to copy.
+ static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::stored_type,
+ int>::value,
+ "StoreCopyPassByPtr<int>::stored_type should be int");
+ static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::passed_type,
+ int*>::value,
+ "StoreCopyPassByPtr<int>::passed_type should be int*");
+ {
+ int i = 1202;
+ r1 = NewRunnableMethod<StoreCopyPassByPtr<int>>(
+ rpt, &ThreadUtilsObject::Test1pi, i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Raw pointer to const copy.
+ static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::stored_type,
+ int>::value,
+ "StoreCopyPassByConstPtr<int>::stored_type should be int");
+ static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::passed_type,
+ const int*>::value,
+ "StoreCopyPassByConstPtr<int>::passed_type should be const int*");
+ {
+ int i = 1203;
+ r1 = NewRunnableMethod<StoreCopyPassByConstPtr<int>>(
+ rpt, &ThreadUtilsObject::Test1pci, i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // nsRefPtr to pointer.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StorensRefPtrPassByPtr<SpyWithISupports>>::Type,
+ StorensRefPtrPassByPtr<SpyWithISupports>>::value,
+ "ParameterStorage<StorensRefPtrPassByPtr<SpyWithISupports>>::Type should be StorensRefPtrPassByPtr<SpyWithISupports>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<SpyWithISupports*>::Type,
+ StorensRefPtrPassByPtr<SpyWithISupports>>::value,
+ "ParameterStorage<SpyWithISupports*>::Type should be StorensRefPtrPassByPtr<SpyWithISupports>");
+ static_assert(mozilla::IsSame<StorensRefPtrPassByPtr<SpyWithISupports>::stored_type,
+ RefPtr<SpyWithISupports>>::value,
+ "StorensRefPtrPassByPtr<SpyWithISupports>::stored_type should be RefPtr<SpyWithISupports>");
+ static_assert(mozilla::IsSame<StorensRefPtrPassByPtr<SpyWithISupports>::passed_type,
+ SpyWithISupports*>::value,
+ "StorensRefPtrPassByPtr<SpyWithISupports>::passed_type should be SpyWithISupports*");
+ // (more nsRefPtr tests below)
+
+ // nsRefPtr for ref-countable classes that do not derive from ISupports.
+ static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedFinal>::value,
+ "ThreadUtilsRefCountedFinal has AddRef() and Release()");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedFinal*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsRefCountedFinal>>::value,
+ "ParameterStorage<ThreadUtilsRefCountedFinal*>::Type should be StorensRefPtrPassByPtr<ThreadUtilsRefCountedFinal>");
+ static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedBase>::value,
+ "ThreadUtilsRefCountedBase has AddRef() and Release()");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedBase*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsRefCountedBase>>::value,
+ "ParameterStorage<ThreadUtilsRefCountedBase*>::Type should be StorensRefPtrPassByPtr<ThreadUtilsRefCountedBase>");
+ static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedDerived>::value,
+ "ThreadUtilsRefCountedDerived has AddRef() and Release()");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedDerived*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsRefCountedDerived>>::value,
+ "ParameterStorage<ThreadUtilsRefCountedDerived*>::Type should be StorensRefPtrPassByPtr<ThreadUtilsRefCountedDerived>");
+
+ static_assert(!::detail::HasRefCountMethods<ThreadUtilsNonRefCounted>::value,
+ "ThreadUtilsNonRefCounted doesn't have AddRef() and Release()");
+ static_assert(!mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsNonRefCounted*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsNonRefCounted>>::value,
+ "ParameterStorage<ThreadUtilsNonRefCounted*>::Type should NOT be StorensRefPtrPassByPtr<ThreadUtilsNonRefCounted>");
+
+ // Lvalue reference.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type,
+ StoreRefPassByLRef<int>>::value,
+ "ParameterStorage<int&>::Type should be StoreRefPassByLRef<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
+ StoreRefPassByLRef<int>::stored_type>::value,
+ "ParameterStorage<int&>::Type::stored_type should be StoreRefPassByLRef<int>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
+ int&>::value,
+ "ParameterStorage<int&>::Type::stored_type should be int&");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::passed_type,
+ int&>::value,
+ "ParameterStorage<int&>::Type::passed_type should be int&");
+ {
+ int i = 13;
+ r1 = NewRunnableMethod<int&>(rpt, &ThreadUtilsObject::Test1ri, i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Rvalue reference -- Actually storing a copy and then moving it.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type,
+ StoreCopyPassByRRef<int>>::value,
+ "ParameterStorage<int&&>::Type should be StoreCopyPassByRRef<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
+ StoreCopyPassByRRef<int>::stored_type>::value,
+ "ParameterStorage<int&&>::Type::stored_type should be StoreCopyPassByRRef<int>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
+ int>::value,
+ "ParameterStorage<int&&>::Type::stored_type should be int");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::passed_type,
+ int&&>::value,
+ "ParameterStorage<int&&>::Type::passed_type should be int&&");
+ {
+ int i = 14;
+ r1 = NewRunnableMethod<int&&>(
+ rpt, &ThreadUtilsObject::Test1rri, mozilla::Move(i));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(14, rpt->mA0);
+
+ // Null unique pointer, by semi-implicit store&move with "T&&" syntax.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type should be StoreCopyPassByRRef<UniquePtr<int>>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
+ mozilla::UniquePtr<int>>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be UniquePtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::passed_type,
+ mozilla::UniquePtr<int>&&>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type::passed_type should be UniquePtr<int>&&");
+ {
+ mozilla::UniquePtr<int> upi;
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(-1, rpt->mA0);
+ rpt->mA0 = 0;
+
+ // Null unique pointer, by explicit store&move with "StoreCopyPassByRRef<T>" syntax.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+ mozilla::UniquePtr<int>>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be UniquePtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::passed_type,
+ mozilla::UniquePtr<int>&&>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::passed_type should be UniquePtr<int>&&");
+ {
+ mozilla::UniquePtr<int> upi;
+ r1 = NewRunnableMethod
+ <StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(-1, rpt->mA0);
+
+ // Unique pointer as xvalue.
+ {
+ mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(1, rpt->mA0);
+
+ {
+ mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
+ r1 = NewRunnableMethod
+ <StoreCopyPassByRRef<mozilla::UniquePtr<int>>>
+ (rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(1, rpt->mA0);
+
+ // Unique pointer as prvalue.
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::MakeUnique<int>(2));
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(2, rpt->mA0);
+
+ // Unique pointer as lvalue to lref.
+ {
+ mozilla::UniquePtr<int> upi;
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&>(
+ rpt, &ThreadUtilsObject::Test1rupi, upi);
+ // Passed as lref, so Run() must be called while local upi is still alive!
+ r1->Run();
+ }
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(-1, rpt->mA0);
+
+ // Verify copy/move assumptions.
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by value\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r2;
+ { // Block around Spy lifetime.
+ if (gDebug) { printf("%d - Spy s(10)\n", __LINE__); }
+ Spy s(10);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(&TestByValue, s)\n", __LINE__); }
+ r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
+ rpt, &ThreadUtilsObject::TestByValue, s);
+ EXPECT_EQ(2, gAlive);
+ EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with Spy s(10)\n", __LINE__); }
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r2->Run();
+ EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+ EXPECT_EQ(10, rpt->mSpy.mID);
+ EXPECT_LE(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by value\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - r3 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(&TestByValue, Spy(11))\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r3 =
+ NewRunnableMethod<StoreCopyPassByValue<Spy>>(
+ rpt, &ThreadUtilsObject::TestByValue, Spy(11));
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_LE(1, gMoveConstructions);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r3->Run();
+ EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+ EXPECT_EQ(11, rpt->mSpy.mID);
+ EXPECT_LE(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ { // Store copy from xvalue, pass by value.
+ nsCOMPtr<nsIRunnable> r4;
+ {
+ Spy s(12);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ r4 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
+ rpt, &ThreadUtilsObject::TestByValue, mozilla::Move(s));
+ EXPECT_LE(1, gMoveConstructions);
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gZombies);
+ Spy::ClearActions();
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(0, gZombies);
+ Spy::ClearActions();
+ r4->Run();
+ EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+ EXPECT_EQ(12, rpt->mSpy.mID);
+ EXPECT_LE(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+ // Won't test xvalues anymore, prvalues are enough to verify all rvalues.
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by const lvalue ref\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r5;
+ { // Block around Spy lifetime.
+ if (gDebug) { printf("%d - Spy s(20)\n", __LINE__); }
+ Spy s(20);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, s)\n", __LINE__); }
+ r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByConstLRef, s);
+ EXPECT_EQ(2, gAlive);
+ EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with Spy s(20)\n", __LINE__); }
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r5->Run();
+ EXPECT_EQ(0, gCopyConstructions); // No copies in call.
+ EXPECT_EQ(20, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by const lvalue ref\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - r6 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, Spy(21))\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r6 =
+ NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByConstLRef, Spy(21));
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_LE(1, gMoveConstructions);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r6->Run();
+ EXPECT_EQ(0, gCopyConstructions); // No copies in call.
+ EXPECT_EQ(21, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by rvalue ref\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r7;
+ { // Block around Spy lifetime.
+ if (gDebug) { printf("%d - Spy s(30)\n", __LINE__); }
+ Spy s(30);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(&TestByRRef, s)\n", __LINE__); }
+ r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByRRef, s);
+ EXPECT_EQ(2, gAlive);
+ EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with Spy s(30)\n", __LINE__); }
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r7->Run();
+ EXPECT_LE(1, gMoves); // Move in call.
+ EXPECT_EQ(30, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
+ EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by rvalue ref\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - r8 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(&TestByRRef, Spy(31))\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r8 =
+ NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByRRef, Spy(31));
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_LE(1, gMoveConstructions);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r8->Run();
+ EXPECT_LE(1, gMoves); // Move in call.
+ EXPECT_EQ(31, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
+ EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store lvalue ref, pass lvalue ref\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - Spy s(40)\n", __LINE__); }
+ Spy s(40);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - r9 = NewRunnableMethod<Spy&>(&TestByLRef, s)\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r9 =
+ NewRunnableMethod<Spy&>(
+ rpt, &ThreadUtilsObject::TestByLRef, s);
+ EXPECT_EQ(0, gAllConstructions);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r9->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from reference in call.
+ EXPECT_EQ(40, rpt->mSpy.mID);
+ EXPECT_EQ(&s, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store nsRefPtr, pass by pointer\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r10;
+ SpyWithISupports* ptr = 0;
+ { // Block around RefPtr<Spy> lifetime.
+ if (gDebug) { printf("%d - RefPtr<SpyWithISupports> s(new SpyWithISupports(45))\n", __LINE__); }
+ RefPtr<SpyWithISupports> s(new SpyWithISupports(45));
+ ptr = s.get();
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r10 = NewRunnableMethod<StorensRefPtrPassByPtr<Spy>>(&TestByRRef, s.get())\n", __LINE__); }
+ r10 = NewRunnableMethod<StorensRefPtrPassByPtr<SpyWithISupports>>(
+ rpt, &ThreadUtilsObject::TestByPointer, s.get());
+ EXPECT_LE(0, gAllConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with RefPtr<Spy> s\n", __LINE__); }
+ }
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r10->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+ EXPECT_EQ(45, rpt->mSpy.mID);
+ EXPECT_EQ(ptr, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store pointer to lvalue, pass by pointer\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - Spy s(55)\n", __LINE__); }
+ Spy s(55);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - r11 = NewRunnableMethod<Spy*>(&TestByPointer, s)\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r11 =
+ NewRunnableMethod<Spy*>(
+ rpt, &ThreadUtilsObject::TestByPointer, &s);
+ EXPECT_EQ(0, gAllConstructions);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r11->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+ EXPECT_EQ(55, rpt->mSpy.mID);
+ EXPECT_EQ(&s, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store pointer to const lvalue, pass by pointer\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - Spy s(60)\n", __LINE__); }
+ Spy s(60);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - r12 = NewRunnableMethod<Spy*>(&TestByPointer, s)\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r12 =
+ NewRunnableMethod<const Spy*>(
+ rpt, &ThreadUtilsObject::TestByPointerToConst, &s);
+ EXPECT_EQ(0, gAllConstructions);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r12->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+ EXPECT_EQ(60, rpt->mSpy.mID);
+ EXPECT_EQ(&s, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+#endif // XPCOM_GLUE_AVOID_NSPR
+}
diff --git a/xpcom/glue/tests/gtest/moz.build b/xpcom/glue/tests/gtest/moz.build
new file mode 100644
index 000000000..9f4d83a3e
--- /dev/null
+++ b/xpcom/glue/tests/gtest/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'TestArray.cpp',
+ 'TestFileUtils.cpp',
+ 'TestGCPostBarriers.cpp',
+ 'TestNsDeque.cpp',
+ 'TestThreadUtils.cpp',
+]
+
+LOCAL_INCLUDES = [
+ '../..',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/xpcom/idl-parser/setup.py b/xpcom/idl-parser/setup.py
new file mode 100644
index 000000000..f42edbb99
--- /dev/null
+++ b/xpcom/idl-parser/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup, find_packages
+
+
+setup(
+ name='xpidl',
+ version='1.0',
+ description='Parser and header generator for xpidl files.',
+ author='Mozilla Foundation',
+ license='MPL 2.0',
+ packages=find_packages(),
+ install_requires=['ply>=3.6,<4.0'],
+ url='https://github.com/pelmers/xpidl',
+ entry_points={'console_scripts': ['header.py = xpidl.header:main']},
+ keywords=['xpidl', 'parser']
+)
diff --git a/xpcom/idl-parser/xpidl/__init__.py b/xpcom/idl-parser/xpidl/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/__init__.py
diff --git a/xpcom/idl-parser/xpidl/header.py b/xpcom/idl-parser/xpidl/header.py
new file mode 100644
index 000000000..8e02a7d11
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/header.py
@@ -0,0 +1,566 @@
+#!/usr/bin/env python
+# header.py - Generate C++ header files from IDL.
+#
+# 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/.
+
+"""Print a C++ header file for the IDL files specified on the command line"""
+
+import sys
+import os.path
+import re
+import xpidl
+import itertools
+import glob
+
+printdoccomments = False
+
+if printdoccomments:
+ def printComments(fd, clist, indent):
+ for c in clist:
+ fd.write("%s%s\n" % (indent, c))
+else:
+ def printComments(fd, clist, indent):
+ pass
+
+
+def firstCap(str):
+ return str[0].upper() + str[1:]
+
+
+def attributeParamName(a):
+ return "a" + firstCap(a.name)
+
+
+def attributeParamNames(a):
+ l = [attributeParamName(a)]
+ if a.implicit_jscontext:
+ l.insert(0, "cx")
+ return ", ".join(l)
+
+
+def attributeNativeName(a, getter):
+ binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
+ return "%s%s" % (getter and 'Get' or 'Set', binaryname)
+
+
+def attributeReturnType(a, macro):
+ """macro should be NS_IMETHOD or NS_IMETHODIMP"""
+ if a.nostdcall:
+ ret = macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
+ else:
+ ret = macro
+ if a.must_use:
+ ret = "MOZ_MUST_USE " + ret
+ return ret
+
+
+def attributeParamlist(a, getter):
+ l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
+ attributeParamName(a))]
+ if a.implicit_jscontext:
+ l.insert(0, "JSContext* cx")
+
+ return ", ".join(l)
+
+
+def attributeAsNative(a, getter, declType = 'NS_IMETHOD'):
+ deprecated = a.deprecated and "NS_DEPRECATED " or ""
+ params = {'deprecated': deprecated,
+ 'returntype': attributeReturnType(a, declType),
+ 'binaryname': attributeNativeName(a, getter),
+ 'paramlist': attributeParamlist(a, getter)}
+ return "%(deprecated)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
+
+
+def methodNativeName(m):
+ return m.binaryname is not None and m.binaryname or firstCap(m.name)
+
+
+def methodReturnType(m, macro):
+ """macro should be NS_IMETHOD or NS_IMETHODIMP"""
+ if m.nostdcall and m.notxpcom:
+ ret = "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
+ m.realtype.nativeType('in').strip())
+ elif m.nostdcall:
+ ret = "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
+ elif m.notxpcom:
+ ret = "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
+ else:
+ ret = macro
+ if m.must_use:
+ ret = "MOZ_MUST_USE " + ret
+ return ret
+
+
+def methodAsNative(m, declType = 'NS_IMETHOD'):
+ return "%s %s(%s)" % (methodReturnType(m, declType),
+ methodNativeName(m),
+ paramlistAsNative(m))
+
+
+def paramlistAsNative(m, empty='void'):
+ l = [paramAsNative(p) for p in m.params]
+
+ if m.implicit_jscontext:
+ l.append("JSContext* cx")
+
+ if m.optional_argc:
+ l.append('uint8_t _argc')
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ l.append(paramAsNative(xpidl.Param(paramtype='out',
+ type=None,
+ name='_retval',
+ attlist=[],
+ location=None,
+ realtype=m.realtype)))
+
+ # Set any optional out params to default to nullptr. Skip if we just added
+ # extra non-optional args to l.
+ if len(l) == len(m.params):
+ paramIter = len(m.params) - 1
+ while (paramIter >= 0 and m.params[paramIter].optional and
+ m.params[paramIter].paramtype == "out"):
+ t = m.params[paramIter].type
+ # Strings can't be optional, so this shouldn't happen, but let's make sure:
+ if t == "AString" or t == "ACString" or t == "DOMString" or t == "AUTF8String":
+ break
+ l[paramIter] += " = nullptr"
+ paramIter -= 1
+
+ if len(l) == 0:
+ return empty
+
+ return ", ".join(l)
+
+
+def paramAsNative(p):
+ return "%s%s" % (p.nativeType(),
+ p.name)
+
+
+def paramlistNames(m):
+ names = [p.name for p in m.params]
+
+ if m.implicit_jscontext:
+ names.append('cx')
+
+ if m.optional_argc:
+ names.append('_argc')
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ names.append('_retval')
+
+ if len(names) == 0:
+ return ''
+ return ', '.join(names)
+
+header = """/*
+ * DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s
+ */
+
+#ifndef __gen_%(basename)s_h__
+#define __gen_%(basename)s_h__
+"""
+
+include = """
+#ifndef __gen_%(basename)s_h__
+#include "%(basename)s.h"
+#endif
+"""
+
+jsvalue_include = """
+#include "js/Value.h"
+"""
+
+infallible_includes = """
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+"""
+
+header_end = """/* For IDL files that don't want to include root IDL files. */
+#ifndef NS_NO_VTABLE
+#define NS_NO_VTABLE
+#endif
+"""
+
+footer = """
+#endif /* __gen_%(basename)s_h__ */
+"""
+
+forward_decl = """class %(name)s; /* forward declaration */
+
+"""
+
+
+def idl_basename(f):
+ """returns the base name of a file with the last extension stripped"""
+ return os.path.basename(f).rpartition('.')[0]
+
+
+def print_header(idl, fd, filename):
+ fd.write(header % {'filename': filename,
+ 'basename': idl_basename(filename)})
+
+ foundinc = False
+ for inc in idl.includes():
+ if not foundinc:
+ foundinc = True
+ fd.write('\n')
+ fd.write(include % {'basename': idl_basename(inc.filename)})
+
+ if idl.needsJSTypes():
+ fd.write(jsvalue_include)
+
+ # Include some extra files if any attributes are infallible.
+ for iface in [p for p in idl.productions if p.kind == 'interface']:
+ for attr in [m for m in iface.members if isinstance(m, xpidl.Attribute)]:
+ if attr.infallible:
+ fd.write(infallible_includes)
+ break
+
+ fd.write('\n')
+ fd.write(header_end)
+
+ for p in idl.productions:
+ if p.kind == 'include':
+ continue
+ if p.kind == 'cdata':
+ fd.write(p.data)
+ continue
+
+ if p.kind == 'forward':
+ fd.write(forward_decl % {'name': p.name})
+ continue
+ if p.kind == 'interface':
+ write_interface(p, fd)
+ continue
+ if p.kind == 'typedef':
+ printComments(fd, p.doccomments, '')
+ fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
+ p.name))
+
+ fd.write(footer % {'basename': idl_basename(filename)})
+
+iface_header = r"""
+/* starting interface: %(name)s */
+#define %(defname)s_IID_STR "%(iid)s"
+
+#define %(defname)s_IID \
+ {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
+ { %(m3joined)s }}
+
+"""
+
+uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
+ (?P<m1>[a-f0-9]{4})-
+ (?P<m2>[a-f0-9]{4})-
+ (?P<m3>[a-f0-9]{4})-
+ (?P<m4>[a-f0-9]{12})$""", re.X)
+
+iface_prolog = """ {
+ public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
+
+"""
+
+iface_epilog = """};
+
+ NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
+
+/* Use this macro when declaring classes that implement this interface. */
+#define NS_DECL_%(macroname)s """
+
+iface_nonvirtual = """
+
+/* Use this macro when declaring the members of this interface when the
+ class doesn't implement the interface. This is useful for forwarding. */
+#define NS_DECL_NON_VIRTUAL_%(macroname)s """
+
+iface_forward = """
+
+/* Use this macro to declare functions that forward the behavior of this interface to another object. */
+#define NS_FORWARD_%(macroname)s(_to) """
+
+iface_forward_safe = """
+
+/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
+#define NS_FORWARD_SAFE_%(macroname)s(_to) """
+
+iface_template_prolog = """
+
+#if 0
+/* Use the code below as a template for the implementation class for this interface. */
+
+/* Header file */
+class %(implclass)s : public %(name)s
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_%(macroname)s
+
+ %(implclass)s();
+
+private:
+ ~%(implclass)s();
+
+protected:
+ /* additional members */
+};
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(%(implclass)s, %(name)s)
+
+%(implclass)s::%(implclass)s()
+{
+ /* member initializers and constructor code */
+}
+
+%(implclass)s::~%(implclass)s()
+{
+ /* destructor code */
+}
+
+"""
+
+example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+"""
+
+iface_template_epilog = """/* End of implementation class template. */
+#endif
+
+"""
+
+attr_infallible_tmpl = """\
+ inline %(realtype)s%(nativename)s(%(args)s)
+ {
+ %(realtype)sresult;
+ mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return result;
+ }
+"""
+
+
+def write_interface(iface, fd):
+ if iface.namemap is None:
+ raise Exception("Interface was not resolved.")
+
+ # Confirm that no names of methods will overload in this interface
+ names = set()
+ def record_name(name):
+ if name in names:
+ raise Exception("Unexpected overloaded virtual method %s in interface %s"
+ % (name, iface.name))
+ names.add(name)
+ for m in iface.members:
+ if type(m) == xpidl.Attribute:
+ record_name(attributeNativeName(m, getter=True))
+ if not m.readonly:
+ record_name(attributeNativeName(m, getter=False))
+ elif type(m) == xpidl.Method:
+ record_name(methodNativeName(m))
+
+ def write_const_decls(g):
+ fd.write(" enum {\n")
+ enums = []
+ for c in g:
+ printComments(fd, c.doccomments, ' ')
+ basetype = c.basetype
+ value = c.getValue()
+ enums.append(" %(name)s = %(value)s%(signed)s" % {
+ 'name': c.name,
+ 'value': value,
+ 'signed': (not basetype.signed) and 'U' or ''})
+ fd.write(",\n".join(enums))
+ fd.write("\n };\n\n")
+
+ def write_method_decl(m):
+ printComments(fd, m.doccomments, ' ')
+
+ fd.write(" /* %s */\n" % m.toIDL())
+ fd.write(" %s = 0;\n\n" % methodAsNative(m))
+
+ def write_attr_decl(a):
+ printComments(fd, a.doccomments, ' ')
+
+ fd.write(" /* %s */\n" % a.toIDL())
+
+ fd.write(" %s = 0;\n" % attributeAsNative(a, True))
+ if a.infallible:
+ fd.write(attr_infallible_tmpl %
+ {'realtype': a.realtype.nativeType('in'),
+ 'nativename': attributeNativeName(a, getter=True),
+ 'args': '' if not a.implicit_jscontext else 'JSContext* cx',
+ 'argnames': '' if not a.implicit_jscontext else 'cx, '})
+
+ if not a.readonly:
+ fd.write(" %s = 0;\n" % attributeAsNative(a, False))
+ fd.write("\n")
+
+ defname = iface.name.upper()
+ if iface.name[0:2] == 'ns':
+ defname = 'NS_' + defname[2:]
+
+ names = uuid_decoder.match(iface.attributes.uuid).groupdict()
+ m3str = names['m3'] + names['m4']
+ names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
+
+ if iface.name[2] == 'I':
+ implclass = iface.name[:2] + iface.name[3:]
+ else:
+ implclass = '_MYCLASS_'
+
+ names.update({'defname': defname,
+ 'macroname': iface.name.upper(),
+ 'name': iface.name,
+ 'iid': iface.attributes.uuid,
+ 'implclass': implclass})
+
+ fd.write(iface_header % names)
+
+ printComments(fd, iface.doccomments, '')
+
+ fd.write("class ")
+ foundcdata = False
+ for m in iface.members:
+ if isinstance(m, xpidl.CDATA):
+ foundcdata = True
+
+ if not foundcdata:
+ fd.write("NS_NO_VTABLE ")
+
+ if iface.attributes.deprecated:
+ fd.write("MOZ_DEPRECATED ")
+ fd.write(iface.name)
+ if iface.base:
+ fd.write(" : public %s" % iface.base)
+ fd.write(iface_prolog % names)
+
+ for key, group in itertools.groupby(iface.members, key=type):
+ if key == xpidl.ConstMember:
+ write_const_decls(group) # iterator of all the consts
+ else:
+ for member in group:
+ if key == xpidl.Attribute:
+ write_attr_decl(member)
+ elif key == xpidl.Method:
+ write_method_decl(member)
+ elif key == xpidl.CDATA:
+ fd.write(" %s" % member.data)
+ else:
+ raise Exception("Unexpected interface member: %s" % member)
+
+ fd.write(iface_epilog % names)
+
+ def writeDeclaration(fd, iface, virtual):
+ declType = "NS_IMETHOD" if virtual else "NS_METHOD"
+ suffix = " override" if virtual else ""
+ for member in iface.members:
+ if isinstance(member, xpidl.Attribute):
+ if member.infallible:
+ fd.write("\\\n using %s::%s; " % (iface.name, attributeNativeName(member, True)))
+ fd.write("\\\n %s%s; " % (attributeAsNative(member, True, declType), suffix))
+ if not member.readonly:
+ fd.write("\\\n %s%s; " % (attributeAsNative(member, False, declType), suffix))
+ elif isinstance(member, xpidl.Method):
+ fd.write("\\\n %s%s; " % (methodAsNative(member, declType), suffix))
+ if len(iface.members) == 0:
+ fd.write('\\\n /* no methods! */')
+ elif not member.kind in ('attribute', 'method'):
+ fd.write('\\')
+
+ writeDeclaration(fd, iface, True);
+ fd.write(iface_nonvirtual % names)
+ writeDeclaration(fd, iface, False);
+ fd.write(iface_forward % names)
+
+ def emitTemplate(forward_infallible, tmpl, tmpl_notxpcom=None):
+ if tmpl_notxpcom is None:
+ tmpl_notxpcom = tmpl
+ for member in iface.members:
+ if isinstance(member, xpidl.Attribute):
+ if forward_infallible and member.infallible:
+ fd.write("\\\n using %s::%s; " % (iface.name, attributeNativeName(member, True)))
+ fd.write(tmpl % {'asNative': attributeAsNative(member, True),
+ 'nativeName': attributeNativeName(member, True),
+ 'paramList': attributeParamNames(member)})
+ if not member.readonly:
+ fd.write(tmpl % {'asNative': attributeAsNative(member, False),
+ 'nativeName': attributeNativeName(member, False),
+ 'paramList': attributeParamNames(member)})
+ elif isinstance(member, xpidl.Method):
+ if member.notxpcom:
+ fd.write(tmpl_notxpcom % {'asNative': methodAsNative(member),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistNames(member)})
+ else:
+ fd.write(tmpl % {'asNative': methodAsNative(member),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistNames(member)})
+ if len(iface.members) == 0:
+ fd.write('\\\n /* no methods! */')
+ elif not member.kind in ('attribute', 'method'):
+ fd.write('\\')
+
+ emitTemplate(True,
+ "\\\n %(asNative)s override { return _to %(nativeName)s(%(paramList)s); } ")
+
+ fd.write(iface_forward_safe % names)
+
+ # Don't try to safely forward notxpcom functions, because we have no
+ # sensible default error return. Instead, the caller will have to
+ # implement them.
+ emitTemplate(False,
+ "\\\n %(asNative)s override { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ",
+ "\\\n %(asNative)s override; ")
+
+ fd.write(iface_template_prolog % names)
+
+ for member in iface.members:
+ if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA):
+ continue
+ fd.write("/* %s */\n" % member.toIDL())
+ if isinstance(member, xpidl.Attribute):
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': attributeNativeName(member, True),
+ 'paramList': attributeParamlist(member, True)})
+ if not member.readonly:
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': attributeNativeName(member, False),
+ 'paramList': attributeParamlist(member, False)})
+ elif isinstance(member, xpidl.Method):
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistAsNative(member, empty='')})
+ fd.write('\n')
+
+ fd.write(iface_template_epilog)
+
+
+def main(outputfile):
+ cachedir = '.'
+ if not os.path.isdir(cachedir):
+ os.mkdir(cachedir)
+ sys.path.append(cachedir)
+
+ # Delete the lex/yacc files. Ply is too stupid to regenerate them
+ # properly
+ for fileglobs in [os.path.join(cachedir, f) for f in ["xpidllex.py*", "xpidlyacc.py*"]]:
+ for filename in glob.glob(fileglobs):
+ os.remove(filename)
+
+ # Instantiate the parser.
+ p = xpidl.IDLParser(outputdir=cachedir)
+
+if __name__ == '__main__':
+ main(None)
diff --git a/xpcom/idl-parser/xpidl/moz.build b/xpcom/idl-parser/xpidl/moz.build
new file mode 100644
index 000000000..33c1e7b13
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/moz.build
@@ -0,0 +1,29 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+PYTHON_UNIT_TESTS += [
+ 'runtests.py',
+]
+
+GENERATED_FILES += [
+ ('xpidl.stub', 'xpidllex.py', 'xpidlyacc.py'),
+]
+
+GENERATED_FILES[('xpidl.stub', 'xpidllex.py', 'xpidlyacc.py')].script = 'header.py:main'
+
+SDK_FILES.bin += [
+ '!xpidllex.py',
+ '!xpidlyacc.py',
+ 'header.py',
+ 'typelib.py',
+ 'xpidl.py',
+]
+
+SDK_FILES.bin.ply += [
+ '/other-licenses/ply/ply/__init__.py',
+ '/other-licenses/ply/ply/lex.py',
+ '/other-licenses/ply/ply/yacc.py',
+]
diff --git a/xpcom/idl-parser/xpidl/runtests.py b/xpcom/idl-parser/xpidl/runtests.py
new file mode 100644
index 000000000..89222d546
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/runtests.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+#
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+#
+# Unit tests for xpidl.py
+
+import mozunit
+import unittest
+import xpidl
+import header
+
+
+class TestParser(unittest.TestCase):
+ def setUp(self):
+ self.p = xpidl.IDLParser()
+
+ def testEmpty(self):
+ i = self.p.parse("", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertEqual([], i.productions)
+
+ def testForwardInterface(self):
+ i = self.p.parse("interface foo;", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Forward))
+ self.assertEqual("foo", i.productions[0].name)
+
+ def testInterface(self):
+ i = self.p.parse("[uuid(abc)] interface foo {};", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ self.assertEqual("foo", i.productions[0].name)
+
+ def testAttributes(self):
+ i = self.p.parse("[scriptable, builtinclass, function, deprecated, uuid(abc)] interface foo {};", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ self.assertEqual("foo", iface.name)
+ self.assertTrue(iface.attributes.scriptable)
+ self.assertTrue(iface.attributes.builtinclass)
+ self.assertTrue(iface.attributes.function)
+ self.assertTrue(iface.attributes.deprecated)
+
+ i = self.p.parse("[noscript, uuid(abc)] interface foo {};", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ self.assertEqual("foo", iface.name)
+ self.assertTrue(iface.attributes.noscript)
+
+ def testMethod(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+void bar();
+};""", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ m = iface.members[0]
+ self.assertTrue(isinstance(m, xpidl.Method))
+ self.assertEqual("bar", m.name)
+ self.assertEqual("void", m.type)
+
+ def testMethodParams(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+long bar(in long a, in float b, [array] in long c);
+};""", filename='f')
+ i.resolve([], self.p)
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ m = iface.members[0]
+ self.assertTrue(isinstance(m, xpidl.Method))
+ self.assertEqual("bar", m.name)
+ self.assertEqual("long", m.type)
+ self.assertEqual(3, len(m.params))
+ self.assertEqual("long", m.params[0].type)
+ self.assertEqual("in", m.params[0].paramtype)
+ self.assertEqual("float", m.params[1].type)
+ self.assertEqual("in", m.params[1].paramtype)
+ self.assertEqual("long", m.params[2].type)
+ self.assertEqual("in", m.params[2].paramtype)
+ self.assertTrue(isinstance(m.params[2].realtype, xpidl.Array))
+ self.assertEqual("long", m.params[2].realtype.type.name)
+
+ def testAttribute(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+attribute long bar;
+};""", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ a = iface.members[0]
+ self.assertTrue(isinstance(a, xpidl.Attribute))
+ self.assertEqual("bar", a.name)
+ self.assertEqual("long", a.type)
+
+ def testOverloadedVirtual(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+attribute long bar;
+void getBar();
+};""", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ class FdMock:
+ def write(self, s):
+ pass
+ try:
+ header.print_header(i, FdMock(), filename='f')
+ except Exception as e:
+ self.assertEqual(e.args[0], "Unexpected overloaded virtual method GetBar in interface foo")
+
+if __name__ == '__main__':
+ mozunit.main()
diff --git a/xpcom/idl-parser/xpidl/typelib.py b/xpcom/idl-parser/xpidl/typelib.py
new file mode 100644
index 000000000..911e3873d
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/typelib.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+# typelib.py - Generate XPCOM typelib files from IDL.
+#
+# 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/.
+
+"""Generate an XPIDL typelib for the IDL files specified on the command line"""
+
+import os
+import sys
+import xpidl
+import xpt
+
+# A map of xpidl.py types to xpt.py types
+TypeMap = {
+ # nsresult is not strictly an xpidl.py type, but it's useful here
+ 'nsresult': xpt.Type.Tags.uint32,
+ # builtins
+ 'boolean': xpt.Type.Tags.boolean,
+ 'void': xpt.Type.Tags.void,
+ 'int16_t': xpt.Type.Tags.int16,
+ 'int32_t': xpt.Type.Tags.int32,
+ 'int64_t': xpt.Type.Tags.int64,
+ 'uint8_t': xpt.Type.Tags.uint8,
+ 'uint16_t': xpt.Type.Tags.uint16,
+ 'uint32_t': xpt.Type.Tags.uint32,
+ 'uint64_t': xpt.Type.Tags.uint64,
+ 'octet': xpt.Type.Tags.uint8,
+ 'short': xpt.Type.Tags.int16,
+ 'long': xpt.Type.Tags.int32,
+ 'long long': xpt.Type.Tags.int64,
+ 'unsigned short': xpt.Type.Tags.uint16,
+ 'unsigned long': xpt.Type.Tags.uint32,
+ 'unsigned long long': xpt.Type.Tags.uint64,
+ 'float': xpt.Type.Tags.float,
+ 'double': xpt.Type.Tags.double,
+ 'char': xpt.Type.Tags.char,
+ 'string': xpt.Type.Tags.char_ptr,
+ 'wchar': xpt.Type.Tags.wchar_t,
+ 'wstring': xpt.Type.Tags.wchar_t_ptr,
+ # special types
+ 'nsid': xpt.Type.Tags.nsIID,
+ 'domstring': xpt.Type.Tags.DOMString,
+ 'astring': xpt.Type.Tags.AString,
+ 'utf8string': xpt.Type.Tags.UTF8String,
+ 'cstring': xpt.Type.Tags.CString,
+ 'jsval': xpt.Type.Tags.jsval
+}
+
+
+# XXXkhuey dipper types should go away (bug 677784)
+def isDipperType(type):
+ return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String
+
+
+def build_interface(iface, ifaces):
+ def get_type(type, calltype, iid_is=None, size_is=None):
+ """ Return the appropriate xpt.Type object for this param """
+
+ while isinstance(type, xpidl.Typedef):
+ type = type.realtype
+
+ if isinstance(type, xpidl.Builtin):
+ if type.name == 'string' and size_is is not None:
+ return xpt.StringWithSizeType(size_is, size_is)
+ elif type.name == 'wstring' and size_is is not None:
+ return xpt.WideStringWithSizeType(size_is, size_is)
+ else:
+ tag = TypeMap[type.name]
+ isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr)
+ return xpt.SimpleType(tag,
+ pointer=isPtr,
+ reference=False)
+
+ if isinstance(type, xpidl.Array):
+ # NB: For an Array<T> we pass down the iid_is to get the type of T.
+ # This allows Arrays of InterfaceIs types to work.
+ return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is,
+ #XXXkhuey length_is duplicates size_is (bug 677788),
+ size_is)
+
+ if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
+ xptiface = None
+ for i in ifaces:
+ if i.name == type.name:
+ xptiface = i
+
+ if not xptiface:
+ xptiface = xpt.Interface(name=type.name)
+ ifaces.append(xptiface)
+
+ return xpt.InterfaceType(xptiface)
+
+ if isinstance(type, xpidl.Native):
+ if type.specialtype:
+ # XXXkhuey jsval is marked differently in the typelib and in the headers :-(
+ isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval'
+ isRef = type.isRef(calltype) and not type.specialtype == 'jsval'
+ return xpt.SimpleType(TypeMap[type.specialtype],
+ pointer=isPtr,
+ reference=isRef)
+ elif iid_is is not None:
+ return xpt.InterfaceIsType(iid_is)
+ else:
+ # void ptr
+ return xpt.SimpleType(TypeMap['void'],
+ pointer=True,
+ reference=False)
+
+ raise Exception("Unknown type!")
+
+ def get_nsresult():
+ return xpt.SimpleType(TypeMap['nsresult'])
+
+ def build_nsresult_param():
+ return xpt.Param(get_nsresult())
+
+ def get_result_type(m):
+ if not m.notxpcom:
+ return get_nsresult()
+
+ return get_type(m.realtype, '')
+
+ def build_result_param(m):
+ return xpt.Param(get_result_type(m))
+
+ def build_retval_param(m):
+ type = get_type(m.realtype, 'out')
+ if isDipperType(type.tag):
+ # NB: The retval bit needs to be set here, contrary to what the
+ # xpt spec says.
+ return xpt.Param(type, in_=True, retval=True, dipper=True)
+ return xpt.Param(type, in_=False, out=True, retval=True)
+
+ def build_attr_param(a, getter=False, setter=False):
+ if not (getter or setter):
+ raise Exception("Attribute param must be for a getter or a setter!")
+
+ type = get_type(a.realtype, getter and 'out' or 'in')
+ if setter:
+ return xpt.Param(type)
+ else:
+ if isDipperType(type.tag):
+ # NB: The retval bit needs to be set here, contrary to what the
+ # xpt spec says.
+ return xpt.Param(type, in_=True, retval=True, dipper=True)
+ return xpt.Param(type, in_=False, out=True, retval=True)
+
+ if iface.namemap is None:
+ raise Exception("Interface was not resolved.")
+
+ consts = []
+ methods = []
+
+ def build_const(c):
+ consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue()))
+
+ def build_method(m):
+ params = []
+
+ def build_param(p):
+ def findattr(p, attr):
+ if hasattr(p, attr) and getattr(p, attr):
+ for i, param in enumerate(m.params):
+ if param.name == getattr(p, attr):
+ return i
+ return None
+
+ iid_is = findattr(p, 'iid_is')
+ size_is = findattr(p, 'size_is')
+
+ in_ = p.paramtype.count("in")
+ out = p.paramtype.count("out")
+ dipper = False
+ type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is)
+ if out and isDipperType(type.tag):
+ out = False
+ dipper = True
+
+ return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional)
+
+ for p in m.params:
+ params.append(build_param(p))
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ params.append(build_retval_param(m))
+
+ methods.append(xpt.Method(m.name, build_result_param(m), params,
+ getter=False, setter=False, notxpcom=m.notxpcom,
+ constructor=False, hidden=m.noscript,
+ optargc=m.optional_argc,
+ implicit_jscontext=m.implicit_jscontext))
+
+ def build_attr(a):
+ # Write the getter
+ methods.append(xpt.Method(a.name, build_nsresult_param(),
+ [build_attr_param(a, getter=True)],
+ getter=True, setter=False,
+ constructor=False, hidden=a.noscript,
+ optargc=False,
+ implicit_jscontext=a.implicit_jscontext))
+
+ # And maybe the setter
+ if not a.readonly:
+ methods.append(xpt.Method(a.name, build_nsresult_param(),
+ [build_attr_param(a, setter=True)],
+ getter=False, setter=True,
+ constructor=False, hidden=a.noscript,
+ optargc=False,
+ implicit_jscontext=a.implicit_jscontext))
+
+ for member in iface.members:
+ if isinstance(member, xpidl.ConstMember):
+ build_const(member)
+ elif isinstance(member, xpidl.Attribute):
+ build_attr(member)
+ elif isinstance(member, xpidl.Method):
+ build_method(member)
+ elif isinstance(member, xpidl.CDATA):
+ pass
+ else:
+ raise Exception("Unexpected interface member: %s" % member)
+
+ parent = None
+ if iface.base:
+ for i in ifaces:
+ if i.name == iface.base:
+ parent = i
+ if not parent:
+ parent = xpt.Interface(name=iface.base)
+ ifaces.append(parent)
+
+ return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods,
+ constants=consts, resolved=True, parent=parent,
+ scriptable=iface.attributes.scriptable,
+ function=iface.attributes.function,
+ builtinclass=iface.attributes.builtinclass,
+ main_process_scriptable_only=iface.attributes.main_process_scriptable_only)
+
+
+def write_typelib(idl, fd, filename):
+ """ Generate the typelib. """
+
+ # We only care about interfaces that are scriptable.
+ ifaces = []
+ for p in idl.productions:
+ if p.kind == 'interface' and p.attributes.scriptable:
+ ifaces.append(build_interface(p, ifaces))
+
+ typelib = xpt.Typelib(interfaces=ifaces)
+ typelib.writefd(fd)
+
+if __name__ == '__main__':
+ from optparse import OptionParser
+ o = OptionParser()
+ o.add_option('-I', action='append', dest='incdirs', default=['.'],
+ help="Directory to search for imported files")
+ o.add_option('--cachedir', dest='cachedir', default=None,
+ help="Directory in which to cache lex/parse tables.")
+ o.add_option('-o', dest='outfile', default=None,
+ help="Output file")
+ o.add_option('-d', dest='depfile', default=None,
+ help="Generate a make dependency file")
+ o.add_option('--regen', action='store_true', dest='regen', default=False,
+ help="Regenerate IDL Parser cache")
+ options, args = o.parse_args()
+ file = args[0] if args else None
+
+ if options.cachedir is not None:
+ if not os.path.isdir(options.cachedir):
+ os.mkdir(options.cachedir)
+ sys.path.append(options.cachedir)
+
+ if options.regen:
+ if options.cachedir is None:
+ print >>sys.stderr, "--regen requires --cachedir"
+ sys.exit(1)
+
+ p = xpidl.IDLParser(outputdir=options.cachedir, regen=True)
+ sys.exit(0)
+
+ if options.depfile is not None and options.outfile is None:
+ print >>sys.stderr, "-d requires -o"
+ sys.exit(1)
+
+ if options.outfile is not None:
+ outfd = open(options.outfile, 'wb')
+ closeoutfd = True
+ else:
+ raise "typelib generation requires an output file"
+
+ p = xpidl.IDLParser(outputdir=options.cachedir)
+ idl = p.parse(open(file).read(), filename=file)
+ idl.resolve(options.incdirs, p)
+ write_typelib(idl, outfd, file)
+
+ if closeoutfd:
+ outfd.close()
+
+ if options.depfile is not None:
+ depfd = open(options.depfile, 'w')
+ deps = [dep.replace('\\', '/') for dep in idl.deps]
+
+ print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))
+ for dep in deps:
+ print >>depfd, "%s:" % dep
diff --git a/xpcom/idl-parser/xpidl/xpidl.py b/xpcom/idl-parser/xpidl/xpidl.py
new file mode 100755
index 000000000..8b2d8c884
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -0,0 +1,1465 @@
+#!/usr/bin/env python
+# xpidl.py - A parser for cross-platform IDL (XPIDL) files.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""A parser for cross-platform IDL (XPIDL) files."""
+
+import sys
+import os.path
+import re
+from ply import lex
+from ply import yacc
+
+"""A type conforms to the following pattern:
+
+ def isScriptable(self):
+ 'returns True or False'
+
+ def nativeType(self, calltype):
+ 'returns a string representation of the native type
+ calltype must be 'in', 'out', or 'inout'
+
+Interface members const/method/attribute conform to the following pattern:
+
+ name = 'string'
+
+ def toIDL(self):
+ 'returns the member signature as IDL'
+"""
+
+
+def attlistToIDL(attlist):
+ if len(attlist) == 0:
+ return ''
+
+ attlist = list(attlist)
+ attlist.sort(cmp=lambda a, b: cmp(a[0], b[0]))
+
+ return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
+ for name, value, aloc in attlist])
+
+_paramsHardcode = {
+ 2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
+ 3: ('array', 'size_is', 'const'),
+}
+
+
+def paramAttlistToIDL(attlist):
+ if len(attlist) == 0:
+ return ''
+
+ # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
+ # quirk
+ attlist = list(attlist)
+ sorted = []
+ if len(attlist) in _paramsHardcode:
+ for p in _paramsHardcode[len(attlist)]:
+ i = 0
+ while i < len(attlist):
+ if attlist[i][0] == p:
+ sorted.append(attlist[i])
+ del attlist[i]
+ continue
+
+ i += 1
+
+ sorted.extend(attlist)
+
+ return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
+ for name, value, aloc in sorted])
+
+
+def unaliasType(t):
+ while t.kind == 'typedef':
+ t = t.realtype
+ assert t is not None
+ return t
+
+
+def getBuiltinOrNativeTypeName(t):
+ t = unaliasType(t)
+ if t.kind == 'builtin':
+ return t.name
+ elif t.kind == 'native':
+ assert t.specialtype is not None
+ return '[%s]' % t.specialtype
+ else:
+ return None
+
+
+class BuiltinLocation(object):
+ def get(self):
+ return "<builtin type>"
+
+ def __str__(self):
+ return self.get()
+
+
+class Builtin(object):
+ kind = 'builtin'
+ location = BuiltinLocation
+
+ def __init__(self, name, nativename, signed=False, maybeConst=False):
+ self.name = name
+ self.nativename = nativename
+ self.signed = signed
+ self.maybeConst = maybeConst
+
+ def isScriptable(self):
+ return True
+
+ def nativeType(self, calltype, shared=False, const=False):
+ if const:
+ print >>sys.stderr, IDLError("[const] doesn't make sense on builtin types.", self.location, warning=True)
+ const = 'const '
+ elif calltype == 'in' and self.nativename.endswith('*'):
+ const = 'const '
+ elif shared:
+ if not self.nativename.endswith('*'):
+ raise IDLError("[shared] not applicable to non-pointer types.", self.location)
+ const = 'const '
+ else:
+ const = ''
+ return "%s%s %s" % (const, self.nativename,
+ calltype != 'in' and '*' or '')
+
+builtinNames = [
+ Builtin('boolean', 'bool'),
+ Builtin('void', 'void'),
+ Builtin('octet', 'uint8_t'),
+ Builtin('short', 'int16_t', True, True),
+ Builtin('long', 'int32_t', True, True),
+ Builtin('long long', 'int64_t', True, False),
+ Builtin('unsigned short', 'uint16_t', False, True),
+ Builtin('unsigned long', 'uint32_t', False, True),
+ Builtin('unsigned long long', 'uint64_t', False, False),
+ Builtin('float', 'float', True, False),
+ Builtin('double', 'double', True, False),
+ Builtin('char', 'char', True, False),
+ Builtin('string', 'char *', False, False),
+ Builtin('wchar', 'char16_t', False, False),
+ Builtin('wstring', 'char16_t *', False, False),
+]
+
+builtinMap = {}
+for b in builtinNames:
+ builtinMap[b.name] = b
+
+
+class Location(object):
+ _line = None
+
+ def __init__(self, lexer, lineno, lexpos):
+ self._lineno = lineno
+ self._lexpos = lexpos
+ self._lexdata = lexer.lexdata
+ self._file = getattr(lexer, 'filename', "<unknown>")
+
+ def __eq__(self, other):
+ return (self._lexpos == other._lexpos and
+ self._file == other._file)
+
+ def resolve(self):
+ if self._line:
+ return
+
+ startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
+ endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
+ self._line = self._lexdata[startofline:endofline]
+ self._colno = self._lexpos - startofline
+
+ def pointerline(self):
+ def i():
+ for i in xrange(0, self._colno):
+ yield " "
+ yield "^"
+
+ return "".join(i())
+
+ def get(self):
+ self.resolve()
+ return "%s line %s:%s" % (self._file, self._lineno, self._colno)
+
+ def __str__(self):
+ self.resolve()
+ return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
+ self._line, self.pointerline())
+
+
+class NameMap(object):
+ """Map of name -> object. Each object must have a .name and .location property.
+ Setting the same name twice throws an error."""
+ def __init__(self):
+ self._d = {}
+
+ def __getitem__(self, key):
+ if key in builtinMap:
+ return builtinMap[key]
+ return self._d[key]
+
+ def __iter__(self):
+ return self._d.itervalues()
+
+ def __contains__(self, key):
+ return key in builtinMap or key in self._d
+
+ def set(self, object):
+ if object.name in builtinMap:
+ raise IDLError("name '%s' is a builtin and cannot be redeclared" % (object.name), object.location)
+ if object.name.startswith("_"):
+ object.name = object.name[1:]
+ if object.name in self._d:
+ old = self._d[object.name]
+ if old == object:
+ return
+ if isinstance(old, Forward) and isinstance(object, Interface):
+ self._d[object.name] = object
+ elif isinstance(old, Interface) and isinstance(object, Forward):
+ pass
+ else:
+ raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name, self._d[object.name].location), object.location)
+ else:
+ self._d[object.name] = object
+
+ def get(self, id, location):
+ try:
+ return self[id]
+ except KeyError:
+ raise IDLError("Name '%s' not found", location)
+
+
+class IDLError(Exception):
+ def __init__(self, message, location, warning=False):
+ self.message = message
+ self.location = location
+ self.warning = warning
+
+ def __str__(self):
+ return "%s: %s, %s" % (self.warning and 'warning' or 'error',
+ self.message, self.location)
+
+
+class Include(object):
+ kind = 'include'
+
+ def __init__(self, filename, location):
+ self.filename = filename
+ self.location = location
+
+ def __str__(self):
+ return "".join(["include '%s'\n" % self.filename])
+
+ def resolve(self, parent):
+ def incfiles():
+ yield self.filename
+ for dir in parent.incdirs:
+ yield os.path.join(dir, self.filename)
+
+ for file in incfiles():
+ if not os.path.exists(file):
+ continue
+
+ self.IDL = parent.parser.parse(open(file).read(), filename=file)
+ self.IDL.resolve(parent.incdirs, parent.parser)
+ for type in self.IDL.getNames():
+ parent.setName(type)
+ parent.deps.extend(self.IDL.deps)
+ return
+
+ raise IDLError("File '%s' not found" % self.filename, self.location)
+
+
+class IDL(object):
+ def __init__(self, productions):
+ self.productions = productions
+ self.deps = []
+
+ def setName(self, object):
+ self.namemap.set(object)
+
+ def getName(self, id, location):
+ try:
+ return self.namemap[id]
+ except KeyError:
+ raise IDLError("type '%s' not found" % id, location)
+
+ def hasName(self, id):
+ return id in self.namemap
+
+ def getNames(self):
+ return iter(self.namemap)
+
+ def __str__(self):
+ return "".join([str(p) for p in self.productions])
+
+ def resolve(self, incdirs, parser):
+ self.namemap = NameMap()
+ self.incdirs = incdirs
+ self.parser = parser
+ for p in self.productions:
+ p.resolve(self)
+
+ def includes(self):
+ for p in self.productions:
+ if p.kind == 'include':
+ yield p
+
+ def needsJSTypes(self):
+ for p in self.productions:
+ if p.kind == 'interface' and p.needsJSTypes():
+ return True
+ return False
+
+
+class CDATA(object):
+ kind = 'cdata'
+ _re = re.compile(r'\n+')
+
+ def __init__(self, data, location):
+ self.data = self._re.sub('\n', data)
+ self.location = location
+
+ def resolve(self, parent):
+ pass
+
+ def __str__(self):
+ return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
+
+ def count(self):
+ return 0
+
+
+class Typedef(object):
+ kind = 'typedef'
+
+ def __init__(self, type, name, location, doccomments):
+ self.type = type
+ self.name = name
+ self.location = location
+ self.doccomments = doccomments
+
+ def __eq__(self, other):
+ return self.name == other.name and self.type == other.type
+
+ def resolve(self, parent):
+ parent.setName(self)
+ self.realtype = parent.getName(self.type, self.location)
+
+ def isScriptable(self):
+ return self.realtype.isScriptable()
+
+ def nativeType(self, calltype):
+ return "%s %s" % (self.name,
+ calltype != 'in' and '*' or '')
+
+ def __str__(self):
+ return "typedef %s %s\n" % (self.type, self.name)
+
+
+class Forward(object):
+ kind = 'forward'
+
+ def __init__(self, name, location, doccomments):
+ self.name = name
+ self.location = location
+ self.doccomments = doccomments
+
+ def __eq__(self, other):
+ return other.kind == 'forward' and other.name == self.name
+
+ def resolve(self, parent):
+ # Hack alert: if an identifier is already present, move the doccomments
+ # forward.
+ if parent.hasName(self.name):
+ for i in xrange(0, len(parent.productions)):
+ if parent.productions[i] is self:
+ break
+ for i in xrange(i + 1, len(parent.productions)):
+ if hasattr(parent.productions[i], 'doccomments'):
+ parent.productions[i].doccomments[0:0] = self.doccomments
+ break
+
+ parent.setName(self)
+
+ def isScriptable(self):
+ return True
+
+ def nativeType(self, calltype):
+ return "%s %s" % (self.name,
+ calltype != 'in' and '* *' or '*')
+
+ def __str__(self):
+ return "forward-declared %s\n" % self.name
+
+
+class Native(object):
+ kind = 'native'
+
+ modifier = None
+ specialtype = None
+
+ specialtypes = {
+ 'nsid': None,
+ 'domstring': 'nsAString',
+ 'utf8string': 'nsACString',
+ 'cstring': 'nsACString',
+ 'astring': 'nsAString',
+ 'jsval': 'JS::Value'
+ }
+
+ def __init__(self, name, nativename, attlist, location):
+ self.name = name
+ self.nativename = nativename
+ self.location = location
+
+ for name, value, aloc in attlist:
+ if value is not None:
+ raise IDLError("Unexpected attribute value", aloc)
+ if name in ('ptr', 'ref'):
+ if self.modifier is not None:
+ raise IDLError("More than one ptr/ref modifier", aloc)
+ self.modifier = name
+ elif name in self.specialtypes.keys():
+ if self.specialtype is not None:
+ raise IDLError("More than one special type", aloc)
+ self.specialtype = name
+ if self.specialtypes[name] is not None:
+ self.nativename = self.specialtypes[name]
+ else:
+ raise IDLError("Unexpected attribute", aloc)
+
+ def __eq__(self, other):
+ return (self.name == other.name and
+ self.nativename == other.nativename and
+ self.modifier == other.modifier and
+ self.specialtype == other.specialtype)
+
+ def resolve(self, parent):
+ parent.setName(self)
+
+ def isScriptable(self):
+ if self.specialtype is None:
+ return False
+
+ if self.specialtype == 'nsid':
+ return self.modifier is not None
+
+ return self.modifier == 'ref'
+
+ def isPtr(self, calltype):
+ return self.modifier == 'ptr'
+
+ def isRef(self, calltype):
+ return self.modifier == 'ref'
+
+ def nativeType(self, calltype, const=False, shared=False):
+ if shared:
+ if calltype != 'out':
+ raise IDLError("[shared] only applies to out parameters.")
+ const = True
+
+ if self.specialtype is not None and calltype == 'in':
+ const = True
+
+ if self.specialtype == 'jsval':
+ if calltype == 'out' or calltype == 'inout':
+ return "JS::MutableHandleValue "
+ return "JS::HandleValue "
+
+ if self.isRef(calltype):
+ m = '& '
+ elif self.isPtr(calltype):
+ m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '')
+ else:
+ m = calltype != 'in' and '*' or ''
+ return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
+
+ def __str__(self):
+ return "native %s(%s)\n" % (self.name, self.nativename)
+
+
+class Interface(object):
+ kind = 'interface'
+
+ def __init__(self, name, attlist, base, members, location, doccomments):
+ self.name = name
+ self.attributes = InterfaceAttributes(attlist, location)
+ self.base = base
+ self.members = members
+ self.location = location
+ self.namemap = NameMap()
+ self.doccomments = doccomments
+ self.nativename = name
+
+ for m in members:
+ if not isinstance(m, CDATA):
+ self.namemap.set(m)
+
+ def __eq__(self, other):
+ return self.name == other.name and self.location == other.location
+
+ def resolve(self, parent):
+ self.idl = parent
+
+ # Hack alert: if an identifier is already present, libIDL assigns
+ # doc comments incorrectly. This is quirks-mode extraordinaire!
+ if parent.hasName(self.name):
+ for member in self.members:
+ if hasattr(member, 'doccomments'):
+ member.doccomments[0:0] = self.doccomments
+ break
+ self.doccomments = parent.getName(self.name, None).doccomments
+
+ if self.attributes.function:
+ has_method = False
+ for member in self.members:
+ if member.kind is 'method':
+ if has_method:
+ raise IDLError("interface '%s' has multiple methods, but marked 'function'" % self.name, self.location)
+ else:
+ has_method = True
+
+ parent.setName(self)
+ if self.base is not None:
+ realbase = parent.getName(self.base, self.location)
+ if realbase.kind != 'interface':
+ raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location)
+
+ if self.attributes.scriptable and not realbase.attributes.scriptable:
+ print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
+
+ if self.attributes.scriptable and realbase.attributes.builtinclass and not self.attributes.builtinclass:
+ raise IDLError("interface '%s' is not builtinclass but derives from builtinclass '%s'" % (self.name, self.base), self.location)
+
+ for member in self.members:
+ member.resolve(self)
+
+ # The number 250 is NOT arbitrary; this number is the maximum number of
+ # stub entries defined in xpcom/reflect/xptcall/genstubs.pl
+ # Do not increase this value without increasing the number in that
+ # location, or you WILL cause otherwise unknown problems!
+ if self.countEntries() > 250 and not self.attributes.builtinclass:
+ raise IDLError("interface '%s' has too many entries" % self.name, self.location)
+
+ def isScriptable(self):
+ # NOTE: this is not whether *this* interface is scriptable... it's
+ # whether, when used as a type, it's scriptable, which is true of all
+ # interfaces.
+ return True
+
+ def nativeType(self, calltype, const=False):
+ return "%s%s %s" % (const and 'const ' or '',
+ self.name,
+ calltype != 'in' and '* *' or '*')
+
+ def __str__(self):
+ l = ["interface %s\n" % self.name]
+ if self.base is not None:
+ l.append("\tbase %s\n" % self.base)
+ l.append(str(self.attributes))
+ if self.members is None:
+ l.append("\tincomplete type\n")
+ else:
+ for m in self.members:
+ l.append(str(m))
+ return "".join(l)
+
+ def getConst(self, name, location):
+ # The constant may be in a base class
+ iface = self
+ while name not in iface.namemap and iface is not None:
+ iface = self.idl.getName(self.base, self.location)
+ if iface is None:
+ raise IDLError("cannot find symbol '%s'" % name)
+ c = iface.namemap.get(name, location)
+ if c.kind != 'const':
+ raise IDLError("symbol '%s' is not a constant", c.location)
+
+ return c.getValue()
+
+ def needsJSTypes(self):
+ for m in self.members:
+ if m.kind == "attribute" and m.type == "jsval":
+ return True
+ if m.kind == "method" and m.needsJSTypes():
+ return True
+ return False
+
+ def countEntries(self):
+ ''' Returns the number of entries in the vtable for this interface. '''
+ total = sum(member.count() for member in self.members)
+ if self.base is not None:
+ realbase = self.idl.getName(self.base, self.location)
+ total += realbase.countEntries()
+ return total
+
+
+class InterfaceAttributes(object):
+ uuid = None
+ scriptable = False
+ builtinclass = False
+ function = False
+ deprecated = False
+ noscript = False
+ main_process_scriptable_only = False
+
+ def setuuid(self, value):
+ self.uuid = value.lower()
+
+ def setscriptable(self):
+ self.scriptable = True
+
+ def setfunction(self):
+ self.function = True
+
+ def setnoscript(self):
+ self.noscript = True
+
+ def setbuiltinclass(self):
+ self.builtinclass = True
+
+ def setdeprecated(self):
+ self.deprecated = True
+
+ def setmain_process_scriptable_only(self):
+ self.main_process_scriptable_only = True
+
+ actions = {
+ 'uuid': (True, setuuid),
+ 'scriptable': (False, setscriptable),
+ 'builtinclass': (False, setbuiltinclass),
+ 'function': (False, setfunction),
+ 'noscript': (False, setnoscript),
+ 'deprecated': (False, setdeprecated),
+ 'object': (False, lambda self: True),
+ 'main_process_scriptable_only': (False, setmain_process_scriptable_only),
+ }
+
+ def __init__(self, attlist, location):
+ def badattribute(self):
+ raise IDLError("Unexpected interface attribute '%s'" % name, location)
+
+ for name, val, aloc in attlist:
+ hasval, action = self.actions.get(name, (False, badattribute))
+ if hasval:
+ if val is None:
+ raise IDLError("Expected value for attribute '%s'" % name,
+ aloc)
+
+ action(self, val)
+ else:
+ if val is not None:
+ raise IDLError("Unexpected value for attribute '%s'" % name,
+ aloc)
+
+ action(self)
+
+ if self.uuid is None:
+ raise IDLError("interface has no uuid", location)
+
+ def __str__(self):
+ l = []
+ if self.uuid:
+ l.append("\tuuid: %s\n" % self.uuid)
+ if self.scriptable:
+ l.append("\tscriptable\n")
+ if self.builtinclass:
+ l.append("\tbuiltinclass\n")
+ if self.function:
+ l.append("\tfunction\n")
+ if self.main_process_scriptable_only:
+ l.append("\tmain_process_scriptable_only\n")
+ return "".join(l)
+
+
+class ConstMember(object):
+ kind = 'const'
+
+ def __init__(self, type, name, value, location, doccomments):
+ self.type = type
+ self.name = name
+ self.value = value
+ self.location = location
+ self.doccomments = doccomments
+
+ def resolve(self, parent):
+ self.realtype = parent.idl.getName(self.type, self.location)
+ self.iface = parent
+ basetype = self.realtype
+ while isinstance(basetype, Typedef):
+ basetype = basetype.realtype
+ if not isinstance(basetype, Builtin) or not basetype.maybeConst:
+ raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
+
+ self.basetype = basetype
+
+ def getValue(self):
+ return self.value(self.iface)
+
+ def __str__(self):
+ return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue())
+
+ def count(self):
+ return 0
+
+
+class Attribute(object):
+ kind = 'attribute'
+ noscript = False
+ readonly = False
+ implicit_jscontext = False
+ nostdcall = False
+ must_use = False
+ binaryname = None
+ null = None
+ undefined = None
+ deprecated = False
+ infallible = False
+
+ def __init__(self, type, name, attlist, readonly, location, doccomments):
+ self.type = type
+ self.name = name
+ self.attlist = attlist
+ self.readonly = readonly
+ self.location = location
+ self.doccomments = doccomments
+
+ for name, value, aloc in attlist:
+ if name == 'binaryname':
+ if value is None:
+ raise IDLError("binaryname attribute requires a value",
+ aloc)
+
+ self.binaryname = value
+ continue
+
+ if name == 'Null':
+ if value is None:
+ raise IDLError("'Null' attribute requires a value", aloc)
+ if readonly:
+ raise IDLError("'Null' attribute only makes sense for setters",
+ aloc)
+ if value not in ('Empty', 'Null', 'Stringify'):
+ raise IDLError("'Null' attribute value must be 'Empty', 'Null' or 'Stringify'",
+ aloc)
+ self.null = value
+ elif name == 'Undefined':
+ if value is None:
+ raise IDLError("'Undefined' attribute requires a value", aloc)
+ if readonly:
+ raise IDLError("'Undefined' attribute only makes sense for setters",
+ aloc)
+ if value not in ('Empty', 'Null'):
+ raise IDLError("'Undefined' attribute value must be 'Empty' or 'Null'",
+ aloc)
+ self.undefined = value
+ else:
+ if value is not None:
+ raise IDLError("Unexpected attribute value", aloc)
+
+ if name == 'noscript':
+ self.noscript = True
+ elif name == 'implicit_jscontext':
+ self.implicit_jscontext = True
+ elif name == 'deprecated':
+ self.deprecated = True
+ elif name == 'nostdcall':
+ self.nostdcall = True
+ elif name == 'must_use':
+ self.must_use = True
+ elif name == 'infallible':
+ self.infallible = True
+ else:
+ raise IDLError("Unexpected attribute '%s'" % name, aloc)
+
+ def resolve(self, iface):
+ self.iface = iface
+ self.realtype = iface.idl.getName(self.type, self.location)
+ if (self.null is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Null' attribute can only be used on DOMString",
+ self.location)
+ if (self.undefined is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Undefined' attribute can only be used on DOMString",
+ self.location)
+ if self.infallible and not self.realtype.kind == 'builtin':
+ raise IDLError('[infallible] only works on builtin types '
+ '(numbers, booleans, and raw char types)',
+ self.location)
+ if self.infallible and not iface.attributes.builtinclass:
+ raise IDLError('[infallible] attributes are only allowed on '
+ '[builtinclass] interfaces',
+ self.location)
+
+ def toIDL(self):
+ attribs = attlistToIDL(self.attlist)
+ readonly = self.readonly and 'readonly ' or ''
+ return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
+
+ def isScriptable(self):
+ if not self.iface.attributes.scriptable:
+ return False
+ return not self.noscript
+
+ def __str__(self):
+ return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
+ self.type, self.name)
+
+ def count(self):
+ return self.readonly and 1 or 2
+
+
+class Method(object):
+ kind = 'method'
+ noscript = False
+ notxpcom = False
+ binaryname = None
+ implicit_jscontext = False
+ nostdcall = False
+ must_use = False
+ optional_argc = False
+ deprecated = False
+
+ def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
+ self.type = type
+ self.name = name
+ self.attlist = attlist
+ self.params = paramlist
+ self.location = location
+ self.doccomments = doccomments
+ self.raises = raises
+
+ for name, value, aloc in attlist:
+ if name == 'binaryname':
+ if value is None:
+ raise IDLError("binaryname attribute requires a value",
+ aloc)
+
+ self.binaryname = value
+ continue
+
+ if value is not None:
+ raise IDLError("Unexpected attribute value", aloc)
+
+ if name == 'noscript':
+ self.noscript = True
+ elif name == 'notxpcom':
+ self.notxpcom = True
+ elif name == 'implicit_jscontext':
+ self.implicit_jscontext = True
+ elif name == 'optional_argc':
+ self.optional_argc = True
+ elif name == 'deprecated':
+ self.deprecated = True
+ elif name == 'nostdcall':
+ self.nostdcall = True
+ elif name == 'must_use':
+ self.must_use = True
+ else:
+ raise IDLError("Unexpected attribute '%s'" % name, aloc)
+
+ self.namemap = NameMap()
+ for p in paramlist:
+ self.namemap.set(p)
+
+ def resolve(self, iface):
+ self.iface = iface
+ self.realtype = self.iface.idl.getName(self.type, self.location)
+ for p in self.params:
+ p.resolve(self)
+ for p in self.params:
+ if p.retval and p != self.params[-1]:
+ raise IDLError("'retval' parameter '%s' is not the last parameter" % p.name, self.location)
+ if p.size_is:
+ found_size_param = False
+ for size_param in self.params:
+ if p.size_is == size_param.name:
+ found_size_param = True
+ if getBuiltinOrNativeTypeName(size_param.realtype) != 'unsigned long':
+ raise IDLError("is_size parameter must have type 'unsigned long'", self.location)
+ if not found_size_param:
+ raise IDLError("could not find is_size parameter '%s'" % p.size_is, self.location)
+
+ def isScriptable(self):
+ if not self.iface.attributes.scriptable:
+ return False
+ return not (self.noscript or self.notxpcom)
+
+ def __str__(self):
+ return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
+
+ def toIDL(self):
+ if len(self.raises):
+ raises = ' raises (%s)' % ','.join(self.raises)
+ else:
+ raises = ''
+
+ return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
+ self.type,
+ self.name,
+ ", ".join([p.toIDL()
+ for p in self.params]),
+ raises)
+
+ def needsJSTypes(self):
+ if self.implicit_jscontext:
+ return True
+ if self.type == "jsval":
+ return True
+ for p in self.params:
+ t = p.realtype
+ if isinstance(t, Native) and t.specialtype == "jsval":
+ return True
+ return False
+
+ def count(self):
+ return 1
+
+
+class Param(object):
+ size_is = None
+ iid_is = None
+ const = False
+ array = False
+ retval = False
+ shared = False
+ optional = False
+ null = None
+ undefined = None
+
+ def __init__(self, paramtype, type, name, attlist, location, realtype=None):
+ self.paramtype = paramtype
+ self.type = type
+ self.name = name
+ self.attlist = attlist
+ self.location = location
+ self.realtype = realtype
+
+ for name, value, aloc in attlist:
+ # Put the value-taking attributes first!
+ if name == 'size_is':
+ if value is None:
+ raise IDLError("'size_is' must specify a parameter", aloc)
+ self.size_is = value
+ elif name == 'iid_is':
+ if value is None:
+ raise IDLError("'iid_is' must specify a parameter", aloc)
+ self.iid_is = value
+ elif name == 'Null':
+ if value is None:
+ raise IDLError("'Null' must specify a parameter", aloc)
+ if value not in ('Empty', 'Null', 'Stringify'):
+ raise IDLError("'Null' parameter value must be 'Empty', 'Null', or 'Stringify'",
+ aloc)
+ self.null = value
+ elif name == 'Undefined':
+ if value is None:
+ raise IDLError("'Undefined' must specify a parameter", aloc)
+ if value not in ('Empty', 'Null'):
+ raise IDLError("'Undefined' parameter value must be 'Empty' or 'Null'",
+ aloc)
+ self.undefined = value
+ else:
+ if value is not None:
+ raise IDLError("Unexpected value for attribute '%s'" % name,
+ aloc)
+
+ if name == 'const':
+ self.const = True
+ elif name == 'array':
+ self.array = True
+ elif name == 'retval':
+ self.retval = True
+ elif name == 'shared':
+ self.shared = True
+ elif name == 'optional':
+ self.optional = True
+ else:
+ raise IDLError("Unexpected attribute '%s'" % name, aloc)
+
+ def resolve(self, method):
+ self.realtype = method.iface.idl.getName(self.type, self.location)
+ if self.array:
+ self.realtype = Array(self.realtype)
+ if (self.null is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Null' attribute can only be used on DOMString",
+ self.location)
+ if (self.undefined is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Undefined' attribute can only be used on DOMString",
+ self.location)
+
+ def nativeType(self):
+ kwargs = {}
+ if self.shared:
+ kwargs['shared'] = True
+ if self.const:
+ kwargs['const'] = True
+
+ try:
+ return self.realtype.nativeType(self.paramtype, **kwargs)
+ except IDLError, e:
+ raise IDLError(e.message, self.location)
+ except TypeError, e:
+ raise IDLError("Unexpected parameter attribute", self.location)
+
+ def toIDL(self):
+ return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
+ self.paramtype,
+ self.type,
+ self.name)
+
+
+class Array(object):
+ def __init__(self, basetype):
+ self.type = basetype
+
+ def isScriptable(self):
+ return self.type.isScriptable()
+
+ def nativeType(self, calltype, const=False):
+ return "%s%s*" % (const and 'const ' or '',
+ self.type.nativeType(calltype))
+
+
+class IDLParser(object):
+ keywords = {
+ 'const': 'CONST',
+ 'interface': 'INTERFACE',
+ 'in': 'IN',
+ 'inout': 'INOUT',
+ 'out': 'OUT',
+ 'attribute': 'ATTRIBUTE',
+ 'raises': 'RAISES',
+ 'readonly': 'READONLY',
+ 'native': 'NATIVE',
+ 'typedef': 'TYPEDEF',
+ }
+
+ tokens = [
+ 'IDENTIFIER',
+ 'CDATA',
+ 'INCLUDE',
+ 'IID',
+ 'NUMBER',
+ 'HEXNUM',
+ 'LSHIFT',
+ 'RSHIFT',
+ 'NATIVEID',
+ ]
+
+ tokens.extend(keywords.values())
+
+ states = (
+ ('nativeid', 'exclusive'),
+ )
+
+ hexchar = r'[a-fA-F0-9]'
+
+ t_NUMBER = r'-?\d+'
+ t_HEXNUM = r'0x%s+' % hexchar
+ t_LSHIFT = r'<<'
+ t_RSHIFT = r'>>'
+
+ literals = '"(){}[],;:=|+-*'
+
+ t_ignore = ' \t'
+
+ def t_multilinecomment(self, t):
+ r'/\*(?s).*?\*/'
+ t.lexer.lineno += t.value.count('\n')
+ if t.value.startswith("/**"):
+ self._doccomments.append(t.value)
+
+ def t_singlelinecomment(self, t):
+ r'(?m)//.*?$'
+
+ def t_IID(self, t):
+ return t
+ t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
+
+ def t_IDENTIFIER(self, t):
+ r'(unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long)(?!_?[A-Za-z][A-Za-z_0-9])|_?[A-Za-z][A-Za-z_0-9]*'
+ t.type = self.keywords.get(t.value, 'IDENTIFIER')
+ return t
+
+ def t_LCDATA(self, t):
+ r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
+ t.type = 'CDATA'
+ t.value = t.lexer.lexmatch.group('cdata')
+ t.lexer.lineno += t.value.count('\n')
+ return t
+
+ def t_INCLUDE(self, t):
+ r'\#include[ \t]+"[^"\n]+"'
+ inc, value, end = t.value.split('"')
+ t.value = value
+ return t
+
+ def t_directive(self, t):
+ r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
+ raise IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
+ Location(lexer=self.lexer, lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos))
+
+ def t_newline(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+
+ def t_nativeid_NATIVEID(self, t):
+ r'[^()\n]+(?=\))'
+ t.lexer.begin('INITIAL')
+ return t
+
+ t_nativeid_ignore = ''
+
+ def t_ANY_error(self, t):
+ raise IDLError("unrecognized input",
+ Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos))
+
+ precedence = (
+ ('left', '|'),
+ ('left', 'LSHIFT', 'RSHIFT'),
+ ('left', '+', '-'),
+ ('left', '*'),
+ ('left', 'UMINUS'),
+ )
+
+ def p_idlfile(self, p):
+ """idlfile : productions"""
+ p[0] = IDL(p[1])
+
+ def p_productions_start(self, p):
+ """productions : """
+ p[0] = []
+
+ def p_productions_cdata(self, p):
+ """productions : CDATA productions"""
+ p[0] = list(p[2])
+ p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
+
+ def p_productions_include(self, p):
+ """productions : INCLUDE productions"""
+ p[0] = list(p[2])
+ p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
+
+ def p_productions_interface(self, p):
+ """productions : interface productions
+ | typedef productions
+ | native productions"""
+ p[0] = list(p[2])
+ p[0].insert(0, p[1])
+
+ def p_typedef(self, p):
+ """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
+ p[0] = Typedef(type=p[2],
+ name=p[3],
+ location=self.getLocation(p, 1),
+ doccomments=p.slice[1].doccomments)
+
+ def p_native(self, p):
+ """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
+ p[0] = Native(name=p[3],
+ nativename=p[6],
+ attlist=p[1]['attlist'],
+ location=self.getLocation(p, 2))
+
+ def p_afternativeid(self, p):
+ """afternativeid : """
+ # this is a place marker: we switch the lexer into literal identifier
+ # mode here, to slurp up everything until the closeparen
+ self.lexer.begin('nativeid')
+
+ def p_anyident(self, p):
+ """anyident : IDENTIFIER
+ | CONST"""
+ p[0] = {'value': p[1],
+ 'location': self.getLocation(p, 1)}
+
+ def p_attributes(self, p):
+ """attributes : '[' attlist ']'
+ | """
+ if len(p) == 1:
+ p[0] = {'attlist': []}
+ else:
+ p[0] = {'attlist': p[2],
+ 'doccomments': p.slice[1].doccomments}
+
+ def p_attlist_start(self, p):
+ """attlist : attribute"""
+ p[0] = [p[1]]
+
+ def p_attlist_continue(self, p):
+ """attlist : attribute ',' attlist"""
+ p[0] = list(p[3])
+ p[0].insert(0, p[1])
+
+ def p_attribute(self, p):
+ """attribute : anyident attributeval"""
+ p[0] = (p[1]['value'], p[2], p[1]['location'])
+
+ def p_attributeval(self, p):
+ """attributeval : '(' IDENTIFIER ')'
+ | '(' IID ')'
+ | """
+ if len(p) > 1:
+ p[0] = p[2]
+
+ def p_interface(self, p):
+ """interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
+ atts, INTERFACE, name, base, body, SEMI = p[1:]
+ attlist = atts['attlist']
+ doccomments = []
+ if 'doccomments' in atts:
+ doccomments.extend(atts['doccomments'])
+ doccomments.extend(p.slice[2].doccomments)
+
+ l = lambda: self.getLocation(p, 2)
+
+ if body is None:
+ # forward-declared interface... must not have attributes!
+ if len(attlist) != 0:
+ raise IDLError("Forward-declared interface must not have attributes",
+ list[0][3])
+
+ if base is not None:
+ raise IDLError("Forward-declared interface must not have a base",
+ l())
+ p[0] = Forward(name=name, location=l(), doccomments=doccomments)
+ else:
+ p[0] = Interface(name=name,
+ attlist=attlist,
+ base=base,
+ members=body,
+ location=l(),
+ doccomments=doccomments)
+
+ def p_ifacebody(self, p):
+ """ifacebody : '{' members '}'
+ | """
+ if len(p) > 1:
+ p[0] = p[2]
+
+ def p_ifacebase(self, p):
+ """ifacebase : ':' IDENTIFIER
+ | """
+ if len(p) == 3:
+ p[0] = p[2]
+
+ def p_members_start(self, p):
+ """members : """
+ p[0] = []
+
+ def p_members_continue(self, p):
+ """members : member members"""
+ p[0] = list(p[2])
+ p[0].insert(0, p[1])
+
+ def p_member_cdata(self, p):
+ """member : CDATA"""
+ p[0] = CDATA(p[1], self.getLocation(p, 1))
+
+ def p_member_const(self, p):
+ """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
+ p[0] = ConstMember(type=p[2], name=p[3],
+ value=p[5], location=self.getLocation(p, 1),
+ doccomments=p.slice[1].doccomments)
+
+# All "number" products return a function(interface)
+
+ def p_number_decimal(self, p):
+ """number : NUMBER"""
+ n = int(p[1])
+ p[0] = lambda i: n
+
+ def p_number_hex(self, p):
+ """number : HEXNUM"""
+ n = int(p[1], 16)
+ p[0] = lambda i: n
+
+ def p_number_identifier(self, p):
+ """number : IDENTIFIER"""
+ id = p[1]
+ loc = self.getLocation(p, 1)
+ p[0] = lambda i: i.getConst(id, loc)
+
+ def p_number_paren(self, p):
+ """number : '(' number ')'"""
+ p[0] = p[2]
+
+ def p_number_neg(self, p):
+ """number : '-' number %prec UMINUS"""
+ n = p[2]
+ p[0] = lambda i: - n(i)
+
+ def p_number_add(self, p):
+ """number : number '+' number
+ | number '-' number
+ | number '*' number"""
+ n1 = p[1]
+ n2 = p[3]
+ if p[2] == '+':
+ p[0] = lambda i: n1(i) + n2(i)
+ elif p[2] == '-':
+ p[0] = lambda i: n1(i) - n2(i)
+ else:
+ p[0] = lambda i: n1(i) * n2(i)
+
+ def p_number_shift(self, p):
+ """number : number LSHIFT number
+ | number RSHIFT number"""
+ n1 = p[1]
+ n2 = p[3]
+ if p[2] == '<<':
+ p[0] = lambda i: n1(i) << n2(i)
+ else:
+ p[0] = lambda i: n1(i) >> n2(i)
+
+ def p_number_bitor(self, p):
+ """number : number '|' number"""
+ n1 = p[1]
+ n2 = p[3]
+ p[0] = lambda i: n1(i) | n2(i)
+
+ def p_member_att(self, p):
+ """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
+ if 'doccomments' in p[1]:
+ doccomments = p[1]['doccomments']
+ elif p[2] is not None:
+ doccomments = p[2]
+ else:
+ doccomments = p.slice[3].doccomments
+
+ p[0] = Attribute(type=p[4],
+ name=p[5],
+ attlist=p[1]['attlist'],
+ readonly=p[2] is not None,
+ location=self.getLocation(p, 3),
+ doccomments=doccomments)
+
+ def p_member_method(self, p):
+ """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
+ if 'doccomments' in p[1]:
+ doccomments = p[1]['doccomments']
+ else:
+ doccomments = p.slice[2].doccomments
+
+ p[0] = Method(type=p[2],
+ name=p[3],
+ attlist=p[1]['attlist'],
+ paramlist=p[5],
+ location=self.getLocation(p, 3),
+ doccomments=doccomments,
+ raises=p[7])
+
+ def p_paramlist(self, p):
+ """paramlist : param moreparams
+ | """
+ if len(p) == 1:
+ p[0] = []
+ else:
+ p[0] = list(p[2])
+ p[0].insert(0, p[1])
+
+ def p_moreparams_start(self, p):
+ """moreparams :"""
+ p[0] = []
+
+ def p_moreparams_continue(self, p):
+ """moreparams : ',' param moreparams"""
+ p[0] = list(p[3])
+ p[0].insert(0, p[2])
+
+ def p_param(self, p):
+ """param : attributes paramtype IDENTIFIER IDENTIFIER"""
+ p[0] = Param(paramtype=p[2],
+ type=p[3],
+ name=p[4],
+ attlist=p[1]['attlist'],
+ location=self.getLocation(p, 3))
+
+ def p_paramtype(self, p):
+ """paramtype : IN
+ | INOUT
+ | OUT"""
+ p[0] = p[1]
+
+ def p_optreadonly(self, p):
+ """optreadonly : READONLY
+ | """
+ if len(p) > 1:
+ p[0] = p.slice[1].doccomments
+ else:
+ p[0] = None
+
+ def p_raises(self, p):
+ """raises : RAISES '(' idlist ')'
+ | """
+ if len(p) == 1:
+ p[0] = []
+ else:
+ p[0] = p[3]
+
+ def p_idlist(self, p):
+ """idlist : IDENTIFIER"""
+ p[0] = [p[1]]
+
+ def p_idlist_continue(self, p):
+ """idlist : IDENTIFIER ',' idlist"""
+ p[0] = list(p[3])
+ p[0].insert(0, p[1])
+
+ def p_error(self, t):
+ if not t:
+ raise IDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", None)
+ else:
+ location = Location(self.lexer, t.lineno, t.lexpos)
+ raise IDLError("invalid syntax", location)
+
+ def __init__(self, outputdir=''):
+ self._doccomments = []
+ self.lexer = lex.lex(object=self,
+ outputdir=outputdir,
+ lextab='xpidllex',
+ optimize=1)
+ self.parser = yacc.yacc(module=self,
+ outputdir=outputdir,
+ debug=0,
+ tabmodule='xpidlyacc',
+ optimize=1)
+
+ def clearComments(self):
+ self._doccomments = []
+
+ def token(self):
+ t = self.lexer.token()
+ if t is not None and t.type != 'CDATA':
+ t.doccomments = self._doccomments
+ self._doccomments = []
+ return t
+
+ def parse(self, data, filename=None):
+ if filename is not None:
+ self.lexer.filename = filename
+ self.lexer.lineno = 1
+ self.lexer.input(data)
+ idl = self.parser.parse(lexer=self)
+ if filename is not None:
+ idl.deps.append(filename)
+ return idl
+
+ def getLocation(self, p, i):
+ return Location(self.lexer, p.lineno(i), p.lexpos(i))
+
+if __name__ == '__main__':
+ p = IDLParser()
+ for f in sys.argv[1:]:
+ print "Parsing %s" % f
+ p.parse(open(f).read(), filename=f)
diff --git a/xpcom/io/Base64.cpp b/xpcom/io/Base64.cpp
new file mode 100644
index 000000000..911c0595a
--- /dev/null
+++ b/xpcom/io/Base64.cpp
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Base64.h"
+
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsIInputStream.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+#include "plbase64.h"
+
+namespace {
+
+// BEGIN base64 encode code copied and modified from NSPR
+const unsigned char* base =
+ (unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+template<typename T>
+static void
+Encode3to4(const unsigned char* aSrc, T* aDest)
+{
+ uint32_t b32 = (uint32_t)0;
+ int i, j = 18;
+
+ for (i = 0; i < 3; ++i) {
+ b32 <<= 8;
+ b32 |= (uint32_t)aSrc[i];
+ }
+
+ for (i = 0; i < 4; ++i) {
+ aDest[i] = base[(uint32_t)((b32 >> j) & 0x3F)];
+ j -= 6;
+ }
+}
+
+template<typename T>
+static void
+Encode2to4(const unsigned char* aSrc, T* aDest)
+{
+ aDest[0] = base[(uint32_t)((aSrc[0] >> 2) & 0x3F)];
+ aDest[1] = base[(uint32_t)(((aSrc[0] & 0x03) << 4) | ((aSrc[1] >> 4) & 0x0F))];
+ aDest[2] = base[(uint32_t)((aSrc[1] & 0x0F) << 2)];
+ aDest[3] = (unsigned char)'=';
+}
+
+template<typename T>
+static void
+Encode1to4(const unsigned char* aSrc, T* aDest)
+{
+ aDest[0] = base[(uint32_t)((aSrc[0] >> 2) & 0x3F)];
+ aDest[1] = base[(uint32_t)((aSrc[0] & 0x03) << 4)];
+ aDest[2] = (unsigned char)'=';
+ aDest[3] = (unsigned char)'=';
+}
+
+template<typename T>
+static void
+Encode(const unsigned char* aSrc, uint32_t aSrcLen, T* aDest)
+{
+ while (aSrcLen >= 3) {
+ Encode3to4(aSrc, aDest);
+ aSrc += 3;
+ aDest += 4;
+ aSrcLen -= 3;
+ }
+
+ switch (aSrcLen) {
+ case 2:
+ Encode2to4(aSrc, aDest);
+ break;
+ case 1:
+ Encode1to4(aSrc, aDest);
+ break;
+ case 0:
+ break;
+ default:
+ NS_NOTREACHED("coding error");
+ }
+}
+
+// END base64 encode code copied and modified from NSPR.
+
+template<typename T>
+struct EncodeInputStream_State
+{
+ unsigned char c[3];
+ uint8_t charsOnStack;
+ typename T::char_type* buffer;
+};
+
+template<typename T>
+nsresult
+EncodeInputStream_Encoder(nsIInputStream* aStream,
+ void* aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ NS_ASSERTION(aCount > 0, "Er, what?");
+
+ EncodeInputStream_State<T>* state =
+ static_cast<EncodeInputStream_State<T>*>(aClosure);
+
+ // If we have any data left from last time, encode it now.
+ uint32_t countRemaining = aCount;
+ const unsigned char* src = (const unsigned char*)aFromSegment;
+ if (state->charsOnStack) {
+ unsigned char firstSet[4];
+ if (state->charsOnStack == 1) {
+ firstSet[0] = state->c[0];
+ firstSet[1] = src[0];
+ firstSet[2] = (countRemaining > 1) ? src[1] : '\0';
+ firstSet[3] = '\0';
+ } else /* state->charsOnStack == 2 */ {
+ firstSet[0] = state->c[0];
+ firstSet[1] = state->c[1];
+ firstSet[2] = src[0];
+ firstSet[3] = '\0';
+ }
+ Encode(firstSet, 3, state->buffer);
+ state->buffer += 4;
+ countRemaining -= (3 - state->charsOnStack);
+ src += (3 - state->charsOnStack);
+ state->charsOnStack = 0;
+ }
+
+ // Encode the bulk of the
+ uint32_t encodeLength = countRemaining - countRemaining % 3;
+ MOZ_ASSERT(encodeLength % 3 == 0,
+ "Should have an exact number of triplets!");
+ Encode(src, encodeLength, state->buffer);
+ state->buffer += (encodeLength / 3) * 4;
+ src += encodeLength;
+ countRemaining -= encodeLength;
+
+ // We must consume all data, so if there's some data left stash it
+ *aWriteCount = aCount;
+
+ if (countRemaining) {
+ // We should never have a full triplet left at this point.
+ MOZ_ASSERT(countRemaining < 3, "We should have encoded more!");
+ state->c[0] = src[0];
+ state->c[1] = (countRemaining == 2) ? src[1] : '\0';
+ state->charsOnStack = countRemaining;
+ }
+
+ return NS_OK;
+}
+
+template<typename T>
+nsresult
+EncodeInputStream(nsIInputStream* aInputStream,
+ T& aDest,
+ uint32_t aCount,
+ uint32_t aOffset)
+{
+ nsresult rv;
+ uint64_t count64 = aCount;
+
+ if (!aCount) {
+ rv = aInputStream->Available(&count64);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // if count64 is over 4GB, it will be failed at the below condition,
+ // then will return NS_ERROR_OUT_OF_MEMORY
+ aCount = (uint32_t)count64;
+ }
+
+ uint64_t countlong =
+ (count64 + 2) / 3 * 4; // +2 due to integer math.
+ if (countlong + aOffset > UINT32_MAX) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t count = uint32_t(countlong);
+
+ if (!aDest.SetLength(count + aOffset, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ EncodeInputStream_State<T> state;
+ state.charsOnStack = 0;
+ state.c[2] = '\0';
+ state.buffer = aOffset + aDest.BeginWriting();
+
+ while (1) {
+ uint32_t read = 0;
+
+ rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder<T>,
+ (void*)&state,
+ aCount,
+ &read);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ NS_RUNTIMEABORT("Not implemented for async streams!");
+ }
+ if (rv == NS_ERROR_NOT_IMPLEMENTED) {
+ NS_RUNTIMEABORT("Requires a stream that implements ReadSegments!");
+ }
+ return rv;
+ }
+
+ if (!read) {
+ break;
+ }
+ }
+
+ // Finish encoding if anything is left
+ if (state.charsOnStack) {
+ Encode(state.c, state.charsOnStack, state.buffer);
+ }
+
+ if (aDest.Length()) {
+ // May belong to an nsCString with an unallocated buffer, so only null
+ // terminate if there is a need to.
+ *aDest.EndWriting() = '\0';
+ }
+
+ return NS_OK;
+}
+
+static const char kBase64URLAlphabet[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+// Maps an encoded character to a value in the Base64 URL alphabet, per
+// RFC 4648, Table 2. Invalid input characters map to UINT8_MAX.
+static const uint8_t kBase64URLDecodeTable[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255,
+ 62 /* - */,
+ 255, 255,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* 0 - 9 */
+ 255, 255, 255, 255, 255, 255, 255,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* A - Z */
+ 255, 255, 255, 255,
+ 63 /* _ */,
+ 255,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* a - z */
+ 255, 255, 255, 255,
+};
+
+bool
+Base64URLCharToValue(char aChar, uint8_t* aValue) {
+ uint8_t index = static_cast<uint8_t>(aChar);
+ *aValue = kBase64URLDecodeTable[index & 0x7f];
+ return (*aValue != 255) && !(index & ~0x7f);
+}
+
+} // namespace
+
+namespace mozilla {
+
+nsresult
+Base64EncodeInputStream(nsIInputStream* aInputStream,
+ nsACString& aDest,
+ uint32_t aCount,
+ uint32_t aOffset)
+{
+ return EncodeInputStream<nsACString>(aInputStream, aDest, aCount, aOffset);
+}
+
+nsresult
+Base64EncodeInputStream(nsIInputStream* aInputStream,
+ nsAString& aDest,
+ uint32_t aCount,
+ uint32_t aOffset)
+{
+ return EncodeInputStream<nsAString>(aInputStream, aDest, aCount, aOffset);
+}
+
+nsresult
+Base64Encode(const char* aBinary, uint32_t aBinaryLen, char** aBase64)
+{
+ // Check for overflow.
+ if (aBinaryLen > (UINT32_MAX / 4) * 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't ask PR_Base64Encode to encode empty strings.
+ if (aBinaryLen == 0) {
+ *aBase64 = (char*)moz_xmalloc(1);
+ (*aBase64)[0] = '\0';
+ return NS_OK;
+ }
+
+ *aBase64 = nullptr;
+ uint32_t base64Len = ((aBinaryLen + 2) / 3) * 4;
+
+ // Add one byte for null termination.
+ UniqueFreePtr<char[]> base64((char*)malloc(base64Len + 1));
+ if (!base64) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_Base64Encode(aBinary, aBinaryLen, base64.get())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // PL_Base64Encode doesn't null terminate the buffer for us when we pass
+ // the buffer in. Do that manually.
+ base64[base64Len] = '\0';
+
+ *aBase64 = base64.release();
+ return NS_OK;
+}
+
+nsresult
+Base64Encode(const nsACString& aBinary, nsACString& aBase64)
+{
+ // Check for overflow.
+ if (aBinary.Length() > (UINT32_MAX / 4) * 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't ask PR_Base64Encode to encode empty strings.
+ if (aBinary.IsEmpty()) {
+ aBase64.Truncate();
+ return NS_OK;
+ }
+
+ uint32_t base64Len = ((aBinary.Length() + 2) / 3) * 4;
+
+ // Add one byte for null termination.
+ if (!aBase64.SetCapacity(base64Len + 1, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char* base64 = aBase64.BeginWriting();
+ if (!PL_Base64Encode(aBinary.BeginReading(), aBinary.Length(), base64)) {
+ aBase64.Truncate();
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // PL_Base64Encode doesn't null terminate the buffer for us when we pass
+ // the buffer in. Do that manually.
+ base64[base64Len] = '\0';
+
+ aBase64.SetLength(base64Len);
+ return NS_OK;
+}
+
+nsresult
+Base64Encode(const nsAString& aBinary, nsAString& aBase64)
+{
+ NS_LossyConvertUTF16toASCII binary(aBinary);
+ nsAutoCString base64;
+
+ nsresult rv = Base64Encode(binary, base64);
+ if (NS_SUCCEEDED(rv)) {
+ CopyASCIItoUTF16(base64, aBase64);
+ } else {
+ aBase64.Truncate();
+ }
+
+ return rv;
+}
+
+static nsresult
+Base64DecodeHelper(const char* aBase64, uint32_t aBase64Len, char* aBinary,
+ uint32_t* aBinaryLen)
+{
+ MOZ_ASSERT(aBinary);
+ if (!PL_Base64Decode(aBase64, aBase64Len, aBinary)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // PL_Base64Decode doesn't null terminate the buffer for us when we pass
+ // the buffer in. Do that manually, taking into account the number of '='
+ // characters we were passed.
+ if (aBase64Len != 0 && aBase64[aBase64Len - 1] == '=') {
+ if (aBase64Len > 1 && aBase64[aBase64Len - 2] == '=') {
+ *aBinaryLen -= 2;
+ } else {
+ *aBinaryLen -= 1;
+ }
+ }
+ aBinary[*aBinaryLen] = '\0';
+ return NS_OK;
+}
+
+nsresult
+Base64Decode(const char* aBase64, uint32_t aBase64Len, char** aBinary,
+ uint32_t* aBinaryLen)
+{
+ // Check for overflow.
+ if (aBase64Len > UINT32_MAX / 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't ask PR_Base64Decode to decode the empty string.
+ if (aBase64Len == 0) {
+ *aBinary = (char*)moz_xmalloc(1);
+ (*aBinary)[0] = '\0';
+ *aBinaryLen = 0;
+ return NS_OK;
+ }
+
+ *aBinary = nullptr;
+ *aBinaryLen = (aBase64Len * 3) / 4;
+
+ // Add one byte for null termination.
+ UniqueFreePtr<char[]> binary((char*)malloc(*aBinaryLen + 1));
+ if (!binary) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv =
+ Base64DecodeHelper(aBase64, aBase64Len, binary.get(), aBinaryLen);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aBinary = binary.release();
+ return NS_OK;
+}
+
+nsresult
+Base64Decode(const nsACString& aBase64, nsACString& aBinary)
+{
+ // Check for overflow.
+ if (aBase64.Length() > UINT32_MAX / 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't ask PR_Base64Decode to decode the empty string
+ if (aBase64.IsEmpty()) {
+ aBinary.Truncate();
+ return NS_OK;
+ }
+
+ uint32_t binaryLen = ((aBase64.Length() * 3) / 4);
+
+ // Add one byte for null termination.
+ if (!aBinary.SetCapacity(binaryLen + 1, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char* binary = aBinary.BeginWriting();
+ nsresult rv = Base64DecodeHelper(aBase64.BeginReading(), aBase64.Length(),
+ binary, &binaryLen);
+ if (NS_FAILED(rv)) {
+ aBinary.Truncate();
+ return rv;
+ }
+
+ aBinary.SetLength(binaryLen);
+ return NS_OK;
+}
+
+nsresult
+Base64Decode(const nsAString& aBase64, nsAString& aBinary)
+{
+ NS_LossyConvertUTF16toASCII base64(aBase64);
+ nsAutoCString binary;
+
+ nsresult rv = Base64Decode(base64, binary);
+ if (NS_SUCCEEDED(rv)) {
+ CopyASCIItoUTF16(binary, aBinary);
+ } else {
+ aBinary.Truncate();
+ }
+
+ return rv;
+}
+
+nsresult
+Base64URLDecode(const nsACString& aBase64,
+ Base64URLDecodePaddingPolicy aPaddingPolicy,
+ FallibleTArray<uint8_t>& aBinary)
+{
+ // Don't decode empty strings.
+ if (aBase64.IsEmpty()) {
+ aBinary.Clear();
+ return NS_OK;
+ }
+
+ // Check for overflow.
+ uint32_t base64Len = aBase64.Length();
+ if (base64Len > UINT32_MAX / 3) {
+ return NS_ERROR_FAILURE;
+ }
+ const char* base64 = aBase64.BeginReading();
+
+ // The decoded length may be 1-2 bytes over, depending on the final quantum.
+ uint32_t binaryLen = (base64Len * 3) / 4;
+
+ // Determine whether to check for and ignore trailing padding.
+ bool maybePadded = false;
+ switch (aPaddingPolicy) {
+ case Base64URLDecodePaddingPolicy::Require:
+ if (base64Len % 4) {
+ // Padded input length must be a multiple of 4.
+ return NS_ERROR_INVALID_ARG;
+ }
+ maybePadded = true;
+ break;
+
+ case Base64URLDecodePaddingPolicy::Ignore:
+ // Check for padding only if the length is a multiple of 4.
+ maybePadded = !(base64Len % 4);
+ break;
+
+ // If we're expecting unpadded input, no need for additional checks.
+ // `=` isn't in the decode table, so padded strings will fail to decode.
+ default:
+ MOZ_FALLTHROUGH_ASSERT("Invalid decode padding policy");
+ case Base64URLDecodePaddingPolicy::Reject:
+ break;
+ }
+ if (maybePadded && base64[base64Len - 1] == '=') {
+ if (base64[base64Len - 2] == '=') {
+ base64Len -= 2;
+ } else {
+ base64Len -= 1;
+ }
+ }
+
+ if (NS_WARN_IF(!aBinary.SetCapacity(binaryLen, mozilla::fallible))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ aBinary.SetLengthAndRetainStorage(binaryLen);
+ uint8_t* binary = aBinary.Elements();
+
+ for (; base64Len >= 4; base64Len -= 4) {
+ uint8_t w, x, y, z;
+ if (!Base64URLCharToValue(*base64++, &w) ||
+ !Base64URLCharToValue(*base64++, &x) ||
+ !Base64URLCharToValue(*base64++, &y) ||
+ !Base64URLCharToValue(*base64++, &z)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *binary++ = w << 2 | x >> 4;
+ *binary++ = x << 4 | y >> 2;
+ *binary++ = y << 6 | z;
+ }
+
+ if (base64Len == 3) {
+ uint8_t w, x, y;
+ if (!Base64URLCharToValue(*base64++, &w) ||
+ !Base64URLCharToValue(*base64++, &x) ||
+ !Base64URLCharToValue(*base64++, &y)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *binary++ = w << 2 | x >> 4;
+ *binary++ = x << 4 | y >> 2;
+ } else if (base64Len == 2) {
+ uint8_t w, x;
+ if (!Base64URLCharToValue(*base64++, &w) ||
+ !Base64URLCharToValue(*base64++, &x)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *binary++ = w << 2 | x >> 4;
+ } else if (base64Len) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Set the length to the actual number of decoded bytes.
+ aBinary.TruncateLength(binary - aBinary.Elements());
+ return NS_OK;
+}
+
+nsresult
+Base64URLEncode(uint32_t aBinaryLen, const uint8_t* aBinary,
+ Base64URLEncodePaddingPolicy aPaddingPolicy,
+ nsACString& aBase64)
+{
+ // Don't encode empty strings.
+ if (aBinaryLen == 0) {
+ aBase64.Truncate();
+ return NS_OK;
+ }
+
+ // Check for overflow.
+ if (aBinaryLen > (UINT32_MAX / 4) * 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Allocate a buffer large enough to hold the encoded string with padding.
+ // Add one byte for null termination.
+ uint32_t base64Len = ((aBinaryLen + 2) / 3) * 4;
+ if (NS_WARN_IF(!aBase64.SetCapacity(base64Len + 1, fallible))) {
+ aBase64.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+
+ char* base64 = aBase64.BeginWriting();
+
+ uint32_t index = 0;
+ for (; index + 3 <= aBinaryLen; index += 3) {
+ *base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
+ *base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) |
+ (aBinary[index + 1] >> 4)];
+ *base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2) |
+ (aBinary[index + 2] >> 6)];
+ *base64++ = kBase64URLAlphabet[aBinary[index + 2] & 0x3f];
+ }
+
+ uint32_t remaining = aBinaryLen - index;
+ if (remaining == 1) {
+ *base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
+ *base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4)];
+ } else if (remaining == 2) {
+ *base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
+ *base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) |
+ (aBinary[index + 1] >> 4)];
+ *base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2)];
+ }
+
+ uint32_t length = base64 - aBase64.BeginWriting();
+ if (aPaddingPolicy == Base64URLEncodePaddingPolicy::Include) {
+ if (length % 4 == 2) {
+ *base64++ = '=';
+ *base64++ = '=';
+ length += 2;
+ } else if (length % 4 == 3) {
+ *base64++ = '=';
+ length += 1;
+ }
+ } else {
+ MOZ_ASSERT(aPaddingPolicy == Base64URLEncodePaddingPolicy::Omit,
+ "Invalid encode padding policy");
+ }
+
+ // Null terminate and truncate to the actual number of characters.
+ *base64 = '\0';
+ aBase64.SetLength(length);
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/io/Base64.h b/xpcom/io/Base64.h
new file mode 100644
index 000000000..0c3f1e140
--- /dev/null
+++ b/xpcom/io/Base64.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Base64_h__
+#define mozilla_Base64_h__
+
+#include "nsString.h"
+
+class nsIInputStream;
+
+namespace mozilla {
+
+MOZ_MUST_USE nsresult
+Base64EncodeInputStream(nsIInputStream* aInputStream,
+ nsACString& aDest,
+ uint32_t aCount,
+ uint32_t aOffset = 0);
+MOZ_MUST_USE nsresult
+Base64EncodeInputStream(nsIInputStream* aInputStream,
+ nsAString& aDest,
+ uint32_t aCount,
+ uint32_t aOffset = 0);
+
+MOZ_MUST_USE nsresult
+Base64Encode(const char* aBinary, uint32_t aBinaryLen, char** aBase64);
+MOZ_MUST_USE nsresult
+Base64Encode(const nsACString& aBinary, nsACString& aBase64);
+MOZ_MUST_USE nsresult
+Base64Encode(const nsAString& aBinary, nsAString& aBase64);
+
+MOZ_MUST_USE nsresult
+Base64Decode(const char* aBase64, uint32_t aBase64Len, char** aBinary,
+ uint32_t* aBinaryLen);
+MOZ_MUST_USE nsresult
+Base64Decode(const nsACString& aBase64, nsACString& aBinary);
+MOZ_MUST_USE nsresult
+Base64Decode(const nsAString& aBase64, nsAString& aBinary);
+
+enum class Base64URLEncodePaddingPolicy {
+ Include,
+ Omit,
+};
+
+/**
+ * Converts |aBinary| to an unpadded, Base64 URL-encoded string per RFC 4648.
+ * Aims to encode the data in constant time. The caller retains ownership
+ * of |aBinary|.
+ */
+MOZ_MUST_USE nsresult
+Base64URLEncode(uint32_t aBinaryLen, const uint8_t* aBinary,
+ Base64URLEncodePaddingPolicy aPaddingPolicy,
+ nsACString& aBase64);
+
+enum class Base64URLDecodePaddingPolicy {
+ Require,
+ Ignore,
+ Reject,
+};
+
+/**
+ * Decodes a Base64 URL-encoded |aBase64| into |aBinary|.
+ */
+MOZ_MUST_USE nsresult
+Base64URLDecode(const nsACString& aBase64,
+ Base64URLDecodePaddingPolicy aPaddingPolicy,
+ FallibleTArray<uint8_t>& aBinary);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/io/CocoaFileUtils.h b/xpcom/io/CocoaFileUtils.h
new file mode 100644
index 000000000..7127cb65d
--- /dev/null
+++ b/xpcom/io/CocoaFileUtils.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This namespace contains methods with Obj-C/Cocoa implementations. The header
+// is C/C++ for inclusion in C/C++-only files.
+
+#ifndef CocoaFileUtils_h_
+#define CocoaFileUtils_h_
+
+#include "nscore.h"
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace CocoaFileUtils {
+
+nsresult RevealFileInFinder(CFURLRef aUrl);
+nsresult OpenURL(CFURLRef aUrl);
+nsresult GetFileCreatorCode(CFURLRef aUrl, OSType* aCreatorCode);
+nsresult SetFileCreatorCode(CFURLRef aUrl, OSType aCreatorCode);
+nsresult GetFileTypeCode(CFURLRef aUrl, OSType* aTypeCode);
+nsresult SetFileTypeCode(CFURLRef aUrl, OSType aTypeCode);
+void AddOriginMetadataToFile(const CFStringRef filePath,
+ const CFURLRef sourceURL,
+ const CFURLRef referrerURL);
+void AddQuarantineMetadataToFile(const CFStringRef filePath,
+ const CFURLRef sourceURL,
+ const CFURLRef referrerURL,
+ const bool isFromWeb);
+CFURLRef GetTemporaryFolderCFURLRef();
+
+} // namespace CocoaFileUtils
+
+#endif
diff --git a/xpcom/io/CocoaFileUtils.mm b/xpcom/io/CocoaFileUtils.mm
new file mode 100644
index 000000000..a02b82ac1
--- /dev/null
+++ b/xpcom/io/CocoaFileUtils.mm
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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 "CocoaFileUtils.h"
+#include "nsCocoaFeatures.h"
+#include "nsCocoaUtils.h"
+#include <Cocoa/Cocoa.h>
+#include "nsObjCExceptions.h"
+#include "nsDebug.h"
+
+// Need to cope with us using old versions of the SDK and needing this on 10.10+
+#if !defined(MAC_OS_X_VERSION_10_10) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
+const CFStringRef kCFURLQuarantinePropertiesKey = CFSTR("NSURLQuarantinePropertiesKey");
+#endif
+
+namespace CocoaFileUtils {
+
+nsresult RevealFileInFinder(CFURLRef url)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (NS_WARN_IF(!url))
+ return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)url path] inFileViewerRootedAtPath:@""];
+ [ap release];
+
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult OpenURL(CFURLRef url)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (NS_WARN_IF(!url))
+ return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)url];
+ [ap release];
+
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult GetFileCreatorCode(CFURLRef url, OSType *creatorCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (NS_WARN_IF(!url) || NS_WARN_IF(!creatorCode))
+ return NS_ERROR_INVALID_ARG;
+
+ nsAutoreleasePool localPool;
+
+ NSString *resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
+ if (!resolvedPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath error:nil];
+ if (!dict) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
+ if (!creatorNum) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *creatorCode = [creatorNum unsignedLongValue];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (NS_WARN_IF(!url))
+ return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode] forKey:NSFileHFSCreatorCode];
+ BOOL success = [[NSFileManager defaultManager] setAttributes:dict ofItemAtPath:[(NSURL*)url path] error:nil];
+ [ap release];
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult GetFileTypeCode(CFURLRef url, OSType *typeCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (NS_WARN_IF(!url) || NS_WARN_IF(!typeCode))
+ return NS_ERROR_INVALID_ARG;
+
+ nsAutoreleasePool localPool;
+
+ NSString *resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
+ if (!resolvedPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath error:nil];
+ if (!dict) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
+ if (!typeNum) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *typeCode = [typeNum unsignedLongValue];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult SetFileTypeCode(CFURLRef url, OSType typeCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (NS_WARN_IF(!url))
+ return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:typeCode] forKey:NSFileHFSTypeCode];
+ BOOL success = [[NSFileManager defaultManager] setAttributes:dict ofItemAtPath:[(NSURL*)url path] error:nil];
+ [ap release];
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+void AddOriginMetadataToFile(const CFStringRef filePath,
+ const CFURLRef sourceURL,
+ const CFURLRef referrerURL) {
+ typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef, CFTypeRef);
+ static MDItemSetAttribute_type mdItemSetAttributeFunc = NULL;
+
+ static bool did_symbol_lookup = false;
+ if (!did_symbol_lookup) {
+ did_symbol_lookup = true;
+
+ CFBundleRef metadata_bundle = ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
+ if (!metadata_bundle) {
+ return;
+ }
+
+ mdItemSetAttributeFunc = (MDItemSetAttribute_type)
+ ::CFBundleGetFunctionPointerForName(metadata_bundle, CFSTR("MDItemSetAttribute"));
+ }
+ if (!mdItemSetAttributeFunc) {
+ return;
+ }
+
+ MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
+ if (!mdItem) {
+ return;
+ }
+
+ CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
+ if (!list) {
+ ::CFRelease(mdItem);
+ return;
+ }
+
+ // The first item in the list is the source URL of the downloaded file.
+ if (sourceURL) {
+ ::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
+ }
+
+ // If the referrer is known, store that in the second position.
+ if (referrerURL) {
+ ::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
+ }
+
+ mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
+
+ ::CFRelease(list);
+ ::CFRelease(mdItem);
+}
+
+void AddQuarantineMetadataToFile(const CFStringRef filePath,
+ const CFURLRef sourceURL,
+ const CFURLRef referrerURL,
+ const bool isFromWeb) {
+ CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+ filePath,
+ kCFURLPOSIXPathStyle,
+ false);
+
+ // The properties key changed in 10.10:
+ CFStringRef quarantinePropKey;
+ if (nsCocoaFeatures::OnYosemiteOrLater()) {
+ quarantinePropKey = kCFURLQuarantinePropertiesKey;
+ } else {
+ quarantinePropKey = kLSItemQuarantineProperties;
+ }
+ CFDictionaryRef quarantineProps = NULL;
+ Boolean success = ::CFURLCopyResourcePropertyForKey(fileURL,
+ quarantinePropKey,
+ &quarantineProps,
+ NULL);
+
+ // If there aren't any quarantine properties then the user probably
+ // set up an exclusion and we don't need to add metadata.
+ if (!success || !quarantineProps) {
+ ::CFRelease(fileURL);
+ return;
+ }
+
+ // We don't know what to do if the props aren't a dictionary.
+ if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
+ ::CFRelease(fileURL);
+ ::CFRelease(quarantineProps);
+ return;
+ }
+
+ // Make a mutable copy of the properties.
+ CFMutableDictionaryRef mutQuarantineProps =
+ ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
+ ::CFRelease(quarantineProps);
+
+ // Add metadata that the OS couldn't infer.
+
+ if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
+ CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload : kLSQuarantineTypeOtherDownload;
+ ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
+ }
+
+ if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) && referrerURL) {
+ ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey, referrerURL);
+ }
+
+ if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) && sourceURL) {
+ ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey, sourceURL);
+ }
+
+ // Set quarantine properties on file.
+ ::CFURLSetResourcePropertyForKey(fileURL,
+ quarantinePropKey,
+ mutQuarantineProps,
+ NULL);
+
+ ::CFRelease(fileURL);
+ ::CFRelease(mutQuarantineProps);
+}
+
+CFURLRef GetTemporaryFolderCFURLRef()
+{
+ NSString* tempDir = ::NSTemporaryDirectory();
+ return tempDir == nil ? NULL : (CFURLRef)[NSURL fileURLWithPath:tempDir
+ isDirectory:YES];
+}
+
+} // namespace CocoaFileUtils
diff --git a/xpcom/io/FileUtilsWin.cpp b/xpcom/io/FileUtilsWin.cpp
new file mode 100644
index 000000000..732c074f7
--- /dev/null
+++ b/xpcom/io/FileUtilsWin.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileUtilsWin.h"
+
+#include <windows.h>
+#include <psapi.h>
+
+#include "mozilla/Unused.h"
+#include "nsWindowsHelpers.h"
+#include "GeckoProfiler.h"
+
+namespace {
+
+// Scoped type used by HandleToFilename
+struct ScopedMappedViewTraits
+{
+ typedef void* type;
+ static void* empty()
+ {
+ return nullptr;
+ }
+ static void release(void* aPtr)
+ {
+ if (aPtr) {
+ mozilla::Unused << UnmapViewOfFile(aPtr);
+ }
+ }
+};
+typedef mozilla::Scoped<ScopedMappedViewTraits> ScopedMappedView;
+
+} // namespace
+
+namespace mozilla {
+
+bool
+HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
+ nsAString& aFilename)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::NETWORK);
+
+ aFilename.Truncate();
+ // This implementation is nice because it uses fully documented APIs that
+ // are available on all Windows versions that we support.
+ nsAutoHandle fileMapping(CreateFileMapping(aHandle, nullptr, PAGE_READONLY,
+ 0, 1, nullptr));
+ if (!fileMapping) {
+ return false;
+ }
+ ScopedMappedView view(MapViewOfFile(fileMapping, FILE_MAP_READ,
+ aOffset.HighPart, aOffset.LowPart, 1));
+ if (!view) {
+ return false;
+ }
+ nsAutoString mappedFilename;
+ DWORD len = 0;
+ SetLastError(ERROR_SUCCESS);
+ do {
+ mappedFilename.SetLength(mappedFilename.Length() + MAX_PATH);
+ len = GetMappedFileNameW(GetCurrentProcess(), view,
+ wwc(mappedFilename.BeginWriting()),
+ mappedFilename.Length());
+ } while (!len && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+ if (!len) {
+ return false;
+ }
+ mappedFilename.Truncate(len);
+ return NtPathToDosPath(mappedFilename, aFilename);
+}
+
+} // namespace mozilla
+
diff --git a/xpcom/io/FileUtilsWin.h b/xpcom/io/FileUtilsWin.h
new file mode 100644
index 000000000..e32e9fd6e
--- /dev/null
+++ b/xpcom/io/FileUtilsWin.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_FileUtilsWin_h
+#define mozilla_FileUtilsWin_h
+
+#include <windows.h>
+
+#include "mozilla/Scoped.h"
+#include "nsStringGlue.h"
+
+namespace mozilla {
+
+inline bool
+EnsureLongPath(nsAString& aDosPath)
+{
+ uint32_t aDosPathOriginalLen = aDosPath.Length();
+ auto inputPath = PromiseFlatString(aDosPath);
+ // Try to get the long path, or else get the required length of the long path
+ DWORD longPathLen = GetLongPathNameW(inputPath.get(),
+ reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()),
+ aDosPathOriginalLen);
+ if (longPathLen == 0) {
+ return false;
+ }
+ aDosPath.SetLength(longPathLen);
+ if (longPathLen <= aDosPathOriginalLen) {
+ // Our string happened to be long enough for the first call to succeed.
+ return true;
+ }
+ // Now we have a large enough buffer, get the actual string
+ longPathLen = GetLongPathNameW(inputPath.get(),
+ reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()), aDosPath.Length());
+ if (longPathLen == 0) {
+ return false;
+ }
+ // This success check should always be less-than because longPathLen excludes
+ // the null terminator on success, but includes it in the first call that
+ // returned the required size.
+ if (longPathLen < aDosPath.Length()) {
+ aDosPath.SetLength(longPathLen);
+ return true;
+ }
+ // We shouldn't reach this, but if we do then it's a failure!
+ return false;
+}
+
+inline bool
+NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath)
+{
+ aDosPath.Truncate();
+ if (aNtPath.IsEmpty()) {
+ return true;
+ }
+ NS_NAMED_LITERAL_STRING(symLinkPrefix, "\\??\\");
+ uint32_t ntPathLen = aNtPath.Length();
+ uint32_t symLinkPrefixLen = symLinkPrefix.Length();
+ if (ntPathLen >= 6 && aNtPath.CharAt(5) == L':' &&
+ ntPathLen >= symLinkPrefixLen &&
+ Substring(aNtPath, 0, symLinkPrefixLen).Equals(symLinkPrefix)) {
+ // Symbolic link for DOS device. Just strip off the prefix.
+ aDosPath = aNtPath;
+ aDosPath.Cut(0, 4);
+ return true;
+ }
+ nsAutoString logicalDrives;
+ DWORD len = 0;
+ while (true) {
+ len = GetLogicalDriveStringsW(
+ len, reinterpret_cast<wchar_t*>(logicalDrives.BeginWriting()));
+ if (!len) {
+ return false;
+ } else if (len > logicalDrives.Length()) {
+ logicalDrives.SetLength(len);
+ } else {
+ break;
+ }
+ }
+ const char16_t* cur = logicalDrives.BeginReading();
+ const char16_t* end = logicalDrives.EndReading();
+ nsString targetPath;
+ targetPath.SetLength(MAX_PATH);
+ wchar_t driveTemplate[] = L" :";
+ do {
+ // Unfortunately QueryDosDevice doesn't support the idiom for querying the
+ // output buffer size, so it may require retries.
+ driveTemplate[0] = *cur;
+ DWORD targetPathLen = 0;
+ SetLastError(ERROR_SUCCESS);
+ while (true) {
+ targetPathLen = QueryDosDeviceW(driveTemplate,
+ reinterpret_cast<wchar_t*>(targetPath.BeginWriting()),
+ targetPath.Length());
+ if (targetPathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ break;
+ }
+ targetPath.SetLength(targetPath.Length() * 2);
+ }
+ if (targetPathLen) {
+ // Need to use wcslen here because targetPath contains embedded NULL chars
+ size_t firstTargetPathLen = wcslen(targetPath.get());
+ const char16_t* pathComponent = aNtPath.BeginReading() +
+ firstTargetPathLen;
+ bool found = _wcsnicmp(char16ptr_t(aNtPath.BeginReading()), targetPath.get(),
+ firstTargetPathLen) == 0 &&
+ *pathComponent == L'\\';
+ if (found) {
+ aDosPath = driveTemplate;
+ aDosPath += pathComponent;
+ return EnsureLongPath(aDosPath);
+ }
+ }
+ // Advance to the next NUL character in logicalDrives
+ while (*cur++);
+ } while (cur != end);
+ // Try to handle UNC paths. NB: This must happen after we've checked drive
+ // mappings in case a UNC path is mapped to a drive!
+ NS_NAMED_LITERAL_STRING(uncPrefix, "\\\\");
+ NS_NAMED_LITERAL_STRING(deviceMupPrefix, "\\Device\\Mup\\");
+ if (StringBeginsWith(aNtPath, deviceMupPrefix)) {
+ aDosPath = uncPrefix;
+ aDosPath += Substring(aNtPath, deviceMupPrefix.Length());
+ return true;
+ }
+ NS_NAMED_LITERAL_STRING(deviceLanmanRedirectorPrefix,
+ "\\Device\\LanmanRedirector\\");
+ if (StringBeginsWith(aNtPath, deviceLanmanRedirectorPrefix)) {
+ aDosPath = uncPrefix;
+ aDosPath += Substring(aNtPath, deviceLanmanRedirectorPrefix.Length());
+ return true;
+ }
+ return false;
+}
+
+bool
+HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
+ nsAString& aFilename);
+
+} // namespace mozilla
+
+#endif // mozilla_FileUtilsWin_h
diff --git a/xpcom/io/SlicedInputStream.cpp b/xpcom/io/SlicedInputStream.cpp
new file mode 100644
index 000000000..7d5fc2b05
--- /dev/null
+++ b/xpcom/io/SlicedInputStream.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "SlicedInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsStreamUtils.h"
+
+NS_IMPL_ISUPPORTS(SlicedInputStream, nsIInputStream,
+ nsICloneableInputStream, nsIAsyncInputStream)
+
+SlicedInputStream::SlicedInputStream(nsIInputStream* aInputStream,
+ uint64_t aStart, uint64_t aLength)
+ : mInputStream(aInputStream)
+ , mStart(aStart)
+ , mLength(aLength)
+ , mCurPos(0)
+ , mClosed(false)
+{
+ MOZ_ASSERT(aInputStream);
+}
+
+SlicedInputStream::~SlicedInputStream()
+{}
+
+NS_IMETHODIMP
+SlicedInputStream::Close()
+{
+ mClosed = true;
+ return NS_OK;
+}
+
+// nsIInputStream interface
+
+NS_IMETHODIMP
+SlicedInputStream::Available(uint64_t* aLength)
+{
+ if (mClosed) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ nsresult rv = mInputStream->Available(aLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Let's remove extra length from the end.
+ if (*aLength + mCurPos > mStart + mLength) {
+ *aLength -= XPCOM_MIN(*aLength, (*aLength + mCurPos) - (mStart + mLength));
+ }
+
+ // Let's remove extra length from the begin.
+ if (mCurPos < mStart) {
+ *aLength -= XPCOM_MIN(*aLength, mStart - mCurPos);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SlicedInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
+{
+ return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+SlicedInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t *aResult)
+{
+ uint32_t result;
+
+ if (!aResult) {
+ aResult = &result;
+ }
+
+ *aResult = 0;
+
+ if (mClosed) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ if (mCurPos < mStart) {
+ nsCOMPtr<nsISeekableStream> seekableStream =
+ do_QueryInterface(mInputStream);
+ if (seekableStream) {
+ nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
+ mStart);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurPos = mStart;
+ } else {
+ char buf[4096];
+ while (mCurPos < mStart) {
+ uint32_t bytesRead;
+ uint64_t bufCount = XPCOM_MIN(mStart - mCurPos, (uint64_t)sizeof(buf));
+ nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
+ if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
+ return rv;
+ }
+
+ mCurPos += bytesRead;
+ }
+ }
+ }
+
+ // Let's reduce aCount in case it's too big.
+ if (mCurPos + aCount > mStart + mLength) {
+ aCount = mStart + mLength - mCurPos;
+ }
+
+ char buf[4096];
+ while (mCurPos < mStart + mLength && *aResult < aCount) {
+ uint32_t bytesRead;
+ uint64_t bufCount = XPCOM_MIN(aCount - *aResult, (uint32_t)sizeof(buf));
+ nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
+ if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
+ return rv;
+ }
+
+ mCurPos += bytesRead;
+
+ uint32_t bytesWritten = 0;
+ while (bytesWritten < bytesRead) {
+ uint32_t writerCount = 0;
+ rv = aWriter(this, aClosure, buf + bytesWritten, *aResult,
+ bytesRead - bytesWritten, &writerCount);
+ if (NS_FAILED(rv) || writerCount == 0) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(writerCount <= bytesRead - bytesWritten);
+ bytesWritten += writerCount;
+ *aResult += writerCount;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SlicedInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ return mInputStream->IsNonBlocking(aNonBlocking);
+}
+
+// nsICloneableInputStream interface
+
+NS_IMETHODIMP
+SlicedInputStream::GetCloneable(bool* aCloneable)
+{
+ *aCloneable = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SlicedInputStream::Clone(nsIInputStream** aResult)
+{
+ nsCOMPtr<nsIInputStream> clonedStream;
+ nsCOMPtr<nsIInputStream> replacementStream;
+
+ nsresult rv = NS_CloneInputStream(mInputStream, getter_AddRefs(clonedStream),
+ getter_AddRefs(replacementStream));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (replacementStream) {
+ mInputStream = replacementStream.forget();
+ }
+
+ nsCOMPtr<nsIInputStream> sis =
+ new SlicedInputStream(clonedStream, mStart, mLength);
+
+ sis.forget(aResult);
+ return NS_OK;
+}
+
+// nsIAsyncInputStream interface
+
+NS_IMETHODIMP
+SlicedInputStream::CloseWithStatus(nsresult aStatus)
+{
+ nsCOMPtr<nsIAsyncInputStream> asyncStream =
+ do_QueryInterface(mInputStream);
+ if (!asyncStream) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return asyncStream->CloseWithStatus(aStatus);
+}
+
+NS_IMETHODIMP
+SlicedInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
+ uint32_t aFlags,
+ uint32_t aRequestedCount,
+ nsIEventTarget* aEventTarget)
+{
+ nsCOMPtr<nsIAsyncInputStream> asyncStream =
+ do_QueryInterface(mInputStream);
+ if (!asyncStream) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return asyncStream->AsyncWait(aCallback, aFlags, aRequestedCount,
+ aEventTarget);
+}
diff --git a/xpcom/io/SlicedInputStream.h b/xpcom/io/SlicedInputStream.h
new file mode 100644
index 000000000..6c38fc39f
--- /dev/null
+++ b/xpcom/io/SlicedInputStream.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 SlicedInputStream_h
+#define SlicedInputStream_h
+
+#include "mozilla/Attributes.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsICloneableInputStream.h"
+
+// A wrapper for a slice of an underlying input stream.
+
+class SlicedInputStream final : public nsIAsyncInputStream
+ , public nsICloneableInputStream
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSICLONEABLEINPUTSTREAM
+ NS_DECL_NSIASYNCINPUTSTREAM
+
+ // Create an input stream whose data comes from a slice of aInputStream. The
+ // slice begins at aStart bytes beyond aInputStream's current position, and
+ // extends for a maximum of aLength bytes. If aInputStream contains fewer
+ // than aStart bytes, reading from SlicedInputStream returns no data. If
+ // aInputStream contains more than aStart bytes, but fewer than aStart +
+ // aLength bytes, reading from SlicedInputStream returns as many bytes as can
+ // be consumed from aInputStream after reading aLength bytes.
+ //
+ // aInputStream should not be read from after constructing a
+ // SlicedInputStream wrapper around it.
+
+ SlicedInputStream(nsIInputStream* aInputStream,
+ uint64_t aStart, uint64_t aLength);
+
+private:
+ ~SlicedInputStream();
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+ uint64_t mStart;
+ uint64_t mLength;
+ uint64_t mCurPos;
+
+ bool mClosed;
+};
+
+#endif // SlicedInputStream_h
diff --git a/xpcom/io/SnappyCompressOutputStream.cpp b/xpcom/io/SnappyCompressOutputStream.cpp
new file mode 100644
index 000000000..89b8a0808
--- /dev/null
+++ b/xpcom/io/SnappyCompressOutputStream.cpp
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/SnappyCompressOutputStream.h"
+
+#include <algorithm>
+#include "nsStreamUtils.h"
+#include "snappy/snappy.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(SnappyCompressOutputStream, nsIOutputStream);
+
+// static
+const size_t
+SnappyCompressOutputStream::kMaxBlockSize = snappy::kBlockSize;
+
+SnappyCompressOutputStream::SnappyCompressOutputStream(nsIOutputStream* aBaseStream,
+ size_t aBlockSize)
+ : mBaseStream(aBaseStream)
+ , mBlockSize(std::min(aBlockSize, kMaxBlockSize))
+ , mNextByte(0)
+ , mCompressedBufferLength(0)
+ , mStreamIdentifierWritten(false)
+{
+ MOZ_ASSERT(mBlockSize > 0);
+
+ // This implementation only supports sync base streams. Verify this in debug
+ // builds. Note, this can be simpler than the check in
+ // SnappyUncompressInputStream because we don't have to deal with the
+ // nsStringInputStream oddness of being non-blocking and sync.
+#ifdef DEBUG
+ bool baseNonBlocking;
+ nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ MOZ_ASSERT(!baseNonBlocking);
+#endif
+}
+
+size_t
+SnappyCompressOutputStream::BlockSize() const
+{
+ return mBlockSize;
+}
+
+NS_IMETHODIMP
+SnappyCompressOutputStream::Close()
+{
+ if (!mBaseStream) {
+ return NS_OK;
+ }
+
+ nsresult rv = Flush();
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ mBaseStream->Close();
+ mBaseStream = nullptr;
+
+ mBuffer = nullptr;
+ mCompressedBuffer = nullptr;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SnappyCompressOutputStream::Flush()
+{
+ if (!mBaseStream) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ nsresult rv = FlushToBaseStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ mBaseStream->Flush();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SnappyCompressOutputStream::Write(const char* aBuf, uint32_t aCount,
+ uint32_t* aResultOut)
+{
+ return WriteSegments(NS_CopySegmentToBuffer, const_cast<char*>(aBuf), aCount,
+ aResultOut);
+}
+
+NS_IMETHODIMP
+SnappyCompressOutputStream::WriteFrom(nsIInputStream*, uint32_t, uint32_t*)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aBytesWrittenOut)
+{
+ *aBytesWrittenOut = 0;
+
+ if (!mBaseStream) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ if (!mBuffer) {
+ mBuffer.reset(new (fallible) char[mBlockSize]);
+ if (NS_WARN_IF(!mBuffer)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ while (aCount > 0) {
+ // Determine how much space is left in our flat, uncompressed buffer.
+ MOZ_ASSERT(mNextByte <= mBlockSize);
+ uint32_t remaining = mBlockSize - mNextByte;
+
+ // If it is full, then compress and flush the data to the base stream.
+ if (remaining == 0) {
+ nsresult rv = FlushToBaseStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ // Now the entire buffer should be available for copying.
+ MOZ_ASSERT(!mNextByte);
+ remaining = mBlockSize;
+ }
+
+ uint32_t numToRead = std::min(remaining, aCount);
+ uint32_t numRead = 0;
+
+ nsresult rv = aReader(this, aClosure, &mBuffer[mNextByte],
+ *aBytesWrittenOut, numToRead, &numRead);
+
+ // As defined in nsIOutputStream.idl, do not pass reader func errors.
+ if (NS_FAILED(rv)) {
+ return NS_OK;
+ }
+
+ // End-of-file
+ if (numRead == 0) {
+ return NS_OK;
+ }
+
+ mNextByte += numRead;
+ *aBytesWrittenOut += numRead;
+ aCount -= numRead;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut)
+{
+ *aNonBlockingOut = false;
+ return NS_OK;
+}
+
+SnappyCompressOutputStream::~SnappyCompressOutputStream()
+{
+ Close();
+}
+
+nsresult
+SnappyCompressOutputStream::FlushToBaseStream()
+{
+ MOZ_ASSERT(mBaseStream);
+
+ // Lazily create the compressed buffer on our first flush. This
+ // allows us to report OOM during stream operation. This buffer
+ // will then get re-used until the stream is closed.
+ if (!mCompressedBuffer) {
+ mCompressedBufferLength = MaxCompressedBufferLength(mBlockSize);
+ mCompressedBuffer.reset(new (fallible) char[mCompressedBufferLength]);
+ if (NS_WARN_IF(!mCompressedBuffer)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // The first chunk must be a StreamIdentifier chunk. Write it out
+ // if we have not done so already.
+ nsresult rv = MaybeFlushStreamIdentifier();
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ // Compress the data to our internal compressed buffer.
+ size_t compressedLength;
+ rv = WriteCompressedData(mCompressedBuffer.get(), mCompressedBufferLength,
+ mBuffer.get(), mNextByte, &compressedLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ MOZ_ASSERT(compressedLength > 0);
+
+ mNextByte = 0;
+
+ // Write the compressed buffer out to the base stream.
+ uint32_t numWritten = 0;
+ rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ MOZ_ASSERT(compressedLength == numWritten);
+
+ return NS_OK;
+}
+
+nsresult
+SnappyCompressOutputStream::MaybeFlushStreamIdentifier()
+{
+ MOZ_ASSERT(mCompressedBuffer);
+
+ if (mStreamIdentifierWritten) {
+ return NS_OK;
+ }
+
+ // Build the StreamIdentifier in our compressed buffer.
+ size_t compressedLength;
+ nsresult rv = WriteStreamIdentifier(mCompressedBuffer.get(),
+ mCompressedBufferLength,
+ &compressedLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ // Write the compressed buffer out to the base stream.
+ uint32_t numWritten = 0;
+ rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ MOZ_ASSERT(compressedLength == numWritten);
+
+ mStreamIdentifierWritten = true;
+
+ return NS_OK;
+}
+
+nsresult
+SnappyCompressOutputStream::WriteAll(const char* aBuf, uint32_t aCount,
+ uint32_t* aBytesWrittenOut)
+{
+ *aBytesWrittenOut = 0;
+
+ if (!mBaseStream) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ uint32_t offset = 0;
+ while (aCount > 0) {
+ uint32_t numWritten = 0;
+ nsresult rv = mBaseStream->Write(aBuf + offset, aCount, &numWritten);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ offset += numWritten;
+ aCount -= numWritten;
+ *aBytesWrittenOut += numWritten;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/io/SnappyCompressOutputStream.h b/xpcom/io/SnappyCompressOutputStream.h
new file mode 100644
index 000000000..36c47e66e
--- /dev/null
+++ b/xpcom/io/SnappyCompressOutputStream.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SnappyCompressOutputStream_h__
+#define mozilla_SnappyCompressOutputStream_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIOutputStream.h"
+#include "nsISupportsImpl.h"
+#include "SnappyFrameUtils.h"
+
+namespace mozilla {
+
+class SnappyCompressOutputStream final : public nsIOutputStream
+ , protected detail::SnappyFrameUtils
+{
+public:
+ // Maximum compression block size.
+ static const size_t kMaxBlockSize;
+
+ // Construct a new blocking output stream to compress data to
+ // the given base stream. The base stream must also be blocking.
+ // The compression block size may optionally be set to a value
+ // up to kMaxBlockSize.
+ explicit SnappyCompressOutputStream(nsIOutputStream* aBaseStream,
+ size_t aBlockSize = kMaxBlockSize);
+
+ // The compression block size. To optimize stream performance
+ // try to write to the stream in segments at least this size.
+ size_t BlockSize() const;
+
+private:
+ virtual ~SnappyCompressOutputStream();
+
+ nsresult FlushToBaseStream();
+ nsresult MaybeFlushStreamIdentifier();
+ nsresult WriteAll(const char* aBuf, uint32_t aCount,
+ uint32_t* aBytesWrittenOut);
+
+ nsCOMPtr<nsIOutputStream> mBaseStream;
+ const size_t mBlockSize;
+
+ // Buffer holding copied uncompressed data. This must be copied here
+ // so that the compression can be performed on a single flat buffer.
+ mozilla::UniquePtr<char[]> mBuffer;
+
+ // The next byte in the uncompressed data to copy incoming data to.
+ size_t mNextByte;
+
+ // Buffer holding the resulting compressed data.
+ mozilla::UniquePtr<char[]> mCompressedBuffer;
+ size_t mCompressedBufferLength;
+
+ // The first thing written to the stream must be a stream identifier.
+ bool mStreamIdentifierWritten;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOUTPUTSTREAM
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SnappyCompressOutputStream_h__
diff --git a/xpcom/io/SnappyFrameUtils.cpp b/xpcom/io/SnappyFrameUtils.cpp
new file mode 100644
index 000000000..97883a362
--- /dev/null
+++ b/xpcom/io/SnappyFrameUtils.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/SnappyFrameUtils.h"
+
+#include "crc32c.h"
+#include "mozilla/EndianUtils.h"
+#include "nsDebug.h"
+#include "snappy/snappy.h"
+
+namespace {
+
+using mozilla::detail::SnappyFrameUtils;
+using mozilla::NativeEndian;
+
+SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte)
+{
+ if (aByte == 0xff) {
+ return SnappyFrameUtils::StreamIdentifier;
+ } else if (aByte == 0x00) {
+ return SnappyFrameUtils::CompressedData;
+ } else if (aByte == 0x01) {
+ return SnappyFrameUtils::UncompressedData;
+ } else if (aByte == 0xfe) {
+ return SnappyFrameUtils::Padding;
+ }
+
+ return SnappyFrameUtils::Reserved;
+}
+
+void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType)
+{
+ unsigned char* dest = reinterpret_cast<unsigned char*>(aDest);
+ if (aType == SnappyFrameUtils::StreamIdentifier) {
+ *dest = 0xff;
+ } else if (aType == SnappyFrameUtils::CompressedData) {
+ *dest = 0x00;
+ } else if (aType == SnappyFrameUtils::UncompressedData) {
+ *dest = 0x01;
+ } else if (aType == SnappyFrameUtils::Padding) {
+ *dest = 0xfe;
+ } else {
+ *dest = 0x02;
+ }
+}
+
+void WriteUInt24(char* aBuf, uint32_t aVal)
+{
+ MOZ_ASSERT(!(aVal & 0xff000000));
+ uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
+ memcpy(aBuf, &tmp, 3);
+}
+
+uint32_t ReadUInt24(const char* aBuf)
+{
+ uint32_t val = 0;
+ memcpy(&val, aBuf, 3);
+ return NativeEndian::swapFromLittleEndian(val);
+}
+
+// This mask is explicitly defined in the snappy framing_format.txt file.
+uint32_t MaskChecksum(uint32_t aValue)
+{
+ return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace detail {
+
+using mozilla::LittleEndian;
+
+// static
+nsresult
+SnappyFrameUtils::WriteStreamIdentifier(char* aDest, size_t aDestLength,
+ size_t* aBytesWrittenOut)
+{
+ if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ WriteChunkType(aDest, StreamIdentifier);
+ aDest[1] = 0x06; // Data length
+ aDest[2] = 0x00;
+ aDest[3] = 0x00;
+ aDest[4] = 0x73; // "sNaPpY"
+ aDest[5] = 0x4e;
+ aDest[6] = 0x61;
+ aDest[7] = 0x50;
+ aDest[8] = 0x70;
+ aDest[9] = 0x59;
+
+ static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
+ "StreamIdentifier chunk should be exactly 10 bytes long");
+ *aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
+
+ return NS_OK;
+}
+
+// static
+nsresult
+SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
+ const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut)
+{
+ *aBytesWrittenOut = 0;
+
+ size_t neededLength = MaxCompressedBufferLength(aDataLength);
+ if (NS_WARN_IF(aDestLength < neededLength)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ size_t offset = 0;
+
+ WriteChunkType(aDest, CompressedData);
+ offset += kChunkTypeLength;
+
+ // Skip length for now and write it out after we know the compressed length.
+ size_t lengthOffset = offset;
+ offset += kChunkLengthLength;
+
+ uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aData),
+ aDataLength);
+ uint32_t maskedCrc = MaskChecksum(crc);
+ LittleEndian::writeUint32(aDest + offset, maskedCrc);
+ offset += kCRCLength;
+
+ size_t compressedLength;
+ snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
+
+ // Go back and write the data length.
+ size_t dataLength = compressedLength + kCRCLength;
+ WriteUInt24(aDest + lengthOffset, dataLength);
+
+ *aBytesWrittenOut = kHeaderLength + dataLength;
+
+ return NS_OK;
+}
+
+// static
+nsresult
+SnappyFrameUtils::ParseHeader(const char* aSource, size_t aSourceLength,
+ ChunkType* aTypeOut, size_t* aDataLengthOut)
+{
+ if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aTypeOut = ReadChunkType(aSource[0]);
+ *aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
+
+ return NS_OK;
+}
+
+// static
+nsresult
+SnappyFrameUtils::ParseData(char* aDest, size_t aDestLength,
+ ChunkType aType, const char* aData,
+ size_t aDataLength,
+ size_t* aBytesWrittenOut, size_t* aBytesReadOut)
+{
+ switch(aType) {
+ case StreamIdentifier:
+ return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
+ aBytesWrittenOut, aBytesReadOut);
+
+ case CompressedData:
+ return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
+ aBytesWrittenOut, aBytesReadOut);
+
+ // TODO: support other snappy chunk types
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+}
+
+// static
+nsresult
+SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
+ const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut,
+ size_t* aBytesReadOut)
+{
+ *aBytesWrittenOut = 0;
+ *aBytesReadOut = 0;
+ if (NS_WARN_IF(aDataLength != kStreamIdentifierDataLength ||
+ aData[0] != 0x73 ||
+ aData[1] != 0x4e ||
+ aData[2] != 0x61 ||
+ aData[3] != 0x50 ||
+ aData[4] != 0x70 ||
+ aData[5] != 0x59)) {
+ return NS_ERROR_CORRUPTED_CONTENT;
+ }
+ *aBytesReadOut = aDataLength;
+ return NS_OK;
+}
+
+// static
+nsresult
+SnappyFrameUtils::ParseCompressedData(char* aDest, size_t aDestLength,
+ const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut,
+ size_t* aBytesReadOut)
+{
+ *aBytesWrittenOut = 0;
+ *aBytesReadOut = 0;
+ size_t offset = 0;
+
+ uint32_t readCrc = LittleEndian::readUint32(aData + offset);
+ offset += kCRCLength;
+
+ size_t uncompressedLength;
+ if (NS_WARN_IF(!snappy::GetUncompressedLength(aData + offset,
+ aDataLength - offset,
+ &uncompressedLength))) {
+ return NS_ERROR_CORRUPTED_CONTENT;
+ }
+
+ if (NS_WARN_IF(aDestLength < uncompressedLength)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
+ aDest))) {
+ return NS_ERROR_CORRUPTED_CONTENT;
+ }
+
+ uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aDest),
+ uncompressedLength);
+ uint32_t maskedCrc = MaskChecksum(crc);
+ if (NS_WARN_IF(readCrc != maskedCrc)) {
+ return NS_ERROR_CORRUPTED_CONTENT;
+ }
+
+ *aBytesWrittenOut = uncompressedLength;
+ *aBytesReadOut = aDataLength;
+
+ return NS_OK;
+}
+
+// static
+size_t
+SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength)
+{
+ size_t neededLength = kHeaderLength;
+ neededLength += kCRCLength;
+ neededLength += snappy::MaxCompressedLength(aSourceLength);
+ return neededLength;
+}
+
+} // namespace detail
+} // namespace mozilla
diff --git a/xpcom/io/SnappyFrameUtils.h b/xpcom/io/SnappyFrameUtils.h
new file mode 100644
index 000000000..41479b14d
--- /dev/null
+++ b/xpcom/io/SnappyFrameUtils.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SnappyFrameUtils_h__
+#define mozilla_SnappyFrameUtils_h__
+
+#include "mozilla/Attributes.h"
+#include "nsError.h"
+
+namespace mozilla {
+namespace detail {
+
+//
+// Utility class providing primitives necessary to build streams based
+// on the snappy compressor. This essentially abstracts the framing format
+// defined in:
+//
+// other-licences/snappy/src/framing_format.txt
+//
+// NOTE: Currently only the StreamIdentifier and CompressedData chunks are
+// supported.
+//
+class SnappyFrameUtils
+{
+public:
+ enum ChunkType
+ {
+ Unknown,
+ StreamIdentifier,
+ CompressedData,
+ UncompressedData,
+ Padding,
+ Reserved,
+ ChunkTypeCount
+ };
+
+ static const size_t kChunkTypeLength = 1;
+ static const size_t kChunkLengthLength = 3;
+ static const size_t kHeaderLength = kChunkTypeLength + kChunkLengthLength;
+ static const size_t kStreamIdentifierDataLength = 6;
+ static const size_t kCRCLength = 4;
+
+ static nsresult
+ WriteStreamIdentifier(char* aDest, size_t aDestLength,
+ size_t* aBytesWrittenOut);
+
+ static nsresult
+ WriteCompressedData(char* aDest, size_t aDestLength,
+ const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut);
+
+ static nsresult
+ ParseHeader(const char* aSource, size_t aSourceLength, ChunkType* aTypeOut,
+ size_t* aDataLengthOut);
+
+ static nsresult
+ ParseData(char* aDest, size_t aDestLength,
+ ChunkType aType, const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut, size_t* aBytesReadOut);
+
+ static nsresult
+ ParseStreamIdentifier(char* aDest, size_t aDestLength,
+ const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut, size_t* aBytesReadOut);
+
+ static nsresult
+ ParseCompressedData(char* aDest, size_t aDestLength,
+ const char* aData, size_t aDataLength,
+ size_t* aBytesWrittenOut, size_t* aBytesReadOut);
+
+ static size_t
+ MaxCompressedBufferLength(size_t aSourceLength);
+
+protected:
+ SnappyFrameUtils() { }
+ virtual ~SnappyFrameUtils() { }
+};
+
+} // namespace detail
+} // namespace mozilla
+
+#endif // mozilla_SnappyFrameUtils_h__
diff --git a/xpcom/io/SnappyUncompressInputStream.cpp b/xpcom/io/SnappyUncompressInputStream.cpp
new file mode 100644
index 000000000..e0a1b6f4c
--- /dev/null
+++ b/xpcom/io/SnappyUncompressInputStream.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/SnappyUncompressInputStream.h"
+
+#include <algorithm>
+#include "nsIAsyncInputStream.h"
+#include "nsStreamUtils.h"
+#include "snappy/snappy.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(SnappyUncompressInputStream,
+ nsIInputStream);
+
+// Putting kCompressedBufferLength inside a function avoids a static
+// constructor.
+static size_t CompressedBufferLength()
+{
+ static size_t kCompressedBufferLength =
+ detail::SnappyFrameUtils::MaxCompressedBufferLength(snappy::kBlockSize);
+
+ MOZ_ASSERT(kCompressedBufferLength > 0);
+ return kCompressedBufferLength;
+}
+
+SnappyUncompressInputStream::SnappyUncompressInputStream(nsIInputStream* aBaseStream)
+ : mBaseStream(aBaseStream)
+ , mUncompressedBytes(0)
+ , mNextByte(0)
+ , mNextChunkType(Unknown)
+ , mNextChunkDataLength(0)
+ , mNeedFirstStreamIdentifier(true)
+{
+ // This implementation only supports sync base streams. Verify this in debug
+ // builds. Note, this is a bit complicated because the streams we support
+ // advertise different capabilities:
+ // - nsFileInputStream - blocking and sync
+ // - nsStringInputStream - non-blocking and sync
+ // - nsPipeInputStream - can be blocking, but provides async interface
+#ifdef DEBUG
+ bool baseNonBlocking;
+ nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (baseNonBlocking) {
+ nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(mBaseStream);
+ MOZ_ASSERT(!async);
+ }
+#endif
+}
+
+NS_IMETHODIMP
+SnappyUncompressInputStream::Close()
+{
+ if (!mBaseStream) {
+ return NS_OK;
+ }
+
+ mBaseStream->Close();
+ mBaseStream = nullptr;
+
+ mUncompressedBuffer = nullptr;
+ mCompressedBuffer = nullptr;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SnappyUncompressInputStream::Available(uint64_t* aLengthOut)
+{
+ if (!mBaseStream) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ // If we have uncompressed bytes, then we are done.
+ *aLengthOut = UncompressedLength();
+ if (*aLengthOut > 0) {
+ return NS_OK;
+ }
+
+ // Otherwise, attempt to uncompress bytes until we get something or the
+ // underlying stream is drained. We loop here because some chunks can
+ // be StreamIdentifiers, padding, etc with no data.
+ uint32_t bytesRead;
+ do {
+ nsresult rv = ParseNextChunk(&bytesRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ *aLengthOut = UncompressedLength();
+ } while(*aLengthOut == 0 && bytesRead);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SnappyUncompressInputStream::Read(char* aBuf, uint32_t aCount,
+ uint32_t* aBytesReadOut)
+{
+ return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aBytesReadOut);
+}
+
+NS_IMETHODIMP
+SnappyUncompressInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure, uint32_t aCount,
+ uint32_t* aBytesReadOut)
+{
+ *aBytesReadOut = 0;
+
+ if (!mBaseStream) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ nsresult rv;
+
+ // Do not try to use the base stream's ReadSegements here. Its very
+ // unlikely we will get a single buffer that contains all of the compressed
+ // data and therefore would have to copy into our own buffer anyways.
+ // Instead, focus on making efficient use of the Read() interface.
+
+ while (aCount > 0) {
+ // We have some decompressed data in our buffer. Provide it to the
+ // callers writer function.
+ if (mUncompressedBytes > 0) {
+ MOZ_ASSERT(mUncompressedBuffer);
+ uint32_t remaining = UncompressedLength();
+ uint32_t numToWrite = std::min(aCount, remaining);
+ uint32_t numWritten;
+ rv = aWriter(this, aClosure, &mUncompressedBuffer[mNextByte], *aBytesReadOut,
+ numToWrite, &numWritten);
+
+ // As defined in nsIInputputStream.idl, do not pass writer func errors.
+ if (NS_FAILED(rv)) {
+ return NS_OK;
+ }
+
+ // End-of-file
+ if (numWritten == 0) {
+ return NS_OK;
+ }
+
+ *aBytesReadOut += numWritten;
+ mNextByte += numWritten;
+ MOZ_ASSERT(mNextByte <= mUncompressedBytes);
+
+ if (mNextByte == mUncompressedBytes) {
+ mNextByte = 0;
+ mUncompressedBytes = 0;
+ }
+
+ aCount -= numWritten;
+
+ continue;
+ }
+
+ // Otherwise uncompress the next chunk and loop. Any resulting data
+ // will set mUncompressedBytes which we check at the top of the loop.
+ uint32_t bytesRead;
+ rv = ParseNextChunk(&bytesRead);
+ if (NS_FAILED(rv)) { return rv; }
+
+ // If we couldn't read anything and there is no more data to provide
+ // to the caller, then this is eof.
+ if (bytesRead == 0 && mUncompressedBytes == 0) {
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SnappyUncompressInputStream::IsNonBlocking(bool* aNonBlockingOut)
+{
+ *aNonBlockingOut = false;
+ return NS_OK;
+}
+
+SnappyUncompressInputStream::~SnappyUncompressInputStream()
+{
+ Close();
+}
+
+nsresult
+SnappyUncompressInputStream::ParseNextChunk(uint32_t* aBytesReadOut)
+{
+ // There must not be any uncompressed data already in mUncompressedBuffer.
+ MOZ_ASSERT(mUncompressedBytes == 0);
+ MOZ_ASSERT(mNextByte == 0);
+
+ nsresult rv;
+ *aBytesReadOut = 0;
+
+ // Lazily create our two buffers so we can report OOM during stream
+ // operation. These allocations only happens once. The buffers are reused
+ // until the stream is closed.
+ if (!mUncompressedBuffer) {
+ mUncompressedBuffer.reset(new (fallible) char[snappy::kBlockSize]);
+ if (NS_WARN_IF(!mUncompressedBuffer)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (!mCompressedBuffer) {
+ mCompressedBuffer.reset(new (fallible) char[CompressedBufferLength()]);
+ if (NS_WARN_IF(!mCompressedBuffer)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // We have no decompressed data and we also have not seen the start of stream
+ // yet. Read and validate the StreamIdentifier chunk. Also read the next
+ // header to determine the size of the first real data chunk.
+ if (mNeedFirstStreamIdentifier) {
+ const uint32_t firstReadLength = kHeaderLength +
+ kStreamIdentifierDataLength +
+ kHeaderLength;
+ MOZ_ASSERT(firstReadLength <= CompressedBufferLength());
+
+ rv = ReadAll(mCompressedBuffer.get(), firstReadLength, firstReadLength,
+ aBytesReadOut);
+ if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) { return rv; }
+
+ rv = ParseHeader(mCompressedBuffer.get(), kHeaderLength,
+ &mNextChunkType, &mNextChunkDataLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ if (NS_WARN_IF(mNextChunkType != StreamIdentifier ||
+ mNextChunkDataLength != kStreamIdentifierDataLength)) {
+ return NS_ERROR_CORRUPTED_CONTENT;
+ }
+ size_t offset = kHeaderLength;
+
+ mNeedFirstStreamIdentifier = false;
+
+ size_t numRead;
+ size_t numWritten;
+ rv = ParseData(mUncompressedBuffer.get(), snappy::kBlockSize, mNextChunkType,
+ &mCompressedBuffer[offset],
+ mNextChunkDataLength, &numWritten, &numRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ MOZ_ASSERT(numWritten == 0);
+ MOZ_ASSERT(numRead == mNextChunkDataLength);
+ offset += numRead;
+
+ rv = ParseHeader(&mCompressedBuffer[offset], *aBytesReadOut - offset,
+ &mNextChunkType, &mNextChunkDataLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ return NS_OK;
+ }
+
+ // We have no compressed data and we don't know how big the next chunk is.
+ // This happens when we get an EOF pause in the middle of a stream and also
+ // at the end of the stream. Simply read the next header and return. The
+ // chunk body will be read on the next entry into this method.
+ if (mNextChunkType == Unknown) {
+ rv = ReadAll(mCompressedBuffer.get(), kHeaderLength, kHeaderLength,
+ aBytesReadOut);
+ if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) { return rv; }
+
+ rv = ParseHeader(mCompressedBuffer.get(), kHeaderLength,
+ &mNextChunkType, &mNextChunkDataLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ return NS_OK;
+ }
+
+ // We have no decompressed data, but we do know the size of the next chunk.
+ // Read at least that much from the base stream.
+ uint32_t readLength = mNextChunkDataLength;
+ MOZ_ASSERT(readLength <= CompressedBufferLength());
+
+ // However, if there is enough data in the base stream, also read the next
+ // chunk header. This helps optimize the stream by avoiding many small reads.
+ uint64_t avail;
+ rv = mBaseStream->Available(&avail);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ if (avail >= (readLength + kHeaderLength)) {
+ readLength += kHeaderLength;
+ MOZ_ASSERT(readLength <= CompressedBufferLength());
+ }
+
+ rv = ReadAll(mCompressedBuffer.get(), readLength, mNextChunkDataLength,
+ aBytesReadOut);
+ if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) { return rv; }
+
+ size_t numRead;
+ size_t numWritten;
+ rv = ParseData(mUncompressedBuffer.get(), snappy::kBlockSize, mNextChunkType,
+ mCompressedBuffer.get(), mNextChunkDataLength,
+ &numWritten, &numRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+ MOZ_ASSERT(numRead == mNextChunkDataLength);
+
+ mUncompressedBytes = numWritten;
+
+ // If we were unable to directly read the next chunk header, then clear
+ // our internal state. We will have to perform a small read to get the
+ // header the next time we enter this method.
+ if (*aBytesReadOut <= mNextChunkDataLength) {
+ mNextChunkType = Unknown;
+ mNextChunkDataLength = 0;
+ return NS_OK;
+ }
+
+ // We got the next chunk header. Parse it so that we are ready to for the
+ // next call into this method.
+ rv = ParseHeader(&mCompressedBuffer[numRead], *aBytesReadOut - numRead,
+ &mNextChunkType, &mNextChunkDataLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ return NS_OK;
+}
+
+nsresult
+SnappyUncompressInputStream::ReadAll(char* aBuf, uint32_t aCount,
+ uint32_t aMinValidCount,
+ uint32_t* aBytesReadOut)
+{
+ MOZ_ASSERT(aCount >= aMinValidCount);
+
+ *aBytesReadOut = 0;
+
+ if (!mBaseStream) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ uint32_t offset = 0;
+ while (aCount > 0) {
+ uint32_t bytesRead = 0;
+ nsresult rv = mBaseStream->Read(aBuf + offset, aCount, &bytesRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ // EOF, but don't immediately return. We need to validate min read bytes
+ // below.
+ if (bytesRead == 0) {
+ break;
+ }
+
+ *aBytesReadOut += bytesRead;
+ offset += bytesRead;
+ aCount -= bytesRead;
+ }
+
+ // Reading zero bytes is not an error. Its the expected EOF condition.
+ // Only compare to the minimum valid count if we read at least one byte.
+ if (*aBytesReadOut != 0 && *aBytesReadOut < aMinValidCount) {
+ return NS_ERROR_CORRUPTED_CONTENT;
+ }
+
+ return NS_OK;
+}
+
+size_t
+SnappyUncompressInputStream::UncompressedLength() const
+{
+ MOZ_ASSERT(mNextByte <= mUncompressedBytes);
+ return mUncompressedBytes - mNextByte;
+}
+
+} // namespace mozilla
diff --git a/xpcom/io/SnappyUncompressInputStream.h b/xpcom/io/SnappyUncompressInputStream.h
new file mode 100644
index 000000000..0d24c0d11
--- /dev/null
+++ b/xpcom/io/SnappyUncompressInputStream.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SnappyUncompressInputStream_h__
+#define mozilla_SnappyUncompressInputStream_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIInputStream.h"
+#include "nsISupportsImpl.h"
+#include "SnappyFrameUtils.h"
+
+namespace mozilla {
+
+class SnappyUncompressInputStream final : public nsIInputStream
+ , protected detail::SnappyFrameUtils
+{
+public:
+ // Construct a new blocking stream to uncompress the given base stream. The
+ // base stream must also be blocking. The base stream does not have to be
+ // buffered.
+ explicit SnappyUncompressInputStream(nsIInputStream* aBaseStream);
+
+private:
+ virtual ~SnappyUncompressInputStream();
+
+ // Parse the next chunk of data. This may populate mBuffer and set
+ // mBufferFillSize. This should not be called when mBuffer already
+ // contains data.
+ nsresult ParseNextChunk(uint32_t* aBytesReadOut);
+
+ // Convenience routine to Read() from the base stream until we get
+ // the given number of bytes or reach EOF.
+ //
+ // aBuf - The buffer to write the bytes into.
+ // aCount - Max number of bytes to read. If the stream closes
+ // fewer bytes my be read.
+ // aMinValidCount - A minimum expected number of bytes. If we find
+ // fewer than this many bytes, then return
+ // NS_ERROR_CORRUPTED_CONTENT. If nothing was read due
+ // due to EOF (aBytesReadOut == 0), then NS_OK is returned.
+ // aBytesReadOut - An out parameter indicating how many bytes were read.
+ nsresult ReadAll(char* aBuf, uint32_t aCount, uint32_t aMinValidCount,
+ uint32_t* aBytesReadOut);
+
+ // Convenience routine to determine how many bytes of uncompressed data
+ // we currently have in our buffer.
+ size_t UncompressedLength() const;
+
+ nsCOMPtr<nsIInputStream> mBaseStream;
+
+ // Buffer to hold compressed data. Must copy here since we need a large
+ // flat buffer to run the uncompress process on. Always the same length
+ // of SnappyFrameUtils::MaxCompressedBufferLength(snappy::kBlockSize)
+ // bytes long.
+ mozilla::UniquePtr<char[]> mCompressedBuffer;
+
+ // Buffer storing the resulting uncompressed data. Exactly snappy::kBlockSize
+ // bytes long.
+ mozilla::UniquePtr<char[]> mUncompressedBuffer;
+
+ // Number of bytes of uncompressed data in mBuffer.
+ size_t mUncompressedBytes;
+
+ // Next byte of mBuffer to return in ReadSegments(). Must be less than
+ // mBufferFillSize
+ size_t mNextByte;
+
+ // Next chunk in the stream that has been parsed during read-ahead.
+ ChunkType mNextChunkType;
+
+ // Length of next chunk's length that has been determined during read-ahead.
+ size_t mNextChunkDataLength;
+
+ // The stream must begin with a StreamIdentifier chunk. Are we still
+ // expecting it?
+ bool mNeedFirstStreamIdentifier;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SnappyUncompressInputStream_h__
diff --git a/xpcom/io/SpecialSystemDirectory.cpp b/xpcom/io/SpecialSystemDirectory.cpp
new file mode 100644
index 000000000..9ce8eb85b
--- /dev/null
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -0,0 +1,830 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SpecialSystemDirectory.h"
+#include "nsString.h"
+#include "nsDependentString.h"
+#include "nsAutoPtr.h"
+
+#if defined(XP_WIN)
+
+#include <windows.h>
+#include <shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <direct.h>
+#include <shlobj.h>
+#include <knownfolders.h>
+#include <guiddef.h>
+#include "mozilla/WindowsVersion.h"
+
+using mozilla::IsWin7OrLater;
+
+#elif defined(XP_UNIX)
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "prenv.h"
+#if defined(MOZ_WIDGET_COCOA)
+#include "CocoaFileUtils.h"
+#endif
+
+#endif
+
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#elif defined(MAX_PATH)
+#define MAXPATHLEN MAX_PATH
+#elif defined(_MAX_PATH)
+#define MAXPATHLEN _MAX_PATH
+#elif defined(CCHMAXPATH)
+#define MAXPATHLEN CCHMAXPATH
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+#ifdef XP_WIN
+typedef HRESULT (WINAPI* nsGetKnownFolderPath)(GUID& rfid,
+ DWORD dwFlags,
+ HANDLE hToken,
+ PWSTR* ppszPath);
+
+static nsGetKnownFolderPath gGetKnownFolderPath = nullptr;
+#endif
+
+void
+StartupSpecialSystemDirectory()
+{
+#if defined (XP_WIN)
+ // SHGetKnownFolderPath is only available on Windows Vista
+ // so that we need to use GetProcAddress to get the pointer.
+ HMODULE hShell32DLLInst = GetModuleHandleW(L"shell32.dll");
+ if (hShell32DLLInst) {
+ gGetKnownFolderPath = (nsGetKnownFolderPath)
+ GetProcAddress(hShell32DLLInst, "SHGetKnownFolderPath");
+ }
+#endif
+}
+
+#if defined (XP_WIN)
+
+static nsresult
+GetKnownFolder(GUID* aGuid, nsIFile** aFile)
+{
+ if (!aGuid || !gGetKnownFolderPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PWSTR path = nullptr;
+ gGetKnownFolderPath(*aGuid, 0, nullptr, &path);
+
+ if (!path) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = NS_NewLocalFile(nsDependentString(path),
+ true,
+ aFile);
+
+ CoTaskMemFree(path);
+ return rv;
+}
+
+static nsresult
+GetWindowsFolder(int aFolder, nsIFile** aFile)
+{
+ WCHAR path_orig[MAX_PATH + 3];
+ WCHAR* path = path_orig + 1;
+ HRESULT result = SHGetSpecialFolderPathW(nullptr, path, aFolder, true);
+
+ if (!SUCCEEDED(result)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Append the trailing slash
+ int len = wcslen(path);
+ if (len > 1 && path[len - 1] != L'\\') {
+ path[len] = L'\\';
+ path[++len] = L'\0';
+ }
+
+ return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
+}
+
+__inline HRESULT
+SHLoadLibraryFromKnownFolder(REFKNOWNFOLDERID aFolderId, DWORD aMode,
+ REFIID riid, void** ppv)
+{
+ *ppv = nullptr;
+ IShellLibrary* plib;
+ HRESULT hr = CoCreateInstance(CLSID_ShellLibrary, nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&plib));
+ if (SUCCEEDED(hr)) {
+ hr = plib->LoadLibraryFromKnownFolder(aFolderId, aMode);
+ if (SUCCEEDED(hr)) {
+ hr = plib->QueryInterface(riid, ppv);
+ }
+ plib->Release();
+ }
+ return hr;
+}
+
+/*
+ * Check to see if we're on Win7 and up, and if so, returns the default
+ * save-to location for the Windows Library passed in through aFolderId.
+ * Otherwise falls back on pre-win7 GetWindowsFolder.
+ */
+static nsresult
+GetLibrarySaveToPath(int aFallbackFolderId, REFKNOWNFOLDERID aFolderId,
+ nsIFile** aFile)
+{
+ // Skip off checking for library support if the os is Vista or lower.
+ if (!IsWin7OrLater()) {
+ return GetWindowsFolder(aFallbackFolderId, aFile);
+ }
+
+ RefPtr<IShellLibrary> shellLib;
+ RefPtr<IShellItem> savePath;
+ HRESULT hr =
+ SHLoadLibraryFromKnownFolder(aFolderId, STGM_READ,
+ IID_IShellLibrary, getter_AddRefs(shellLib));
+
+ if (shellLib &&
+ SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
+ getter_AddRefs(savePath)))) {
+ wchar_t* str = nullptr;
+ if (SUCCEEDED(savePath->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
+ nsAutoString path;
+ path.Assign(str);
+ path.Append('\\');
+ nsresult rv =
+ NS_NewLocalFile(path, false, aFile);
+ CoTaskMemFree(str);
+ return rv;
+ }
+ }
+
+ return GetWindowsFolder(aFallbackFolderId, aFile);
+}
+
+/**
+ * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
+ * querying the registry when the call to SHGetSpecialFolderPathW is unable to
+ * provide these paths (Bug 513958).
+ */
+static nsresult
+GetRegWindowsAppDataFolder(bool aLocal, nsIFile** aFile)
+{
+ HKEY key;
+ NS_NAMED_LITERAL_STRING(keyName,
+ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
+ DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, KEY_READ,
+ &key);
+ if (res != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ WCHAR path[MAX_PATH + 2];
+ DWORD type, size;
+ res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"),
+ nullptr, &type, (LPBYTE)&path, &size);
+ ::RegCloseKey(key);
+ // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
+ // buffer size must not equal 0, and the buffer size be a multiple of 2.
+ if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Append the trailing slash
+ int len = wcslen(path);
+ if (len > 1 && path[len - 1] != L'\\') {
+ path[len] = L'\\';
+ path[++len] = L'\0';
+ }
+
+ return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
+}
+
+#endif // XP_WIN
+
+#if defined(XP_UNIX)
+static nsresult
+GetUnixHomeDir(nsIFile** aFile)
+{
+#if defined(ANDROID)
+ // XXX no home dir on android; maybe we should return the sdcard if present?
+ return NS_ERROR_FAILURE;
+#else
+ return NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")),
+ true, aFile);
+#endif
+}
+
+/*
+ The following license applies to the xdg_user_dir_lookup function:
+
+ Copyright (c) 2007 Red Hat, Inc.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+static char*
+xdg_user_dir_lookup(const char* aType)
+{
+ FILE* file;
+ char* home_dir;
+ char* config_home;
+ char* config_file;
+ char buffer[512];
+ char* user_dir;
+ char* p;
+ char* d;
+ int len;
+ int relative;
+
+ home_dir = getenv("HOME");
+
+ if (!home_dir) {
+ goto error;
+ }
+
+ config_home = getenv("XDG_CONFIG_HOME");
+ if (!config_home || config_home[0] == 0) {
+ config_file = (char*)malloc(strlen(home_dir) +
+ strlen("/.config/user-dirs.dirs") + 1);
+ if (!config_file) {
+ goto error;
+ }
+
+ strcpy(config_file, home_dir);
+ strcat(config_file, "/.config/user-dirs.dirs");
+ } else {
+ config_file = (char*)malloc(strlen(config_home) +
+ strlen("/user-dirs.dirs") + 1);
+ if (!config_file) {
+ goto error;
+ }
+
+ strcpy(config_file, config_home);
+ strcat(config_file, "/user-dirs.dirs");
+ }
+
+ file = fopen(config_file, "r");
+ free(config_file);
+ if (!file) {
+ goto error;
+ }
+
+ user_dir = nullptr;
+ while (fgets(buffer, sizeof(buffer), file)) {
+ /* Remove newline at end */
+ len = strlen(buffer);
+ if (len > 0 && buffer[len - 1] == '\n') {
+ buffer[len - 1] = 0;
+ }
+
+ p = buffer;
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp(p, "XDG_", 4) != 0) {
+ continue;
+ }
+ p += 4;
+ if (strncmp(p, aType, strlen(aType)) != 0) {
+ continue;
+ }
+ p += strlen(aType);
+ if (strncmp(p, "_DIR", 4) != 0) {
+ continue;
+ }
+ p += 4;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p != '=') {
+ continue;
+ }
+ p++;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p != '"') {
+ continue;
+ }
+ p++;
+
+ relative = 0;
+ if (strncmp(p, "$HOME/", 6) == 0) {
+ p += 6;
+ relative = 1;
+ } else if (*p != '/') {
+ continue;
+ }
+
+ if (relative) {
+ user_dir = (char*)malloc(strlen(home_dir) + 1 + strlen(p) + 1);
+ if (!user_dir) {
+ goto error2;
+ }
+
+ strcpy(user_dir, home_dir);
+ strcat(user_dir, "/");
+ } else {
+ user_dir = (char*)malloc(strlen(p) + 1);
+ if (!user_dir) {
+ goto error2;
+ }
+
+ *user_dir = 0;
+ }
+
+ d = user_dir + strlen(user_dir);
+ while (*p && *p != '"') {
+ if ((*p == '\\') && (*(p + 1) != 0)) {
+ p++;
+ }
+ *d++ = *p++;
+ }
+ *d = 0;
+ }
+error2:
+ fclose(file);
+
+ if (user_dir) {
+ return user_dir;
+ }
+
+error:
+ return nullptr;
+}
+
+static const char xdg_user_dirs[] =
+ "DESKTOP\0"
+ "DOCUMENTS\0"
+ "DOWNLOAD\0"
+ "MUSIC\0"
+ "PICTURES\0"
+ "PUBLICSHARE\0"
+ "TEMPLATES\0"
+ "VIDEOS";
+
+static const uint8_t xdg_user_dir_offsets[] = {
+ 0,
+ 8,
+ 18,
+ 27,
+ 33,
+ 42,
+ 54,
+ 64
+};
+
+static nsresult
+GetUnixXDGUserDirectory(SystemDirectories aSystemDirectory,
+ nsIFile** aFile)
+{
+ char* dir = xdg_user_dir_lookup(
+ xdg_user_dirs + xdg_user_dir_offsets[aSystemDirectory - Unix_XDG_Desktop]);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> file;
+ if (dir) {
+ rv = NS_NewNativeLocalFile(nsDependentCString(dir), true,
+ getter_AddRefs(file));
+ free(dir);
+ } else if (Unix_XDG_Desktop == aSystemDirectory) {
+ // for the XDG desktop dir, fall back to HOME/Desktop
+ // (for historical compatibility)
+ rv = GetUnixHomeDir(getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = file->AppendNative(NS_LITERAL_CSTRING("Desktop"));
+ } else {
+ // no fallback for the other XDG dirs
+ rv = NS_ERROR_FAILURE;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!exists) {
+ rv = file->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ *aFile = nullptr;
+ file.swap(*aFile);
+
+ return NS_OK;
+}
+#endif
+
+nsresult
+GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
+ nsIFile** aFile)
+{
+#if defined(XP_WIN)
+ WCHAR path[MAX_PATH];
+#else
+ char path[MAXPATHLEN];
+#endif
+
+ switch (aSystemSystemDirectory) {
+ case OS_CurrentWorkingDirectory:
+#if defined(XP_WIN)
+ if (!_wgetcwd(path, MAX_PATH)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_NewLocalFile(nsDependentString(path),
+ true,
+ aFile);
+#else
+ if (!getcwd(path, MAXPATHLEN)) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+#if !defined(XP_WIN)
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ true,
+ aFile);
+#endif
+
+ case OS_DriveDirectory:
+#if defined (XP_WIN)
+ {
+ int32_t len = ::GetWindowsDirectoryW(path, MAX_PATH);
+ if (len == 0) {
+ break;
+ }
+ if (path[1] == char16_t(':') && path[2] == char16_t('\\')) {
+ path[3] = 0;
+ }
+
+ return NS_NewLocalFile(nsDependentString(path),
+ true,
+ aFile);
+ }
+#else
+ return NS_NewNativeLocalFile(nsDependentCString("/"),
+ true,
+ aFile);
+
+#endif
+
+ case OS_TemporaryDirectory:
+#if defined (XP_WIN)
+ {
+ DWORD len = ::GetTempPathW(MAX_PATH, path);
+ if (len == 0) {
+ break;
+ }
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+#elif defined(MOZ_WIDGET_COCOA)
+ {
+ return GetOSXFolderType(kUserDomain, kTemporaryFolderType, aFile);
+ }
+
+#elif defined(XP_UNIX)
+ {
+ static const char* tPath = nullptr;
+ if (!tPath) {
+ tPath = PR_GetEnv("TMPDIR");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TMP");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TEMP");
+ if (!tPath || !*tPath) {
+ tPath = "/tmp/";
+ }
+ }
+ }
+ }
+ return NS_NewNativeLocalFile(nsDependentCString(tPath),
+ true,
+ aFile);
+ }
+#else
+ break;
+#endif
+#if defined (XP_WIN)
+ case Win_SystemDirectory: {
+ int32_t len = ::GetSystemDirectoryW(path, MAX_PATH);
+
+ // Need enough space to add the trailing backslash
+ if (!len || len > MAX_PATH - 2) {
+ break;
+ }
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+
+ case Win_WindowsDirectory: {
+ int32_t len = ::GetWindowsDirectoryW(path, MAX_PATH);
+
+ // Need enough space to add the trailing backslash
+ if (!len || len > MAX_PATH - 2) {
+ break;
+ }
+
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+
+ case Win_ProgramFiles: {
+ return GetWindowsFolder(CSIDL_PROGRAM_FILES, aFile);
+ }
+
+ case Win_HomeDirectory: {
+ nsresult rv = GetWindowsFolder(CSIDL_PROFILE, aFile);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+
+ int32_t len;
+ if ((len = ::GetEnvironmentVariableW(L"HOME", path, MAX_PATH)) > 0) {
+ // Need enough space to add the trailing backslash
+ if (len > MAX_PATH - 2) {
+ break;
+ }
+
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ rv = NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+ }
+
+ len = ::GetEnvironmentVariableW(L"HOMEDRIVE", path, MAX_PATH);
+ if (0 < len && len < MAX_PATH) {
+ WCHAR temp[MAX_PATH];
+ DWORD len2 = ::GetEnvironmentVariableW(L"HOMEPATH", temp, MAX_PATH);
+ if (0 < len2 && len + len2 < MAX_PATH) {
+ wcsncat(path, temp, len2);
+ }
+
+ len = wcslen(path);
+
+ // Need enough space to add the trailing backslash
+ if (len > MAX_PATH - 2) {
+ break;
+ }
+
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+ }
+ case Win_Desktop: {
+ return GetWindowsFolder(CSIDL_DESKTOP, aFile);
+ }
+ case Win_Programs: {
+ return GetWindowsFolder(CSIDL_PROGRAMS, aFile);
+ }
+
+ case Win_Downloads: {
+ // Defined in KnownFolders.h.
+ GUID folderid_downloads = {
+ 0x374de290, 0x123f, 0x4565,
+ { 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b }
+ };
+ nsresult rv = GetKnownFolder(&folderid_downloads, aFile);
+ // On WinXP, there is no downloads folder, default
+ // to 'Desktop'.
+ if (NS_ERROR_FAILURE == rv) {
+ rv = GetWindowsFolder(CSIDL_DESKTOP, aFile);
+ }
+ return rv;
+ }
+
+ case Win_Controls: {
+ return GetWindowsFolder(CSIDL_CONTROLS, aFile);
+ }
+ case Win_Printers: {
+ return GetWindowsFolder(CSIDL_PRINTERS, aFile);
+ }
+ case Win_Personal: {
+ return GetWindowsFolder(CSIDL_PERSONAL, aFile);
+ }
+ case Win_Favorites: {
+ return GetWindowsFolder(CSIDL_FAVORITES, aFile);
+ }
+ case Win_Startup: {
+ return GetWindowsFolder(CSIDL_STARTUP, aFile);
+ }
+ case Win_Recent: {
+ return GetWindowsFolder(CSIDL_RECENT, aFile);
+ }
+ case Win_Sendto: {
+ return GetWindowsFolder(CSIDL_SENDTO, aFile);
+ }
+ case Win_Bitbucket: {
+ return GetWindowsFolder(CSIDL_BITBUCKET, aFile);
+ }
+ case Win_Startmenu: {
+ return GetWindowsFolder(CSIDL_STARTMENU, aFile);
+ }
+ case Win_Desktopdirectory: {
+ return GetWindowsFolder(CSIDL_DESKTOPDIRECTORY, aFile);
+ }
+ case Win_Drives: {
+ return GetWindowsFolder(CSIDL_DRIVES, aFile);
+ }
+ case Win_Network: {
+ return GetWindowsFolder(CSIDL_NETWORK, aFile);
+ }
+ case Win_Nethood: {
+ return GetWindowsFolder(CSIDL_NETHOOD, aFile);
+ }
+ case Win_Fonts: {
+ return GetWindowsFolder(CSIDL_FONTS, aFile);
+ }
+ case Win_Templates: {
+ return GetWindowsFolder(CSIDL_TEMPLATES, aFile);
+ }
+ case Win_Common_Startmenu: {
+ return GetWindowsFolder(CSIDL_COMMON_STARTMENU, aFile);
+ }
+ case Win_Common_Programs: {
+ return GetWindowsFolder(CSIDL_COMMON_PROGRAMS, aFile);
+ }
+ case Win_Common_Startup: {
+ return GetWindowsFolder(CSIDL_COMMON_STARTUP, aFile);
+ }
+ case Win_Common_Desktopdirectory: {
+ return GetWindowsFolder(CSIDL_COMMON_DESKTOPDIRECTORY, aFile);
+ }
+ case Win_Common_AppData: {
+ return GetWindowsFolder(CSIDL_COMMON_APPDATA, aFile);
+ }
+ case Win_Printhood: {
+ return GetWindowsFolder(CSIDL_PRINTHOOD, aFile);
+ }
+ case Win_Cookies: {
+ return GetWindowsFolder(CSIDL_COOKIES, aFile);
+ }
+ case Win_Appdata: {
+ nsresult rv = GetWindowsFolder(CSIDL_APPDATA, aFile);
+ if (NS_FAILED(rv)) {
+ rv = GetRegWindowsAppDataFolder(false, aFile);
+ }
+ return rv;
+ }
+ case Win_LocalAppdata: {
+ nsresult rv = GetWindowsFolder(CSIDL_LOCAL_APPDATA, aFile);
+ if (NS_FAILED(rv)) {
+ rv = GetRegWindowsAppDataFolder(true, aFile);
+ }
+ return rv;
+ }
+#if defined(MOZ_CONTENT_SANDBOX)
+ case Win_LocalAppdataLow: {
+ // This should only really fail on versions pre-Vista, in which case this
+ // shouldn't have been used in the first place.
+ GUID localAppDataLowGuid = FOLDERID_LocalAppDataLow;
+ return GetKnownFolder(&localAppDataLowGuid, aFile);
+ }
+#endif
+ case Win_Documents: {
+ return GetLibrarySaveToPath(CSIDL_MYDOCUMENTS,
+ FOLDERID_DocumentsLibrary,
+ aFile);
+ }
+ case Win_Pictures: {
+ return GetLibrarySaveToPath(CSIDL_MYPICTURES,
+ FOLDERID_PicturesLibrary,
+ aFile);
+ }
+ case Win_Music: {
+ return GetLibrarySaveToPath(CSIDL_MYMUSIC,
+ FOLDERID_MusicLibrary,
+ aFile);
+ }
+ case Win_Videos: {
+ return GetLibrarySaveToPath(CSIDL_MYVIDEO,
+ FOLDERID_VideosLibrary,
+ aFile);
+ }
+#endif // XP_WIN
+
+#if defined(XP_UNIX)
+ case Unix_LocalDirectory:
+ return NS_NewNativeLocalFile(nsDependentCString("/usr/local/netscape/"),
+ true,
+ aFile);
+ case Unix_LibDirectory:
+ return NS_NewNativeLocalFile(nsDependentCString("/usr/local/lib/netscape/"),
+ true,
+ aFile);
+
+ case Unix_HomeDirectory:
+ return GetUnixHomeDir(aFile);
+
+ case Unix_XDG_Desktop:
+ case Unix_XDG_Documents:
+ case Unix_XDG_Download:
+ case Unix_XDG_Music:
+ case Unix_XDG_Pictures:
+ case Unix_XDG_PublicShare:
+ case Unix_XDG_Templates:
+ case Unix_XDG_Videos:
+ return GetUnixXDGUserDirectory(aSystemSystemDirectory, aFile);
+#endif
+
+ default:
+ break;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+#if defined (MOZ_WIDGET_COCOA)
+nsresult
+GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (aFolderType == kTemporaryFolderType) {
+ NS_NewLocalFile(EmptyString(), true, aLocalFile);
+ nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
+ if (localMacFile) {
+ rv = localMacFile->InitWithCFURL(
+ CocoaFileUtils::GetTemporaryFolderCFURLRef());
+ }
+ return rv;
+ }
+
+ OSErr err;
+ FSRef fsRef;
+ err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
+ if (err == noErr) {
+ NS_NewLocalFile(EmptyString(), true, aLocalFile);
+ nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
+ if (localMacFile) {
+ rv = localMacFile->InitWithFSRef(&fsRef);
+ }
+ }
+ return rv;
+}
+#endif
+
diff --git a/xpcom/io/SpecialSystemDirectory.h b/xpcom/io/SpecialSystemDirectory.h
new file mode 100644
index 000000000..dd3d88379
--- /dev/null
+++ b/xpcom/io/SpecialSystemDirectory.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SPECIALSYSTEMDIRECTORY_H_
+#define _SPECIALSYSTEMDIRECTORY_H_
+
+#include "nscore.h"
+#include "nsIFile.h"
+
+#ifdef MOZ_WIDGET_COCOA
+#include <Carbon/Carbon.h>
+#include "nsILocalFileMac.h"
+#include "prenv.h"
+#endif
+
+extern void StartupSpecialSystemDirectory();
+
+
+enum SystemDirectories {
+ OS_DriveDirectory = 1,
+ OS_TemporaryDirectory = 2,
+ OS_CurrentProcessDirectory = 3,
+ OS_CurrentWorkingDirectory = 4,
+ XPCOM_CurrentProcessComponentDirectory = 5,
+ XPCOM_CurrentProcessComponentRegistry = 6,
+
+ Moz_BinDirectory = 100 ,
+ Mac_SystemDirectory = 101,
+ Mac_DesktopDirectory = 102,
+ Mac_TrashDirectory = 103,
+ Mac_StartupDirectory = 104,
+ Mac_ShutdownDirectory = 105,
+ Mac_AppleMenuDirectory = 106,
+ Mac_ControlPanelDirectory = 107,
+ Mac_ExtensionDirectory = 108,
+ Mac_FontsDirectory = 109,
+ Mac_ClassicPreferencesDirectory = 110,
+ Mac_DocumentsDirectory = 111,
+ Mac_InternetSearchDirectory = 112,
+ Mac_DefaultDownloadDirectory = 113,
+ Mac_UserLibDirectory = 114,
+ Mac_PreferencesDirectory = 115,
+
+ Win_SystemDirectory = 201,
+ Win_WindowsDirectory = 202,
+ Win_HomeDirectory = 203,
+ Win_Desktop = 204,
+ Win_Programs = 205,
+ Win_Controls = 206,
+ Win_Printers = 207,
+ Win_Personal = 208,
+ Win_Favorites = 209,
+ Win_Startup = 210,
+ Win_Recent = 211,
+ Win_Sendto = 212,
+ Win_Bitbucket = 213,
+ Win_Startmenu = 214,
+ Win_Desktopdirectory = 215,
+ Win_Drives = 216,
+ Win_Network = 217,
+ Win_Nethood = 218,
+ Win_Fonts = 219,
+ Win_Templates = 220,
+ Win_Common_Startmenu = 221,
+ Win_Common_Programs = 222,
+ Win_Common_Startup = 223,
+ Win_Common_Desktopdirectory = 224,
+ Win_Appdata = 225,
+ Win_Printhood = 226,
+ Win_Cookies = 227,
+ Win_LocalAppdata = 228,
+ Win_ProgramFiles = 229,
+ Win_Downloads = 230,
+ Win_Common_AppData = 231,
+ Win_Documents = 232,
+ Win_Pictures = 233,
+ Win_Music = 234,
+ Win_Videos = 235,
+#if defined(MOZ_CONTENT_SANDBOX)
+ Win_LocalAppdataLow = 236,
+#endif
+
+ Unix_LocalDirectory = 301,
+ Unix_LibDirectory = 302,
+ Unix_HomeDirectory = 303,
+ Unix_XDG_Desktop = 304,
+ Unix_XDG_Documents = 305,
+ Unix_XDG_Download = 306,
+ Unix_XDG_Music = 307,
+ Unix_XDG_Pictures = 308,
+ Unix_XDG_PublicShare = 309,
+ Unix_XDG_Templates = 310,
+ Unix_XDG_Videos = 311
+};
+
+nsresult
+GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
+ nsIFile** aFile);
+#ifdef MOZ_WIDGET_COCOA
+nsresult
+GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile);
+#endif
+
+#endif
diff --git a/xpcom/io/crc32c.c b/xpcom/io/crc32c.c
new file mode 100644
index 000000000..3494945c0
--- /dev/null
+++ b/xpcom/io/crc32c.c
@@ -0,0 +1,154 @@
+/*
+ * Based on file found here:
+ *
+ * https://svnweb.freebsd.org/base/stable/10/sys/libkern/crc32.c?revision=256281
+ */
+
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+#include "crc32c.h"
+
+/* CRC32C routines, these use a different polynomial */
+/*****************************************************************/
+/* */
+/* CRC LOOKUP TABLE */
+/* ================ */
+/* The following CRC lookup table was generated automagically */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
+/* Program V1.0 using the following model parameters: */
+/* */
+/* Width : 4 bytes. */
+/* Poly : 0x1EDC6F41L */
+/* Reverse : TRUE. */
+/* */
+/* For more information on the Rocksoft^tm Model CRC Algorithm, */
+/* see the document titled "A Painless Guide to CRC Error */
+/* Detection Algorithms" by Ross Williams */
+/* (ross@guest.adelaide.edu.au.). This document is likely to be */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
+/* */
+/*****************************************************************/
+
+static const uint32_t crc32Table[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+// NOTE: See source URL at top of this file for multitable implementation which
+// offers a performance boost at the cost of ~8KB of static tables.
+
+uint32_t
+ComputeCrc32c(uint32_t crc, const void *buf, size_t size)
+{
+ const uint8_t *p = buf;
+
+
+ while (size--)
+ crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+
+ return crc;
+}
diff --git a/xpcom/io/crc32c.h b/xpcom/io/crc32c.h
new file mode 100644
index 000000000..f7035fa15
--- /dev/null
+++ b/xpcom/io/crc32c.h
@@ -0,0 +1,23 @@
+#ifndef crc32c_h
+#define crc32c_h
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Compute a CRC32c as defined in RFC3720. This is a different polynomial than
+// what is used in the crc for zlib, etc. Typical usage to calculate a new CRC:
+//
+// ComputeCrc32c(~0, buffer, bufferLength);
+//
+uint32_t
+ComputeCrc32c(uint32_t aCrc, const void *aBuf, size_t aSize);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // crc32c_h
diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build
new file mode 100644
index 000000000..6f21e0a72
--- /dev/null
+++ b/xpcom/io/moz.build
@@ -0,0 +1,140 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIAsyncInputStream.idl',
+ 'nsIAsyncOutputStream.idl',
+ 'nsIBinaryInputStream.idl',
+ 'nsIBinaryOutputStream.idl',
+ 'nsICloneableInputStream.idl',
+ 'nsIConverterInputStream.idl',
+ 'nsIConverterOutputStream.idl',
+ 'nsIDirectoryEnumerator.idl',
+ 'nsIDirectoryService.idl',
+ 'nsIFile.idl',
+ 'nsIInputStream.idl',
+ 'nsIInputStreamTee.idl',
+ 'nsIIOUtil.idl',
+ 'nsILineInputStream.idl',
+ 'nsILocalFile.idl',
+ 'nsILocalFileWin.idl',
+ 'nsIMultiplexInputStream.idl',
+ 'nsIObjectInputStream.idl',
+ 'nsIObjectOutputStream.idl',
+ 'nsIOutputStream.idl',
+ 'nsIPipe.idl',
+ 'nsISafeOutputStream.idl',
+ 'nsIScriptableBase64Encoder.idl',
+ 'nsIScriptableInputStream.idl',
+ 'nsISeekableStream.idl',
+ 'nsIStorageStream.idl',
+ 'nsIStreamBufferAccess.idl',
+ 'nsIStringStream.idl',
+ 'nsIUnicharInputStream.idl',
+ 'nsIUnicharLineInputStream.idl',
+ 'nsIUnicharOutputStream.idl',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ XPIDL_SOURCES += [
+ 'nsILocalFileMac.idl',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ EXPORTS += ['nsLocalFileWin.h']
+ EXPORTS.mozilla += [
+ 'FileUtilsWin.h',
+ ]
+ SOURCES += [
+ 'FileUtilsWin.cpp',
+ 'nsLocalFileWin.cpp',
+ ]
+else:
+ EXPORTS += ['nsLocalFileUnix.h']
+ SOURCES += [
+ 'nsLocalFileUnix.cpp',
+ ]
+
+XPIDL_MODULE = 'xpcom_io'
+
+EXPORTS += [
+ 'nsAnonymousTemporaryFile.h',
+ 'nsAppDirectoryServiceDefs.h',
+ 'nsDirectoryService.h',
+ 'nsDirectoryServiceAtomList.h',
+ 'nsDirectoryServiceDefs.h',
+ 'nsDirectoryServiceUtils.h',
+ 'nsEscape.h',
+ 'nsLinebreakConverter.h',
+ 'nsLocalFile.h',
+ 'nsMultiplexInputStream.h',
+ 'nsNativeCharsetUtils.h',
+ 'nsScriptableInputStream.h',
+ 'nsStorageStream.h',
+ 'nsStreamUtils.h',
+ 'nsStringStream.h',
+ 'nsUnicharInputStream.h',
+ 'nsWildCard.h',
+ 'SlicedInputStream.h',
+ 'SpecialSystemDirectory.h',
+]
+
+EXPORTS.mozilla += [
+ 'Base64.h',
+ 'SnappyCompressOutputStream.h',
+ 'SnappyFrameUtils.h',
+ 'SnappyUncompressInputStream.h',
+]
+
+UNIFIED_SOURCES += [
+ 'Base64.cpp',
+ 'crc32c.c',
+ 'nsAnonymousTemporaryFile.cpp',
+ 'nsAppFileLocationProvider.cpp',
+ 'nsBinaryStream.cpp',
+ 'nsDirectoryService.cpp',
+ 'nsEscape.cpp',
+ 'nsInputStreamTee.cpp',
+ 'nsIOUtil.cpp',
+ 'nsLinebreakConverter.cpp',
+ 'nsLocalFileCommon.cpp',
+ 'nsMultiplexInputStream.cpp',
+ 'nsNativeCharsetUtils.cpp',
+ 'nsPipe3.cpp',
+ 'nsScriptableBase64Encoder.cpp',
+ 'nsScriptableInputStream.cpp',
+ 'nsSegmentedBuffer.cpp',
+ 'nsStorageStream.cpp',
+ 'nsStreamUtils.cpp',
+ 'nsStringStream.cpp',
+ 'nsUnicharInputStream.cpp',
+ 'nsWildCard.cpp',
+ 'SlicedInputStream.cpp',
+ 'SnappyCompressOutputStream.cpp',
+ 'SnappyFrameUtils.cpp',
+ 'SnappyUncompressInputStream.cpp',
+ 'SpecialSystemDirectory.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'CocoaFileUtils.mm',
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['OS_ARCH'] == 'Linux' and 'lib64' in CONFIG['libdir']:
+ DEFINES['HAVE_USR_LIB64_DIR'] = True
+
+LOCAL_INCLUDES += ['!..']
+
+if CONFIG['_MSC_VER']:
+ # This is intended as a temporary hack to support building with VS2015.
+ # '_snwprintf' : format string '%s' requires an argument of type 'wchar_t *',
+ # but variadic argument 3 has type 'char16ptr_t'
+ CXXFLAGS += ['-wd4477']
diff --git a/xpcom/io/nsAnonymousTemporaryFile.cpp b/xpcom/io/nsAnonymousTemporaryFile.cpp
new file mode 100644
index 000000000..586e552c4
--- /dev/null
+++ b/xpcom/io/nsAnonymousTemporaryFile.cpp
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "prio.h"
+#include "private/pprio.h"
+
+#ifdef XP_WIN
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsIIdleService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIFile.h"
+#include "nsAutoPtr.h"
+#include "nsITimer.h"
+#include "nsCRT.h"
+
+#endif
+
+using namespace mozilla;
+
+// We store the temp files in the system temp dir.
+//
+// On Windows systems in particular we use a sub-directory of the temp
+// directory, because:
+// 1. DELETE_ON_CLOSE is unreliable on Windows, in particular if we power
+// cycle (and perhaps if we crash) the files are not deleted. We store
+// the temporary files in a known sub-dir so that we can find and delete
+// them easily and quickly.
+// 2. On Windows NT the system temp dir is in the user's $HomeDir/AppData,
+// so we can be sure the user always has write privileges to that directory;
+// if the sub-dir for our temp files was in some shared location and
+// was created by a privileged user, it's possible that other users
+// wouldn't have write access to that sub-dir. (Non-Windows systems
+// don't store their temp files in a sub-dir, so this isn't an issue on
+// those platforms).
+// 3. Content processes can access the system temp dir
+// (NS_GetSpecialDirectory fails on NS_APP_USER_PROFILE_LOCAL_50_DIR
+// for content process for example, which is where we previously stored
+// temp files on Windows). This argument applies to all platforms, not
+// just Windows.
+static nsresult
+GetTempDir(nsIFile** aTempDir)
+{
+ if (NS_WARN_IF(!aTempDir)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsCOMPtr<nsIFile> tmpFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifdef XP_WIN
+ // On windows DELETE_ON_CLOSE is unreliable, so we store temporary files
+ // in a subdir of the temp dir and delete that in an idle service observer
+ // to ensure it's been cleared.
+ rv = tmpFile->AppendNative(nsDependentCString("mozilla-temp-files"));
+ 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;
+ }
+#endif
+
+ tmpFile.forget(aTempDir);
+
+ return NS_OK;
+}
+
+namespace {
+
+class nsRemoteAnonymousTemporaryFileRunnable : public Runnable
+{
+public:
+ dom::FileDescOrError *mResultPtr;
+ explicit nsRemoteAnonymousTemporaryFileRunnable(dom::FileDescOrError *aResultPtr)
+ : mResultPtr(aResultPtr)
+ { }
+
+protected:
+ NS_IMETHOD Run() override {
+ dom::ContentChild* child = dom::ContentChild::GetSingleton();
+ MOZ_ASSERT(child);
+ child->SendOpenAnonymousTemporaryFile(mResultPtr);
+ return NS_OK;
+ }
+};
+
+} // namespace
+
+nsresult
+NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
+{
+ if (NS_WARN_IF(!aOutFileDesc)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (dom::ContentChild* child = dom::ContentChild::GetSingleton()) {
+ dom::FileDescOrError fd = NS_OK;
+ if (NS_IsMainThread()) {
+ child->SendOpenAnonymousTemporaryFile(&fd);
+ } else {
+ nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+ MOZ_ASSERT(mainThread);
+ SyncRunnable::DispatchToThread(mainThread,
+ new nsRemoteAnonymousTemporaryFileRunnable(&fd));
+ }
+ if (fd.type() == dom::FileDescOrError::Tnsresult) {
+ nsresult rv = fd.get_nsresult();
+ MOZ_ASSERT(NS_FAILED(rv));
+ return rv;
+ }
+ auto rawFD = fd.get_FileDescriptor().ClonePlatformHandle();
+ *aOutFileDesc = PR_ImportFile(PROsfd(rawFD.release()));
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> tmpFile;
+ rv = GetTempDir(getter_AddRefs(tmpFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Give the temp file a name with a random element. CreateUnique will also
+ // append a counter to the name if it encounters a name collision. Adding
+ // a random element to the name reduces the likelihood of a name collision,
+ // so that CreateUnique() doesn't end up trying a lot of name variants in
+ // its "try appending an incrementing counter" loop, as file IO can be
+ // expensive on some mobile flash drives.
+ nsAutoCString name("mozilla-temp-");
+ name.AppendInt(rand());
+
+ rv = tmpFile->AppendNative(name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
+ PR_IRWXU, aOutFileDesc);
+
+ return rv;
+}
+
+#ifdef XP_WIN
+
+// On Windows we have an idle service observer that runs some time after
+// startup and deletes any stray anonymous temporary files...
+
+// Duration of idle time before we'll get a callback whereupon we attempt to
+// remove any stray and unused anonymous temp files.
+#define TEMP_FILE_IDLE_TIME_S 30
+
+// The nsAnonTempFileRemover is created in a timer, which sets an idle observer.
+// This is expiration time (in ms) which initial timer is set for (3 minutes).
+#define SCHEDULE_TIMEOUT_MS 3 * 60 * 1000
+
+#define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown"
+
+// This class adds itself as an idle observer. When the application has
+// been idle for about 30 seconds we'll get a notification, whereupon we'll
+// attempt to delete ${TempDir}/mozilla-temp-files/. This is to ensure all
+// temp files that were supposed to be deleted on application exit were actually
+// deleted, as they may not be if we previously crashed. See bugs 572579 and
+// 785662. This is only needed on some versions of Windows,
+// nsIFile::DELETE_ON_CLOSE works on other platforms.
+// This class adds itself as a shutdown observer so that it can cancel the
+// idle observer and its timer on shutdown. Note: the observer and idle
+// services hold references to instances of this object, and those references
+// are what keep this object alive.
+class nsAnonTempFileRemover final : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsAnonTempFileRemover() {}
+
+ nsresult Init()
+ {
+ // We add the idle observer in a timer, so that the app has enough
+ // time to start up before we add the idle observer. If we register the
+ // idle observer too early, it will be registered before the fake idle
+ // service is installed when running in xpcshell, and this interferes with
+ // the fake idle service, causing xpcshell-test failures.
+ mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ if (NS_WARN_IF(!mTimer)) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv = mTimer->Init(this,
+ SCHEDULE_TIMEOUT_MS,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Register shutdown observer so we can cancel the timer if we shutdown before
+ // the timer runs.
+ nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
+ if (NS_WARN_IF(!obsSrv)) {
+ return NS_ERROR_FAILURE;
+ }
+ return obsSrv->AddObserver(this, XPCOM_SHUTDOWN_TOPIC, false);
+ }
+
+ void Cleanup()
+ {
+ // Cancel timer.
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ // Remove idle service observer.
+ nsCOMPtr<nsIIdleService> idleSvc =
+ do_GetService("@mozilla.org/widget/idleservice;1");
+ if (idleSvc) {
+ idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
+ }
+ // Remove shutdown observer.
+ nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
+ if (obsSrv) {
+ obsSrv->RemoveObserver(this, XPCOM_SHUTDOWN_TOPIC);
+ }
+ }
+
+ NS_IMETHODIMP Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+ {
+ if (nsCRT::strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0 &&
+ NS_FAILED(RegisterIdleObserver())) {
+ Cleanup();
+ } else if (nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0) {
+ // The user has been idle for a while, clean up the temp files.
+ // The idle service will drop its reference to this object after
+ // we exit, destroying this object.
+ RemoveAnonTempFileFiles();
+ Cleanup();
+ } else if (nsCRT::strcmp(aTopic, XPCOM_SHUTDOWN_TOPIC) == 0) {
+ Cleanup();
+ }
+ return NS_OK;
+ }
+
+ nsresult RegisterIdleObserver()
+ {
+ // Add this as an idle observer. When we've been idle for
+ // TEMP_FILE_IDLE_TIME_S seconds, we'll get a notification, and we'll then
+ // try to delete any stray temp files.
+ nsCOMPtr<nsIIdleService> idleSvc =
+ do_GetService("@mozilla.org/widget/idleservice;1");
+ if (!idleSvc) {
+ return NS_ERROR_FAILURE;
+ }
+ return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
+ }
+
+ void RemoveAnonTempFileFiles()
+ {
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv = GetTempDir(getter_AddRefs(tmpDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Remove the directory recursively.
+ tmpDir->Remove(true);
+ }
+
+private:
+ ~nsAnonTempFileRemover() {}
+
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(nsAnonTempFileRemover, nsIObserver)
+
+nsresult
+CreateAnonTempFileRemover()
+{
+ // Create a temp file remover. If Init() succeeds, the temp file remover is kept
+ // alive by a reference held by the observer service, since the temp file remover
+ // is a shutdown observer. We only create the temp file remover if we're running
+ // in the main process; there's no point in doing the temp file removal multiple
+ // times per startup.
+ if (!XRE_IsParentProcess()) {
+ return NS_OK;
+ }
+ RefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover();
+ return tempRemover->Init();
+}
+
+#endif
+
diff --git a/xpcom/io/nsAnonymousTemporaryFile.h b/xpcom/io/nsAnonymousTemporaryFile.h
new file mode 100644
index 000000000..d2f39528f
--- /dev/null
+++ b/xpcom/io/nsAnonymousTemporaryFile.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "prio.h"
+#include "nscore.h"
+
+/**
+ * OpenAnonymousTemporaryFile
+ *
+ * Creates and opens a temporary file which has a random name. Callers have no
+ * control over the file name, and the file is opened in a temporary location
+ * which is appropriate for the platform.
+ *
+ * Upon success, aOutFileDesc contains an opened handle to the temporary file.
+ * The caller is responsible for closing the file when they're finished with it.
+ *
+ * The file will be deleted when the file handle is closed. On non-Windows
+ * platforms the file will be unlinked before this function returns. On Windows
+ * the OS supplied delete-on-close mechanism is unreliable if the application
+ * crashes or the computer power cycles unexpectedly, so unopened temporary
+ * files are purged at some time after application startup.
+ *
+ */
+nsresult
+NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc);
+
diff --git a/xpcom/io/nsAppDirectoryServiceDefs.h b/xpcom/io/nsAppDirectoryServiceDefs.h
new file mode 100644
index 000000000..aa0a68816
--- /dev/null
+++ b/xpcom/io/nsAppDirectoryServiceDefs.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAppDirectoryServiceDefs_h___
+#define nsAppDirectoryServiceDefs_h___
+
+//========================================================================================
+//
+// Defines property names for directories available from standard nsIDirectoryServiceProviders.
+// These keys are not guaranteed to exist because the nsIDirectoryServiceProviders which
+// provide them are optional.
+//
+// Keys whose definition ends in "DIR" or "FILE" return a single nsIFile (or subclass).
+// Keys whose definition ends in "LIST" return an nsISimpleEnumerator which enumerates a
+// list of file objects.
+//
+// System and XPCOM level properties are defined in nsDirectoryServiceDefs.h.
+//
+//========================================================================================
+
+
+// --------------------------------------------------------------------------------------
+// Files and directories which exist on a per-product basis
+// --------------------------------------------------------------------------------------
+
+#define NS_APP_APPLICATION_REGISTRY_FILE "AppRegF"
+#define NS_APP_APPLICATION_REGISTRY_DIR "AppRegD"
+
+#define NS_APP_DEFAULTS_50_DIR "DefRt" // The root dir of all defaults dirs
+#define NS_APP_PREF_DEFAULTS_50_DIR "PrfDef"
+
+#define NS_APP_USER_PROFILES_ROOT_DIR "DefProfRt" // The dir where user profile dirs live.
+#define NS_APP_USER_PROFILES_LOCAL_ROOT_DIR "DefProfLRt" // The dir where user profile temp dirs live.
+
+#define NS_APP_RES_DIR "ARes"
+#define NS_APP_CHROME_DIR "AChrom"
+#define NS_APP_PLUGINS_DIR "APlugns" // Deprecated - use NS_APP_PLUGINS_DIR_LIST
+#define NS_APP_SEARCH_DIR "SrchPlugns"
+
+#define NS_APP_CHROME_DIR_LIST "AChromDL"
+#define NS_APP_PLUGINS_DIR_LIST "APluginsDL"
+#define NS_APP_SEARCH_DIR_LIST "SrchPluginsDL"
+#define NS_APP_DISTRIBUTION_SEARCH_DIR_LIST "SrchPluginsDistDL"
+
+// --------------------------------------------------------------------------------------
+// Files and directories which exist on a per-profile basis
+// These locations are typically provided by the profile mgr
+// --------------------------------------------------------------------------------------
+
+// In a shared profile environment, prefixing a profile-relative
+// key with NS_SHARED returns a location that is shared by
+// other users of the profile. Without this prefix, the consumer
+// has exclusive access to this location.
+
+#define NS_SHARED "SHARED"
+
+#define NS_APP_PREFS_50_DIR "PrefD" // Directory which contains user prefs
+#define NS_APP_PREFS_50_FILE "PrefF"
+#define NS_APP_PREFS_DEFAULTS_DIR_LIST "PrefDL"
+#define NS_EXT_PREFS_DEFAULTS_DIR_LIST "ExtPrefDL"
+#define NS_APP_PREFS_OVERRIDE_DIR "PrefDOverride" // Directory for per-profile defaults
+
+#define NS_APP_USER_PROFILE_50_DIR "ProfD"
+#define NS_APP_USER_PROFILE_LOCAL_50_DIR "ProfLD"
+
+#define NS_APP_USER_CHROME_DIR "UChrm"
+#define NS_APP_USER_SEARCH_DIR "UsrSrchPlugns"
+
+#define NS_APP_LOCALSTORE_50_FILE "LclSt"
+#define NS_APP_USER_PANELS_50_FILE "UPnls"
+#define NS_APP_USER_MIMETYPES_50_FILE "UMimTyp"
+#define NS_APP_CACHE_PARENT_DIR "cachePDir"
+
+#define NS_APP_DOWNLOADS_50_FILE "DLoads"
+
+#define NS_APP_SEARCH_50_FILE "SrchF"
+
+#define NS_APP_INSTALL_CLEANUP_DIR "XPIClnupD" //location of xpicleanup.dat xpicleanup.exe
+
+#define NS_APP_INDEXEDDB_PARENT_DIR "indexedDBPDir"
+
+#define NS_APP_PERMISSION_PARENT_DIR "permissionDBPDir"
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+//
+// NS_APP_CONTENT_PROCESS_TEMP_DIR refers to a directory that is read and
+// write accessible from a sandboxed content process. The key may be used in
+// either process, but the directory is intended to be used for short-lived
+// files that need to be saved to the filesystem by the content process and
+// don't need to survive browser restarts. The directory is reset on startup.
+// The key is only valid when MOZ_CONTENT_SANDBOX is defined. When
+// MOZ_CONTENT_SANDBOX is defined, the directory the key refers to differs
+// depending on whether or not content sandboxing is enabled.
+//
+// When MOZ_CONTENT_SANDBOX is defined and sandboxing is enabled (versus
+// manually disabled via prefs), the content process replaces NS_OS_TEMP_DIR
+// with NS_APP_CONTENT_PROCESS_TEMP_DIR so that legacy code in content
+// attempting to write to NS_OS_TEMP_DIR will write to
+// NS_APP_CONTENT_PROCESS_TEMP_DIR instead. When MOZ_CONTENT_SANDBOX is
+// defined but sandboxing is disabled, NS_APP_CONTENT_PROCESS_TEMP_DIR
+// falls back to NS_OS_TEMP_DIR in both content and chrome processes.
+//
+// New code should avoid writing to the filesystem from the content process
+// and should instead proxy through the parent process whenever possible.
+//
+// At present, all sandboxed content processes use the same directory for
+// NS_APP_CONTENT_PROCESS_TEMP_DIR, but that should not be relied upon.
+//
+#define NS_APP_CONTENT_PROCESS_TEMP_DIR "ContentTmpD"
+#else
+// Otherwise NS_APP_CONTENT_PROCESS_TEMP_DIR must match NS_OS_TEMP_DIR.
+#define NS_APP_CONTENT_PROCESS_TEMP_DIR "TmpD"
+#endif // (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+
+#endif // nsAppDirectoryServiceDefs_h___
diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp
new file mode 100644
index 000000000..27ccd56dc
--- /dev/null
+++ b/xpcom/io/nsAppFileLocationProvider.cpp
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAppFileLocationProvider.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIAtom.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "nsISimpleEnumerator.h"
+#include "prenv.h"
+#include "nsCRT.h"
+
+#if defined(MOZ_WIDGET_COCOA)
+#include <Carbon/Carbon.h>
+#include "nsILocalFileMac.h"
+#elif defined(XP_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#elif defined(XP_UNIX)
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#endif
+
+
+// WARNING: These hard coded names need to go away. They need to
+// come from localizable resources
+
+#if defined(MOZ_WIDGET_COCOA)
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("Application Registry")
+#define ESSENTIAL_FILES NS_LITERAL_CSTRING("Essential Files")
+#elif defined(XP_WIN)
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("registry.dat")
+#else
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("appreg")
+#endif
+
+// define default product directory
+#define DEFAULT_PRODUCT_DIR NS_LITERAL_CSTRING(MOZ_USER_DIR)
+
+// Locally defined keys used by nsAppDirectoryEnumerator
+#define NS_ENV_PLUGINS_DIR "EnvPlugins" // env var MOZ_PLUGIN_PATH
+#define NS_USER_PLUGINS_DIR "UserPlugins"
+
+#ifdef MOZ_WIDGET_COCOA
+#define NS_MACOSX_USER_PLUGIN_DIR "OSXUserPlugins"
+#define NS_MACOSX_LOCAL_PLUGIN_DIR "OSXLocalPlugins"
+#define NS_MACOSX_JAVA2_PLUGIN_DIR "OSXJavaPlugins"
+#elif XP_UNIX
+#define NS_SYSTEM_PLUGINS_DIR "SysPlugins"
+#endif
+
+#define DEFAULTS_DIR_NAME NS_LITERAL_CSTRING("defaults")
+#define DEFAULTS_PREF_DIR_NAME NS_LITERAL_CSTRING("pref")
+#define RES_DIR_NAME NS_LITERAL_CSTRING("res")
+#define CHROME_DIR_NAME NS_LITERAL_CSTRING("chrome")
+#define PLUGINS_DIR_NAME NS_LITERAL_CSTRING("plugins")
+#define SEARCH_DIR_NAME NS_LITERAL_CSTRING("searchplugins")
+
+//*****************************************************************************
+// nsAppFileLocationProvider::Constructor/Destructor
+//*****************************************************************************
+
+nsAppFileLocationProvider::nsAppFileLocationProvider()
+{
+}
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsISupports
+//*****************************************************************************
+
+NS_IMPL_ISUPPORTS(nsAppFileLocationProvider,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2)
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsIDirectoryServiceProvider
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsAppFileLocationProvider::GetFile(const char* aProp, bool* aPersistent,
+ nsIFile** aResult)
+{
+ if (NS_WARN_IF(!aProp)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ *aResult = nullptr;
+ *aPersistent = true;
+
+#ifdef MOZ_WIDGET_COCOA
+ FSRef fileRef;
+ nsCOMPtr<nsILocalFileMac> macFile;
+#endif
+
+ if (nsCRT::strcmp(aProp, NS_APP_APPLICATION_REGISTRY_DIR) == 0) {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ } else if (nsCRT::strcmp(aProp, NS_APP_APPLICATION_REGISTRY_FILE) == 0) {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendNative(APP_REGISTRY_NAME);
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_DEFAULTS_50_DIR) == 0) {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_PREF_DEFAULTS_50_DIR) == 0) {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_PREF_DIR_NAME);
+ }
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_USER_PROFILES_ROOT_DIR) == 0) {
+ rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile));
+ } else if (nsCRT::strcmp(aProp, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR) == 0) {
+ rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile), true);
+ } else if (nsCRT::strcmp(aProp, NS_APP_RES_DIR) == 0) {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(RES_DIR_NAME);
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_CHROME_DIR) == 0) {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(CHROME_DIR_NAME);
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_PLUGINS_DIR) == 0) {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
+ }
+ }
+#ifdef MOZ_WIDGET_COCOA
+ else if (nsCRT::strcmp(aProp, NS_MACOSX_USER_PLUGIN_DIR) == 0) {
+ if (::FSFindFolder(kUserDomain, kInternetPlugInFolderType, false,
+ &fileRef) == noErr) {
+ rv = NS_NewLocalFileWithFSRef(&fileRef, true, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv)) {
+ localFile = macFile;
+ }
+ }
+ } else if (nsCRT::strcmp(aProp, NS_MACOSX_LOCAL_PLUGIN_DIR) == 0) {
+ if (::FSFindFolder(kLocalDomain, kInternetPlugInFolderType, false,
+ &fileRef) == noErr) {
+ rv = NS_NewLocalFileWithFSRef(&fileRef, true, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv)) {
+ localFile = macFile;
+ }
+ }
+ } else if (nsCRT::strcmp(aProp, NS_MACOSX_JAVA2_PLUGIN_DIR) == 0) {
+ static const char* const java2PluginDirPath =
+ "/System/Library/Java/Support/Deploy.bundle/Contents/Resources/";
+ rv = NS_NewNativeLocalFile(nsDependentCString(java2PluginDirPath), true,
+ getter_AddRefs(localFile));
+ }
+#else
+ else if (nsCRT::strcmp(aProp, NS_ENV_PLUGINS_DIR) == 0) {
+ NS_ERROR("Don't use nsAppFileLocationProvider::GetFile(NS_ENV_PLUGINS_DIR, ...). "
+ "Use nsAppFileLocationProvider::GetFiles(...).");
+ const char* pathVar = PR_GetEnv("MOZ_PLUGIN_PATH");
+ if (pathVar && *pathVar)
+ rv = NS_NewNativeLocalFile(nsDependentCString(pathVar), true,
+ getter_AddRefs(localFile));
+ } else if (nsCRT::strcmp(aProp, NS_USER_PLUGINS_DIR) == 0) {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
+ }
+#else
+ rv = NS_ERROR_FAILURE;
+#endif
+ }
+#ifdef XP_UNIX
+ else if (nsCRT::strcmp(aProp, NS_SYSTEM_PLUGINS_DIR) == 0) {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ static const char* const sysLPlgDir =
+#if defined(HAVE_USR_LIB64_DIR) && defined(__LP64__)
+ "/usr/lib64/mozilla/plugins";
+#elif defined(__OpenBSD__) || defined (__FreeBSD__)
+ "/usr/local/lib/mozilla/plugins";
+#else
+ "/usr/lib/mozilla/plugins";
+#endif
+ rv = NS_NewNativeLocalFile(nsDependentCString(sysLPlgDir),
+ false, getter_AddRefs(localFile));
+#else
+ rv = NS_ERROR_FAILURE;
+#endif
+ }
+#endif
+#endif
+ else if (nsCRT::strcmp(aProp, NS_APP_SEARCH_DIR) == 0) {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(SEARCH_DIR_NAME);
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_USER_SEARCH_DIR) == 0) {
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aResult);
+ if (NS_SUCCEEDED(rv)) {
+ rv = (*aResult)->AppendNative(SEARCH_DIR_NAME);
+ }
+ } else if (nsCRT::strcmp(aProp, NS_APP_INSTALL_CLEANUP_DIR) == 0) {
+ // This is cloned so that embeddors will have a hook to override
+ // with their own cleanup dir. See bugzilla bug #105087
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ }
+
+ if (localFile && NS_SUCCEEDED(rv)) {
+ localFile.forget(aResult);
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+
+nsresult
+nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile)
+{
+ if (NS_WARN_IF(!aLocalFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsresult rv;
+
+ if (!mMozBinDirectory) {
+ // Get the mozilla bin directory
+ // 1. Check the directory service first for NS_XPCOM_CURRENT_PROCESS_DIR
+ // This will be set if a directory was passed to NS_InitXPCOM
+ // 2. If that doesn't work, set it to be the current process directory
+ nsCOMPtr<nsIProperties>
+ directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(mMozBinDirectory));
+ if (NS_FAILED(rv)) {
+ rv = directoryService->Get(NS_OS_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(mMozBinDirectory));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIFile> aFile;
+ rv = mMozBinDirectory->Clone(getter_AddRefs(aFile));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_IF_ADDREF(*aLocalFile = aFile);
+ return NS_OK;
+}
+
+
+//----------------------------------------------------------------------------------------
+// GetProductDirectory - Gets the directory which contains the application data folder
+//
+// UNIX : ~/.mozilla/
+// WIN : <Application Data folder on user's machine>\Mozilla
+// Mac : :Documents:Mozilla:
+//----------------------------------------------------------------------------------------
+nsresult
+nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
+ bool aLocal)
+{
+ if (NS_WARN_IF(!aLocalFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+ bool exists;
+ nsCOMPtr<nsIFile> localDir;
+
+#if defined(MOZ_WIDGET_COCOA)
+ FSRef fsRef;
+ OSType folderType = aLocal ? (OSType)kCachedDataFolderType :
+ (OSType)kDomainLibraryFolderType;
+ OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
+ if (err) {
+ return NS_ERROR_FAILURE;
+ }
+ NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localDir));
+ if (!localDir) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsILocalFileMac> localDirMac(do_QueryInterface(localDir));
+ rv = localDirMac->InitWithFSRef(&fsRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+#elif defined(XP_WIN)
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ const char* prop = aLocal ? NS_WIN_LOCAL_APPDATA_DIR : NS_WIN_APPDATA_DIR;
+ rv = directoryService->Get(prop, NS_GET_IID(nsIFile), getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+#elif defined(XP_UNIX)
+ rv = NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), true,
+ getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+#else
+#error dont_know_how_to_get_product_dir_on_your_platform
+#endif
+
+ rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = localDir->Exists(&exists);
+
+ if (NS_SUCCEEDED(rv) && !exists) {
+ rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ localDir.forget(aLocalFile);
+
+ return rv;
+}
+
+
+//----------------------------------------------------------------------------------------
+// GetDefaultUserProfileRoot - Gets the directory which contains each user profile dir
+//
+// UNIX : ~/.mozilla/
+// WIN : <Application Data folder on user's machine>\Mozilla\Profiles
+// Mac : :Documents:Mozilla:Profiles:
+//----------------------------------------------------------------------------------------
+nsresult
+nsAppFileLocationProvider::GetDefaultUserProfileRoot(nsIFile** aLocalFile,
+ bool aLocal)
+{
+ if (NS_WARN_IF(!aLocalFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> localDir;
+
+ rv = GetProductDirectory(getter_AddRefs(localDir), aLocal);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+#if defined(MOZ_WIDGET_COCOA) || defined(XP_WIN)
+ // These 3 platforms share this part of the path - do them as one
+ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Profiles"));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool exists;
+ rv = localDir->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists) {
+ rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+#endif
+
+ localDir.forget(aLocalFile);
+
+ return rv;
+}
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsIDirectoryServiceProvider2
+//*****************************************************************************
+
+class nsAppDirectoryEnumerator : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ /**
+ * aKeyList is a null-terminated list of properties which are provided by aProvider
+ * They do not need to be publicly defined keys.
+ */
+ nsAppDirectoryEnumerator(nsIDirectoryServiceProvider* aProvider,
+ const char* aKeyList[]) :
+ mProvider(aProvider),
+ mCurrentKey(aKeyList)
+ {
+ }
+
+ NS_IMETHOD HasMoreElements(bool* aResult) override
+ {
+ while (!mNext && *mCurrentKey) {
+ bool dontCare;
+ nsCOMPtr<nsIFile> testFile;
+ (void)mProvider->GetFile(*mCurrentKey++, &dontCare, getter_AddRefs(testFile));
+ // Don't return a file which does not exist.
+ bool exists;
+ if (testFile && NS_SUCCEEDED(testFile->Exists(&exists)) && exists) {
+ mNext = testFile;
+ }
+ }
+ *aResult = mNext != nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports** aResult) override
+ {
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = nullptr;
+
+ bool hasMore;
+ HasMoreElements(&hasMore);
+ if (!hasMore) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = mNext;
+ NS_IF_ADDREF(*aResult);
+ mNext = nullptr;
+
+ return *aResult ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+protected:
+ nsCOMPtr<nsIDirectoryServiceProvider> mProvider;
+ const char** mCurrentKey;
+ nsCOMPtr<nsIFile> mNext;
+
+ // Virtual destructor since subclass nsPathsDirectoryEnumerator
+ // does not re-implement Release()
+ virtual ~nsAppDirectoryEnumerator()
+ {
+ }
+};
+
+NS_IMPL_ISUPPORTS(nsAppDirectoryEnumerator, nsISimpleEnumerator)
+
+/* nsPathsDirectoryEnumerator and PATH_SEPARATOR
+ * are not used on MacOS/X. */
+
+#if defined(XP_WIN) /* Win32 */
+#define PATH_SEPARATOR ';'
+#else
+#define PATH_SEPARATOR ':'
+#endif
+
+class nsPathsDirectoryEnumerator final
+ : public nsAppDirectoryEnumerator
+{
+ ~nsPathsDirectoryEnumerator() {}
+
+public:
+ /**
+ * aKeyList is a null-terminated list.
+ * The first element is a path list.
+ * The remainder are properties provided by aProvider.
+ * They do not need to be publicly defined keys.
+ */
+ nsPathsDirectoryEnumerator(nsIDirectoryServiceProvider* aProvider,
+ const char* aKeyList[]) :
+ nsAppDirectoryEnumerator(aProvider, aKeyList + 1),
+ mEndPath(aKeyList[0])
+ {
+ }
+
+ NS_IMETHOD HasMoreElements(bool* aResult)
+ {
+ if (mEndPath)
+ while (!mNext && *mEndPath) {
+ const char* pathVar = mEndPath;
+
+ // skip PATH_SEPARATORs at the begining of the mEndPath
+ while (*pathVar == PATH_SEPARATOR) {
+ ++pathVar;
+ }
+
+ do {
+ ++mEndPath;
+ } while (*mEndPath && *mEndPath != PATH_SEPARATOR);
+
+ nsCOMPtr<nsIFile> localFile;
+ NS_NewNativeLocalFile(Substring(pathVar, mEndPath),
+ true,
+ getter_AddRefs(localFile));
+ if (*mEndPath == PATH_SEPARATOR) {
+ ++mEndPath;
+ }
+ // Don't return a "file" (directory) which does not exist.
+ bool exists;
+ if (localFile &&
+ NS_SUCCEEDED(localFile->Exists(&exists)) &&
+ exists) {
+ mNext = localFile;
+ }
+ }
+ if (mNext) {
+ *aResult = true;
+ } else {
+ nsAppDirectoryEnumerator::HasMoreElements(aResult);
+ }
+
+ return NS_OK;
+ }
+
+protected:
+ const char* mEndPath;
+};
+
+NS_IMETHODIMP
+nsAppFileLocationProvider::GetFiles(const char* aProp,
+ nsISimpleEnumerator** aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = nullptr;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (!nsCRT::strcmp(aProp, NS_APP_PLUGINS_DIR_LIST)) {
+#ifdef MOZ_WIDGET_COCOA
+ // As of Java for Mac OS X 10.5 Update 10, Apple has (in effect) deprecated Java Plugin2 on
+ // on OS X 10.5, and removed the soft link to it from /Library/Internet Plug-Ins/. Java
+ // Plugin2 is still present and usable, but there are no longer any links to it in the
+ // "normal" locations. So we won't be able to find it unless we look in the "non-normal"
+ // location where it actually is. Safari can use the WebKit-specific JavaPluginCocoa.bundle,
+ // which (of course) is still fully supported on OS X 10.5. But we have no alternative to
+ // using Java Plugin2. For more information see bug 668639.
+ static const char* keys[] = {
+ NS_APP_PLUGINS_DIR,
+ NS_MACOSX_USER_PLUGIN_DIR,
+ NS_MACOSX_LOCAL_PLUGIN_DIR,
+ IsOSXLeopard() ? NS_MACOSX_JAVA2_PLUGIN_DIR : nullptr,
+ nullptr
+ };
+ *aResult = new nsAppDirectoryEnumerator(this, keys);
+#else
+#ifdef XP_UNIX
+ static const char* keys[] = { nullptr, NS_USER_PLUGINS_DIR, NS_APP_PLUGINS_DIR, NS_SYSTEM_PLUGINS_DIR, nullptr };
+#else
+ static const char* keys[] = { nullptr, NS_USER_PLUGINS_DIR, NS_APP_PLUGINS_DIR, nullptr };
+#endif
+ if (!keys[0] && !(keys[0] = PR_GetEnv("MOZ_PLUGIN_PATH"))) {
+ static const char nullstr = 0;
+ keys[0] = &nullstr;
+ }
+ *aResult = new nsPathsDirectoryEnumerator(this, keys);
+#endif
+ NS_ADDREF(*aResult);
+ rv = NS_OK;
+ }
+ if (!nsCRT::strcmp(aProp, NS_APP_SEARCH_DIR_LIST)) {
+ static const char* keys[] = { nullptr, NS_APP_USER_SEARCH_DIR, nullptr };
+ if (!keys[0] && !(keys[0] = PR_GetEnv("MOZ_SEARCH_ENGINE_PATH"))) {
+ static const char nullstr = 0;
+ keys[0] = &nullstr;
+ }
+ *aResult = new nsPathsDirectoryEnumerator(this, keys);
+ NS_ADDREF(*aResult);
+ rv = NS_OK;
+ }
+ if (!strcmp(aProp, NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)) {
+ return NS_NewEmptyEnumerator(aResult);
+ }
+ return rv;
+}
+
+#if defined(MOZ_WIDGET_COCOA)
+bool
+nsAppFileLocationProvider::IsOSXLeopard()
+{
+ static SInt32 version = 0;
+
+ if (!version) {
+ OSErr err = ::Gestalt(gestaltSystemVersion, &version);
+ if (err != noErr) {
+ version = 0;
+ } else {
+ version &= 0xFFFF; // The system version is in the low order word
+ }
+ }
+
+ return ((version >= 0x1050) && (version < 0x1060));
+}
+#endif
diff --git a/xpcom/io/nsAppFileLocationProvider.h b/xpcom/io/nsAppFileLocationProvider.h
new file mode 100644
index 000000000..248c5b828
--- /dev/null
+++ b/xpcom/io/nsAppFileLocationProvider.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAppFileLocationProvider_h
+#define nsAppFileLocationProvider_h
+
+#include "nsIDirectoryService.h"
+#include "nsIFile.h"
+#include "mozilla/Attributes.h"
+
+class nsIFile;
+
+//*****************************************************************************
+// class nsAppFileLocationProvider
+//*****************************************************************************
+
+class nsAppFileLocationProvider final : public nsIDirectoryServiceProvider2
+{
+public:
+ nsAppFileLocationProvider();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+private:
+ ~nsAppFileLocationProvider()
+ {
+ }
+
+protected:
+ nsresult CloneMozBinDirectory(nsIFile** aLocalFile);
+ /**
+ * Get the product directory. This is a user-specific directory for storing
+ * application settings (e.g. the Application Data directory on windows
+ * systems).
+ * @param aLocal If true, should try to get a directory that is only stored
+ * locally (ie not transferred with roaming profiles)
+ */
+ nsresult GetProductDirectory(nsIFile** aLocalFile,
+ bool aLocal = false);
+ nsresult GetDefaultUserProfileRoot(nsIFile** aLocalFile,
+ bool aLocal = false);
+
+#if defined(MOZ_WIDGET_COCOA)
+ static bool IsOSXLeopard();
+#endif
+
+ nsCOMPtr<nsIFile> mMozBinDirectory;
+};
+
+#endif
diff --git a/xpcom/io/nsBinaryStream.cpp b/xpcom/io/nsBinaryStream.cpp
new file mode 100644
index 000000000..82159952e
--- /dev/null
+++ b/xpcom/io/nsBinaryStream.cpp
@@ -0,0 +1,1016 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file contains implementations of the nsIBinaryInputStream and
+ * nsIBinaryOutputStream interfaces. Together, these interfaces allows reading
+ * and writing of primitive data types (integers, floating-point values,
+ * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format.
+ * This might be used, for example, to implement network protocols or to
+ * produce architecture-neutral binary disk files, i.e. ones that can be read
+ * and written by both big-endian and little-endian platforms. Output is
+ * written in big-endian order (high-order byte first), as this is traditional
+ * network order.
+ *
+ * @See nsIBinaryInputStream
+ * @See nsIBinaryOutputStream
+ */
+#include <algorithm>
+#include <string.h>
+
+#include "nsBinaryStream.h"
+
+#include "mozilla/EndianUtils.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsCRT.h"
+#include "nsString.h"
+#include "nsISerializable.h"
+#include "nsIClassInfo.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIURI.h" // for NS_IURI_IID
+#include "nsIX509Cert.h" // for NS_IX509CERT_IID
+
+#include "jsfriendapi.h"
+
+using mozilla::MakeUnique;
+using mozilla::PodCopy;
+using mozilla::UniquePtr;
+
+NS_IMPL_ISUPPORTS(nsBinaryOutputStream,
+ nsIObjectOutputStream,
+ nsIBinaryOutputStream,
+ nsIOutputStream)
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Flush()
+{
+ if (NS_WARN_IF(!mOutputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mOutputStream->Flush();
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Close()
+{
+ if (NS_WARN_IF(!mOutputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mOutputStream->Close();
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write(const char* aBuf, uint32_t aCount,
+ uint32_t* aActualBytes)
+{
+ if (NS_WARN_IF(!mOutputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mOutputStream->Write(aBuf, aCount, aActualBytes);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteFrom(nsIInputStream* aInStr, uint32_t aCount,
+ uint32_t* aResult)
+{
+ NS_NOTREACHED("WriteFrom");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
+ uint32_t aCount, uint32_t* aResult)
+{
+ NS_NOTREACHED("WriteSegments");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ if (NS_WARN_IF(!mOutputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mOutputStream->IsNonBlocking(aNonBlocking);
+}
+
+nsresult
+nsBinaryOutputStream::WriteFully(const char* aBuf, uint32_t aCount)
+{
+ if (NS_WARN_IF(!mOutputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv;
+ uint32_t bytesWritten;
+
+ rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesWritten != aCount) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::SetOutputStream(nsIOutputStream* aOutputStream)
+{
+ if (NS_WARN_IF(!aOutputStream)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ mOutputStream = aOutputStream;
+ mBufferAccess = do_QueryInterface(aOutputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteBoolean(bool aBoolean)
+{
+ return Write8(aBoolean);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write8(uint8_t aByte)
+{
+ return WriteFully((const char*)&aByte, sizeof(aByte));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write16(uint16_t aNum)
+{
+ aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
+ return WriteFully((const char*)&aNum, sizeof(aNum));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write32(uint32_t aNum)
+{
+ aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
+ return WriteFully((const char*)&aNum, sizeof(aNum));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write64(uint64_t aNum)
+{
+ nsresult rv;
+ uint32_t bytesWritten;
+
+ aNum = mozilla::NativeEndian::swapToBigEndian(aNum);
+ rv = Write(reinterpret_cast<char*>(&aNum), sizeof(aNum), &bytesWritten);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesWritten != sizeof(aNum)) {
+ return NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteFloat(float aFloat)
+{
+ NS_ASSERTION(sizeof(float) == sizeof(uint32_t),
+ "False assumption about sizeof(float)");
+ return Write32(*reinterpret_cast<uint32_t*>(&aFloat));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteDouble(double aDouble)
+{
+ NS_ASSERTION(sizeof(double) == sizeof(uint64_t),
+ "False assumption about sizeof(double)");
+ return Write64(*reinterpret_cast<uint64_t*>(&aDouble));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteStringZ(const char* aString)
+{
+ uint32_t length;
+ nsresult rv;
+
+ length = strlen(aString);
+ rv = Write32(length);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return WriteFully(aString, length);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteWStringZ(const char16_t* aString)
+{
+ uint32_t length, byteCount;
+ nsresult rv;
+
+ length = NS_strlen(aString);
+ rv = Write32(length);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (length == 0) {
+ return NS_OK;
+ }
+ byteCount = length * sizeof(char16_t);
+
+#ifdef IS_BIG_ENDIAN
+ rv = WriteBytes(reinterpret_cast<const char*>(aString), byteCount);
+#else
+ // XXX use WriteSegments here to avoid copy!
+ char16_t* copy;
+ char16_t temp[64];
+ if (length <= 64) {
+ copy = temp;
+ } else {
+ copy = reinterpret_cast<char16_t*>(malloc(byteCount));
+ if (!copy) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ NS_ASSERTION((uintptr_t(aString) & 0x1) == 0, "aString not properly aligned");
+ mozilla::NativeEndian::copyAndSwapToBigEndian(copy, aString, length);
+ rv = WriteBytes(reinterpret_cast<const char*>(copy), byteCount);
+ if (copy != temp) {
+ free(copy);
+ }
+#endif
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteUtf8Z(const char16_t* aString)
+{
+ return WriteStringZ(NS_ConvertUTF16toUTF8(aString).get());
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteBytes(const char* aString, uint32_t aLength)
+{
+ nsresult rv;
+ uint32_t bytesWritten;
+
+ rv = Write(aString, aLength, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesWritten != aLength) {
+ return NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteByteArray(uint8_t* aBytes, uint32_t aLength)
+{
+ return WriteBytes(reinterpret_cast<char*>(aBytes), aLength);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
+{
+ return WriteCompoundObject(aObject, NS_GET_IID(nsISupports),
+ aIsStrongRef);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject)
+{
+ return WriteCompoundObject(aObject, NS_GET_IID(nsISupports),
+ true);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject,
+ const nsIID& aIID,
+ bool aIsStrongRef)
+{
+ nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
+ nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aObject);
+
+ // Can't deal with weak refs
+ if (NS_WARN_IF(!aIsStrongRef)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (NS_WARN_IF(!classInfo) || NS_WARN_IF(!serializable)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCID cid;
+ nsresult rv = classInfo->GetClassIDNoAlloc(&cid);
+ if (NS_SUCCEEDED(rv)) {
+ rv = WriteID(cid);
+ } else {
+ nsCID* cidptr = nullptr;
+ rv = classInfo->GetClassID(&cidptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = WriteID(*cidptr);
+
+ free(cidptr);
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = WriteID(aIID);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return serializable->Write(this);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteID(const nsIID& aIID)
+{
+ nsresult rv = Write32(aIID.m0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = Write16(aIID.m1);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = Write16(aIID.m2);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ for (int i = 0; i < 8; ++i) {
+ rv = Write8(aIID.m3[i]);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(char*)
+nsBinaryOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
+{
+ if (mBufferAccess) {
+ return mBufferAccess->GetBuffer(aLength, aAlignMask);
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP_(void)
+nsBinaryOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
+{
+ if (mBufferAccess) {
+ mBufferAccess->PutBuffer(aBuffer, aLength);
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsBinaryInputStream,
+ nsIObjectInputStream,
+ nsIBinaryInputStream,
+ nsIInputStream)
+
+NS_IMETHODIMP
+nsBinaryInputStream::Available(uint64_t* aResult)
+{
+ if (NS_WARN_IF(!mInputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mInputStream->Available(aResult);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aNumRead)
+{
+ if (NS_WARN_IF(!mInputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // mInputStream might give us short reads, so deal with that.
+ uint32_t totalRead = 0;
+
+ uint32_t bytesRead;
+ do {
+ nsresult rv = mInputStream->Read(aBuffer, aCount, &bytesRead);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK && totalRead != 0) {
+ // We already read some data. Return it.
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ totalRead += bytesRead;
+ aBuffer += bytesRead;
+ aCount -= bytesRead;
+ } while (aCount != 0 && bytesRead != 0);
+
+ *aNumRead = totalRead;
+
+ return NS_OK;
+}
+
+
+// when forwarding ReadSegments to mInputStream, we need to make sure
+// 'this' is being passed to the writer each time. To do this, we need
+// a thunking function which keeps the real input stream around.
+
+// the closure wrapper
+struct MOZ_STACK_CLASS ReadSegmentsClosure
+{
+ nsCOMPtr<nsIInputStream> mRealInputStream;
+ void* mRealClosure;
+ nsWriteSegmentFun mRealWriter;
+ nsresult mRealResult;
+ uint32_t mBytesRead; // to properly implement aToOffset
+};
+
+// the thunking function
+static nsresult
+ReadSegmentForwardingThunk(nsIInputStream* aStream,
+ void* aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ ReadSegmentsClosure* thunkClosure =
+ reinterpret_cast<ReadSegmentsClosure*>(aClosure);
+
+ NS_ASSERTION(NS_SUCCEEDED(thunkClosure->mRealResult),
+ "How did this get to be a failure status?");
+
+ thunkClosure->mRealResult =
+ thunkClosure->mRealWriter(thunkClosure->mRealInputStream,
+ thunkClosure->mRealClosure,
+ aFromSegment,
+ thunkClosure->mBytesRead + aToOffset,
+ aCount, aWriteCount);
+
+ return thunkClosure->mRealResult;
+}
+
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t* aResult)
+{
+ if (NS_WARN_IF(!mInputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ReadSegmentsClosure thunkClosure = { this, aClosure, aWriter, NS_OK, 0 };
+
+ // mInputStream might give us short reads, so deal with that.
+ uint32_t bytesRead;
+ do {
+ nsresult rv = mInputStream->ReadSegments(ReadSegmentForwardingThunk,
+ &thunkClosure,
+ aCount, &bytesRead);
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK && thunkClosure.mBytesRead != 0) {
+ // We already read some data. Return it.
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ thunkClosure.mBytesRead += bytesRead;
+ aCount -= bytesRead;
+ } while (aCount != 0 && bytesRead != 0 &&
+ NS_SUCCEEDED(thunkClosure.mRealResult));
+
+ *aResult = thunkClosure.mBytesRead;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ if (NS_WARN_IF(!mInputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mInputStream->IsNonBlocking(aNonBlocking);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Close()
+{
+ if (NS_WARN_IF(!mInputStream)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return mInputStream->Close();
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::SetInputStream(nsIInputStream* aInputStream)
+{
+ if (NS_WARN_IF(!aInputStream)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ mInputStream = aInputStream;
+ mBufferAccess = do_QueryInterface(aInputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadBoolean(bool* aBoolean)
+{
+ uint8_t byteResult;
+ nsresult rv = Read8(&byteResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aBoolean = !!byteResult;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read8(uint8_t* aByte)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+
+ rv = Read(reinterpret_cast<char*>(aByte), sizeof(*aByte), &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesRead != 1) {
+ return NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read16(uint16_t* aNum)
+{
+ uint32_t bytesRead;
+ nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesRead != sizeof(*aNum)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read32(uint32_t* aNum)
+{
+ uint32_t bytesRead;
+ nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesRead != sizeof(*aNum)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read64(uint64_t* aNum)
+{
+ uint32_t bytesRead;
+ nsresult rv = Read(reinterpret_cast<char*>(aNum), sizeof(*aNum), &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (bytesRead != sizeof(*aNum)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aNum = mozilla::NativeEndian::swapFromBigEndian(*aNum);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadFloat(float* aFloat)
+{
+ NS_ASSERTION(sizeof(float) == sizeof(uint32_t),
+ "False assumption about sizeof(float)");
+ return Read32(reinterpret_cast<uint32_t*>(aFloat));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadDouble(double* aDouble)
+{
+ NS_ASSERTION(sizeof(double) == sizeof(uint64_t),
+ "False assumption about sizeof(double)");
+ return Read64(reinterpret_cast<uint64_t*>(aDouble));
+}
+
+static nsresult
+WriteSegmentToCString(nsIInputStream* aStream,
+ void* aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ nsACString* outString = static_cast<nsACString*>(aClosure);
+
+ outString->Append(aFromSegment, aCount);
+
+ *aWriteCount = aCount;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadCString(nsACString& aString)
+{
+ nsresult rv;
+ uint32_t length, bytesRead;
+
+ rv = Read32(&length);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ aString.Truncate();
+ rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (bytesRead != length) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+
+// sometimes, WriteSegmentToString will be handed an odd-number of
+// bytes, which means we only have half of the last char16_t
+struct WriteStringClosure
+{
+ char16_t* mWriteCursor;
+ bool mHasCarryoverByte;
+ char mCarryoverByte;
+};
+
+// there are a few cases we have to account for here:
+// * even length buffer, no carryover - easy, just append
+// * odd length buffer, no carryover - the last byte needs to be saved
+// for carryover
+// * odd length buffer, with carryover - first byte needs to be used
+// with the carryover byte, and
+// the rest of the even length
+// buffer is appended as normal
+// * even length buffer, with carryover - the first byte needs to be
+// used with the previous carryover byte.
+// this gives you an odd length buffer,
+// so you have to save the last byte for
+// the next carryover
+
+
+// same version of the above, but with correct casting and endian swapping
+static nsresult
+WriteSegmentToString(nsIInputStream* aStream,
+ void* aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ NS_PRECONDITION(aCount > 0, "Why are we being told to write 0 bytes?");
+ NS_PRECONDITION(sizeof(char16_t) == 2, "We can't handle other sizes!");
+
+ WriteStringClosure* closure = static_cast<WriteStringClosure*>(aClosure);
+ char16_t* cursor = closure->mWriteCursor;
+
+ // we're always going to consume the whole buffer no matter what
+ // happens, so take care of that right now.. that allows us to
+ // tweak aCount later. Do NOT move this!
+ *aWriteCount = aCount;
+
+ // if the last Write had an odd-number of bytes read, then
+ if (closure->mHasCarryoverByte) {
+ // re-create the two-byte sequence we want to work with
+ char bytes[2] = { closure->mCarryoverByte, *aFromSegment };
+ *cursor = *(char16_t*)bytes;
+ // Now the little endianness dance
+ mozilla::NativeEndian::swapToBigEndianInPlace(cursor, 1);
+ ++cursor;
+
+ // now skip past the first byte of the buffer.. code from here
+ // can assume normal operations, but should not assume aCount
+ // is relative to the ORIGINAL buffer
+ ++aFromSegment;
+ --aCount;
+
+ closure->mHasCarryoverByte = false;
+ }
+
+ // this array is possibly unaligned... be careful how we access it!
+ const char16_t* unicodeSegment =
+ reinterpret_cast<const char16_t*>(aFromSegment);
+
+ // calculate number of full characters in segment (aCount could be odd!)
+ uint32_t segmentLength = aCount / sizeof(char16_t);
+
+ // copy all data into our aligned buffer. byte swap if necessary.
+ // cursor may be unaligned, so we cannot use copyAndSwapToBigEndian directly
+ memcpy(cursor, unicodeSegment, segmentLength * sizeof(char16_t));
+ char16_t* end = cursor + segmentLength;
+ mozilla::NativeEndian::swapToBigEndianInPlace(cursor, segmentLength);
+ closure->mWriteCursor = end;
+
+ // remember this is the modifed aCount and aFromSegment,
+ // so that will take into account the fact that we might have
+ // skipped the first byte in the buffer
+ if (aCount % sizeof(char16_t) != 0) {
+ // we must have had a carryover byte, that we'll need the next
+ // time around
+ closure->mCarryoverByte = aFromSegment[aCount - 1];
+ closure->mHasCarryoverByte = true;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadString(nsAString& aString)
+{
+ nsresult rv;
+ uint32_t length, bytesRead;
+
+ rv = Read32(&length);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (length == 0) {
+ aString.Truncate();
+ return NS_OK;
+ }
+
+ // pre-allocate output buffer, and get direct access to buffer...
+ if (!aString.SetLength(length, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsAString::iterator start;
+ aString.BeginWriting(start);
+
+ WriteStringClosure closure;
+ closure.mWriteCursor = start.get();
+ closure.mHasCarryoverByte = false;
+
+ rv = ReadSegments(WriteSegmentToString, &closure,
+ length * sizeof(char16_t), &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!");
+
+ if (bytesRead != length * sizeof(char16_t)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadBytes(uint32_t aLength, char** aResult)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+ char* s;
+
+ s = reinterpret_cast<char*>(malloc(aLength));
+ if (!s) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = Read(s, aLength, &bytesRead);
+ if (NS_FAILED(rv)) {
+ free(s);
+ return rv;
+ }
+ if (bytesRead != aLength) {
+ free(s);
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = s;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadByteArray(uint32_t aLength, uint8_t** aResult)
+{
+ return ReadBytes(aLength, reinterpret_cast<char**>(aResult));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength,
+ JS::Handle<JS::Value> aBuffer,
+ JSContext* aCx, uint32_t* aReadLength)
+{
+ if (!aBuffer.isObject()) {
+ return NS_ERROR_FAILURE;
+ }
+ JS::RootedObject buffer(aCx, &aBuffer.toObject());
+ if (!JS_IsArrayBufferObject(buffer)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t bufferLength = JS_GetArrayBufferByteLength(buffer);
+ if (bufferLength < aLength) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t bufSize = std::min<uint32_t>(aLength, 4096);
+ UniquePtr<char[]> buf = MakeUnique<char[]>(bufSize);
+
+ uint32_t pos = 0;
+ *aReadLength = 0;
+ do {
+ // Read data into temporary buffer.
+ uint32_t bytesRead;
+ uint32_t amount = std::min(aLength - pos, bufSize);
+ nsresult rv = Read(buf.get(), amount, &bytesRead);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ MOZ_ASSERT(bytesRead <= amount);
+
+ if (bytesRead == 0) {
+ break;
+ }
+
+ // Copy data into actual buffer.
+
+ JS::AutoCheckCannotGC nogc;
+ bool isShared;
+ if (bufferLength != JS_GetArrayBufferByteLength(buffer)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(buffer, &isShared, nogc));
+ MOZ_ASSERT(!isShared); // Implied by JS_GetArrayBufferData()
+ if (!data) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aReadLength += bytesRead;
+ PodCopy(data + pos, buf.get(), bytesRead);
+
+ pos += bytesRead;
+ } while (pos < aLength);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports** aObject)
+{
+ nsCID cid;
+ nsIID iid;
+ nsresult rv = ReadID(&cid);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = ReadID(&iid);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // HACK: Intercept old (pre-gecko6) nsIURI IID, and replace with
+ // the updated IID, so that we're QI'ing to an actual interface.
+ // (As soon as we drop support for upgrading from pre-gecko6, we can
+ // remove this chunk.)
+ static const nsIID oldURIiid = {
+ 0x7a22cc0, 0xce5, 0x11d3,
+ { 0x93, 0x31, 0x0, 0x10, 0x4b, 0xa0, 0xfd, 0x40 }
+ };
+
+ // hackaround for bug 670542
+ static const nsIID oldURIiid2 = {
+ 0xd6d04c36, 0x0fa4, 0x4db3,
+ { 0xbe, 0x05, 0x4a, 0x18, 0x39, 0x71, 0x03, 0xe2 }
+ };
+
+ // hackaround for bug 682031
+ static const nsIID oldURIiid3 = {
+ 0x12120b20, 0x0929, 0x40e9,
+ { 0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23 }
+ };
+
+ // hackaround for bug 1195415
+ static const nsIID oldURIiid4 = {
+ 0x395fe045, 0x7d18, 0x4adb,
+ { 0xa3, 0xfd, 0xaf, 0x98, 0xc8, 0xa1, 0xaf, 0x11 }
+ };
+
+ if (iid.Equals(oldURIiid) ||
+ iid.Equals(oldURIiid2) ||
+ iid.Equals(oldURIiid3) ||
+ iid.Equals(oldURIiid4)) {
+ const nsIID newURIiid = NS_IURI_IID;
+ iid = newURIiid;
+ }
+ // END HACK
+
+ // HACK: Service workers store resource security info on disk in the dom
+ // Cache API. When the uuid of the nsIX509Cert interface changes
+ // these serialized objects cannot be loaded any more. This hack
+ // works around this issue.
+
+ // hackaround for bug 1247580 (FF45 to FF46 transition)
+ static const nsIID oldCertIID = {
+ 0xf8ed8364, 0xced9, 0x4c6e,
+ { 0x86, 0xba, 0x48, 0xaf, 0x53, 0xc3, 0x93, 0xe6 }
+ };
+
+ if (iid.Equals(oldCertIID)) {
+ const nsIID newCertIID = NS_IX509CERT_IID;
+ iid = newCertIID;
+ }
+ // END HACK
+
+ nsCOMPtr<nsISupports> object = do_CreateInstance(cid, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsISerializable> serializable = do_QueryInterface(object);
+ if (NS_WARN_IF(!serializable)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = serializable->Read(this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return object->QueryInterface(iid, reinterpret_cast<void**>(aObject));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadID(nsID* aResult)
+{
+ nsresult rv = Read32(&aResult->m0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = Read16(&aResult->m1);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = Read16(&aResult->m2);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ for (int i = 0; i < 8; ++i) {
+ rv = Read8(&aResult->m3[i]);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(char*)
+nsBinaryInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
+{
+ if (mBufferAccess) {
+ return mBufferAccess->GetBuffer(aLength, aAlignMask);
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP_(void)
+nsBinaryInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
+{
+ if (mBufferAccess) {
+ mBufferAccess->PutBuffer(aBuffer, aLength);
+ }
+}
diff --git a/xpcom/io/nsBinaryStream.h b/xpcom/io/nsBinaryStream.h
new file mode 100644
index 000000000..2520d9201
--- /dev/null
+++ b/xpcom/io/nsBinaryStream.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBinaryStream_h___
+#define nsBinaryStream_h___
+
+#include "nsCOMPtr.h"
+#include "nsAString.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIStreamBufferAccess.h"
+
+#define NS_BINARYOUTPUTSTREAM_CID \
+{ /* 86c37b9a-74e7-4672-844e-6e7dd83ba484 */ \
+ 0x86c37b9a, \
+ 0x74e7, \
+ 0x4672, \
+ {0x84, 0x4e, 0x6e, 0x7d, 0xd8, 0x3b, 0xa4, 0x84} \
+}
+
+#define NS_BINARYOUTPUTSTREAM_CONTRACTID "@mozilla.org/binaryoutputstream;1"
+
+// Derive from nsIObjectOutputStream so this class can be used as a superclass
+// by nsObjectOutputStream.
+class nsBinaryOutputStream final : public nsIObjectOutputStream
+{
+public:
+ nsBinaryOutputStream()
+ {
+ }
+
+protected:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIOutputStream methods
+ NS_DECL_NSIOUTPUTSTREAM
+
+ // nsIBinaryOutputStream methods
+ NS_DECL_NSIBINARYOUTPUTSTREAM
+
+ // nsIObjectOutputStream methods
+ NS_DECL_NSIOBJECTOUTPUTSTREAM
+
+ // Call Write(), ensuring that all proffered data is written
+ nsresult WriteFully(const char* aBuf, uint32_t aCount);
+
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIStreamBufferAccess> mBufferAccess;
+
+private:
+ // virtual dtor since subclasses call our Release()
+ virtual ~nsBinaryOutputStream()
+ {
+ }
+};
+
+#define NS_BINARYINPUTSTREAM_CID \
+{ /* c521a612-2aad-46db-b6ab-3b821fb150b1 */ \
+ 0xc521a612, \
+ 0x2aad, \
+ 0x46db, \
+ {0xb6, 0xab, 0x3b, 0x82, 0x1f, 0xb1, 0x50, 0xb1} \
+}
+
+#define NS_BINARYINPUTSTREAM_CONTRACTID "@mozilla.org/binaryinputstream;1"
+
+class nsBinaryInputStream final : public nsIObjectInputStream
+{
+public:
+ nsBinaryInputStream()
+ {
+ }
+
+protected:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIInputStream methods
+ NS_DECL_NSIINPUTSTREAM
+
+ // nsIBinaryInputStream methods
+ NS_DECL_NSIBINARYINPUTSTREAM
+
+ // nsIObjectInputStream methods
+ NS_DECL_NSIOBJECTINPUTSTREAM
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+ nsCOMPtr<nsIStreamBufferAccess> mBufferAccess;
+
+private:
+ // virtual dtor since subclasses call our Release()
+ virtual ~nsBinaryInputStream()
+ {
+ }
+};
+
+#endif // nsBinaryStream_h___
diff --git a/xpcom/io/nsDirectoryService.cpp b/xpcom/io/nsDirectoryService.cpp
new file mode 100644
index 000000000..a4d962395
--- /dev/null
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -0,0 +1,766 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsLocalFile.h"
+#include "nsDebug.h"
+#include "nsStaticAtom.h"
+#include "nsEnumeratorUtils.h"
+
+#include "nsICategoryManager.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#elif defined(XP_UNIX)
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "prenv.h"
+#ifdef MOZ_WIDGET_COCOA
+#include <CoreServices/CoreServices.h>
+#include <Carbon/Carbon.h>
+#endif
+#endif
+
+#include "SpecialSystemDirectory.h"
+#include "nsAppFileLocationProvider.h"
+
+using namespace mozilla;
+
+// define home directory
+// For Windows platform, We are choosing Appdata folder as HOME
+#if defined (XP_WIN)
+#define HOME_DIR NS_WIN_APPDATA_DIR
+#elif defined (MOZ_WIDGET_COCOA)
+#define HOME_DIR NS_OSX_HOME_DIR
+#elif defined (XP_UNIX)
+#define HOME_DIR NS_UNIX_HOME_DIR
+#endif
+
+//----------------------------------------------------------------------------------------
+nsresult
+nsDirectoryService::GetCurrentProcessDirectory(nsIFile** aFile)
+//----------------------------------------------------------------------------------------
+{
+ if (NS_WARN_IF(!aFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aFile = nullptr;
+
+ // Set the component registry location:
+ if (!gService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ nsCOMPtr<nsIProperties> dirService;
+ rv = nsDirectoryService::Create(nullptr,
+ NS_GET_IID(nsIProperties),
+ getter_AddRefs(dirService)); // needs to be around for life of product
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (dirService) {
+ nsCOMPtr<nsIFile> localFile;
+ dirService->Get(NS_XPCOM_INIT_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(localFile));
+ if (localFile) {
+ localFile.forget(aFile);
+ return NS_OK;
+ }
+ }
+
+ RefPtr<nsLocalFile> localFile = new nsLocalFile;
+
+#ifdef XP_WIN
+ wchar_t buf[MAX_PATH + 1];
+ SetLastError(ERROR_SUCCESS);
+ if (GetModuleFileNameW(0, buf, mozilla::ArrayLength(buf)) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ // chop off the executable name by finding the rightmost backslash
+ wchar_t* lastSlash = wcsrchr(buf, L'\\');
+ if (lastSlash) {
+ *(lastSlash + 1) = L'\0';
+ }
+
+ localFile->InitWithPath(nsDependentString(buf));
+ localFile.forget(aFile);
+ return NS_OK;
+ }
+
+#elif defined(MOZ_WIDGET_COCOA)
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = CFBundleGetMainBundle();
+ if (appBundle) {
+ CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle);
+ if (bundleURL) {
+ CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(
+ kCFAllocatorDefault, bundleURL);
+ if (parentURL) {
+ // Pass true for the "resolveAgainstBase" arg to CFURLGetFileSystemRepresentation.
+ // This will resolve the relative portion of the CFURL against it base, giving a full
+ // path, which CFURLCopyFileSystemPath doesn't do.
+ char buffer[PATH_MAX];
+ if (CFURLGetFileSystemRepresentation(parentURL, true,
+ (UInt8*)buffer, sizeof(buffer))) {
+#ifdef DEBUG_conrad
+ printf("nsDirectoryService - CurrentProcessDir is: %s\n", buffer);
+#endif
+ rv = localFile->InitWithNativePath(nsDependentCString(buffer));
+ if (NS_SUCCEEDED(rv)) {
+ localFile.forget(aFile);
+ }
+ }
+ CFRelease(parentURL);
+ }
+ CFRelease(bundleURL);
+ }
+ }
+
+ NS_ASSERTION(*aFile, "nsDirectoryService - Could not determine CurrentProcessDir.\n");
+ if (*aFile) {
+ return NS_OK;
+ }
+
+#elif defined(XP_UNIX)
+
+ // In the absence of a good way to get the executable directory let
+ // us try this for unix:
+ // - if MOZILLA_FIVE_HOME is defined, that is it
+ // - else give the current directory
+ char buf[MAXPATHLEN];
+
+ // The MOZ_DEFAULT_MOZILLA_FIVE_HOME variable can be set at configure time with
+ // a --with-default-mozilla-five-home=foo autoconf flag.
+ //
+ // The idea here is to allow for builds that have a default MOZILLA_FIVE_HOME
+ // regardless of the environment. This makes it easier to write apps that
+ // embed mozilla without having to worry about setting up the environment
+ //
+ // We do this by putenv()ing the default value into the environment. Note that
+ // we only do this if it is not already set.
+#ifdef MOZ_DEFAULT_MOZILLA_FIVE_HOME
+ const char* home = PR_GetEnv("MOZILLA_FIVE_HOME");
+ if (!home || !*home) {
+ putenv("MOZILLA_FIVE_HOME=" MOZ_DEFAULT_MOZILLA_FIVE_HOME);
+ }
+#endif
+
+ char* moz5 = PR_GetEnv("MOZILLA_FIVE_HOME");
+ if (moz5 && *moz5) {
+ if (realpath(moz5, buf)) {
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ localFile.forget(aFile);
+ return NS_OK;
+ }
+ }
+#if defined(DEBUG)
+ static bool firstWarning = true;
+
+ if ((!moz5 || !*moz5) && firstWarning) {
+ // Warn that MOZILLA_FIVE_HOME not set, once.
+ printf("Warning: MOZILLA_FIVE_HOME not set.\n");
+ firstWarning = false;
+ }
+#endif /* DEBUG */
+
+ // Fall back to current directory.
+ if (getcwd(buf, sizeof(buf))) {
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ localFile.forget(aFile);
+ return NS_OK;
+ }
+
+#endif
+
+ NS_ERROR("unable to get current process directory");
+ return NS_ERROR_FAILURE;
+} // GetCurrentProcessDirectory()
+
+StaticRefPtr<nsDirectoryService> nsDirectoryService::gService;
+
+nsDirectoryService::nsDirectoryService()
+ : mHashtable(128)
+{
+}
+
+nsresult
+nsDirectoryService::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ if (!gService) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return gService->QueryInterface(aIID, aResult);
+}
+
+#define DIR_ATOM(name_, value_) nsIAtom* nsDirectoryService::name_ = nullptr;
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+
+#define DIR_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+
+static const nsStaticAtom directory_atoms[] = {
+#define DIR_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &nsDirectoryService::name_),
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+};
+
+NS_IMETHODIMP
+nsDirectoryService::Init()
+{
+ NS_NOTREACHED("nsDirectoryService::Init() for internal use only!");
+ return NS_OK;
+}
+
+void
+nsDirectoryService::RealInit()
+{
+ NS_ASSERTION(!gService,
+ "nsDirectoryService::RealInit Mustn't initialize twice!");
+
+ gService = new nsDirectoryService();
+
+ NS_RegisterStaticAtoms(directory_atoms);
+
+ // Let the list hold the only reference to the provider.
+ nsAppFileLocationProvider* defaultProvider = new nsAppFileLocationProvider;
+ gService->mProviders.AppendElement(defaultProvider);
+}
+
+nsDirectoryService::~nsDirectoryService()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsDirectoryService,
+ nsIProperties,
+ nsIDirectoryService,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2)
+
+
+NS_IMETHODIMP
+nsDirectoryService::Undefine(const char* aProp)
+{
+ if (NS_WARN_IF(!aProp)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsDependentCString key(aProp);
+ if (!mHashtable.Get(key, nullptr)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mHashtable.Remove(key);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::GetKeys(uint32_t* aCount, char*** aKeys)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+struct MOZ_STACK_CLASS FileData
+{
+ FileData(const char* aProperty, const nsIID& aUUID)
+ : property(aProperty)
+ , data(nullptr)
+ , persistent(true)
+ , uuid(aUUID)
+ {
+ }
+
+ const char* property;
+ nsCOMPtr<nsISupports> data;
+ bool persistent;
+ const nsIID& uuid;
+};
+
+static bool
+FindProviderFile(nsIDirectoryServiceProvider* aElement, FileData* aData)
+{
+ nsresult rv;
+ if (aData->uuid.Equals(NS_GET_IID(nsISimpleEnumerator))) {
+ // Not all providers implement this iface
+ nsCOMPtr<nsIDirectoryServiceProvider2> prov2 = do_QueryInterface(aElement);
+ if (prov2) {
+ nsCOMPtr<nsISimpleEnumerator> newFiles;
+ rv = prov2->GetFiles(aData->property, getter_AddRefs(newFiles));
+ if (NS_SUCCEEDED(rv) && newFiles) {
+ if (aData->data) {
+ nsCOMPtr<nsISimpleEnumerator> unionFiles;
+
+ NS_NewUnionEnumerator(getter_AddRefs(unionFiles),
+ (nsISimpleEnumerator*)aData->data.get(), newFiles);
+
+ if (unionFiles) {
+ unionFiles.swap(*(nsISimpleEnumerator**)&aData->data);
+ }
+ } else {
+ aData->data = newFiles;
+ }
+
+ aData->persistent = false; // Enumerators can never be persistent
+ return rv == NS_SUCCESS_AGGREGATE_RESULT;
+ }
+ }
+ } else {
+ rv = aElement->GetFile(aData->property, &aData->persistent,
+ (nsIFile**)&aData->data);
+ if (NS_SUCCEEDED(rv) && aData->data) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Get(const char* aProp, const nsIID& aUuid, void** aResult)
+{
+ if (NS_WARN_IF(!aProp)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsDependentCString key(aProp);
+
+ nsCOMPtr<nsIFile> cachedFile = mHashtable.Get(key);
+
+ if (cachedFile) {
+ nsCOMPtr<nsIFile> cloneFile;
+ cachedFile->Clone(getter_AddRefs(cloneFile));
+ return cloneFile->QueryInterface(aUuid, aResult);
+ }
+
+ // it is not one of our defaults, lets check any providers
+ FileData fileData(aProp, aUuid);
+
+ for (int32_t i = mProviders.Length() - 1; i >= 0; i--) {
+ if (!FindProviderFile(mProviders[i], &fileData)) {
+ break;
+ }
+ }
+ if (fileData.data) {
+ if (fileData.persistent) {
+ Set(aProp, static_cast<nsIFile*>(fileData.data.get()));
+ }
+ nsresult rv = (fileData.data)->QueryInterface(aUuid, aResult);
+ fileData.data = nullptr; // AddRef occurs in FindProviderFile()
+ return rv;
+ }
+
+ FindProviderFile(static_cast<nsIDirectoryServiceProvider*>(this), &fileData);
+ if (fileData.data) {
+ if (fileData.persistent) {
+ Set(aProp, static_cast<nsIFile*>(fileData.data.get()));
+ }
+ nsresult rv = (fileData.data)->QueryInterface(aUuid, aResult);
+ fileData.data = nullptr; // AddRef occurs in FindProviderFile()
+ return rv;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Set(const char* aProp, nsISupports* aValue)
+{
+ if (NS_WARN_IF(!aProp)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsDependentCString key(aProp);
+ if (mHashtable.Get(key, nullptr) || !aValue) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIFile> ourFile = do_QueryInterface(aValue);
+ if (ourFile) {
+ nsCOMPtr<nsIFile> cloneFile;
+ ourFile->Clone(getter_AddRefs(cloneFile));
+ mHashtable.Put(key, cloneFile);
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Has(const char* aProp, bool* aResult)
+{
+ if (NS_WARN_IF(!aProp)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = false;
+ nsCOMPtr<nsIFile> value;
+ nsresult rv = Get(aProp, NS_GET_IID(nsIFile), getter_AddRefs(value));
+ if (NS_FAILED(rv)) {
+ return NS_OK;
+ }
+
+ if (value) {
+ *aResult = true;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::RegisterProvider(nsIDirectoryServiceProvider* aProv)
+{
+ if (!aProv) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mProviders.AppendElement(aProv);
+ return NS_OK;
+}
+
+void
+nsDirectoryService::RegisterCategoryProviders()
+{
+ nsCOMPtr<nsICategoryManager> catman
+ (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (!catman) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ catman->EnumerateCategory(XPCOM_DIRECTORY_PROVIDER_CATEGORY,
+ getter_AddRefs(entries));
+
+ nsCOMPtr<nsIUTF8StringEnumerator> strings(do_QueryInterface(entries));
+ if (!strings) {
+ return;
+ }
+
+ bool more;
+ while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
+ nsAutoCString entry;
+ strings->GetNext(entry);
+
+ nsXPIDLCString contractID;
+ catman->GetCategoryEntry(XPCOM_DIRECTORY_PROVIDER_CATEGORY, entry.get(),
+ getter_Copies(contractID));
+
+ if (contractID) {
+ nsCOMPtr<nsIDirectoryServiceProvider> provider = do_GetService(contractID.get());
+ if (provider) {
+ RegisterProvider(provider);
+ }
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsDirectoryService::UnregisterProvider(nsIDirectoryServiceProvider* aProv)
+{
+ if (!aProv) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mProviders.RemoveElement(aProv);
+ return NS_OK;
+}
+
+#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
+static nsresult
+GetLowIntegrityTempBase(nsIFile** aLowIntegrityTempBase)
+{
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = GetSpecialSystemDirectory(Win_LocalAppdataLow,
+ getter_AddRefs(localFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = localFile->Append(NS_LITERAL_STRING(MOZ_USER_DIR));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ localFile.forget(aLowIntegrityTempBase);
+ return rv;
+}
+#endif
+
+// DO NOT ADD ANY LOCATIONS TO THIS FUNCTION UNTIL YOU TALK TO: dougt@netscape.com.
+// This is meant to be a place of xpcom or system specific file locations, not
+// application specific locations. If you need the later, register a callback for
+// your application.
+
+NS_IMETHODIMP
+nsDirectoryService::GetFile(const char* aProp, bool* aPersistent,
+ nsIFile** aResult)
+{
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ *aResult = nullptr;
+ *aPersistent = true;
+
+ nsCOMPtr<nsIAtom> inAtom = NS_Atomize(aProp);
+
+ // check to see if it is one of our defaults
+
+ if (inAtom == nsDirectoryService::sCurrentProcess ||
+ inAtom == nsDirectoryService::sOS_CurrentProcessDirectory) {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ }
+
+ // Unless otherwise set, the core pieces of the GRE exist
+ // in the current process directory.
+ else if (inAtom == nsDirectoryService::sGRE_Directory ||
+ inAtom == nsDirectoryService::sGRE_BinDirectory) {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_DriveDirectory) {
+ rv = GetSpecialSystemDirectory(OS_DriveDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_TemporaryDirectory) {
+ rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_CurrentProcessDirectory) {
+ rv = GetSpecialSystemDirectory(OS_CurrentProcessDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_CurrentWorkingDirectory) {
+ rv = GetSpecialSystemDirectory(OS_CurrentWorkingDirectory, getter_AddRefs(localFile));
+ }
+
+#if defined(MOZ_WIDGET_COCOA)
+ else if (inAtom == nsDirectoryService::sDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kSystemFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sTrashDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kTrashFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sStartupDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kStartupFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sShutdownDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kShutdownFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sAppleMenuDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kAppleMenuFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sControlPanelDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kControlPanelFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sExtensionDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kExtensionFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sFontsDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kFontsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sPreferencesDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDocumentsDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sInternetSearchDirectory) {
+ rv = GetOSXFolderType(kClassicDomain, kInternetSearchSitesFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sUserLibDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kDomainLibraryFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_HomeDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kDomainTopLevelFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDefaultDownloadDirectory) {
+ // 10.5 and later, we can use kDownloadsFolderType which is defined in
+ // Folders.h as "down". However, in order to support 10.4 still, we
+ // cannot use the named constant. We'll use it's value, and if it
+ // fails, fall back to the desktop.
+#ifndef kDownloadsFolderType
+#define kDownloadsFolderType 'down'
+#endif
+
+ rv = GetOSXFolderType(kUserDomain, kDownloadsFolderType,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv)) {
+ rv = GetOSXFolderType(kUserDomain, kDesktopFolderType,
+ getter_AddRefs(localFile));
+ }
+ } else if (inAtom == nsDirectoryService::sUserDesktopDirectory ||
+ inAtom == nsDirectoryService::sOS_DesktopDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalDesktopDirectory) {
+ rv = GetOSXFolderType(kLocalDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sUserApplicationsDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kApplicationsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalApplicationsDirectory) {
+ rv = GetOSXFolderType(kLocalDomain, kApplicationsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sUserDocumentsDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalDocumentsDirectory) {
+ rv = GetOSXFolderType(kLocalDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sUserInternetPlugInDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kInternetPlugInFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalInternetPlugInDirectory) {
+ rv = GetOSXFolderType(kLocalDomain, kInternetPlugInFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sUserFrameworksDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kFrameworksFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalFrameworksDirectory) {
+ rv = GetOSXFolderType(kLocalDomain, kFrameworksFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sUserPreferencesDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalPreferencesDirectory) {
+ rv = GetOSXFolderType(kLocalDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sPictureDocumentsDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kPictureDocumentsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sMovieDocumentsDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kMovieDocumentsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sMusicDocumentsDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kMusicDocumentsFolderType, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sInternetSitesDirectory) {
+ rv = GetOSXFolderType(kUserDomain, kInternetSitesFolderType, getter_AddRefs(localFile));
+ }
+#elif defined (XP_WIN)
+ else if (inAtom == nsDirectoryService::sSystemDirectory) {
+ rv = GetSpecialSystemDirectory(Win_SystemDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sWindowsDirectory) {
+ rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sWindowsProgramFiles) {
+ rv = GetSpecialSystemDirectory(Win_ProgramFiles, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_HomeDirectory) {
+ rv = GetSpecialSystemDirectory(Win_HomeDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDesktop) {
+ rv = GetSpecialSystemDirectory(Win_Desktop, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sPrograms) {
+ rv = GetSpecialSystemDirectory(Win_Programs, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sControls) {
+ rv = GetSpecialSystemDirectory(Win_Controls, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sPrinters) {
+ rv = GetSpecialSystemDirectory(Win_Printers, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sPersonal) {
+ rv = GetSpecialSystemDirectory(Win_Personal, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sFavorites) {
+ rv = GetSpecialSystemDirectory(Win_Favorites, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sStartup) {
+ rv = GetSpecialSystemDirectory(Win_Startup, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sRecent) {
+ rv = GetSpecialSystemDirectory(Win_Recent, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sSendto) {
+ rv = GetSpecialSystemDirectory(Win_Sendto, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sBitbucket) {
+ rv = GetSpecialSystemDirectory(Win_Bitbucket, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sStartmenu) {
+ rv = GetSpecialSystemDirectory(Win_Startmenu, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDesktopdirectory ||
+ inAtom == nsDirectoryService::sOS_DesktopDirectory) {
+ rv = GetSpecialSystemDirectory(Win_Desktopdirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDrives) {
+ rv = GetSpecialSystemDirectory(Win_Drives, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sNetwork) {
+ rv = GetSpecialSystemDirectory(Win_Network, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sNethood) {
+ rv = GetSpecialSystemDirectory(Win_Nethood, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sFonts) {
+ rv = GetSpecialSystemDirectory(Win_Fonts, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sTemplates) {
+ rv = GetSpecialSystemDirectory(Win_Templates, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sCommon_Startmenu) {
+ rv = GetSpecialSystemDirectory(Win_Common_Startmenu, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sCommon_Programs) {
+ rv = GetSpecialSystemDirectory(Win_Common_Programs, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sCommon_Startup) {
+ rv = GetSpecialSystemDirectory(Win_Common_Startup, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sCommon_Desktopdirectory) {
+ rv = GetSpecialSystemDirectory(Win_Common_Desktopdirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sCommon_AppData) {
+ rv = GetSpecialSystemDirectory(Win_Common_AppData, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sAppdata) {
+ rv = GetSpecialSystemDirectory(Win_Appdata, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLocalAppdata) {
+ rv = GetSpecialSystemDirectory(Win_LocalAppdata, getter_AddRefs(localFile));
+#if defined(MOZ_CONTENT_SANDBOX)
+ } else if (inAtom == nsDirectoryService::sLocalAppdataLow) {
+ rv = GetSpecialSystemDirectory(Win_LocalAppdataLow, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLowIntegrityTempBase) {
+ rv = GetLowIntegrityTempBase(getter_AddRefs(localFile));
+#endif
+ } else if (inAtom == nsDirectoryService::sPrinthood) {
+ rv = GetSpecialSystemDirectory(Win_Printhood, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sWinCookiesDirectory) {
+ rv = GetSpecialSystemDirectory(Win_Cookies, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDefaultDownloadDirectory) {
+ rv = GetSpecialSystemDirectory(Win_Downloads, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sDocs) {
+ rv = GetSpecialSystemDirectory(Win_Documents, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sPictures) {
+ rv = GetSpecialSystemDirectory(Win_Pictures, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sMusic) {
+ rv = GetSpecialSystemDirectory(Win_Music, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sVideos) {
+ rv = GetSpecialSystemDirectory(Win_Videos, getter_AddRefs(localFile));
+ }
+#elif defined (XP_UNIX)
+
+ else if (inAtom == nsDirectoryService::sLocalDirectory) {
+ rv = GetSpecialSystemDirectory(Unix_LocalDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sLibDirectory) {
+ rv = GetSpecialSystemDirectory(Unix_LibDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sOS_HomeDirectory) {
+ rv = GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(localFile));
+ } else if (inAtom == nsDirectoryService::sXDGDesktop ||
+ inAtom == nsDirectoryService::sOS_DesktopDirectory) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Desktop, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGDocuments) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Documents, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGDownload ||
+ inAtom == nsDirectoryService::sDefaultDownloadDirectory) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Download, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGMusic) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Music, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGPictures) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Pictures, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGPublicShare) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_PublicShare, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGTemplates) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Templates, getter_AddRefs(localFile));
+ *aPersistent = false;
+ } else if (inAtom == nsDirectoryService::sXDGVideos) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Videos, getter_AddRefs(localFile));
+ *aPersistent = false;
+ }
+#endif
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!localFile) {
+ return NS_ERROR_FAILURE;
+ }
+
+ localFile.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::GetFiles(const char* aProp, nsISimpleEnumerator** aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = nullptr;
+
+ return NS_ERROR_FAILURE;
+}
diff --git a/xpcom/io/nsDirectoryService.h b/xpcom/io/nsDirectoryService.h
new file mode 100644
index 000000000..d0f92b75a
--- /dev/null
+++ b/xpcom/io/nsDirectoryService.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDirectoryService_h___
+#define nsDirectoryService_h___
+
+#include "nsIDirectoryService.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIFile.h"
+#include "nsIAtom.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/StaticPtr.h"
+
+#define NS_XPCOM_INIT_CURRENT_PROCESS_DIR "MozBinD" // Can be used to set NS_XPCOM_CURRENT_PROCESS_DIR
+ // CANNOT be used to GET a location
+#define NS_DIRECTORY_SERVICE_CID {0xf00152d0,0xb40b,0x11d3,{0x8c, 0x9c, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
+
+class nsDirectoryService final
+ : public nsIDirectoryService
+ , public nsIProperties
+ , public nsIDirectoryServiceProvider2
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIPROPERTIES
+
+ NS_DECL_NSIDIRECTORYSERVICE
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+ nsDirectoryService();
+
+ static void RealInit();
+ void RegisterCategoryProviders();
+
+ static nsresult
+ Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+ static mozilla::StaticRefPtr<nsDirectoryService> gService;
+
+private:
+ ~nsDirectoryService();
+
+ nsresult GetCurrentProcessDirectory(nsIFile** aFile);
+
+ nsInterfaceHashtable<nsCStringHashKey, nsIFile> mHashtable;
+ nsTArray<nsCOMPtr<nsIDirectoryServiceProvider>> mProviders;
+
+public:
+
+#define DIR_ATOM(name_, value_) static nsIAtom* name_;
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+
+};
+
+
+#endif
+
diff --git a/xpcom/io/nsDirectoryServiceAtomList.h b/xpcom/io/nsDirectoryServiceAtomList.h
new file mode 100644
index 000000000..38a2f0e9d
--- /dev/null
+++ b/xpcom/io/nsDirectoryServiceAtomList.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+DIR_ATOM(sCurrentProcess, NS_XPCOM_CURRENT_PROCESS_DIR)
+DIR_ATOM(sGRE_Directory, NS_GRE_DIR)
+DIR_ATOM(sGRE_BinDirectory, NS_GRE_BIN_DIR)
+DIR_ATOM(sOS_DriveDirectory, NS_OS_DRIVE_DIR)
+DIR_ATOM(sOS_TemporaryDirectory, NS_OS_TEMP_DIR)
+DIR_ATOM(sOS_CurrentProcessDirectory, NS_OS_CURRENT_PROCESS_DIR)
+DIR_ATOM(sOS_CurrentWorkingDirectory, NS_OS_CURRENT_WORKING_DIR)
+DIR_ATOM(sOS_HomeDirectory, NS_OS_HOME_DIR)
+DIR_ATOM(sOS_DesktopDirectory, NS_OS_DESKTOP_DIR)
+DIR_ATOM(sInitCurrentProcess_dummy, NS_XPCOM_INIT_CURRENT_PROCESS_DIR)
+#if defined (MOZ_WIDGET_COCOA)
+DIR_ATOM(sDirectory, NS_OS_SYSTEM_DIR)
+DIR_ATOM(sTrashDirectory, NS_MAC_TRASH_DIR)
+DIR_ATOM(sStartupDirectory, NS_MAC_STARTUP_DIR)
+DIR_ATOM(sShutdownDirectory, NS_MAC_SHUTDOWN_DIR)
+DIR_ATOM(sAppleMenuDirectory, NS_MAC_APPLE_MENU_DIR)
+DIR_ATOM(sControlPanelDirectory, NS_MAC_CONTROL_PANELS_DIR)
+DIR_ATOM(sExtensionDirectory, NS_MAC_EXTENSIONS_DIR)
+DIR_ATOM(sFontsDirectory, NS_MAC_FONTS_DIR)
+DIR_ATOM(sPreferencesDirectory, NS_MAC_PREFS_DIR)
+DIR_ATOM(sDocumentsDirectory, NS_MAC_DOCUMENTS_DIR)
+DIR_ATOM(sInternetSearchDirectory, NS_MAC_INTERNET_SEARCH_DIR)
+DIR_ATOM(sUserLibDirectory, NS_MAC_USER_LIB_DIR)
+DIR_ATOM(sDefaultDownloadDirectory, NS_OSX_DEFAULT_DOWNLOAD_DIR)
+DIR_ATOM(sUserDesktopDirectory, NS_OSX_USER_DESKTOP_DIR)
+DIR_ATOM(sLocalDesktopDirectory, NS_OSX_LOCAL_DESKTOP_DIR)
+DIR_ATOM(sUserApplicationsDirectory, NS_OSX_USER_APPLICATIONS_DIR)
+DIR_ATOM(sLocalApplicationsDirectory, NS_OSX_LOCAL_APPLICATIONS_DIR)
+DIR_ATOM(sUserDocumentsDirectory, NS_OSX_USER_DOCUMENTS_DIR)
+DIR_ATOM(sLocalDocumentsDirectory, NS_OSX_LOCAL_DOCUMENTS_DIR)
+DIR_ATOM(sUserInternetPlugInDirectory, NS_OSX_USER_INTERNET_PLUGIN_DIR)
+DIR_ATOM(sLocalInternetPlugInDirectory, NS_OSX_LOCAL_INTERNET_PLUGIN_DIR)
+DIR_ATOM(sUserFrameworksDirectory, NS_OSX_USER_FRAMEWORKS_DIR)
+DIR_ATOM(sLocalFrameworksDirectory, NS_OSX_LOCAL_FRAMEWORKS_DIR)
+DIR_ATOM(sUserPreferencesDirectory, NS_OSX_USER_PREFERENCES_DIR)
+DIR_ATOM(sLocalPreferencesDirectory, NS_OSX_LOCAL_PREFERENCES_DIR)
+DIR_ATOM(sPictureDocumentsDirectory, NS_OSX_PICTURE_DOCUMENTS_DIR)
+DIR_ATOM(sMovieDocumentsDirectory, NS_OSX_MOVIE_DOCUMENTS_DIR)
+DIR_ATOM(sMusicDocumentsDirectory, NS_OSX_MUSIC_DOCUMENTS_DIR)
+DIR_ATOM(sInternetSitesDirectory, NS_OSX_INTERNET_SITES_DIR)
+#elif defined (XP_WIN)
+DIR_ATOM(sSystemDirectory, NS_OS_SYSTEM_DIR)
+DIR_ATOM(sWindowsDirectory, NS_WIN_WINDOWS_DIR)
+DIR_ATOM(sWindowsProgramFiles, NS_WIN_PROGRAM_FILES_DIR)
+DIR_ATOM(sDesktop, NS_WIN_DESKTOP_DIR)
+DIR_ATOM(sPrograms, NS_WIN_PROGRAMS_DIR)
+DIR_ATOM(sControls, NS_WIN_CONTROLS_DIR)
+DIR_ATOM(sPrinters, NS_WIN_PRINTERS_DIR)
+DIR_ATOM(sPersonal, NS_WIN_PERSONAL_DIR)
+DIR_ATOM(sFavorites, NS_WIN_FAVORITES_DIR)
+DIR_ATOM(sStartup, NS_WIN_STARTUP_DIR)
+DIR_ATOM(sRecent, NS_WIN_RECENT_DIR)
+DIR_ATOM(sSendto, NS_WIN_SEND_TO_DIR)
+DIR_ATOM(sBitbucket, NS_WIN_BITBUCKET_DIR)
+DIR_ATOM(sStartmenu, NS_WIN_STARTMENU_DIR)
+DIR_ATOM(sDesktopdirectory, NS_WIN_DESKTOP_DIRECTORY)
+DIR_ATOM(sDrives, NS_WIN_DRIVES_DIR)
+DIR_ATOM(sNetwork, NS_WIN_NETWORK_DIR)
+DIR_ATOM(sNethood, NS_WIN_NETHOOD_DIR)
+DIR_ATOM(sFonts, NS_WIN_FONTS_DIR)
+DIR_ATOM(sTemplates, NS_WIN_TEMPLATES_DIR)
+DIR_ATOM(sCommon_Startmenu, NS_WIN_COMMON_STARTMENU_DIR)
+DIR_ATOM(sCommon_Programs, NS_WIN_COMMON_PROGRAMS_DIR)
+DIR_ATOM(sCommon_Startup, NS_WIN_COMMON_STARTUP_DIR)
+DIR_ATOM(sCommon_Desktopdirectory, NS_WIN_COMMON_DESKTOP_DIRECTORY)
+DIR_ATOM(sCommon_AppData, NS_WIN_COMMON_APPDATA_DIR)
+DIR_ATOM(sAppdata, NS_WIN_APPDATA_DIR)
+DIR_ATOM(sLocalAppdata, NS_WIN_LOCAL_APPDATA_DIR)
+#if defined(MOZ_CONTENT_SANDBOX)
+DIR_ATOM(sLocalAppdataLow, NS_WIN_LOCAL_APPDATA_LOW_DIR)
+DIR_ATOM(sLowIntegrityTempBase, NS_WIN_LOW_INTEGRITY_TEMP_BASE)
+#endif
+DIR_ATOM(sPrinthood, NS_WIN_PRINTHOOD)
+DIR_ATOM(sWinCookiesDirectory, NS_WIN_COOKIES_DIR)
+DIR_ATOM(sDefaultDownloadDirectory, NS_WIN_DEFAULT_DOWNLOAD_DIR)
+DIR_ATOM(sDocs, NS_WIN_DOCUMENTS_DIR)
+DIR_ATOM(sPictures, NS_WIN_PICTURES_DIR)
+DIR_ATOM(sMusic, NS_WIN_MUSIC_DIR)
+DIR_ATOM(sVideos, NS_WIN_VIDEOS_DIR)
+#elif defined (XP_UNIX)
+DIR_ATOM(sLocalDirectory, NS_UNIX_LOCAL_DIR)
+DIR_ATOM(sLibDirectory, NS_UNIX_LIB_DIR)
+DIR_ATOM(sDefaultDownloadDirectory, NS_UNIX_DEFAULT_DOWNLOAD_DIR)
+DIR_ATOM(sXDGDesktop, NS_UNIX_XDG_DESKTOP_DIR)
+DIR_ATOM(sXDGDocuments, NS_UNIX_XDG_DOCUMENTS_DIR)
+DIR_ATOM(sXDGDownload, NS_UNIX_XDG_DOWNLOAD_DIR)
+DIR_ATOM(sXDGMusic, NS_UNIX_XDG_MUSIC_DIR)
+DIR_ATOM(sXDGPictures, NS_UNIX_XDG_PICTURES_DIR)
+DIR_ATOM(sXDGPublicShare, NS_UNIX_XDG_PUBLIC_SHARE_DIR)
+DIR_ATOM(sXDGTemplates, NS_UNIX_XDG_TEMPLATES_DIR)
+DIR_ATOM(sXDGVideos, NS_UNIX_XDG_VIDEOS_DIR)
+#endif
diff --git a/xpcom/io/nsDirectoryServiceDefs.h b/xpcom/io/nsDirectoryServiceDefs.h
new file mode 100644
index 000000000..0bdc5e390
--- /dev/null
+++ b/xpcom/io/nsDirectoryServiceDefs.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Defines the property names for directories available from
+ * nsIDirectoryService. These dirs are always available even if no
+ * nsIDirectoryServiceProviders have been registered with the service.
+ * Application level keys are defined in nsAppDirectoryServiceDefs.h.
+ *
+ * Keys whose definition ends in "DIR" or "FILE" return a single nsIFile (or
+ * subclass). Keys whose definition ends in "LIST" return an nsISimpleEnumerator
+ * which enumerates a list of file objects.
+ *
+ * Defines listed in this file are FROZEN. This list may grow.
+ */
+
+#ifndef nsDirectoryServiceDefs_h___
+#define nsDirectoryServiceDefs_h___
+
+/* General OS specific locations */
+
+#define NS_OS_HOME_DIR "Home"
+#define NS_OS_TEMP_DIR "TmpD"
+#define NS_OS_CURRENT_WORKING_DIR "CurWorkD"
+/* Files stored in this directory will appear on the user's desktop,
+ * if there is one, otherwise it's just the same as "Home"
+ */
+#define NS_OS_DESKTOP_DIR "Desk"
+
+/* Property returns the directory in which the procces was started from.
+ * On Unix this will be the path in the MOZILLA_FIVE_HOME env var and if
+ * unset will be the current working directory.
+ */
+#define NS_OS_CURRENT_PROCESS_DIR "CurProcD"
+
+/* This location is similar to NS_OS_CURRENT_PROCESS_DIR, however,
+ * NS_XPCOM_CURRENT_PROCESS_DIR can be overriden by passing a "bin
+ * directory" to NS_InitXPCOM2().
+ */
+#define NS_XPCOM_CURRENT_PROCESS_DIR "XCurProcD"
+
+/* Property will return the location of the the XPCOM Shared Library.
+ */
+#define NS_XPCOM_LIBRARY_FILE "XpcomLib"
+
+/* Property will return the current location of the GRE directory.
+ * On OSX, this typically points to Contents/Resources in the app bundle.
+ * If no GRE is used, this propery will behave like
+ * NS_XPCOM_CURRENT_PROCESS_DIR.
+ */
+#define NS_GRE_DIR "GreD"
+
+/* Property will return the current location of the GRE-binaries directory.
+ * On OSX, this typically points to Contents/MacOS in the app bundle. On
+ * all other platforms, this will be identical to NS_GRE_DIR.
+ * Since this property is based on the NS_GRE_DIR, if no GRE is used, this
+ * propery will behave like NS_XPCOM_CURRENT_PROCESS_DIR.
+ */
+#define NS_GRE_BIN_DIR "GreBinD"
+
+/* Platform Specific Locations */
+
+#if !defined (XP_UNIX) || defined(MOZ_WIDGET_COCOA)
+ #define NS_OS_SYSTEM_DIR "SysD"
+#endif
+
+#if defined (MOZ_WIDGET_COCOA)
+ #define NS_MAC_DESKTOP_DIR NS_OS_DESKTOP_DIR
+ #define NS_MAC_TRASH_DIR "Trsh"
+ #define NS_MAC_STARTUP_DIR "Strt"
+ #define NS_MAC_SHUTDOWN_DIR "Shdwn"
+ #define NS_MAC_APPLE_MENU_DIR "ApplMenu"
+ #define NS_MAC_CONTROL_PANELS_DIR "CntlPnl"
+ #define NS_MAC_EXTENSIONS_DIR "Exts"
+ #define NS_MAC_FONTS_DIR "Fnts"
+ #define NS_MAC_PREFS_DIR "Prfs"
+ #define NS_MAC_DOCUMENTS_DIR "Docs"
+ #define NS_MAC_INTERNET_SEARCH_DIR "ISrch"
+ #define NS_OSX_HOME_DIR NS_OS_HOME_DIR
+ #define NS_MAC_HOME_DIR NS_OS_HOME_DIR
+ #define NS_MAC_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+ #define NS_MAC_USER_LIB_DIR "ULibDir" // Only available under OS X
+ #define NS_OSX_DEFAULT_DOWNLOAD_DIR NS_MAC_DEFAULT_DOWNLOAD_DIR
+ #define NS_OSX_USER_DESKTOP_DIR "UsrDsk"
+ #define NS_OSX_LOCAL_DESKTOP_DIR "LocDsk"
+ #define NS_OSX_USER_APPLICATIONS_DIR "UsrApp"
+ #define NS_OSX_LOCAL_APPLICATIONS_DIR "LocApp"
+ #define NS_OSX_USER_DOCUMENTS_DIR "UsrDocs"
+ #define NS_OSX_LOCAL_DOCUMENTS_DIR "LocDocs"
+ #define NS_OSX_USER_INTERNET_PLUGIN_DIR "UsrIntrntPlgn"
+ #define NS_OSX_LOCAL_INTERNET_PLUGIN_DIR "LoclIntrntPlgn"
+ #define NS_OSX_USER_FRAMEWORKS_DIR "UsrFrmwrks"
+ #define NS_OSX_LOCAL_FRAMEWORKS_DIR "LocFrmwrks"
+ #define NS_OSX_USER_PREFERENCES_DIR "UsrPrfs"
+ #define NS_OSX_LOCAL_PREFERENCES_DIR "LocPrfs"
+ #define NS_OSX_PICTURE_DOCUMENTS_DIR "Pct"
+ #define NS_OSX_MOVIE_DOCUMENTS_DIR "Mov"
+ #define NS_OSX_MUSIC_DOCUMENTS_DIR "Music"
+ #define NS_OSX_INTERNET_SITES_DIR "IntrntSts"
+#elif defined (XP_WIN)
+ #define NS_WIN_WINDOWS_DIR "WinD"
+ #define NS_WIN_PROGRAM_FILES_DIR "ProgF"
+ #define NS_WIN_HOME_DIR NS_OS_HOME_DIR
+ #define NS_WIN_DESKTOP_DIR "DeskV" // virtual folder at the root of the namespace
+ #define NS_WIN_PROGRAMS_DIR "Progs" // User start menu programs directory!
+ #define NS_WIN_CONTROLS_DIR "Cntls"
+ #define NS_WIN_PRINTERS_DIR "Prnts"
+ #define NS_WIN_PERSONAL_DIR "Pers"
+ #define NS_WIN_FAVORITES_DIR "Favs"
+ #define NS_WIN_STARTUP_DIR "Strt"
+ #define NS_WIN_RECENT_DIR "Rcnt"
+ #define NS_WIN_SEND_TO_DIR "SndTo"
+ #define NS_WIN_BITBUCKET_DIR "Buckt"
+ #define NS_WIN_STARTMENU_DIR "Strt"
+// This gives the same thing as NS_OS_DESKTOP_DIR
+ #define NS_WIN_DESKTOP_DIRECTORY "DeskP" // file sys dir which physically stores objects on desktop
+ #define NS_WIN_DRIVES_DIR "Drivs"
+ #define NS_WIN_NETWORK_DIR "NetW"
+ #define NS_WIN_NETHOOD_DIR "netH"
+ #define NS_WIN_FONTS_DIR "Fnts"
+ #define NS_WIN_TEMPLATES_DIR "Tmpls"
+ #define NS_WIN_COMMON_STARTMENU_DIR "CmStrt"
+ #define NS_WIN_COMMON_PROGRAMS_DIR "CmPrgs"
+ #define NS_WIN_COMMON_STARTUP_DIR "CmStrt"
+ #define NS_WIN_COMMON_DESKTOP_DIRECTORY "CmDeskP"
+ #define NS_WIN_COMMON_APPDATA_DIR "CmAppData"
+ #define NS_WIN_APPDATA_DIR "AppData"
+ #define NS_WIN_LOCAL_APPDATA_DIR "LocalAppData"
+#if defined(MOZ_CONTENT_SANDBOX)
+ #define NS_WIN_LOCAL_APPDATA_LOW_DIR "LocalAppDataLow"
+ #define NS_WIN_LOW_INTEGRITY_TEMP_BASE "LowTmpDBase"
+#endif
+ #define NS_WIN_PRINTHOOD "PrntHd"
+ #define NS_WIN_COOKIES_DIR "CookD"
+ #define NS_WIN_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+ // On Win7 and up these ids will return the default save-to location for
+ // Windows Libraries associated with the specific content type. For other
+ // os they return the local user folder. Note these can return network file
+ // paths which can jank the ui thread so be careful how you access them.
+ #define NS_WIN_DOCUMENTS_DIR "Docs"
+ #define NS_WIN_PICTURES_DIR "Pict"
+ #define NS_WIN_MUSIC_DIR "Music"
+ #define NS_WIN_VIDEOS_DIR "Vids"
+#elif defined (XP_UNIX)
+ #define NS_UNIX_LOCAL_DIR "Locl"
+ #define NS_UNIX_LIB_DIR "LibD"
+ #define NS_UNIX_HOME_DIR NS_OS_HOME_DIR
+ #define NS_UNIX_XDG_DESKTOP_DIR "XDGDesk"
+ #define NS_UNIX_XDG_DOCUMENTS_DIR "XDGDocs"
+ #define NS_UNIX_XDG_DOWNLOAD_DIR "XDGDwnld"
+ #define NS_UNIX_XDG_MUSIC_DIR "XDGMusic"
+ #define NS_UNIX_XDG_PICTURES_DIR "XDGPict"
+ #define NS_UNIX_XDG_PUBLIC_SHARE_DIR "XDGPubSh"
+ #define NS_UNIX_XDG_TEMPLATES_DIR "XDGTempl"
+ #define NS_UNIX_XDG_VIDEOS_DIR "XDGVids"
+ #define NS_UNIX_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+#endif
+
+/* Deprecated */
+
+#define NS_OS_DRIVE_DIR "DrvD"
+
+
+
+#endif
diff --git a/xpcom/io/nsDirectoryServiceUtils.h b/xpcom/io/nsDirectoryServiceUtils.h
new file mode 100644
index 000000000..6100e7565
--- /dev/null
+++ b/xpcom/io/nsDirectoryServiceUtils.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDirectoryServiceUtils_h___
+#define nsDirectoryServiceUtils_h___
+
+#include "nsIServiceManager.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsXPCOMCID.h"
+#include "nsIFile.h"
+
+inline nsresult
+NS_GetSpecialDirectory(const char* aSpecialDirName, nsIFile** aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProperties> serv(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,
+ &rv));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return serv->Get(aSpecialDirName, NS_GET_IID(nsIFile),
+ reinterpret_cast<void**>(aResult));
+}
+
+#endif
diff --git a/xpcom/io/nsEscape.cpp b/xpcom/io/nsEscape.cpp
new file mode 100644
index 000000000..f16edc4ce
--- /dev/null
+++ b/xpcom/io/nsEscape.cpp
@@ -0,0 +1,633 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsEscape.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/BinarySearch.h"
+#include "nsTArray.h"
+#include "nsCRT.h"
+#include "plstr.h"
+
+static const char hexCharsUpper[] = "0123456789ABCDEF";
+static const char hexCharsUpperLower[] = "0123456789ABCDEFabcdef";
+
+static const int netCharType[256] =
+/* Bit 0 xalpha -- the alphas
+** Bit 1 xpalpha -- as xalpha but
+** converts spaces to plus and plus to %2B
+** Bit 3 ... path -- as xalphas but doesn't escape '/'
+*/
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
+ 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
+ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
+ /* bits for '@' changed from 7 to 0 so '@' can be escaped */
+ /* in usernames and passwords in publishing. */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
+ 0, };
+
+/* decode % escaped hex codes into character values
+ */
+#define UNHEX(C) \
+ ((C >= '0' && C <= '9') ? C - '0' : \
+ ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
+ ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
+
+
+#define IS_OK(C) (netCharType[((unsigned int)(C))] & (aFlags))
+#define HEX_ESCAPE '%'
+
+static const uint32_t ENCODE_MAX_LEN = 6; // %uABCD
+
+static uint32_t
+AppendPercentHex(char* aBuffer, unsigned char aChar)
+{
+ uint32_t i = 0;
+ aBuffer[i++] = '%';
+ aBuffer[i++] = hexCharsUpper[aChar >> 4]; // high nibble
+ aBuffer[i++] = hexCharsUpper[aChar & 0xF]; // low nibble
+ return i;
+}
+
+static uint32_t
+AppendPercentHex(char16_t* aBuffer, char16_t aChar)
+{
+ uint32_t i = 0;
+ aBuffer[i++] = '%';
+ if (aChar & 0xff00) {
+ aBuffer[i++] = 'u';
+ aBuffer[i++] = hexCharsUpper[aChar >> 12]; // high-byte high nibble
+ aBuffer[i++] = hexCharsUpper[(aChar >> 8) & 0xF]; // high-byte low nibble
+ }
+ aBuffer[i++] = hexCharsUpper[(aChar >> 4) & 0xF]; // low-byte high nibble
+ aBuffer[i++] = hexCharsUpper[aChar & 0xF]; // low-byte low nibble
+ return i;
+}
+
+//----------------------------------------------------------------------------------------
+char*
+nsEscape(const char* aStr, size_t aLength, size_t* aOutputLength,
+ nsEscapeMask aFlags)
+//----------------------------------------------------------------------------------------
+{
+ if (!aStr) {
+ return nullptr;
+ }
+
+ size_t charsToEscape = 0;
+
+ const unsigned char* src = (const unsigned char*)aStr;
+ for (size_t i = 0; i < aLength; ++i) {
+ if (!IS_OK(src[i])) {
+ charsToEscape++;
+ }
+ }
+
+ // calculate how much memory should be allocated
+ // original length + 2 bytes for each escaped character + terminating '\0'
+ // do the sum in steps to check for overflow
+ size_t dstSize = aLength + 1 + charsToEscape;
+ if (dstSize <= aLength) {
+ return nullptr;
+ }
+ dstSize += charsToEscape;
+ if (dstSize < aLength) {
+ return nullptr;
+ }
+
+ // fail if we need more than 4GB
+ if (dstSize > UINT32_MAX) {
+ return nullptr;
+ }
+
+ char* result = (char*)moz_xmalloc(dstSize);
+ if (!result) {
+ return nullptr;
+ }
+
+ unsigned char* dst = (unsigned char*)result;
+ src = (const unsigned char*)aStr;
+ if (aFlags == url_XPAlphas) {
+ for (size_t i = 0; i < aLength; ++i) {
+ unsigned char c = *src++;
+ if (IS_OK(c)) {
+ *dst++ = c;
+ } else if (c == ' ') {
+ *dst++ = '+'; /* convert spaces to pluses */
+ } else {
+ *dst++ = HEX_ESCAPE;
+ *dst++ = hexCharsUpper[c >> 4]; /* high nibble */
+ *dst++ = hexCharsUpper[c & 0x0f]; /* low nibble */
+ }
+ }
+ } else {
+ for (size_t i = 0; i < aLength; ++i) {
+ unsigned char c = *src++;
+ if (IS_OK(c)) {
+ *dst++ = c;
+ } else {
+ *dst++ = HEX_ESCAPE;
+ *dst++ = hexCharsUpper[c >> 4]; /* high nibble */
+ *dst++ = hexCharsUpper[c & 0x0f]; /* low nibble */
+ }
+ }
+ }
+
+ *dst = '\0'; /* tack on eos */
+ if (aOutputLength) {
+ *aOutputLength = dst - (unsigned char*)result;
+ }
+
+ return result;
+}
+
+//----------------------------------------------------------------------------------------
+char*
+nsUnescape(char* aStr)
+//----------------------------------------------------------------------------------------
+{
+ nsUnescapeCount(aStr);
+ return aStr;
+}
+
+//----------------------------------------------------------------------------------------
+int32_t
+nsUnescapeCount(char* aStr)
+//----------------------------------------------------------------------------------------
+{
+ char* src = aStr;
+ char* dst = aStr;
+
+ char c1[] = " ";
+ char c2[] = " ";
+ char* const pc1 = c1;
+ char* const pc2 = c2;
+
+ if (!*src) {
+ // A null string was passed in. Nothing to escape.
+ // Returns early as the string might not actually be mutable with
+ // length 0.
+ return 0;
+ }
+
+ while (*src) {
+ c1[0] = *(src + 1);
+ if (*(src + 1) == '\0') {
+ c2[0] = '\0';
+ } else {
+ c2[0] = *(src + 2);
+ }
+
+ if (*src != HEX_ESCAPE || PL_strpbrk(pc1, hexCharsUpperLower) == 0 ||
+ PL_strpbrk(pc2, hexCharsUpperLower) == 0) {
+ *dst++ = *src++;
+ } else {
+ src++; /* walk over escape */
+ if (*src) {
+ *dst = UNHEX(*src) << 4;
+ src++;
+ }
+ if (*src) {
+ *dst = (*dst + UNHEX(*src));
+ src++;
+ }
+ dst++;
+ }
+ }
+
+ *dst = 0;
+ return (int)(dst - aStr);
+
+} /* NET_UnEscapeCnt */
+
+
+char*
+nsEscapeHTML(const char* aString)
+{
+ char* rv = nullptr;
+ /* XXX Hardcoded max entity len. The +1 is for the trailing null. */
+ uint32_t len = strlen(aString);
+ if (len >= (UINT32_MAX / 6)) {
+ return nullptr;
+ }
+
+ rv = (char*)moz_xmalloc((6 * len) + 1);
+ char* ptr = rv;
+
+ if (rv) {
+ for (; *aString != '\0'; ++aString) {
+ if (*aString == '<') {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (*aString == '>') {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (*aString == '&') {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ } else if (*aString == '"') {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (*aString == '\'') {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ } else {
+ *ptr++ = *aString;
+ }
+ }
+ *ptr = '\0';
+ }
+
+ return rv;
+}
+
+char16_t*
+nsEscapeHTML2(const char16_t* aSourceBuffer, int32_t aSourceBufferLen)
+{
+ // Calculate the length, if the caller didn't.
+ if (aSourceBufferLen < 0) {
+ aSourceBufferLen = NS_strlen(aSourceBuffer);
+ }
+
+ /* XXX Hardcoded max entity len. */
+ if (uint32_t(aSourceBufferLen) >=
+ ((UINT32_MAX - sizeof(char16_t)) / (6 * sizeof(char16_t)))) {
+ return nullptr;
+ }
+
+ char16_t* resultBuffer = (char16_t*)moz_xmalloc(
+ aSourceBufferLen * 6 * sizeof(char16_t) + sizeof(char16_t('\0')));
+ char16_t* ptr = resultBuffer;
+
+ if (resultBuffer) {
+ int32_t i;
+
+ for (i = 0; i < aSourceBufferLen; ++i) {
+ if (aSourceBuffer[i] == '<') {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '>') {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '&') {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '"') {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '\'') {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ } else {
+ *ptr++ = aSourceBuffer[i];
+ }
+ }
+ *ptr = 0;
+ }
+
+ return resultBuffer;
+}
+
+//----------------------------------------------------------------------------------------
+//
+// The following table encodes which characters needs to be escaped for which
+// parts of an URL. The bits are the "url components" in the enum EscapeMask,
+// see nsEscape.h.
+//
+// esc_Scheme = 1
+// esc_Username = 2
+// esc_Password = 4
+// esc_Host = 8
+// esc_Directory = 16
+// esc_FileBaseName = 32
+// esc_FileExtension = 64
+// esc_Param = 128
+// esc_Query = 256
+// esc_Ref = 512
+
+static const uint32_t EscapeChars[256] =
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 0,1023, 0, 512,1023, 0,1023, 112,1023,1023,1023,1023,1023,1023, 953, 784, // 2x !"#$%&'()*+,-./
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008,1008, 0,1008, 0, 768, // 3x 0123456789:;<=>?
+ 1008,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 4x @ABCDEFGHIJKLMNO
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008, 896,1008, 896,1023, // 5x PQRSTUVWXYZ[\]^_
+ 384,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 6x `abcdefghijklmno
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896,1012, 896,1023, 0, // 7x pqrstuvwxyz{|}~ DEL
+ 0 // 80 to FF are zero
+};
+
+static uint16_t dontNeedEscape(unsigned char aChar, uint32_t aFlags)
+{
+ return EscapeChars[(uint32_t)aChar] & aFlags;
+}
+static uint16_t dontNeedEscape(uint16_t aChar, uint32_t aFlags)
+{
+ return aChar < mozilla::ArrayLength(EscapeChars) ?
+ (EscapeChars[(uint32_t)aChar] & aFlags) : 0;
+}
+
+//----------------------------------------------------------------------------------------
+
+/**
+ * Templated helper for URL escaping a portion of a string.
+ *
+ * @param aPart The pointer to the beginning of the portion of the string to
+ * escape.
+ * @param aPartLen The length of the string to escape.
+ * @param aFlags Flags used to configure escaping. @see EscapeMask
+ * @param aResult String that has the URL escaped portion appended to. Only
+ * altered if the string is URL escaped or |esc_AlwaysCopy| is specified.
+ * @param aDidAppend Indicates whether or not data was appended to |aResult|.
+ * @return NS_ERROR_INVALID_ARG, NS_ERROR_OUT_OF_MEMORY on failure.
+ */
+template<class T>
+static nsresult
+T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
+ uint32_t aFlags, T& aResult, bool& aDidAppend)
+{
+ typedef nsCharTraits<typename T::char_type> traits;
+ typedef typename traits::unsigned_char_type unsigned_char_type;
+ static_assert(sizeof(*aPart) == 1 || sizeof(*aPart) == 2,
+ "unexpected char type");
+
+ if (!aPart) {
+ NS_NOTREACHED("null pointer");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ bool forced = !!(aFlags & esc_Forced);
+ bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
+ bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII);
+ bool writing = !!(aFlags & esc_AlwaysCopy);
+ bool colon = !!(aFlags & esc_Colon);
+
+ auto src = reinterpret_cast<const unsigned_char_type*>(aPart);
+
+ typename T::char_type tempBuffer[100];
+ unsigned int tempBufferPos = 0;
+
+ bool previousIsNonASCII = false;
+ for (size_t i = 0; i < aPartLen; ++i) {
+ unsigned_char_type c = *src++;
+
+ // if the char has not to be escaped or whatever follows % is
+ // a valid escaped string, just copy the char.
+ //
+ // Also the % will not be escaped until forced
+ // See bugzilla bug 61269 for details why we changed this
+ //
+ // And, we will not escape non-ascii characters if requested.
+ // On special request we will also escape the colon even when
+ // not covered by the matrix.
+ // ignoreAscii is not honored for control characters (C0 and DEL)
+ //
+ // And, we should escape the '|' character when it occurs after any
+ // non-ASCII character as it may be aPart of a multi-byte character.
+ //
+ // 0x20..0x7e are the valid ASCII characters. We also escape spaces
+ // (0x20) since they are not legal in URLs.
+ if ((dontNeedEscape(c, aFlags) || (c == HEX_ESCAPE && !forced)
+ || (c > 0x7f && ignoreNonAscii)
+ || (c > 0x20 && c < 0x7f && ignoreAscii))
+ && !(c == ':' && colon)
+ && !(previousIsNonASCII && c == '|' && !ignoreNonAscii)) {
+ if (writing) {
+ tempBuffer[tempBufferPos++] = c;
+ }
+ } else { /* do the escape magic */
+ if (!writing) {
+ if (!aResult.Append(aPart, i, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ writing = true;
+ }
+ uint32_t len = ::AppendPercentHex(tempBuffer + tempBufferPos, c);
+ tempBufferPos += len;
+ MOZ_ASSERT(len <= ENCODE_MAX_LEN, "potential buffer overflow");
+ }
+
+ // Flush the temp buffer if it doesnt't have room for another encoded char.
+ if (tempBufferPos >= mozilla::ArrayLength(tempBuffer) - ENCODE_MAX_LEN) {
+ NS_ASSERTION(writing, "should be writing");
+ if (!aResult.Append(tempBuffer, tempBufferPos, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ tempBufferPos = 0;
+ }
+
+ previousIsNonASCII = (c > 0x7f);
+ }
+ if (writing) {
+ if (!aResult.Append(tempBuffer, tempBufferPos, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ aDidAppend = writing;
+ return NS_OK;
+}
+
+bool
+NS_EscapeURL(const char* aPart, int32_t aPartLen, uint32_t aFlags,
+ nsACString& aResult)
+{
+ if (aPartLen < 0) {
+ aPartLen = strlen(aPart);
+ }
+
+ bool result = false;
+ nsresult rv = T_EscapeURL(aPart, aPartLen, aFlags, aResult, result);
+ if (NS_FAILED(rv)) {
+ ::NS_ABORT_OOM(aResult.Length() * sizeof(nsACString::char_type));
+ }
+
+ return result;
+}
+
+nsresult
+NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult,
+ const mozilla::fallible_t&)
+{
+ bool appended = false;
+ nsresult rv = T_EscapeURL(aStr.Data(), aStr.Length(), aFlags, aResult, appended);
+ if (NS_FAILED(rv)) {
+ aResult.Truncate();
+ return rv;
+ }
+
+ if (!appended) {
+ aResult = aStr;
+ }
+
+ return rv;
+}
+
+const nsSubstring&
+NS_EscapeURL(const nsSubstring& aStr, uint32_t aFlags, nsSubstring& aResult)
+{
+ bool result = false;
+ nsresult rv = T_EscapeURL<nsSubstring>(aStr.Data(), aStr.Length(), aFlags, aResult, result);
+
+ if (NS_FAILED(rv)) {
+ ::NS_ABORT_OOM(aResult.Length() * sizeof(nsSubstring::char_type));
+ }
+
+ if (result) {
+ return aResult;
+ }
+ return aStr;
+}
+
+// Starting at aStr[aStart] find the first index in aStr that matches any
+// character in aForbidden. Return false if not found.
+static bool
+FindFirstMatchFrom(const nsAFlatString& aStr, size_t aStart,
+ const nsTArray<char16_t>& aForbidden, size_t* aIndex)
+{
+ const size_t len = aForbidden.Length();
+ for (size_t j = aStart, l = aStr.Length(); j < l; ++j) {
+ size_t unused;
+ if (mozilla::BinarySearch(aForbidden, 0, len, aStr[j], &unused)) {
+ *aIndex = j;
+ return true;
+ }
+ }
+ return false;
+}
+
+const nsSubstring&
+NS_EscapeURL(const nsAFlatString& aStr, const nsTArray<char16_t>& aForbidden,
+ nsSubstring& aResult)
+{
+ bool didEscape = false;
+ for (size_t i = 0, strLen = aStr.Length(); i < strLen; ) {
+ size_t j;
+ if (MOZ_UNLIKELY(FindFirstMatchFrom(aStr, i, aForbidden, &j))) {
+ if (i == 0) {
+ didEscape = true;
+ aResult.Truncate();
+ aResult.SetCapacity(aStr.Length());
+ }
+ if (j != i) {
+ // The substring from 'i' up to 'j' that needs no escaping.
+ aResult.Append(nsDependentSubstring(aStr, i, j - i));
+ }
+ char16_t buffer[ENCODE_MAX_LEN];
+ uint32_t bufferLen = ::AppendPercentHex(buffer, aStr[j]);
+ MOZ_ASSERT(bufferLen <= ENCODE_MAX_LEN, "buffer overflow");
+ aResult.Append(buffer, bufferLen);
+ i = j + 1;
+ } else {
+ if (MOZ_UNLIKELY(didEscape)) {
+ // The tail of the string that needs no escaping.
+ aResult.Append(nsDependentSubstring(aStr, i, strLen - i));
+ }
+ break;
+ }
+ }
+ if (MOZ_UNLIKELY(didEscape)) {
+ return aResult;
+ }
+ return aStr;
+}
+
+#define ISHEX(c) memchr(hexCharsUpperLower, c, sizeof(hexCharsUpperLower)-1)
+
+bool
+NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags,
+ nsACString& aResult)
+{
+ if (!aStr) {
+ NS_NOTREACHED("null pointer");
+ return false;
+ }
+
+ MOZ_ASSERT(aResult.IsEmpty(),
+ "Passing a non-empty string as an out parameter!");
+
+ if (aLen < 0) {
+ aLen = strlen(aStr);
+ }
+
+ bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
+ bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII);
+ bool writing = !!(aFlags & esc_AlwaysCopy);
+ bool skipControl = !!(aFlags & esc_SkipControl);
+ bool skipInvalidHostChar = !!(aFlags & esc_Host);
+
+ if (writing) {
+ aResult.SetCapacity(aLen);
+ }
+
+ const char* last = aStr;
+ const char* p = aStr;
+
+ for (int i = 0; i < aLen; ++i, ++p) {
+ if (*p == HEX_ESCAPE && i < aLen - 2) {
+ unsigned char c1 = *((unsigned char*)p + 1);
+ unsigned char c2 = *((unsigned char*)p + 2);
+ unsigned char u = (UNHEX(c1) << 4) + UNHEX(c2);
+ if (ISHEX(c1) && ISHEX(c2) &&
+ (!skipInvalidHostChar || dontNeedEscape(u, aFlags) || c1 >= '8') &&
+ ((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) &&
+ !(skipControl &&
+ (c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) {
+ if (!writing) {
+ writing = true;
+ aResult.SetCapacity(aLen);
+ }
+ if (p > last) {
+ aResult.Append(last, p - last);
+ last = p;
+ }
+ aResult.Append(u);
+ i += 2;
+ p += 2;
+ last += 3;
+ }
+ }
+ }
+ if (writing && last < aStr + aLen) {
+ aResult.Append(last, aStr + aLen - last);
+ }
+
+ return writing;
+}
diff --git a/xpcom/io/nsEscape.h b/xpcom/io/nsEscape.h
new file mode 100644
index 000000000..bf89b737a
--- /dev/null
+++ b/xpcom/io/nsEscape.h
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* First checked in on 98/12/03 by John R. McMullen, derived from net.h/mkparse.c. */
+
+#ifndef _ESCAPE_H_
+#define _ESCAPE_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+
+/**
+ * Valid mask values for nsEscape
+ * Note: these values are copied in nsINetUtil.idl. Any changes should be kept
+ * in sync.
+ */
+typedef enum {
+ url_All = 0, // %-escape every byte unconditionally
+ url_XAlphas = 1u << 0, // Normal escape - leave alphas intact, escape the rest
+ url_XPAlphas = 1u << 1, // As url_XAlphas, but convert spaces (0x20) to '+' and plus to %2B
+ url_Path = 1u << 2 // As url_XAlphas, but don't escape slash ('/')
+} nsEscapeMask;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Escape the given string according to mask
+ * @param aSstr The string to escape
+ * @param aLength The length of the string to escape
+ * @param aOutputLen A pointer that will be used to store the length of the
+ * output string, if not null
+ * @param aMask How to escape the string
+ * @return A newly allocated escaped string that must be free'd with
+ * nsCRT::free, or null on failure
+ * @note: Please, don't use this function. Use NS_Escape instead!
+ */
+char* nsEscape(const char* aStr, size_t aLength, size_t* aOutputLen,
+ nsEscapeMask aMask);
+
+char* nsUnescape(char* aStr);
+/* decode % escaped hex codes into character values,
+ * modifies the parameter, returns the same buffer
+ */
+
+int32_t nsUnescapeCount(char* aStr);
+/* decode % escaped hex codes into character values,
+ * modifies the parameter buffer, returns the length of the result
+ * (result may contain \0's).
+ */
+
+char*
+nsEscapeHTML(const char* aString);
+
+char16_t*
+nsEscapeHTML2(const char16_t* aSourceBuffer,
+ int32_t aSourceBufferLen = -1);
+/*
+ * Escape problem char's for HTML display
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * NS_EscapeURL/NS_UnescapeURL constants for |flags| parameter:
+ *
+ * Note: These values are copied to nsINetUtil.idl
+ * Any changes should be kept in sync
+ */
+enum EscapeMask {
+ /** url components **/
+ esc_Scheme = 1u << 0,
+ esc_Username = 1u << 1,
+ esc_Password = 1u << 2,
+ esc_Host = 1u << 3,
+ esc_Directory = 1u << 4,
+ esc_FileBaseName = 1u << 5,
+ esc_FileExtension = 1u << 6,
+ esc_FilePath = esc_Directory | esc_FileBaseName | esc_FileExtension,
+ esc_Param = 1u << 7,
+ esc_Query = 1u << 8,
+ esc_Ref = 1u << 9,
+ /** special flags **/
+ esc_Minimal = esc_Scheme | esc_Username | esc_Password | esc_Host | esc_FilePath | esc_Param | esc_Query | esc_Ref,
+ esc_Forced = 1u << 10, /* forces escaping of existing escape sequences */
+ esc_OnlyASCII = 1u << 11, /* causes non-ascii octets to be skipped */
+ esc_OnlyNonASCII = 1u << 12, /* causes _graphic_ ascii octets (0x20-0x7E)
+ * to be skipped when escaping. causes all
+ * ascii octets (<= 0x7F) to be skipped when unescaping */
+ esc_AlwaysCopy = 1u << 13, /* copy input to result buf even if escaping is unnecessary */
+ esc_Colon = 1u << 14, /* forces escape of colon */
+ esc_SkipControl = 1u << 15 /* skips C0 and DEL from unescaping */
+};
+
+/**
+ * NS_EscapeURL
+ *
+ * Escapes invalid char's in an URL segment. Has no side-effect if the URL
+ * segment is already escaped, unless aFlags has the esc_Forced bit in which
+ * case % will also be escaped. Iff some part of aStr is escaped is the
+ * final result appended to aResult. You can also request that aStr is
+ * always appended to aResult with esc_AlwaysCopy.
+ *
+ * @param aStr url segment string
+ * @param aLen url segment string length (-1 if unknown)
+ * @param aFlags url segment type flag (see EscapeMask above)
+ * @param aResult result buffer, untouched if aStr is already escaped unless
+ * aFlags has esc_AlwaysCopy
+ *
+ * @return true if aResult was written to (i.e. at least one character was
+ * escaped or esc_AlwaysCopy was requested), false otherwise.
+ */
+bool NS_EscapeURL(const char* aStr,
+ int32_t aLen,
+ uint32_t aFlags,
+ nsACString& aResult);
+
+/**
+ * Expands URL escape sequences... beware embedded null bytes!
+ *
+ * @param aStr url string to unescape
+ * @param aLen length of aStr
+ * @param aFlags only esc_OnlyNonASCII, esc_SkipControl and esc_AlwaysCopy
+ * are recognized
+ * @param aResult result buffer, untouched if aStr is already unescaped unless
+ * aFlags has esc_AlwaysCopy
+ *
+ * @return true if aResult was written to (i.e. at least one character was
+ * unescaped or esc_AlwaysCopy was requested), false otherwise.
+ */
+bool NS_UnescapeURL(const char* aStr,
+ int32_t aLen,
+ uint32_t aFlags,
+ nsACString& aResult);
+
+/** returns resultant string length **/
+inline int32_t
+NS_UnescapeURL(char* aStr)
+{
+ return nsUnescapeCount(aStr);
+}
+
+/**
+ * String friendly versions...
+ */
+inline const nsCSubstring&
+NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult)
+{
+ if (NS_EscapeURL(aStr.Data(), aStr.Length(), aFlags, aResult)) {
+ return aResult;
+ }
+ return aStr;
+}
+
+/**
+ * Fallible version of NS_EscapeURL. On success aResult will point to either
+ * the original string or an escaped copy.
+ */
+nsresult
+NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult,
+ const mozilla::fallible_t&);
+
+inline const nsCSubstring&
+NS_UnescapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult)
+{
+ if (NS_UnescapeURL(aStr.Data(), aStr.Length(), aFlags, aResult)) {
+ return aResult;
+ }
+ return aStr;
+}
+const nsSubstring&
+NS_EscapeURL(const nsSubstring& aStr, uint32_t aFlags, nsSubstring& aResult);
+
+/**
+ * Percent-escapes all characters in aStr that occurs in aForbidden.
+ * @param aStr the input URL string
+ * @param aForbidden the characters that should be escaped if found in aStr
+ * @note that aForbidden MUST be sorted (low to high)
+ * @param aResult the result if some characters were escaped
+ * @return aResult if some characters were escaped, or aStr otherwise (aResult
+ * is unmodified in that case)
+ */
+const nsSubstring&
+NS_EscapeURL(const nsAFlatString& aStr, const nsTArray<char16_t>& aForbidden,
+ nsSubstring& aResult);
+
+/**
+ * CString version of nsEscape. Returns true on success, false
+ * on out of memory. To reverse this function, use NS_UnescapeURL.
+ */
+inline bool
+NS_Escape(const nsACString& aOriginal, nsACString& aEscaped,
+ nsEscapeMask aMask)
+{
+ size_t escLen = 0;
+ char* esc = nsEscape(aOriginal.BeginReading(), aOriginal.Length(), &escLen,
+ aMask);
+ if (! esc) {
+ return false;
+ }
+ aEscaped.Adopt(esc, escLen);
+ return true;
+}
+
+/**
+ * Inline unescape of mutable string object.
+ */
+inline nsACString&
+NS_UnescapeURL(nsACString& aStr)
+{
+ aStr.SetLength(nsUnescapeCount(aStr.BeginWriting()));
+ return aStr;
+}
+
+#endif // _ESCAPE_H_
diff --git a/xpcom/io/nsIAsyncInputStream.idl b/xpcom/io/nsIAsyncInputStream.idl
new file mode 100644
index 000000000..5570817dd
--- /dev/null
+++ b/xpcom/io/nsIAsyncInputStream.idl
@@ -0,0 +1,104 @@
+/* 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 "nsIInputStream.idl"
+
+interface nsIInputStreamCallback;
+interface nsIEventTarget;
+
+/**
+ * If an input stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK
+ * when read. The caller must then wait for the stream to have some data to
+ * read. If the stream implements nsIAsyncInputStream, then the caller can use
+ * this interface to request an asynchronous notification when the stream
+ * becomes readable or closed (via the AsyncWait method).
+ *
+ * While this interface is almost exclusively used with non-blocking streams, it
+ * is not necessary that nsIInputStream::isNonBlocking return true. Nor is it
+ * necessary that a non-blocking nsIInputStream implementation also implement
+ * nsIAsyncInputStream.
+ */
+[scriptable, uuid(a5f255ab-4801-4161-8816-277ac92f6ad1)]
+interface nsIAsyncInputStream : nsIInputStream
+{
+ /**
+ * This method closes the stream and sets its internal status. If the
+ * stream is already closed, then this method is ignored. Once the stream
+ * is closed, the stream's status cannot be changed. Any successful status
+ * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which
+ * has an effect equivalent to nsIInputStream::close.
+ *
+ * NOTE: this method exists in part to support pipes, which have both an
+ * input end and an output end. If the input end of a pipe is closed, then
+ * writes to the output end of the pipe will fail. The error code returned
+ * when an attempt is made to write to a "broken" pipe corresponds to the
+ * status code passed in when the input end of the pipe was closed, which
+ * greatly simplifies working with pipes in some cases.
+ *
+ * @param aStatus
+ * The error that will be reported if this stream is accessed after
+ * it has been closed.
+ */
+ void closeWithStatus(in nsresult aStatus);
+
+ /**
+ * Asynchronously wait for the stream to be readable or closed. The
+ * notification is one-shot, meaning that each asyncWait call will result
+ * in exactly one notification callback. After the OnInputStreamReady event
+ * is dispatched, the stream releases its reference to the
+ * nsIInputStreamCallback object. It is safe to call asyncWait again from the
+ * notification handler.
+ *
+ * This method may be called at any time (even if read has not been called).
+ * In other words, this method may be called when the stream already has
+ * data to read. It may also be called when the stream is closed. If the
+ * stream is already readable or closed when AsyncWait is called, then the
+ * OnInputStreamReady event will be dispatched immediately. Otherwise, the
+ * event will be dispatched when the stream becomes readable or closed.
+ *
+ * @param aCallback
+ * This object is notified when the stream becomes ready. This
+ * parameter may be null to clear an existing callback.
+ * @param aFlags
+ * This parameter specifies optional flags passed in to configure
+ * the behavior of this method. Pass zero to specify no flags.
+ * @param aRequestedCount
+ * Wait until at least this many bytes can be read. This is only
+ * a suggestion to the underlying stream; it may be ignored. The
+ * caller may pass zero to indicate no preference.
+ * @param aEventTarget
+ * Specify NULL to receive notification on ANY thread (possibly even
+ * recursively on the calling thread -- i.e., synchronously), or
+ * specify that the notification be delivered to a specific event
+ * target.
+ */
+ void asyncWait(in nsIInputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+
+ /**
+ * If passed to asyncWait, this flag overrides the default behavior,
+ * causing the OnInputStreamReady notification to be suppressed until the
+ * stream becomes closed (either as a result of closeWithStatus/close being
+ * called on the stream or possibly due to some error in the underlying
+ * stream).
+ */
+ const unsigned long WAIT_CLOSURE_ONLY = (1<<0);
+};
+
+/**
+ * This is a companion interface for nsIAsyncInputStream::asyncWait.
+ */
+[function, scriptable, uuid(d1f28e94-3a6e-4050-a5f5-2e81b1fc2a43)]
+interface nsIInputStreamCallback : nsISupports
+{
+ /**
+ * Called to indicate that the stream is either readable or closed.
+ *
+ * @param aStream
+ * The stream whose asyncWait method was called.
+ */
+ void onInputStreamReady(in nsIAsyncInputStream aStream);
+};
diff --git a/xpcom/io/nsIAsyncOutputStream.idl b/xpcom/io/nsIAsyncOutputStream.idl
new file mode 100644
index 000000000..7e74579c6
--- /dev/null
+++ b/xpcom/io/nsIAsyncOutputStream.idl
@@ -0,0 +1,104 @@
+/* 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 "nsIOutputStream.idl"
+
+interface nsIOutputStreamCallback;
+interface nsIEventTarget;
+
+/**
+ * If an output stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK
+ * when written to. The caller must then wait for the stream to become
+ * writable. If the stream implements nsIAsyncOutputStream, then the caller can
+ * use this interface to request an asynchronous notification when the stream
+ * becomes writable or closed (via the AsyncWait method).
+ *
+ * While this interface is almost exclusively used with non-blocking streams, it
+ * is not necessary that nsIOutputStream::isNonBlocking return true. Nor is it
+ * necessary that a non-blocking nsIOutputStream implementation also implement
+ * nsIAsyncOutputStream.
+ */
+[scriptable, uuid(beb632d3-d77a-4e90-9134-f9ece69e8200)]
+interface nsIAsyncOutputStream : nsIOutputStream
+{
+ /**
+ * This method closes the stream and sets its internal status. If the
+ * stream is already closed, then this method is ignored. Once the stream
+ * is closed, the stream's status cannot be changed. Any successful status
+ * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which
+ * is equivalent to nsIInputStream::close.
+ *
+ * NOTE: this method exists in part to support pipes, which have both an
+ * input end and an output end. If the output end of a pipe is closed, then
+ * reads from the input end of the pipe will fail. The error code returned
+ * when an attempt is made to read from a "closed" pipe corresponds to the
+ * status code passed in when the output end of the pipe is closed, which
+ * greatly simplifies working with pipes in some cases.
+ *
+ * @param aStatus
+ * The error that will be reported if this stream is accessed after
+ * it has been closed.
+ */
+ void closeWithStatus(in nsresult reason);
+
+ /**
+ * Asynchronously wait for the stream to be writable or closed. The
+ * notification is one-shot, meaning that each asyncWait call will result
+ * in exactly one notification callback. After the OnOutputStreamReady event
+ * is dispatched, the stream releases its reference to the
+ * nsIOutputStreamCallback object. It is safe to call asyncWait again from the
+ * notification handler.
+ *
+ * This method may be called at any time (even if write has not been called).
+ * In other words, this method may be called when the stream already has
+ * room for more data. It may also be called when the stream is closed. If
+ * the stream is already writable or closed when AsyncWait is called, then the
+ * OnOutputStreamReady event will be dispatched immediately. Otherwise, the
+ * event will be dispatched when the stream becomes writable or closed.
+ *
+ * @param aCallback
+ * This object is notified when the stream becomes ready. This
+ * parameter may be null to clear an existing callback.
+ * @param aFlags
+ * This parameter specifies optional flags passed in to configure
+ * the behavior of this method. Pass zero to specify no flags.
+ * @param aRequestedCount
+ * Wait until at least this many bytes can be written. This is only
+ * a suggestion to the underlying stream; it may be ignored. The
+ * caller may pass zero to indicate no preference.
+ * @param aEventTarget
+ * Specify NULL to receive notification on ANY thread (possibly even
+ * recursively on the calling thread -- i.e., synchronously), or
+ * specify that the notification be delivered to a specific event
+ * target.
+ */
+ void asyncWait(in nsIOutputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+
+ /**
+ * If passed to asyncWait, this flag overrides the default behavior,
+ * causing the OnOutputStreamReady notification to be suppressed until the
+ * stream becomes closed (either as a result of closeWithStatus/close being
+ * called on the stream or possibly due to some error in the underlying
+ * stream).
+ */
+ const unsigned long WAIT_CLOSURE_ONLY = (1<<0);
+};
+
+/**
+ * This is a companion interface for nsIAsyncOutputStream::asyncWait.
+ */
+[function, scriptable, uuid(40dbcdff-9053-42c5-a57c-3ec910d0f148)]
+interface nsIOutputStreamCallback : nsISupports
+{
+ /**
+ * Called to indicate that the stream is either writable or closed.
+ *
+ * @param aStream
+ * The stream whose asyncWait method was called.
+ */
+ void onOutputStreamReady(in nsIAsyncOutputStream aStream);
+};
diff --git a/xpcom/io/nsIBinaryInputStream.idl b/xpcom/io/nsIBinaryInputStream.idl
new file mode 100644
index 000000000..c3a27e203
--- /dev/null
+++ b/xpcom/io/nsIBinaryInputStream.idl
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsIInputStream.idl"
+
+/**
+ * This interface allows consumption of primitive data types from a "binary
+ * stream" containing untagged, big-endian binary data, i.e. as produced by an
+ * implementation of nsIBinaryOutputStream. This might be used, for example,
+ * to implement network protocols or to read from architecture-neutral disk
+ * files, i.e. ones that can be read and written by both big-endian and
+ * little-endian platforms.
+ *
+ * @See nsIBinaryOutputStream
+ */
+
+[scriptable, uuid(899b826b-2eb3-469c-8b31-4c29f5d341a6)]
+interface nsIBinaryInputStream : nsIInputStream {
+ void setInputStream(in nsIInputStream aInputStream);
+
+ /**
+ * Read 8-bits from the stream.
+ *
+ * @return that byte to be treated as a boolean.
+ */
+ boolean readBoolean();
+
+ uint8_t read8();
+ uint16_t read16();
+ uint32_t read32();
+ uint64_t read64();
+
+ float readFloat();
+ double readDouble();
+
+ /**
+ * Read an 8-bit pascal style string from the stream.
+ * 32-bit length field, followed by length 8-bit chars.
+ */
+ ACString readCString();
+
+ /**
+ * Read an 16-bit pascal style string from the stream.
+ * 32-bit length field, followed by length PRUnichars.
+ */
+ AString readString();
+
+ /**
+ * Read an opaque byte array from the stream.
+ *
+ * @param aLength the number of bytes that must be read.
+ *
+ * @throws NS_ERROR_FAILURE if it can't read aLength bytes
+ */
+ void readBytes(in uint32_t aLength,
+ [size_is(aLength), retval] out string aString);
+
+ /**
+ * Read an opaque byte array from the stream, storing the results
+ * as an array of PRUint8s.
+ *
+ * @param aLength the number of bytes that must be read.
+ *
+ * @throws NS_ERROR_FAILURE if it can't read aLength bytes
+ */
+ void readByteArray(in uint32_t aLength,
+ [array, size_is(aLength), retval] out uint8_t aBytes);
+
+ /**
+ * Read opaque bytes from the stream, storing the results in an ArrayBuffer.
+ *
+ * @param aLength the number of bytes that must be read
+ * @param aArrayBuffer the arraybuffer in which to store the results
+ * Note: passing view.buffer, where view is an ArrayBufferView of an
+ * ArrayBuffer, is not valid unless view.byteOffset == 0.
+ *
+ * @return The number of bytes actually read into aArrayBuffer.
+ */
+ [implicit_jscontext]
+ unsigned long readArrayBuffer(in uint32_t aLength, in jsval aArrayBuffer);
+};
+
+%{C++
+
+#ifdef MOZILLA_INTERNAL_API
+#include "nsString.h"
+
+inline nsresult
+NS_ReadOptionalCString(nsIBinaryInputStream* aStream, nsACString& aResult)
+{
+ bool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadCString(aResult);
+ else
+ aResult.Truncate();
+ }
+ return rv;
+}
+
+inline nsresult
+NS_ReadOptionalString(nsIBinaryInputStream* aStream, nsAString& aResult)
+{
+ bool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadString(aResult);
+ else
+ aResult.Truncate();
+ }
+ return rv;
+}
+#endif
+
+%}
diff --git a/xpcom/io/nsIBinaryOutputStream.idl b/xpcom/io/nsIBinaryOutputStream.idl
new file mode 100644
index 000000000..4d426d580
--- /dev/null
+++ b/xpcom/io/nsIBinaryOutputStream.idl
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsIOutputStream.idl"
+
+/**
+ * This interface allows writing of primitive data types (integers,
+ * floating-point values, booleans, etc.) to a stream in a binary, untagged,
+ * fixed-endianness format. This might be used, for example, to implement
+ * network protocols or to produce architecture-neutral binary disk files,
+ * i.e. ones that can be read and written by both big-endian and little-endian
+ * platforms. Output is written in big-endian order (high-order byte first),
+ * as this is traditional network order.
+ *
+ * @See nsIBinaryInputStream
+ */
+
+[scriptable, uuid(204ee610-8765-11d3-90cf-0040056a906e)]
+interface nsIBinaryOutputStream : nsIOutputStream {
+ void setOutputStream(in nsIOutputStream aOutputStream);
+
+ /**
+ * Write a boolean as an 8-bit char to the stream.
+ */
+ void writeBoolean(in boolean aBoolean);
+
+ void write8(in uint8_t aByte);
+ void write16(in uint16_t a16);
+ void write32(in uint32_t a32);
+ void write64(in uint64_t a64);
+
+ void writeFloat(in float aFloat);
+ void writeDouble(in double aDouble);
+
+ /**
+ * Write an 8-bit pascal style string to the stream.
+ * 32-bit length field, followed by length 8-bit chars.
+ */
+ void writeStringZ(in string aString);
+
+ /**
+ * Write a 16-bit pascal style string to the stream.
+ * 32-bit length field, followed by length PRUnichars.
+ */
+ void writeWStringZ(in wstring aString);
+
+ /**
+ * Write an 8-bit pascal style string (UTF8-encoded) to the stream.
+ * 32-bit length field, followed by length 8-bit chars.
+ */
+ void writeUtf8Z(in wstring aString);
+
+ /**
+ * Write an opaque byte array to the stream.
+ */
+ void writeBytes([size_is(aLength)] in string aString, in uint32_t aLength);
+
+ /**
+ * Write an opaque byte array to the stream.
+ */
+ void writeByteArray([array, size_is(aLength)] in uint8_t aBytes,
+ in uint32_t aLength);
+
+};
+
+%{C++
+
+inline nsresult
+NS_WriteOptionalStringZ(nsIBinaryOutputStream* aStream, const char* aString)
+{
+ bool nonnull = (aString != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteStringZ(aString);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalWStringZ(nsIBinaryOutputStream* aStream, const char16_t* aString)
+{
+ bool nonnull = (aString != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteWStringZ(aString);
+ return rv;
+}
+
+%}
diff --git a/xpcom/io/nsICloneableInputStream.idl b/xpcom/io/nsICloneableInputStream.idl
new file mode 100644
index 000000000..4fd9a74f7
--- /dev/null
+++ b/xpcom/io/nsICloneableInputStream.idl
@@ -0,0 +1,22 @@
+/* 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 "nsIInputStream.idl"
+
+[scriptable, builtinclass, uuid(8149be1f-44d3-4f14-8b65-a57a5fbbeb97)]
+interface nsICloneableInputStream : nsISupports
+{
+ // Allow streams that implement the interface to determine if cloning
+ // possible at runtime. For example, this allows wrappers to check if
+ // their base stream supports cloning.
+ [infallible] readonly attribute boolean cloneable;
+
+ // Produce a copy of the current stream in the most efficient way possible.
+ // In this case "copy" means that both the original and cloned streams
+ // should produce the same bytes for all future reads. Bytes that have
+ // already been consumed from the original stream are not copied to the
+ // clone. Operations on the two streams should be completely independent
+ // after the clone() occurs.
+ nsIInputStream clone();
+};
diff --git a/xpcom/io/nsIConverterInputStream.idl b/xpcom/io/nsIConverterInputStream.idl
new file mode 100644
index 000000000..7f35d8c5c
--- /dev/null
+++ b/xpcom/io/nsIConverterInputStream.idl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIUnicharInputStream.idl"
+
+interface nsIInputStream;
+
+/**
+ * A unichar input stream that wraps an input stream.
+ * This allows reading unicode strings from a stream, automatically converting
+ * the bytes from a selected character encoding.
+ */
+[scriptable, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)]
+interface nsIConverterInputStream : nsIUnicharInputStream {
+ /**
+ * Default replacement char value, U+FFFD REPLACEMENT CHARACTER.
+ */
+ const char16_t DEFAULT_REPLACEMENT_CHARACTER = 0xFFFD;
+
+ /**
+ * Initialize this stream.
+ * @param aStream
+ * The underlying stream to read from.
+ * @param aCharset
+ * The character encoding to use for converting the bytes of the
+ * stream. A null charset will be interpreted as UTF-8.
+ * @param aBufferSize
+ * How many bytes to buffer.
+ * @param aReplacementChar
+ * The character to replace unknown byte sequences in the stream
+ * with. The standard replacement character is U+FFFD.
+ * A value of 0x0000 will cause an exception to be thrown if unknown
+ * byte sequences are encountered in the stream.
+ */
+ void init (in nsIInputStream aStream, in string aCharset,
+ in long aBufferSize, in char16_t aReplacementChar);
+};
+
diff --git a/xpcom/io/nsIConverterOutputStream.idl b/xpcom/io/nsIConverterOutputStream.idl
new file mode 100644
index 000000000..a93d3cfa6
--- /dev/null
+++ b/xpcom/io/nsIConverterOutputStream.idl
@@ -0,0 +1,44 @@
+/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
+/* 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 "nsIUnicharOutputStream.idl"
+
+interface nsIOutputStream;
+
+/**
+ * This interface allows writing strings to a stream, doing automatic
+ * character encoding conversion.
+ */
+[scriptable, uuid(4b71113a-cb0d-479f-8ed5-01daeba2e8d4)]
+interface nsIConverterOutputStream : nsIUnicharOutputStream
+{
+ /**
+ * Initialize this stream. Must be called before any other method on this
+ * interface, or you will crash. The output stream passed to this method
+ * must not be null, or you will crash.
+ *
+ * @param aOutStream
+ * The underlying output stream to which the converted strings will
+ * be written.
+ * @param aCharset
+ * The character set to use for encoding the characters. A null
+ * charset will be interpreted as UTF-8.
+ * @param aBufferSize
+ * How many bytes to buffer. A value of 0 means that no bytes will be
+ * buffered. Implementations not supporting buffering may ignore
+ * this parameter.
+ * @param aReplacementCharacter
+ * The replacement character to use when an unsupported character is found.
+ * The character must be encodable in the selected character
+ * encoding; otherwise, attempts to write an unsupported character
+ * will throw NS_ERROR_LOSS_OF_SIGNIFICANT_DATA.
+ *
+ * A value of 0x0000 will cause an exception to be thrown upon
+ * attempts to write unsupported characters.
+ */
+ void init(in nsIOutputStream aOutStream, in string aCharset,
+ in unsigned long aBufferSize,
+ in char16_t aReplacementCharacter);
+};
diff --git a/xpcom/io/nsIDirectoryEnumerator.idl b/xpcom/io/nsIDirectoryEnumerator.idl
new file mode 100644
index 000000000..7a1135fda
--- /dev/null
+++ b/xpcom/io/nsIDirectoryEnumerator.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIFile;
+
+/**
+ * This interface provides a means for enumerating the contents of a directory.
+ * It is similar to nsISimpleEnumerator except the retrieved entries are QI'ed
+ * to nsIFile, and there is a mechanism for closing the directory when the
+ * enumeration is complete.
+ */
+[scriptable, uuid(31f7f4ae-6916-4f2d-a81e-926a4e3022ee)]
+interface nsIDirectoryEnumerator : nsISupports
+{
+ /**
+ * Retrieves the next file in the sequence. The "nextFile" element is the
+ * first element upon the first call. This attribute is null if there is no
+ * next element.
+ */
+ readonly attribute nsIFile nextFile;
+
+ /**
+ * Closes the directory being enumerated, releasing the system resource.
+ * @throws NS_OK if the call succeeded and the directory was closed.
+ * NS_ERROR_FAILURE if the directory close failed.
+ * It is safe to call this function many times.
+ */
+ void close();
+};
+
diff --git a/xpcom/io/nsIDirectoryService.idl b/xpcom/io/nsIDirectoryService.idl
new file mode 100644
index 000000000..6f58e37b9
--- /dev/null
+++ b/xpcom/io/nsIDirectoryService.idl
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIFile;
+interface nsISimpleEnumerator;
+
+/**
+ * nsIDirectoryServiceProvider
+ *
+ * Used by Directory Service to get file locations.
+ */
+
+[scriptable, uuid(bbf8cab0-d43a-11d3-8cc2-00609792278c)]
+interface nsIDirectoryServiceProvider: nsISupports
+{
+ /**
+ * getFile
+ *
+ * Directory Service calls this when it gets the first request for
+ * a prop or on every request if the prop is not persistent.
+ *
+ * @param prop The symbolic name of the file.
+ * @param persistent TRUE - The returned file will be cached by Directory
+ * Service. Subsequent requests for this prop will
+ * bypass the provider and use the cache.
+ * FALSE - The provider will be asked for this prop
+ * each time it is requested.
+ *
+ * @return The file represented by the property.
+ *
+ */
+ nsIFile getFile(in string prop, out boolean persistent);
+};
+
+/**
+ * nsIDirectoryServiceProvider2
+ *
+ * An extension of nsIDirectoryServiceProvider which allows
+ * multiple files to be returned for the given key.
+ */
+
+[scriptable, uuid(2f977d4b-5485-11d4-87e2-0010a4e75ef2)]
+interface nsIDirectoryServiceProvider2: nsIDirectoryServiceProvider
+{
+ /**
+ * getFiles
+ *
+ * Directory Service calls this when it gets a request for
+ * a prop and the requested type is nsISimpleEnumerator.
+ *
+ * @param prop The symbolic name of the file list.
+ *
+ * @return An enumerator for a list of file locations.
+ * The elements in the enumeration are nsIFile
+ * @returnCode NS_SUCCESS_AGGREGATE_RESULT if this result should be
+ * aggregated with other "lower" providers.
+ */
+ nsISimpleEnumerator getFiles(in string prop);
+};
+
+/**
+ * nsIDirectoryService
+ */
+
+[scriptable, uuid(57a66a60-d43a-11d3-8cc2-00609792278c)]
+interface nsIDirectoryService: nsISupports
+{
+ /**
+ * init
+ *
+ * Must be called. Used internally by XPCOM initialization.
+ *
+ */
+ void init();
+
+ /**
+ * registerProvider
+ *
+ * Register a provider with the service.
+ *
+ * @param prov The service will keep a strong reference
+ * to this object. It will be released when
+ * the service is released.
+ *
+ */
+ void registerProvider(in nsIDirectoryServiceProvider prov);
+
+ /**
+ * unregisterProvider
+ *
+ * Unregister a provider with the service.
+ *
+ * @param prov
+ *
+ */
+ void unregisterProvider(in nsIDirectoryServiceProvider prov);
+};
+
+
diff --git a/xpcom/io/nsIFile.idl b/xpcom/io/nsIFile.idl
new file mode 100644
index 000000000..fc07106b9
--- /dev/null
+++ b/xpcom/io/nsIFile.idl
@@ -0,0 +1,521 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+%{C++
+struct PRFileDesc;
+struct PRLibrary;
+#include <stdio.h>
+%}
+
+[ptr] native PRFileDescStar(PRFileDesc);
+[ptr] native PRLibraryStar(PRLibrary);
+[ptr] native FILE(FILE);
+
+interface nsISimpleEnumerator;
+
+/**
+ * An nsIFile is an abstract representation of a filename. It manages
+ * filename encoding issues, pathname component separators ('/' vs. '\\'
+ * vs. ':') and weird stuff like differing volumes with identical names, as
+ * on pre-Darwin Macintoshes.
+ *
+ * This file has long introduced itself to new hackers with this opening
+ * paragraph:
+ *
+ * This is the only correct cross-platform way to specify a file.
+ * Strings are not such a way. If you grew up on windows or unix, you
+ * may think they are. Welcome to reality.
+ *
+ * While taking the pose struck here to heart would be uncalled for, one
+ * may safely conclude that writing cross-platform code is an embittering
+ * experience.
+ *
+ * All methods with string parameters have two forms. The preferred
+ * form operates on UCS-2 encoded characters strings. An alternate
+ * form operates on characters strings encoded in the "native" charset.
+ *
+ * A string containing characters encoded in the native charset cannot
+ * be safely passed to javascript via xpconnect. Therefore, the "native
+ * methods" are not scriptable.
+ */
+[scriptable, main_process_scriptable_only, uuid(2fa6884a-ae65-412a-9d4c-ce6e34544ba1), builtinclass]
+interface nsIFile : nsISupports
+{
+ /**
+ * Create Types
+ *
+ * NORMAL_FILE_TYPE - A normal file.
+ * DIRECTORY_TYPE - A directory/folder.
+ */
+ const unsigned long NORMAL_FILE_TYPE = 0;
+ const unsigned long DIRECTORY_TYPE = 1;
+
+ /**
+ * append[Native]
+ *
+ * This function is used for constructing a descendent of the
+ * current nsIFile.
+ *
+ * @param node
+ * A string which is intended to be a child node of the nsIFile.
+ * For the |appendNative| method, the node must be in the native
+ * filesystem charset.
+ */
+ void append(in AString node);
+ [noscript] void appendNative(in ACString node);
+
+ /**
+ * Normalize the pathName (e.g. removing .. and . components on Unix).
+ */
+ void normalize();
+
+ /**
+ * create
+ *
+ * This function will create a new file or directory in the
+ * file system. Any nodes that have not been created or
+ * resolved, will be. If the file or directory already
+ * exists create() will return NS_ERROR_FILE_ALREADY_EXISTS.
+ *
+ * @param type
+ * This specifies the type of file system object
+ * to be made. The only two types at this time
+ * are file and directory which are defined above.
+ * If the type is unrecongnized, we will return an
+ * error (NS_ERROR_FILE_UNKNOWN_TYPE).
+ *
+ * @param permissions
+ * The unix style octal permissions. This may
+ * be ignored on systems that do not need to do
+ * permissions.
+ */
+ [must_use] void create(in unsigned long type, in unsigned long permissions);
+
+ /**
+ * Accessor to the leaf name of the file itself.
+ * For the |nativeLeafName| method, the nativeLeafName must
+ * be in the native filesystem charset.
+ */
+ attribute AString leafName;
+ [noscript] attribute ACString nativeLeafName;
+
+ /**
+ * copyTo[Native]
+ *
+ * This will copy this file to the specified newParentDir.
+ * If a newName is specified, the file will be renamed.
+ * If 'this' is not created we will return an error
+ * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
+ *
+ * copyTo may fail if the file already exists in the destination
+ * directory.
+ *
+ * copyTo will NOT resolve aliases/shortcuts during the copy.
+ *
+ * @param newParentDir
+ * This param is the destination directory. If the
+ * newParentDir is null, copyTo() will use the parent
+ * directory of this file. If the newParentDir is not
+ * empty and is not a directory, an error will be
+ * returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For the
+ * |CopyToNative| method, the newName must be in the
+ * native filesystem charset.
+ *
+ * @param newName
+ * This param allows you to specify a new name for
+ * the file to be copied. This param may be empty, in
+ * which case the current leaf name will be used.
+ */
+ void copyTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void CopyToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * copyToFollowingLinks[Native]
+ *
+ * This function is identical to copyTo with the exception that,
+ * as the name implies, it follows symbolic links. The XP_UNIX
+ * implementation always follow symbolic links when copying. For
+ * the |CopyToFollowingLinks| method, the newName must be in the
+ * native filesystem charset.
+ */
+ void copyToFollowingLinks(in nsIFile newParentDir, in AString newName);
+ [noscript] void copyToFollowingLinksNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * moveTo[Native]
+ *
+ * A method to move this file or directory to newParentDir.
+ * If a newName is specified, the file or directory will be renamed.
+ * If 'this' is not created we will return an error
+ * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
+ * If 'this' is a file, and the destination file already exists, moveTo
+ * will replace the old file.
+ * This object is updated to refer to the new file.
+ *
+ * moveTo will NOT resolve aliases/shortcuts during the copy.
+ * moveTo will do the right thing and allow copies across volumes.
+ * moveTo will return an error (NS_ERROR_FILE_DIR_NOT_EMPTY) if 'this' is
+ * a directory and the destination directory is not empty.
+ * moveTo will return an error (NS_ERROR_FILE_ACCESS_DENIED) if 'this' is
+ * a directory and the destination directory is not writable.
+ *
+ * @param newParentDir
+ * This param is the destination directory. If the
+ * newParentDir is empty, moveTo() will rename the file
+ * within its current directory. If the newParentDir is
+ * not empty and does not name a directory, an error will
+ * be returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For
+ * the |moveToNative| method, the newName must be in the
+ * native filesystem charset.
+ *
+ * @param newName
+ * This param allows you to specify a new name for
+ * the file to be moved. This param may be empty, in
+ * which case the current leaf name will be used.
+ */
+ void moveTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void moveToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * renameTo
+ *
+ * This method is identical to moveTo except that if this file or directory
+ * is moved to a a different volume, it fails and returns an error
+ * (NS_ERROR_FILE_ACCESS_DENIED).
+ * This object will still point to the old location after renaming.
+ */
+ void renameTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void renameToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * This will try to delete this file. The 'recursive' flag
+ * must be PR_TRUE to delete directories which are not empty.
+ *
+ * This will not resolve any symlinks.
+ */
+ void remove(in boolean recursive);
+
+ /**
+ * Attributes of nsIFile.
+ */
+
+ attribute unsigned long permissions;
+ attribute unsigned long permissionsOfLink;
+
+ /**
+ * File Times are to be in milliseconds from
+ * midnight (00:00:00), January 1, 1970 Greenwich Mean
+ * Time (GMT).
+ */
+ attribute PRTime lastModifiedTime;
+ attribute PRTime lastModifiedTimeOfLink;
+
+ /**
+ * WARNING! On the Mac, getting/setting the file size with nsIFile
+ * only deals with the size of the data fork. If you need to
+ * know the size of the combined data and resource forks use the
+ * GetFileSizeWithResFork() method defined on nsILocalFileMac.
+ */
+ attribute int64_t fileSize;
+ readonly attribute int64_t fileSizeOfLink;
+
+ /**
+ * target & path
+ *
+ * Accessor to the string path. The native version of these
+ * strings are not guaranteed to be a usable path to pass to
+ * NSPR or the C stdlib. There are problems that affect
+ * platforms on which a path does not fully specify a file
+ * because two volumes can have the same name (e.g., mac).
+ * This is solved by holding "private", native data in the
+ * nsIFile implementation. This native data is lost when
+ * you convert to a string.
+ *
+ * DO NOT PASS TO USE WITH NSPR OR STDLIB!
+ *
+ * target
+ * Find out what the symlink points at. Will give error
+ * (NS_ERROR_FILE_INVALID_PATH) if not a symlink.
+ *
+ * path
+ * Find out what the nsIFile points at.
+ *
+ * Note that the ACString attributes are returned in the
+ * native filesystem charset.
+ *
+ */
+ readonly attribute AString target;
+ [noscript] readonly attribute ACString nativeTarget;
+ readonly attribute AString path;
+ [noscript] readonly attribute ACString nativePath;
+
+ boolean exists();
+ boolean isWritable();
+ boolean isReadable();
+ boolean isExecutable();
+ boolean isHidden();
+ boolean isDirectory();
+ boolean isFile();
+ boolean isSymlink();
+ /**
+ * Not a regular file, not a directory, not a symlink.
+ */
+ boolean isSpecial();
+
+ /**
+ * createUnique
+ *
+ * This function will create a new file or directory in the
+ * file system. Any nodes that have not been created or
+ * resolved, will be. If this file already exists, we try
+ * variations on the leaf name "suggestedName" until we find
+ * one that did not already exist.
+ *
+ * If the search for nonexistent files takes too long
+ * (thousands of the variants already exist), we give up and
+ * return NS_ERROR_FILE_TOO_BIG.
+ *
+ * @param type
+ * This specifies the type of file system object
+ * to be made. The only two types at this time
+ * are file and directory which are defined above.
+ * If the type is unrecongnized, we will return an
+ * error (NS_ERROR_FILE_UNKNOWN_TYPE).
+ *
+ * @param permissions
+ * The unix style octal permissions. This may
+ * be ignored on systems that do not need to do
+ * permissions.
+ */
+ [must_use]
+ void createUnique(in unsigned long type, in unsigned long permissions);
+
+ /**
+ * clone()
+ *
+ * This function will allocate and initialize a nsIFile object to the
+ * exact location of the |this| nsIFile.
+ *
+ * @param file
+ * A nsIFile which this object will be initialize
+ * with.
+ *
+ */
+ nsIFile clone();
+
+ /**
+ * Will determine if the inFile equals this.
+ */
+ boolean equals(in nsIFile inFile);
+
+ /**
+ * Will determine if inFile is a descendant of this file.
+ * This routine looks in subdirectories too.
+ */
+ boolean contains(in nsIFile inFile);
+
+ /**
+ * Parent will be null when this is at the top of the volume.
+ */
+ readonly attribute nsIFile parent;
+
+ /**
+ * Returns an enumeration of the elements in a directory. Each
+ * element in the enumeration is an nsIFile.
+ *
+ * @throws NS_ERROR_FILE_NOT_DIRECTORY if the current nsIFile does
+ * not specify a directory.
+ */
+ readonly attribute nsISimpleEnumerator directoryEntries;
+
+ /**
+ * initWith[Native]Path
+ *
+ * This function will initialize the nsIFile object. Any
+ * internal state information will be reset.
+ *
+ * @param filePath
+ * A string which specifies a full file path to a
+ * location. Relative paths will be treated as an
+ * error (NS_ERROR_FILE_UNRECOGNIZED_PATH). For
+ * initWithNativePath, the filePath must be in the native
+ * filesystem charset.
+ */
+ void initWithPath(in AString filePath);
+ [noscript] void initWithNativePath(in ACString filePath);
+
+ /**
+ * initWithFile
+ *
+ * Initialize this object with another file
+ *
+ * @param aFile
+ * the file this becomes equivalent to
+ */
+ void initWithFile(in nsIFile aFile);
+
+ /**
+ * followLinks
+ *
+ * This attribute will determine if the nsLocalFile will auto
+ * resolve symbolic links. By default, this value will be false
+ * on all non unix systems. On unix, this attribute is effectively
+ * a noop.
+ */
+ attribute boolean followLinks;
+
+ /**
+ * Flag for openNSPRFileDesc(), to hint to the OS that the file will be
+ * read sequentially with agressive readahead.
+ */
+ const unsigned long OS_READAHEAD = 0x40000000;
+
+ /**
+ * Flag for openNSPRFileDesc(). Deprecated and unreliable!
+ * Instead use NS_OpenAnonymousTemporaryFile() to create a temporary
+ * file which will be deleted upon close!
+ */
+ const unsigned long DELETE_ON_CLOSE = 0x80000000;
+
+ /**
+ * Return the result of PR_Open on the file. The caller is
+ * responsible for calling PR_Close on the result. On success, the
+ * returned PRFileDescr must be non-null.
+ *
+ * @param flags the PR_Open flags from prio.h, plus optionally
+ * OS_READAHEAD or DELETE_ON_CLOSE. OS_READAHEAD is a hint to the
+ * OS that the file will be read sequentially with agressive
+ * readahead. DELETE_ON_CLOSE is unreliable on Windows and is deprecated.
+ * Instead use NS_OpenAnonymousTemporaryFile() to create a temporary
+ * file which will be deleted upon close.
+ */
+ [noscript, must_use] PRFileDescStar openNSPRFileDesc(in long flags,
+ in long mode);
+
+ /**
+ * Return the result of fopen on the file. The caller is
+ * responsible for calling fclose on the result. On success, the
+ * returned FILE pointer must be non-null.
+ */
+ [noscript, must_use] FILE openANSIFileDesc(in string mode);
+
+ /**
+ * Return the result of PR_LoadLibrary on the file. The caller is
+ * responsible for calling PR_UnloadLibrary on the result.
+ */
+ [noscript, must_use] PRLibraryStar load();
+
+ // number of bytes available on disk to non-superuser
+ [must_use] readonly attribute int64_t diskSpaceAvailable;
+
+ /**
+ * appendRelative[Native]Path
+ *
+ * Append a relative path to the current path of the nsIFile object.
+ *
+ * @param relativeFilePath
+ * relativeFilePath is a native relative path. For security reasons,
+ * this cannot contain .. or cannot start with a directory separator.
+ * For the |appendRelativeNativePath| method, the relativeFilePath
+ * must be in the native filesystem charset.
+ */
+ void appendRelativePath(in AString relativeFilePath);
+ [noscript] void appendRelativeNativePath(in ACString relativeFilePath);
+
+ /**
+ * Accessor to a null terminated string which will specify
+ * the file in a persistent manner for disk storage.
+ *
+ * The character set of this attribute is undefined. DO NOT TRY TO
+ * INTERPRET IT AS HUMAN READABLE TEXT!
+ */
+ [must_use] attribute ACString persistentDescriptor;
+
+ /**
+ * reveal
+ *
+ * Ask the operating system to open the folder which contains
+ * this file or folder. This routine only works on platforms which
+ * support the ability to open a folder and is run async on Windows.
+ * This routine must be called on the main.
+ */
+ [must_use] void reveal();
+
+ /**
+ * launch
+ *
+ * Ask the operating system to attempt to open the file.
+ * this really just simulates "double clicking" the file on your platform.
+ * This routine only works on platforms which support this functionality
+ * and is run async on Windows. This routine must be called on the
+ * main thread.
+ */
+ [must_use] void launch();
+
+ /**
+ * getRelativeDescriptor
+ *
+ * Returns a relative file path in an opaque, XP format. It is therefore
+ * not a native path.
+ *
+ * The character set of the string returned from this function is
+ * undefined. DO NOT TRY TO INTERPRET IT AS HUMAN READABLE TEXT!
+ *
+ * @param fromFile
+ * the file from which the descriptor is relative.
+ * Throws if fromFile is null.
+ */
+ [must_use] ACString getRelativeDescriptor(in nsIFile fromFile);
+
+ /**
+ * setRelativeDescriptor
+ *
+ * Initializes the file to the location relative to fromFile using
+ * a string returned by getRelativeDescriptor.
+ *
+ * @param fromFile
+ * the file to which the descriptor is relative
+ * @param relative
+ * the relative descriptor obtained from getRelativeDescriptor
+ */
+ [must_use]
+ void setRelativeDescriptor(in nsIFile fromFile, in ACString relativeDesc);
+
+ /**
+ * getRelativePath
+ *
+ * Returns a relative file from 'fromFile' to this file as a UTF-8 string.
+ * Going up the directory tree is represented via "../". '/' is used as
+ * the path segment separator. This is not a native path, since it's UTF-8
+ * encoded.
+ *
+ * @param fromFile
+ * the file from which the path is relative.
+ * Throws if fromFile is null.
+ */
+ [must_use] AUTF8String getRelativePath(in nsIFile fromFile);
+
+ /**
+ * setRelativePath
+ *
+ * Initializes the file to the location relative to fromFile using
+ * a string returned by getRelativePath.
+ *
+ * @param fromFile
+ * the file from which the path is relative
+ * @param relative
+ * the relative path obtained from getRelativePath
+ */
+ [must_use]
+ void setRelativePath(in nsIFile fromFile, in AUTF8String relativeDesc);
+};
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+#include "nsDirectoryServiceUtils.h"
+#endif
+%}
diff --git a/xpcom/io/nsIIOUtil.idl b/xpcom/io/nsIIOUtil.idl
new file mode 100644
index 000000000..577ba4046
--- /dev/null
+++ b/xpcom/io/nsIIOUtil.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIInputStream;
+interface nsIOutputStream;
+
+/**
+ * nsIIOUtil provdes various xpcom/io-related utility methods.
+ */
+[scriptable, uuid(e8152f7f-4209-4c63-ad23-c3d2aa0c5a49)]
+interface nsIIOUtil : nsISupports
+{
+ /**
+ * Test whether an input stream is buffered. See nsStreamUtils.h
+ * documentation for NS_InputStreamIsBuffered for the definition of
+ * "buffered" used here and for edge-case behavior.
+ *
+ * @throws NS_ERROR_INVALID_POINTER if null is passed in.
+ */
+ boolean inputStreamIsBuffered(in nsIInputStream aStream);
+
+ /**
+ * Test whether an output stream is buffered. See nsStreamUtils.h
+ * documentation for NS_OutputStreamIsBuffered for the definition of
+ * "buffered" used here and for edge-case behavior.
+ *
+ * @throws NS_ERROR_INVALID_POINTER if null is passed in.
+ */
+ boolean outputStreamIsBuffered(in nsIOutputStream aStream);
+};
diff --git a/xpcom/io/nsIInputStream.idl b/xpcom/io/nsIInputStream.idl
new file mode 100644
index 000000000..b2f165274
--- /dev/null
+++ b/xpcom/io/nsIInputStream.idl
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature of the writer function passed to ReadSegments. This
+ * is the "consumer" of data that gets read from the stream's buffer.
+ *
+ * @param aInStream stream being read
+ * @param aClosure opaque parameter passed to ReadSegments
+ * @param aFromSegment pointer to memory owned by the input stream. This is
+ * where the writer function should start consuming data.
+ * @param aToOffset amount of data already consumed by this writer during this
+ * ReadSegments call. This is also the sum of the aWriteCount
+ * returns from this writer over the previous invocations of
+ * the writer by this ReadSegments call.
+ * @param aCount Number of bytes available to be read starting at aFromSegment
+ * @param [out] aWriteCount number of bytes read by this writer function call
+ *
+ * Implementers should return the following:
+ *
+ * @return NS_OK and (*aWriteCount > 0) if consumed some data
+ * @return <any-error> if not interested in consuming any data
+ *
+ * Errors are never passed to the caller of ReadSegments.
+ *
+ * NOTE: returning NS_OK and (*aWriteCount = 0) has undefined behavior.
+ */
+typedef nsresult (*nsWriteSegmentFun)(nsIInputStream *aInStream,
+ void *aClosure,
+ const char *aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount);
+%}
+
+native nsWriteSegmentFun(nsWriteSegmentFun);
+
+/**
+ * nsIInputStream
+ *
+ * An interface describing a readable stream of data. An input stream may be
+ * "blocking" or "non-blocking" (see the IsNonBlocking method). A blocking
+ * input stream may suspend the calling thread in order to satisfy a call to
+ * Close, Available, Read, or ReadSegments. A non-blocking input stream, on
+ * the other hand, must not block the calling thread of execution.
+ *
+ * NOTE: blocking input streams are often read on a background thread to avoid
+ * locking up the main application thread. For this reason, it is generally
+ * the case that a blocking input stream should be implemented using thread-
+ * safe AddRef and Release.
+ */
+[scriptable, uuid(53cdbc97-c2d7-4e30-b2c3-45b2ee79db18)]
+interface nsIInputStream : nsISupports
+{
+ /**
+ * Close the stream. This method causes subsequent calls to Read and
+ * ReadSegments to return 0 bytes read to indicate end-of-file. Any
+ * subsequent calls to Available should throw NS_BASE_STREAM_CLOSED.
+ */
+ void close();
+
+ /**
+ * Determine number of bytes available in the stream. A non-blocking
+ * stream that does not yet have any data to read should return 0 bytes
+ * from this method (i.e., it must not throw the NS_BASE_STREAM_WOULD_BLOCK
+ * exception).
+ *
+ * In addition to the number of bytes available in the stream, this method
+ * also informs the caller of the current status of the stream. A stream
+ * that is closed will throw an exception when this method is called. That
+ * enables the caller to know the condition of the stream before attempting
+ * to read from it. If a stream is at end-of-file, but not closed, then
+ * this method returns 0 bytes available. (Note: some nsIInputStream
+ * implementations automatically close when eof is reached; some do not).
+ *
+ * @return number of bytes currently available in the stream.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if the stream is closed normally.
+ * @throws <other-error> if the stream is closed due to some error
+ * condition
+ */
+ unsigned long long available();
+
+ /**
+ * Read data from the stream.
+ *
+ * @param aBuf the buffer into which the data is to be read
+ * @param aCount the maximum number of bytes to be read
+ *
+ * @return number of bytes read (may be less than aCount).
+ * @return 0 if reached end-of-file
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: this method should not throw NS_BASE_STREAM_CLOSED.
+ */
+ [noscript] unsigned long read(in charPtr aBuf, in unsigned long aCount);
+
+ /**
+ * Low-level read method that provides access to the stream's underlying
+ * buffer. The writer function may be called multiple times for segmented
+ * buffers. ReadSegments is expected to keep calling the writer until
+ * either there is nothing left to read or the writer returns an error.
+ * ReadSegments should not call the writer with zero bytes to consume.
+ *
+ * @param aWriter the "consumer" of the data to be read
+ * @param aClosure opaque parameter passed to writer
+ * @param aCount the maximum number of bytes to be read
+ *
+ * @return number of bytes read (may be less than aCount)
+ * @return 0 if reached end-of-file (or if aWriter refused to consume data)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws NS_ERROR_NOT_IMPLEMENTED if the stream has no underlying buffer
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer (e.g., socket input stream).
+ *
+ * NOTE: this method should not throw NS_BASE_STREAM_CLOSED.
+ */
+ [noscript] unsigned long readSegments(in nsWriteSegmentFun aWriter,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * @return true if stream is non-blocking
+ *
+ * NOTE: reading from a blocking input stream will block the calling thread
+ * until at least one byte of data can be extracted from the stream.
+ *
+ * NOTE: a non-blocking input stream may implement nsIAsyncInputStream to
+ * provide consumers with a way to wait for the stream to have more data
+ * once its read method is unable to return any data without blocking.
+ */
+ boolean isNonBlocking();
+};
diff --git a/xpcom/io/nsIInputStreamTee.idl b/xpcom/io/nsIInputStreamTee.idl
new file mode 100644
index 000000000..953be7a7c
--- /dev/null
+++ b/xpcom/io/nsIInputStreamTee.idl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIInputStream.idl"
+
+interface nsIOutputStream;
+interface nsIEventTarget;
+
+/**
+ * A nsIInputStreamTee is a wrapper for an input stream, that when read
+ * reads the specified amount of data from its |source| and copies that
+ * data to its |sink|. |sink| must be a blocking output stream.
+ */
+[scriptable, uuid(90a9d790-3bca-421e-a73b-98f68e13c917)]
+interface nsIInputStreamTee : nsIInputStream
+{
+ attribute nsIInputStream source;
+ attribute nsIOutputStream sink;
+
+ /**
+ * If |eventTarget| is set, copying to sink is done asynchronously using
+ * the event-target (e.g. a thread). If |eventTarget| is not set, copying
+ * to sink happens synchronously while reading from the source.
+ */
+ attribute nsIEventTarget eventTarget;
+};
+
+%{C++
+// factory methods
+extern nsresult
+NS_NewInputStreamTee(nsIInputStream **tee, // read from this input stream
+ nsIInputStream *source,
+ nsIOutputStream *sink);
+
+extern nsresult
+NS_NewInputStreamTeeAsync(nsIInputStream **tee, // read from this input stream
+ nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *eventTarget);
+%}
diff --git a/xpcom/io/nsILineInputStream.idl b/xpcom/io/nsILineInputStream.idl
new file mode 100644
index 000000000..4a8eff42b
--- /dev/null
+++ b/xpcom/io/nsILineInputStream.idl
@@ -0,0 +1,26 @@
+/* -*- 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 "nsISupports.idl"
+
+[scriptable, uuid(c97b466c-1e6e-4773-a4ab-2b2b3190a7a6)]
+interface nsILineInputStream : nsISupports
+{
+ /**
+ * Read a single line from the stream, where a line is a
+ * possibly zero length sequence of 8bit chars terminated by a
+ * CR, LF, CRLF, LFCR, or eof.
+ * The line terminator is not returned.
+ * @retval false
+ * End of file. This line is the last line of the file
+ * (aLine is valid).
+ * @retval true
+ * The file contains further lines.
+ * @note Do not mix readLine with other read functions.
+ * Doing so can cause various problems and is not supported.
+ */
+ boolean readLine(out ACString aLine);
+};
diff --git a/xpcom/io/nsILocalFile.idl b/xpcom/io/nsILocalFile.idl
new file mode 100644
index 000000000..cead3e3f4
--- /dev/null
+++ b/xpcom/io/nsILocalFile.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIFile.idl"
+
+/**
+ * An empty interface to provide backwards compatibility for existing code.
+ *
+ * @see nsIFile
+ */
+[scriptable, builtinclass, uuid(7ba8c6ba-2ce2-48b1-bd60-4c32aac35f9c)]
+interface nsILocalFile : nsIFile
+{
+};
+
diff --git a/xpcom/io/nsILocalFileMac.idl b/xpcom/io/nsILocalFileMac.idl
new file mode 100644
index 000000000..d8655449b
--- /dev/null
+++ b/xpcom/io/nsILocalFileMac.idl
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsILocalFile.idl"
+
+%{C++
+#include <Carbon/Carbon.h>
+#include <CoreFoundation/CoreFoundation.h>
+%}
+
+ native OSType(OSType);
+ native FSSpec(FSSpec);
+ native FSRef(FSRef);
+[ptr] native FSRefPtr(FSRef);
+ native CFURLRef(CFURLRef);
+
+[scriptable, builtinclass, uuid(623eca5b-c25d-4e27-be5a-789a66c4b2f7)]
+interface nsILocalFileMac : nsILocalFile
+{
+ /**
+ * initWithCFURL
+ *
+ * Init this object with a CFURLRef
+ *
+ * NOTE: Supported only for XP_MACOSX
+ * NOTE: If the path of the CFURL is /a/b/c, at least a/b must exist beforehand.
+ *
+ * @param aCFURL the CoreFoundation URL
+ *
+ */
+ [noscript] void initWithCFURL(in CFURLRef aCFURL);
+
+ /**
+ * initWithFSRef
+ *
+ * Init this object with an FSRef
+ *
+ * NOTE: Supported only for XP_MACOSX
+ *
+ * @param aFSRef the native FSRef
+ *
+ */
+ [noscript] void initWithFSRef([const] in FSRefPtr aFSRef);
+
+ /**
+ * getCFURL
+ *
+ * Returns the CFURLRef of the file object. The caller is
+ * responsible for calling CFRelease() on it.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * NOTE: Supported only for XP_MACOSX
+ *
+ * @return
+ *
+ */
+ [noscript] CFURLRef getCFURL();
+
+ /**
+ * getFSRef
+ *
+ * Returns the FSRef of the file object.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * NOTE: Supported only for XP_MACOSX
+ *
+ * @return
+ *
+ */
+ [noscript] FSRef getFSRef();
+
+ /**
+ * getFSSpec
+ *
+ * Returns the FSSpec of the file object.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * @return
+ *
+ */
+ [noscript] FSSpec getFSSpec();
+
+ /**
+ * fileSizeWithResFork
+ *
+ * Returns the combined size of both the data fork and the resource
+ * fork (if present) rather than just the size of the data fork
+ * as returned by GetFileSize()
+ *
+ */
+ readonly attribute int64_t fileSizeWithResFork;
+
+ /**
+ * fileType, creator
+ *
+ * File type and creator attributes
+ *
+ */
+ [noscript] attribute OSType fileType;
+ [noscript] attribute OSType fileCreator;
+
+ /**
+ * launchWithDoc
+ *
+ * Launch the application that this file points to with a document.
+ *
+ * @param aDocToLoad Must not be NULL. If no document, use nsIFile::launch
+ * @param aLaunchInBackground TRUE if the application should not come to the front.
+ *
+ */
+ void launchWithDoc(in nsIFile aDocToLoad, in boolean aLaunchInBackground);
+
+ /**
+ * openDocWithApp
+ *
+ * Open the document that this file points to with the given application.
+ *
+ * @param aAppToOpenWith The application with which to open the document.
+ * If NULL, the creator code of the document is used
+ * to determine the application.
+ * @param aLaunchInBackground TRUE if the application should not come to the front.
+ *
+ */
+ void openDocWithApp(in nsIFile aAppToOpenWith, in boolean aLaunchInBackground);
+
+ /**
+ * isPackage
+ *
+ * returns true if a directory is determined to be a package under Mac OS 9/X
+ *
+ */
+ boolean isPackage();
+
+ /**
+ * bundleDisplayName
+ *
+ * returns the display name of the application bundle (usually the human
+ * readable name of the application)
+ */
+ readonly attribute AString bundleDisplayName;
+
+ /**
+ * bundleIdentifier
+ *
+ * returns the identifier of the bundle
+ */
+ readonly attribute AUTF8String bundleIdentifier;
+
+ /**
+ * Last modified time of a bundle's contents (as opposed to its package
+ * directory). Our convention is to make the bundle's Info.plist file
+ * stand in for the rest of its contents -- since this file contains the
+ * bundle's version information and other identifiers. For non-bundles
+ * this is the same as lastModifiedTime.
+ */
+ readonly attribute int64_t bundleContentsLastModifiedTime;
+};
+
+%{C++
+extern "C"
+{
+NS_EXPORT nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowSymlinks, nsILocalFileMac** result);
+NS_EXPORT nsresult NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowSymlinks, nsILocalFileMac** result);
+}
+%}
diff --git a/xpcom/io/nsILocalFileWin.idl b/xpcom/io/nsILocalFileWin.idl
new file mode 100644
index 000000000..c036cb96b
--- /dev/null
+++ b/xpcom/io/nsILocalFileWin.idl
@@ -0,0 +1,121 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsILocalFile.idl"
+
+%{C++
+struct PRFileDesc;
+%}
+
+[ptr] native PRFileDescStar(PRFileDesc);
+
+[scriptable, builtinclass, uuid(e7a3a954-384b-4aeb-a5f7-55626b0de9be)]
+interface nsILocalFileWin : nsILocalFile
+{
+ /**
+ * initWithCommandLine
+ *
+ * Initialize this object based on the main app path of a commandline
+ * handler.
+ *
+ * @param aCommandLine
+ * the commandline to parse an app path out of.
+ */
+ void initWithCommandLine(in AString aCommandLine);
+ /**
+ * getVersionInfoValue
+ *
+ * Retrieve a metadata field from the file's VERSIONINFO block.
+ * Throws NS_ERROR_FAILURE if no value is found, or the value is empty.
+ *
+ * @param aField The field to look up.
+ *
+ */
+ AString getVersionInfoField(in string aField);
+
+ /**
+ * The canonical path of the file, which avoids short/long
+ * pathname inconsistencies. The nsIFile persistent
+ * descriptor is not guaranteed to be canonicalized (it may
+ * persist either the long or the short path name). The format of
+ * the canonical path will vary with the underlying file system:
+ * it will typically be the short pathname on filesystems that
+ * support both short and long path forms.
+ */
+ readonly attribute AString canonicalPath;
+ [noscript] readonly attribute ACString nativeCanonicalPath;
+
+ /**
+ * Windows specific file attributes.
+ */
+
+ /*
+ * WFA_SEARCH_INDEXED: Generally the default on files in Windows except
+ * those created in temp locations. Valid on XP and up. When set the
+ * file or directory is marked to be indexed by desktop search services.
+ */
+ const unsigned long WFA_SEARCH_INDEXED = 1;
+
+ /*
+ * WFA_READONLY: Whether the file is readonly or not.
+ */
+ const unsigned long WFA_READONLY = 2;
+
+ /*
+ * WFA_READWRITE: Used to clear the readonly attribute.
+ */
+ const unsigned long WFA_READWRITE = 4;
+
+ /**
+ * fileAttributesWin
+ *
+ * Set or get windows specific file attributes.
+ *
+ * Throws NS_ERROR_FILE_INVALID_PATH for an invalid file.
+ * Throws NS_ERROR_FAILURE if the set or get fails.
+ */
+ attribute unsigned long fileAttributesWin;
+
+ /**
+ * setShortcut
+ *
+ * Creates the specified shortcut, or updates it if it already exists.
+ *
+ * If the shortcut is being updated (i.e. the shortcut already exists),
+ * any excluded parameters will remain unchanged in the shortcut file.
+ * For example, if you want to change the description of a specific
+ * shortcut but keep the target, working dir, args, and icon the same,
+ * pass null for those parameters and only pass in a value for the
+ * description.
+ *
+ * If the shortcut does not already exist and targetFile is not specified,
+ * setShortcut will throw NS_ERROR_FILE_TARGET_DOES_NOT_EXIST.
+ *
+ * @param targetFile the path that the shortcut should target
+ * @param workingDir the working dir that should be set for the shortcut
+ * @param args the args string that should be set for the shortcut
+ * @param description the description that should be set for the shortcut
+ * @param iconFile the file containing an icon to be used for this
+ shortcut
+ * @param iconIndex this value selects a specific icon from within
+ iconFile. If iconFile contains only one icon, this
+ value should be 0.
+ */
+ void setShortcut([optional] in nsIFile targetFile,
+ [optional] in nsIFile workingDir,
+ [optional] in wstring args,
+ [optional] in wstring description,
+ [optional] in nsIFile iconFile,
+ [optional] in long iconIndex);
+
+ /**
+ * Identical to nsIFile::openNSPRFileDesc except it also uses the
+ * FILE_SHARE_DELETE flag.
+ */
+ [noscript] PRFileDescStar openNSPRFileDescShareDelete(in long flags,
+ in long mode);
+};
+
diff --git a/xpcom/io/nsIMultiplexInputStream.idl b/xpcom/io/nsIMultiplexInputStream.idl
new file mode 100644
index 000000000..d42adcbd4
--- /dev/null
+++ b/xpcom/io/nsIMultiplexInputStream.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIInputStream.idl"
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+[scriptable, uuid(a076fd12-1dd1-11b2-b19a-d53b5dffaade)]
+interface nsIMultiplexInputStream : nsIInputStream
+{
+ /**
+ * Number of streams in this multiplex-stream
+ */
+ readonly attribute unsigned long count;
+
+ /**
+ * Appends a stream to the end of the streams. The cursor of the stream
+ * should be located at the beginning of the stream if the implementation
+ * of this nsIMultiplexInputStream also is used as an nsISeekableStream.
+ * @param stream stream to append
+ */
+ void appendStream(in nsIInputStream stream);
+
+ /**
+ * Insert a stream at specified index. If the cursor of this stream is at
+ * the beginning of the stream at index, the cursor will be placed at the
+ * beginning of the inserted stream instead.
+ * The cursor of the new stream should be located at the beginning of the
+ * stream if the implementation of this nsIMultiplexInputStream also is
+ * used as an nsISeekableStream.
+ * @param stream stream to insert
+ * @param index index to insert stream at, must be <= count
+ */
+ void insertStream(in nsIInputStream stream, in unsigned long index);
+
+ /**
+ * Remove stream at specified index. If this stream is the one currently
+ * being read the readcursor is moved to the beginning of the next
+ * stream
+ * @param index remove stream at this index, must be < count
+ */
+ void removeStream(in unsigned long index);
+
+ /**
+ * Get stream at specified index.
+ * @param index return stream at this index, must be < count
+ * @return stream at specified index
+ */
+ nsIInputStream getStream(in unsigned long index);
+};
diff --git a/xpcom/io/nsIOUtil.cpp b/xpcom/io/nsIOUtil.cpp
new file mode 100644
index 000000000..d583dd75b
--- /dev/null
+++ b/xpcom/io/nsIOUtil.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIOUtil.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsStreamUtils.h"
+
+NS_IMPL_ISUPPORTS(nsIOUtil, nsIIOUtil)
+
+NS_IMETHODIMP
+nsIOUtil::InputStreamIsBuffered(nsIInputStream* aStream, bool* aResult)
+{
+ if (NS_WARN_IF(!aStream)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = NS_InputStreamIsBuffered(aStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIOUtil::OutputStreamIsBuffered(nsIOutputStream* aStream, bool* aResult)
+{
+ if (NS_WARN_IF(!aStream)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = NS_OutputStreamIsBuffered(aStream);
+ return NS_OK;
+}
diff --git a/xpcom/io/nsIOUtil.h b/xpcom/io/nsIOUtil.h
new file mode 100644
index 000000000..47b02a02e
--- /dev/null
+++ b/xpcom/io/nsIOUtil.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIOUtil_h__
+#define nsIOUtil_h__
+
+#define NS_IOUTIL_CID \
+{ 0xeb833911, 0x4f49, 0x4623, \
+ { 0x84, 0x5f, 0xe5, 0x8a, 0x8e, 0x6d, 0xe4, 0xc2 } }
+
+
+#include "nsIIOUtil.h"
+#include "mozilla/Attributes.h"
+
+class nsIOUtil final : public nsIIOUtil
+{
+ ~nsIOUtil() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIOUTIL
+};
+
+#endif /* nsIOUtil_h__ */
diff --git a/xpcom/io/nsIObjectInputStream.idl b/xpcom/io/nsIObjectInputStream.idl
new file mode 100644
index 000000000..c482d3b89
--- /dev/null
+++ b/xpcom/io/nsIObjectInputStream.idl
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIBinaryInputStream.idl"
+
+/**
+ * @see nsIObjectOutputStream
+ * @see nsIBinaryInputStream
+ */
+
+[scriptable, uuid(6c248606-4eae-46fa-9df0-ba58502368eb)]
+interface nsIObjectInputStream : nsIBinaryInputStream
+{
+ /**
+ * Read an object from this stream to satisfy a strong or weak reference
+ * to one of its interfaces. If the interface was not along the primary
+ * inheritance chain ending in the "root" or XPCOM-identity nsISupports,
+ * readObject will QueryInterface from the deserialized object root to the
+ * correct interface, which was specified when the object was serialized.
+ *
+ * @see nsIObjectOutputStream
+ */
+ nsISupports readObject(in boolean aIsStrongRef);
+
+ [notxpcom] nsresult readID(out nsID aID);
+
+ /**
+ * Optimized deserialization support -- see nsIStreamBufferAccess.idl.
+ */
+ [notxpcom] charPtr getBuffer(in uint32_t aLength, in uint32_t aAlignMask);
+ [notxpcom] void putBuffer(in charPtr aBuffer, in uint32_t aLength);
+};
+
+%{C++
+
+inline nsresult
+NS_ReadOptionalObject(nsIObjectInputStream* aStream, bool aIsStrongRef,
+ nsISupports* *aResult)
+{
+ bool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadObject(aIsStrongRef, aResult);
+ else
+ *aResult = nullptr;
+ }
+ return rv;
+}
+
+%}
diff --git a/xpcom/io/nsIObjectOutputStream.idl b/xpcom/io/nsIObjectOutputStream.idl
new file mode 100644
index 000000000..3ef6711d4
--- /dev/null
+++ b/xpcom/io/nsIObjectOutputStream.idl
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIBinaryOutputStream.idl"
+
+/**
+ * @See nsIObjectInputStream
+ * @See nsIBinaryOutputStream
+ */
+
+[scriptable, uuid(92c898ac-5fde-4b99-87b3-5d486422094b)]
+interface nsIObjectOutputStream : nsIBinaryOutputStream
+{
+ /**
+ * Write the object whose "root" or XPCOM-identity nsISupports is aObject.
+ * The cause for writing this object is a strong or weak reference, so the
+ * aIsStrongRef argument must tell which kind of pointer is being followed
+ * here during serialization.
+ *
+ * If the object has only one strong reference in the serialization and no
+ * weak refs, use writeSingleRefObject. This is a valuable optimization:
+ * it saves space in the stream, and cycles on both ends of the process.
+ *
+ * If the reference being serialized is a pointer to an interface not on
+ * the primary inheritance chain ending in the root nsISupports, you must
+ * call writeCompoundObject instead of this method.
+ */
+ void writeObject(in nsISupports aObject, in boolean aIsStrongRef);
+
+ /**
+ * Write an object referenced singly and strongly via its root nsISupports
+ * or a subclass of its root nsISupports. There must not be other refs to
+ * aObject in memory, or in the serialization.
+ */
+ void writeSingleRefObject(in nsISupports aObject);
+
+ /**
+ * Write the object referenced by an interface pointer at aObject that
+ * inherits from a non-primary nsISupports, i.e., a reference to one of
+ * the multiply inherited interfaces derived from an nsISupports other
+ * than the root or XPCOM-identity nsISupports; or a reference to an
+ * inner object in the case of true XPCOM aggregation. aIID identifies
+ * this interface.
+ */
+ void writeCompoundObject(in nsISupports aObject,
+ in nsIIDRef aIID,
+ in boolean aIsStrongRef);
+
+ void writeID(in nsIDRef aID);
+
+ /**
+ * Optimized serialization support -- see nsIStreamBufferAccess.idl.
+ */
+ [notxpcom] charPtr getBuffer(in uint32_t aLength, in uint32_t aAlignMask);
+ [notxpcom] void putBuffer(in charPtr aBuffer, in uint32_t aLength);
+};
+
+%{C++
+
+inline nsresult
+NS_WriteOptionalObject(nsIObjectOutputStream* aStream, nsISupports* aObject,
+ bool aIsStrongRef)
+{
+ bool nonnull = (aObject != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteObject(aObject, aIsStrongRef);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalSingleRefObject(nsIObjectOutputStream* aStream,
+ nsISupports* aObject)
+{
+ bool nonnull = (aObject != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteSingleRefObject(aObject);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalCompoundObject(nsIObjectOutputStream* aStream,
+ nsISupports* aObject,
+ const nsIID& aIID,
+ bool aIsStrongRef)
+{
+ bool nonnull = (aObject != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
+ return rv;
+}
+
+%}
diff --git a/xpcom/io/nsIOutputStream.idl b/xpcom/io/nsIOutputStream.idl
new file mode 100644
index 000000000..0e04a3910
--- /dev/null
+++ b/xpcom/io/nsIOutputStream.idl
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIOutputStream;
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature for the reader function passed to WriteSegments. This
+ * is the "provider" of data that gets written into the stream's buffer.
+ *
+ * @param aOutStream stream being written to
+ * @param aClosure opaque parameter passed to WriteSegments
+ * @param aToSegment pointer to memory owned by the output stream
+ * @param aFromOffset amount already written (since WriteSegments was called)
+ * @param aCount length of toSegment
+ * @param aReadCount number of bytes written
+ *
+ * Implementers should return the following:
+ *
+ * @throws <any-error> if not interested in providing any data
+ *
+ * Errors are never passed to the caller of WriteSegments.
+ */
+typedef nsresult (*nsReadSegmentFun)(nsIOutputStream *aOutStream,
+ void *aClosure,
+ char *aToSegment,
+ uint32_t aFromOffset,
+ uint32_t aCount,
+ uint32_t *aReadCount);
+%}
+
+native nsReadSegmentFun(nsReadSegmentFun);
+
+/**
+ * nsIOutputStream
+ *
+ * An interface describing a writable stream of data. An output stream may be
+ * "blocking" or "non-blocking" (see the IsNonBlocking method). A blocking
+ * output stream may suspend the calling thread in order to satisfy a call to
+ * Close, Flush, Write, WriteFrom, or WriteSegments. A non-blocking output
+ * stream, on the other hand, must not block the calling thread of execution.
+ *
+ * NOTE: blocking output streams are often written to on a background thread to
+ * avoid locking up the main application thread. For this reason, it is
+ * generally the case that a blocking output stream should be implemented using
+ * thread- safe AddRef and Release.
+ */
+[scriptable, uuid(0d0acd2a-61b4-11d4-9877-00c04fa0cf4a)]
+interface nsIOutputStream : nsISupports
+{
+ /**
+ * Close the stream. Forces the output stream to flush any buffered data.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if unable to flush without blocking
+ * the calling thread (non-blocking mode only)
+ */
+ void close();
+
+ /**
+ * Flush the stream.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if unable to flush without blocking
+ * the calling thread (non-blocking mode only)
+ */
+ void flush();
+
+ /**
+ * Write data into the stream.
+ *
+ * @param aBuf the buffer containing the data to be written
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ */
+ unsigned long write(in string aBuf, in unsigned long aCount);
+
+ /**
+ * Writes data into the stream from an input stream.
+ *
+ * @param aFromStream the stream containing the data to be written
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only). This failure
+ * means no bytes were transferred.
+ * @throws <other-error> on failure
+ *
+ * NOTE: This method is defined by this interface in order to allow the
+ * output stream to efficiently copy the data from the input stream into
+ * its internal buffer (if any). If this method was provided as an external
+ * facility, a separate char* buffer would need to be used in order to call
+ * the output stream's other Write method.
+ */
+ unsigned long writeFrom(in nsIInputStream aFromStream,
+ in unsigned long aCount);
+
+ /**
+ * Low-level write method that has access to the stream's underlying buffer.
+ * The reader function may be called multiple times for segmented buffers.
+ * WriteSegments is expected to keep calling the reader until either there
+ * is nothing left to write or the reader returns an error. WriteSegments
+ * should not call the reader with zero bytes to provide.
+ *
+ * @param aReader the "provider" of the data to be written
+ * @param aClosure opaque parameter passed to reader
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only). This failure
+ * means no bytes were transferred.
+ * @throws NS_ERROR_NOT_IMPLEMENTED if the stream has no underlying buffer
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer (e.g., socket output stream).
+ */
+ [noscript] unsigned long writeSegments(in nsReadSegmentFun aReader,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * @return true if stream is non-blocking
+ *
+ * NOTE: writing to a blocking output stream will block the calling thread
+ * until all given data can be consumed by the stream.
+ *
+ * NOTE: a non-blocking output stream may implement nsIAsyncOutputStream to
+ * provide consumers with a way to wait for the stream to accept more data
+ * once its write method is unable to accept any data without blocking.
+ */
+ boolean isNonBlocking();
+};
diff --git a/xpcom/io/nsIPipe.idl b/xpcom/io/nsIPipe.idl
new file mode 100644
index 000000000..596be92e9
--- /dev/null
+++ b/xpcom/io/nsIPipe.idl
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIAsyncInputStream;
+interface nsIAsyncOutputStream;
+
+/**
+ * nsIPipe represents an in-process buffer that can be read using nsIInputStream
+ * and written using nsIOutputStream. The reader and writer of a pipe do not
+ * have to be on the same thread. As a result, the pipe is an ideal mechanism
+ * to bridge data exchange between two threads. For example, a worker thread
+ * might write data to a pipe from which the main thread will read.
+ *
+ * Each end of the pipe can be either blocking or non-blocking. Recall that a
+ * non-blocking stream will return NS_BASE_STREAM_WOULD_BLOCK if it cannot be
+ * read or written to without blocking the calling thread. For example, if you
+ * try to read from an empty pipe that has not yet been closed, then if that
+ * pipe's input end is non-blocking, then the read call will fail immediately
+ * with NS_BASE_STREAM_WOULD_BLOCK as the error condition. However, if that
+ * pipe's input end is blocking, then the read call will not return until the
+ * pipe has data or until the pipe is closed. This example presumes that the
+ * pipe is being filled asynchronously on some background thread.
+ *
+ * The pipe supports nsIAsyncInputStream and nsIAsyncOutputStream, which give
+ * the user of a non-blocking pipe the ability to wait for the pipe to become
+ * ready again. For example, in the case of an empty non-blocking pipe, the
+ * user can call AsyncWait on the input end of the pipe to be notified when
+ * the pipe has data to read (or when the pipe becomes closed).
+ *
+ * NS_NewPipe2 and NS_NewPipe provide convenient pipe constructors. In most
+ * cases nsIPipe is not actually used. It is usually enough to just get
+ * references to the pipe's input and output end. In which case, the pipe is
+ * automatically closed when the respective pipe ends are released.
+ */
+[scriptable, uuid(25d0de93-685e-4ea4-95d3-d884e31df63c)]
+interface nsIPipe : nsISupports
+{
+ /**
+ * initialize this pipe
+ *
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param segmentCount
+ * specifies the max number of segments (pass 0 to use default
+ * value). Passing UINT32_MAX here causes the pipe to have
+ * "infinite" space. This mode can be useful in some cases, but
+ * should always be used with caution. The default value for this
+ * parameter is a finite value.
+ */
+ [must_use] void init(in boolean nonBlockingInput,
+ in boolean nonBlockingOutput,
+ in unsigned long segmentSize,
+ in unsigned long segmentCount);
+
+ /**
+ * The pipe's input end, which also implements nsISearchableInputStream.
+ * Getting fails if the pipe hasn't been initialized.
+ */
+ [must_use] readonly attribute nsIAsyncInputStream inputStream;
+
+ /**
+ * The pipe's output end. Getting fails if the pipe hasn't been
+ * initialized.
+ */
+ [must_use] readonly attribute nsIAsyncOutputStream outputStream;
+};
+
+/**
+ * XXX this interface doesn't really belong in here. It is here because
+ * currently nsPipeInputStream is the only implementation of this interface.
+ */
+[scriptable, uuid(8C39EF62-F7C9-11d4-98F5-001083010E9B)]
+interface nsISearchableInputStream : nsISupports
+{
+ /**
+ * Searches for a string in the input stream. Since the stream has a notion
+ * of EOF, it is possible that the string may at some time be in the
+ * buffer, but is is not currently found up to some offset. Consequently,
+ * both the found and not found cases return an offset:
+ * if found, return offset where it was found
+ * if not found, return offset of the first byte not searched
+ * In the case the stream is at EOF and the string is not found, the first
+ * byte not searched will correspond to the length of the buffer.
+ */
+ void search(in string forString,
+ in boolean ignoreCase,
+ out boolean found,
+ out unsigned long offsetSearchedTo);
+};
+
+%{C++
+
+class nsIInputStream;
+class nsIOutputStream;
+
+/**
+ * NS_NewPipe2
+ *
+ * This function supersedes NS_NewPipe. It differs from NS_NewPipe in two
+ * major ways:
+ * (1) returns nsIAsyncInputStream and nsIAsyncOutputStream, so it is
+ * not necessary to QI in order to access these interfaces.
+ * (2) the size of the pipe is determined by the number of segments
+ * times the size of each segment.
+ *
+ * @param pipeIn
+ * resulting input end of the pipe
+ * @param pipeOut
+ * resulting output end of the pipe
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param segmentCount
+ * specifies the max number of segments (pass 0 to use default value)
+ * passing UINT32_MAX here causes the pipe to have "infinite" space.
+ * this mode can be useful in some cases, but should always be used with
+ * caution. the default value for this parameter is a finite value.
+ */
+extern MOZ_MUST_USE nsresult
+NS_NewPipe2(nsIAsyncInputStream **pipeIn,
+ nsIAsyncOutputStream **pipeOut,
+ bool nonBlockingInput = false,
+ bool nonBlockingOutput = false,
+ uint32_t segmentSize = 0,
+ uint32_t segmentCount = 0);
+
+/**
+ * NS_NewPipe
+ *
+ * Preserved for backwards compatibility. Plus, this interface is more
+ * amiable in certain contexts (e.g., when you don't need the pipe's async
+ * capabilities).
+ *
+ * @param pipeIn
+ * resulting input end of the pipe
+ * @param pipeOut
+ * resulting output end of the pipe
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param maxSize
+ * specifies the max size of the pipe (pass 0 to use default value)
+ * number of segments is maxSize / segmentSize, and maxSize must be a
+ * multiple of segmentSize. passing UINT32_MAX here causes the
+ * pipe to have "infinite" space. this mode can be useful in some
+ * cases, but should always be used with caution. the default value
+ * for this parameter is a finite value.
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ */
+extern MOZ_MUST_USE nsresult
+NS_NewPipe(nsIInputStream **pipeIn,
+ nsIOutputStream **pipeOut,
+ uint32_t segmentSize = 0,
+ uint32_t maxSize = 0,
+ bool nonBlockingInput = false,
+ bool nonBlockingOutput = false);
+
+%}
diff --git a/xpcom/io/nsISafeOutputStream.idl b/xpcom/io/nsISafeOutputStream.idl
new file mode 100644
index 000000000..c62d85f07
--- /dev/null
+++ b/xpcom/io/nsISafeOutputStream.idl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * This interface provides a mechanism to control an output stream
+ * that takes care not to overwrite an existing target until it is known
+ * that all writes to the destination succeeded.
+ *
+ * An object that supports this interface is intended to also support
+ * nsIOutputStream.
+ *
+ * For example, a file output stream that supports this interface writes to
+ * a temporary file, and moves it over the original file when |finish| is
+ * called only if the stream can be successfully closed and all writes
+ * succeeded. If |finish| is called but something went wrong during
+ * writing, it will delete the temporary file and not touch the original.
+ * If the stream is closed by calling |close| directly, or the stream
+ * goes away, the original file will not be overwritten, and the temporary
+ * file will be deleted.
+ *
+ * Currently, this interface is implemented only for file output streams.
+ */
+[scriptable, uuid(5f914307-5c34-4e1f-8e32-ec749d25b27a)]
+interface nsISafeOutputStream : nsISupports
+{
+ /**
+ * Call this method to close the stream and cause the original target
+ * to be overwritten. Note: if any call to |write| failed to write out
+ * all of the data given to it, then calling this method will |close| the
+ * stream and return failure. Further, if closing the stream fails, this
+ * method will return failure. The original target will be overwritten only
+ * if all calls to |write| succeeded and the stream was successfully closed.
+ */
+ void finish();
+};
diff --git a/xpcom/io/nsIScriptableBase64Encoder.idl b/xpcom/io/nsIScriptableBase64Encoder.idl
new file mode 100644
index 000000000..641451805
--- /dev/null
+++ b/xpcom/io/nsIScriptableBase64Encoder.idl
@@ -0,0 +1,32 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsIInputStream;
+
+/**
+ * nsIScriptableBase64Encoder efficiently encodes the contents
+ * of a nsIInputStream to a Base64 string. This avoids the need
+ * to read the entire stream into a buffer, and only then do the
+ * Base64 encoding.
+ *
+ * If you already have a buffer full of data, you should use
+ * btoa instead!
+ */
+[scriptable, uuid(9479c864-d1f9-45ab-b7b9-28b907bd2ba9)]
+interface nsIScriptableBase64Encoder : nsISupports
+{
+ /**
+ * These methods take an nsIInputStream and return a narrow or wide
+ * string with the contents of the nsIInputStream base64 encoded.
+ *
+ * The stream passed in must support ReadSegments and must not be
+ * a non-blocking stream that will return NS_BASE_STREAM_WOULD_BLOCK.
+ * If either of these restrictions are violated we will abort.
+ */
+ ACString encodeToCString(in nsIInputStream stream, in unsigned long length);
+ AString encodeToString(in nsIInputStream stream, in unsigned long length);
+};
diff --git a/xpcom/io/nsIScriptableInputStream.idl b/xpcom/io/nsIScriptableInputStream.idl
new file mode 100644
index 000000000..d9248f425
--- /dev/null
+++ b/xpcom/io/nsIScriptableInputStream.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIInputStream;
+
+/**
+ * nsIScriptableInputStream provides scriptable access to an nsIInputStream
+ * instance.
+ */
+[scriptable, uuid(3fce9015-472a-4080-ac3e-cd875dbe361e)]
+interface nsIScriptableInputStream : nsISupports
+{
+ /**
+ * Closes the stream.
+ */
+ void close();
+
+ /**
+ * Wrap the given nsIInputStream with this nsIScriptableInputStream.
+ *
+ * @param aInputStream parameter providing the stream to wrap
+ */
+ void init(in nsIInputStream aInputStream);
+
+ /**
+ * Return the number of bytes currently available in the stream
+ *
+ * @return the number of bytes
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called after the stream has been closed
+ */
+ unsigned long long available();
+
+ /**
+ * Read data from the stream.
+ *
+ * WARNING: If the data contains a null byte, then this method will return
+ * a truncated string.
+ *
+ * @param aCount the maximum number of bytes to read
+ *
+ * @return the data, which will be an empty string if the stream is at EOF.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called after the stream has been closed
+ * @throws NS_ERROR_NOT_INITIALIZED if init was not called
+ */
+ string read(in unsigned long aCount);
+
+ /**
+ * Read data from the stream, including NULL bytes.
+ *
+ * @param aCount the maximum number of bytes to read.
+ *
+ * @return the data from the stream, which will be an empty string if EOF
+ * has been reached.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream
+ * would block the calling thread (non-blocking mode only).
+ * @throws NS_ERROR_FAILURE if there are not enough bytes available to read
+ * aCount amount of data.
+ */
+ ACString readBytes(in unsigned long aCount);
+};
diff --git a/xpcom/io/nsISeekableStream.idl b/xpcom/io/nsISeekableStream.idl
new file mode 100644
index 000000000..1be7ea60c
--- /dev/null
+++ b/xpcom/io/nsISeekableStream.idl
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+
+/*
+ * nsISeekableStream
+ *
+ * Note that a stream might not implement all methods (e.g., a readonly stream
+ * won't implement setEOF)
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(8429d350-1040-4661-8b71-f2a6ba455980)]
+interface nsISeekableStream : nsISupports
+{
+ /*
+ * Sets the stream pointer to the value of the 'offset' parameter
+ */
+ const int32_t NS_SEEK_SET = 0;
+
+ /*
+ * Sets the stream pointer to its current location plus the value
+ * of the offset parameter.
+ */
+ const int32_t NS_SEEK_CUR = 1;
+
+ /*
+ * Sets the stream pointer to the size of the stream plus the value
+ * of the offset parameter.
+ */
+ const int32_t NS_SEEK_END = 2;
+
+ /**
+ * seek
+ *
+ * This method moves the stream offset of the steam implementing this
+ * interface.
+ *
+ * @param whence specifies how to interpret the 'offset' parameter in
+ * setting the stream offset associated with the implementing
+ * stream.
+ *
+ * @param offset specifies a value, in bytes, that is used in conjunction
+ * with the 'whence' parameter to set the stream offset of the
+ * implementing stream. A negative value causes seeking in
+ * the reverse direction.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+ */
+ void seek(in long whence, in long long offset);
+
+ /**
+ * tell
+ *
+ * This method reports the current offset, in bytes, from the start of the
+ * stream.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+ */
+ long long tell();
+
+
+ /**
+ * setEOF
+ *
+ * This method truncates the stream at the current offset.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+ */
+ void setEOF();
+};
diff --git a/xpcom/io/nsIStorageStream.idl b/xpcom/io/nsIStorageStream.idl
new file mode 100644
index 000000000..7d5061e23
--- /dev/null
+++ b/xpcom/io/nsIStorageStream.idl
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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"
+
+interface nsIInputStream;
+interface nsIOutputStream;
+
+/**
+ * The nsIStorageStream interface maintains an internal data buffer that can be
+ * filled using a single output stream. One or more independent input streams
+ * can be created to read the data from the buffer non-destructively.
+ */
+
+[scriptable, uuid(44a200fe-6c2b-4b41-b4e3-63e8c14e7c0d)]
+interface nsIStorageStream : nsISupports
+{
+ /**
+ *
+ * Initialize the stream, setting up the amount of space that will be
+ * allocated for the stream's backing-store.
+ *
+ * @param segmentSize
+ * Size of each segment. Must be a power of two.
+ * @param maxSize
+ * Maximum total size of this stream. length will always be less
+ * than or equal to this value. Passing UINT32_MAX is safe.
+ */
+ void init(in uint32_t segmentSize, in uint32_t maxSize);
+
+ /**
+ * Get a reference to the one and only output stream for this instance.
+ * The zero-based startPosition argument is used is used to set the initial
+ * write cursor position. The startPosition cannot be set larger than the
+ * current buffer length. Calling this method has the side-effect of
+ * truncating the internal buffer to startPosition bytes.
+ */
+ nsIOutputStream getOutputStream(in int32_t startPosition);
+
+ /**
+ * Create a new input stream to read data (written by the singleton output
+ * stream) from the internal buffer. Multiple, independent input streams
+ * can be created.
+ */
+ nsIInputStream newInputStream(in int32_t startPosition);
+
+ /**
+ * The length attribute indicates the total number of bytes stored in the
+ * nsIStorageStream internal buffer, regardless of any consumption by input
+ * streams. Assigning to the length field can be used to truncate the
+ * buffer data, but can not be used when either the instance's output
+ * stream is in use.
+ *
+ * @See #writeInProgress */
+ attribute uint32_t length;
+
+ /**
+ * True, when output stream has not yet been Close'ed
+ */
+ readonly attribute boolean writeInProgress;
+};
+
+%{C++
+// Factory method
+nsresult
+NS_NewStorageStream(uint32_t segmentSize, uint32_t maxSize, nsIStorageStream **result);
+%}
diff --git a/xpcom/io/nsIStreamBufferAccess.idl b/xpcom/io/nsIStreamBufferAccess.idl
new file mode 100644
index 000000000..c37afd8dc
--- /dev/null
+++ b/xpcom/io/nsIStreamBufferAccess.idl
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * An interface for access to a buffering stream implementation's underlying
+ * memory buffer.
+ *
+ * Stream implementations that QueryInterface to nsIStreamBufferAccess must
+ * ensure that all buffers are aligned on the most restrictive type size for
+ * the current architecture (e.g., sizeof(double) for RISCy CPUs). malloc(3)
+ * satisfies this requirement.
+ */
+[scriptable, uuid(ac923b72-ac87-4892-ac7a-ca385d429435)]
+interface nsIStreamBufferAccess : nsISupports
+{
+ /**
+ * Get access to a contiguous, aligned run of bytes in the stream's buffer.
+ * Exactly one successful getBuffer call must occur before a putBuffer call
+ * taking the non-null pointer returned by the successful getBuffer.
+ *
+ * The run of bytes are the next bytes (modulo alignment padding) to read
+ * for an input stream, and the next bytes (modulo alignment padding) to
+ * store before (eventually) writing buffered data to an output stream.
+ * There can be space beyond this run of bytes in the buffer for further
+ * accesses before the fill or flush point is reached.
+ *
+ * @param aLength
+ * Count of contiguous bytes requested at the address A that satisfies
+ * (A & aAlignMask) == 0 in the buffer, starting from the current stream
+ * position, mapped to a buffer address B. The stream implementation
+ * must pad from B to A by skipping bytes (if input stream) or storing
+ * zero bytes (if output stream).
+ *
+ * @param aAlignMask
+ * Bit-mask computed by subtracting 1 from the power-of-two alignment
+ * modulus (e.g., 3 or sizeof(uint32_t)-1 for uint32_t alignment).
+ *
+ * @return
+ * The aligned pointer to aLength bytes in the buffer, or null if the
+ * buffer has no room for aLength bytes starting at the next address A
+ * after the current position that satisfies (A & aAlignMask) == 0.
+ */
+ [notxpcom,noscript] charPtr getBuffer(in uint32_t aLength, in uint32_t aAlignMask);
+
+ /**
+ * Relinquish access to the stream's buffer, filling if at end of an input
+ * buffer, flushing if completing an output buffer. After a getBuffer call
+ * that returns non-null, putBuffer must be called.
+ *
+ * @param aBuffer
+ * A non-null pointer returned by getBuffer on the same stream buffer
+ * access object.
+ *
+ * @param aLength
+ * The same count of contiguous bytes passed to the getBuffer call that
+ * returned aBuffer.
+ */
+ [notxpcom,noscript] void putBuffer(in charPtr aBuffer, in uint32_t aLength);
+
+ /**
+ * Disable and enable buffering on the stream implementing this interface.
+ * DisableBuffering flushes an output stream's buffer, and invalidates an
+ * input stream's buffer.
+ */
+ void disableBuffering();
+ void enableBuffering();
+
+ /**
+ * The underlying, unbuffered input or output stream.
+ */
+ readonly attribute nsISupports unbufferedStream;
+};
+
+%{C++
+
+/**
+ * These macros get and put a buffer given either an sba parameter that may
+ * point to an object implementing nsIStreamBufferAccess, nsIObjectInputStream,
+ * or nsIObjectOutputStream.
+ */
+#define NS_GET_BUFFER(sba,n,a) ((sba)->GetBuffer(n, a))
+#define NS_PUT_BUFFER(sba,p,n) ((sba)->PutBuffer(p, n))
+
+%}
diff --git a/xpcom/io/nsIStringStream.idl b/xpcom/io/nsIStringStream.idl
new file mode 100644
index 000000000..d43200382
--- /dev/null
+++ b/xpcom/io/nsIStringStream.idl
@@ -0,0 +1,66 @@
+/* -*- 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 "nsIInputStream.idl"
+
+%{C++
+#include "mozilla/MemoryReporting.h"
+%}
+
+native MallocSizeOf(mozilla::MallocSizeOf);
+
+/**
+ * nsIStringInputStream
+ *
+ * Provides scriptable and specialized C++-only methods for initializing a
+ * nsIInputStream implementation with a simple character array.
+ */
+[scriptable, builtinclass, uuid(450cd2d4-f0fd-424d-b365-b1251f80fd53)]
+interface nsIStringInputStream : nsIInputStream
+{
+ /**
+ * SetData - assign data to the input stream (copied on assignment).
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ *
+ * NOTE: C++ code should consider using AdoptData or ShareData to avoid
+ * making an extra copy of the stream data.
+ *
+ * NOTE: For JS callers, the given data must not contain null characters
+ * (other than a null terminator) because a null character in the middle of
+ * the data string will be seen as a terminator when the data is converted
+ * from a JS string to a C++ character array.
+ */
+ void setData(in string data, in long dataLen);
+
+ /**
+ * NOTE: the following methods are designed to give C++ code added control
+ * over the ownership and lifetime of the stream data. Use with care :-)
+ */
+
+ /**
+ * AdoptData - assign data to the input stream. the input stream takes
+ * ownership of the given data buffer and will free it when
+ * the input stream is destroyed.
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ */
+ [noscript] void adoptData(in charPtr data, in long dataLen);
+
+ /**
+ * ShareData - assign data to the input stream. the input stream references
+ * the given data buffer until the input stream is destroyed. the given
+ * data buffer must outlive the input stream.
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ */
+ [noscript] void shareData(in string data, in long dataLen);
+
+ [noscript, notxpcom]
+ size_t SizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);
+};
diff --git a/xpcom/io/nsIUnicharInputStream.idl b/xpcom/io/nsIUnicharInputStream.idl
new file mode 100644
index 000000000..8d0459fed
--- /dev/null
+++ b/xpcom/io/nsIUnicharInputStream.idl
@@ -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 "nsISupports.idl"
+
+interface nsIUnicharInputStream;
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature of the writer function passed to ReadSegments. This
+ * is the "consumer" of data that gets read from the stream's buffer.
+ *
+ * @param aInStream stream being read
+ * @param aClosure opaque parameter passed to ReadSegments
+ * @param aFromSegment pointer to memory owned by the input stream
+ * @param aToOffset amount already read (since ReadSegments was called)
+ * @param aCount length of fromSegment
+ * @param aWriteCount number of bytes read
+ *
+ * Implementers should return the following:
+ *
+ * @throws <any-error> if not interested in consuming any data
+ *
+ * Errors are never passed to the caller of ReadSegments.
+ *
+ * NOTE: returning NS_OK and (*aWriteCount = 0) has undefined behavior.
+ */
+typedef nsresult (*nsWriteUnicharSegmentFun)(nsIUnicharInputStream *aInStream,
+ void *aClosure,
+ const char16_t *aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount);
+%}
+native nsWriteUnicharSegmentFun(nsWriteUnicharSegmentFun);
+
+/**
+ * Abstract unicode character input stream
+ * @see nsIInputStream
+ */
+[scriptable, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)]
+interface nsIUnicharInputStream : nsISupports {
+ /**
+ * Reads into a caller-provided character array.
+ *
+ * @return The number of characters that were successfully read. May be less
+ * than aCount, even if there is more data in the input stream.
+ * A return value of 0 means EOF.
+ *
+ * @note To read more than 2^32 characters, call this method multiple times.
+ */
+ [noscript] unsigned long read([array, size_is(aCount)] in char16_t aBuf,
+ in unsigned long aCount);
+
+ /**
+ * Low-level read method that has access to the stream's underlying buffer.
+ * The writer function may be called multiple times for segmented buffers.
+ * ReadSegments is expected to keep calling the writer until either there is
+ * nothing left to read or the writer returns an error. ReadSegments should
+ * not call the writer with zero characters to consume.
+ *
+ * @param aWriter the "consumer" of the data to be read
+ * @param aClosure opaque parameter passed to writer
+ * @param aCount the maximum number of characters to be read
+ *
+ * @return number of characters read (may be less than aCount)
+ * @return 0 if reached end of file (or if aWriter refused to consume data)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer
+ */
+ [noscript] unsigned long readSegments(in nsWriteUnicharSegmentFun aWriter,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * Read into a string object.
+ * @param aCount The number of characters that should be read
+ * @return The number of characters that were read.
+ */
+ unsigned long readString(in unsigned long aCount, out AString aString);
+
+ /**
+ * Close the stream and free associated resources. This also closes the
+ * underlying stream, if any.
+ */
+ void close();
+};
diff --git a/xpcom/io/nsIUnicharLineInputStream.idl b/xpcom/io/nsIUnicharLineInputStream.idl
new file mode 100644
index 000000000..34a67f099
--- /dev/null
+++ b/xpcom/io/nsIUnicharLineInputStream.idl
@@ -0,0 +1,26 @@
+/* -*- 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 "nsISupports.idl"
+
+[scriptable, uuid(67f42475-ba80-40f8-ac0b-649c89230184)]
+interface nsIUnicharLineInputStream : nsISupports
+{
+ /**
+ * Read a single line from the stream, where a line is a
+ * possibly zero length sequence of characters terminated by a
+ * CR, LF, CRLF, LFCR, or eof.
+ * The line terminator is not returned.
+ * @retval false
+ * End of file. This line is the last line of the file
+ * (aLine is valid).
+ * @retval true
+ * The file contains further lines.
+ * @note Do not mix readLine with other read functions.
+ * Doing so can cause various problems and is not supported.
+ */
+ boolean readLine(out AString aLine);
+};
diff --git a/xpcom/io/nsIUnicharOutputStream.idl b/xpcom/io/nsIUnicharOutputStream.idl
new file mode 100644
index 000000000..599224dd0
--- /dev/null
+++ b/xpcom/io/nsIUnicharOutputStream.idl
@@ -0,0 +1,47 @@
+/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
+/* 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"
+
+/**
+ * An interface that allows writing unicode data.
+ */
+[scriptable, uuid(2d00b1bb-8b21-4a63-bcc6-7213f513ac2e)]
+interface nsIUnicharOutputStream : nsISupports
+{
+ /**
+ * Write a single character to the stream. When writing many characters,
+ * prefer the string-taking write method.
+ *
+ * @retval true The character was written successfully
+ * @retval false Not all bytes of the character could be written.
+ */
+ boolean write(in unsigned long aCount,
+ [const, array, size_is(aCount)] in char16_t c);
+
+ /**
+ * Write a string to the stream.
+ *
+ * @retval true The string was written successfully
+ * @retval false Not all bytes of the string could be written.
+ */
+ boolean writeString(in AString str);
+
+ /**
+ * Flush the stream. This finishes the conversion and writes any bytes that
+ * finish the current byte sequence.
+ *
+ * It does NOT flush the underlying stream.
+ *
+ * @see nsIUnicodeEncoder::Finish
+ */
+ void flush();
+
+ /**
+ * Close the stream and free associated resources. This also closes the
+ * underlying stream.
+ */
+ void close();
+};
diff --git a/xpcom/io/nsInputStreamTee.cpp b/xpcom/io/nsInputStreamTee.cpp
new file mode 100644
index 000000000..8f01cb87e
--- /dev/null
+++ b/xpcom/io/nsInputStreamTee.cpp
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include "mozilla/Logging.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+#include "nsIInputStreamTee.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIEventTarget.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+#ifdef LOG
+#undef LOG
+#endif
+
+static LazyLogModule sTeeLog("nsInputStreamTee");
+#define LOG(args) MOZ_LOG(sTeeLog, mozilla::LogLevel::Debug, args)
+
+class nsInputStreamTee final : public nsIInputStreamTee
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIINPUTSTREAMTEE
+
+ nsInputStreamTee();
+ bool SinkIsValid();
+ void InvalidateSink();
+
+private:
+ ~nsInputStreamTee()
+ {
+ }
+
+ nsresult TeeSegment(const char* aBuf, uint32_t aCount);
+
+ static nsresult WriteSegmentFun(nsIInputStream*, void*, const char*,
+ uint32_t, uint32_t, uint32_t*);
+
+private:
+ nsCOMPtr<nsIInputStream> mSource;
+ nsCOMPtr<nsIOutputStream> mSink;
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+ nsWriteSegmentFun mWriter; // for implementing ReadSegments
+ void* mClosure; // for implementing ReadSegments
+ nsAutoPtr<Mutex> mLock; // synchronize access to mSinkIsValid
+ bool mSinkIsValid; // False if TeeWriteEvent fails
+};
+
+class nsInputStreamTeeWriteEvent : public Runnable
+{
+public:
+ // aTee's lock is held across construction of this object
+ nsInputStreamTeeWriteEvent(const char* aBuf, uint32_t aCount,
+ nsIOutputStream* aSink, nsInputStreamTee* aTee)
+ {
+ // copy the buffer - will be free'd by dtor
+ mBuf = (char*)malloc(aCount);
+ if (mBuf) {
+ memcpy(mBuf, (char*)aBuf, aCount);
+ }
+ mCount = aCount;
+ mSink = aSink;
+ bool isNonBlocking;
+ mSink->IsNonBlocking(&isNonBlocking);
+ NS_ASSERTION(isNonBlocking == false, "mSink is nonblocking");
+ mTee = aTee;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ if (!mBuf) {
+ NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
+ "memory not allocated\n");
+ return NS_OK;
+ }
+ MOZ_ASSERT(mSink, "mSink is null!");
+
+ // The output stream could have been invalidated between when
+ // this event was dispatched and now, so check before writing.
+ if (!mTee->SinkIsValid()) {
+ return NS_OK;
+ }
+
+ LOG(("nsInputStreamTeeWriteEvent::Run() [%p]"
+ "will write %u bytes to %p\n",
+ this, mCount, mSink.get()));
+
+ uint32_t totalBytesWritten = 0;
+ while (mCount) {
+ nsresult rv;
+ uint32_t bytesWritten = 0;
+ rv = mSink->Write(mBuf + totalBytesWritten, mCount, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ LOG(("nsInputStreamTeeWriteEvent::Run[%p] error %x in writing",
+ this, rv));
+ mTee->InvalidateSink();
+ break;
+ }
+ totalBytesWritten += bytesWritten;
+ NS_ASSERTION(bytesWritten <= mCount, "wrote too much");
+ mCount -= bytesWritten;
+ }
+ return NS_OK;
+ }
+
+protected:
+ virtual ~nsInputStreamTeeWriteEvent()
+ {
+ if (mBuf) {
+ free(mBuf);
+ }
+ mBuf = nullptr;
+ }
+
+private:
+ char* mBuf;
+ uint32_t mCount;
+ nsCOMPtr<nsIOutputStream> mSink;
+ // back pointer to the tee that created this runnable
+ RefPtr<nsInputStreamTee> mTee;
+};
+
+nsInputStreamTee::nsInputStreamTee(): mLock(nullptr)
+ , mSinkIsValid(true)
+{
+}
+
+bool
+nsInputStreamTee::SinkIsValid()
+{
+ MutexAutoLock lock(*mLock);
+ return mSinkIsValid;
+}
+
+void
+nsInputStreamTee::InvalidateSink()
+{
+ MutexAutoLock lock(*mLock);
+ mSinkIsValid = false;
+}
+
+nsresult
+nsInputStreamTee::TeeSegment(const char* aBuf, uint32_t aCount)
+{
+ if (!mSink) {
+ return NS_OK; // nothing to do
+ }
+ if (mLock) { // asynchronous case
+ NS_ASSERTION(mEventTarget, "mEventTarget is null, mLock is not null.");
+ if (!SinkIsValid()) {
+ return NS_OK; // nothing to do
+ }
+ nsCOMPtr<nsIRunnable> event =
+ new nsInputStreamTeeWriteEvent(aBuf, aCount, mSink, this);
+ LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
+ this, aCount));
+ return mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
+ } else { // synchronous case
+ NS_ASSERTION(!mEventTarget, "mEventTarget is not null, mLock is null.");
+ nsresult rv;
+ uint32_t totalBytesWritten = 0;
+ while (aCount) {
+ uint32_t bytesWritten = 0;
+ rv = mSink->Write(aBuf + totalBytesWritten, aCount, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ // ok, this is not a fatal error... just drop our reference to mSink
+ // and continue on as if nothing happened.
+ NS_WARNING("Write failed (non-fatal)");
+ // catch possible misuse of the input stream tee
+ NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
+ mSink = nullptr;
+ break;
+ }
+ totalBytesWritten += bytesWritten;
+ NS_ASSERTION(bytesWritten <= aCount, "wrote too much");
+ aCount -= bytesWritten;
+ }
+ return NS_OK;
+ }
+}
+
+nsresult
+nsInputStreamTee::WriteSegmentFun(nsIInputStream* aIn, void* aClosure,
+ const char* aFromSegment, uint32_t aOffset,
+ uint32_t aCount, uint32_t* aWriteCount)
+{
+ nsInputStreamTee* tee = reinterpret_cast<nsInputStreamTee*>(aClosure);
+ nsresult rv = tee->mWriter(aIn, tee->mClosure, aFromSegment, aOffset,
+ aCount, aWriteCount);
+ if (NS_FAILED(rv) || (*aWriteCount == 0)) {
+ NS_ASSERTION((NS_FAILED(rv) ? (*aWriteCount == 0) : true),
+ "writer returned an error with non-zero writeCount");
+ return rv;
+ }
+
+ return tee->TeeSegment(aFromSegment, *aWriteCount);
+}
+
+NS_IMPL_ISUPPORTS(nsInputStreamTee,
+ nsIInputStreamTee,
+ nsIInputStream)
+NS_IMETHODIMP
+nsInputStreamTee::Close()
+{
+ if (NS_WARN_IF(!mSource)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ nsresult rv = mSource->Close();
+ mSource = nullptr;
+ mSink = nullptr;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::Available(uint64_t* aAvail)
+{
+ if (NS_WARN_IF(!mSource)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return mSource->Available(aAvail);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::Read(char* aBuf, uint32_t aCount, uint32_t* aBytesRead)
+{
+ if (NS_WARN_IF(!mSource)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsresult rv = mSource->Read(aBuf, aCount, aBytesRead);
+ if (NS_FAILED(rv) || (*aBytesRead == 0)) {
+ return rv;
+ }
+
+ return TeeSegment(aBuf, *aBytesRead);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aBytesRead)
+{
+ if (NS_WARN_IF(!mSource)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ mWriter = aWriter;
+ mClosure = aClosure;
+
+ return mSource->ReadSegments(WriteSegmentFun, this, aCount, aBytesRead);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::IsNonBlocking(bool* aResult)
+{
+ if (NS_WARN_IF(!mSource)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return mSource->IsNonBlocking(aResult);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetSource(nsIInputStream* aSource)
+{
+ mSource = aSource;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetSource(nsIInputStream** aSource)
+{
+ NS_IF_ADDREF(*aSource = mSource);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetSink(nsIOutputStream* aSink)
+{
+#ifdef DEBUG
+ if (aSink) {
+ bool nonBlocking;
+ nsresult rv = aSink->IsNonBlocking(&nonBlocking);
+ if (NS_FAILED(rv) || nonBlocking) {
+ NS_ERROR("aSink should be a blocking stream");
+ }
+ }
+#endif
+ mSink = aSink;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetSink(nsIOutputStream** aSink)
+{
+ NS_IF_ADDREF(*aSink = mSink);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetEventTarget(nsIEventTarget* aEventTarget)
+{
+ mEventTarget = aEventTarget;
+ if (mEventTarget) {
+ // Only need synchronization if this is an async tee
+ mLock = new Mutex("nsInputStreamTee.mLock");
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetEventTarget(nsIEventTarget** aEventTarget)
+{
+ NS_IF_ADDREF(*aEventTarget = mEventTarget);
+ return NS_OK;
+}
+
+
+nsresult
+NS_NewInputStreamTeeAsync(nsIInputStream** aResult,
+ nsIInputStream* aSource,
+ nsIOutputStream* aSink,
+ nsIEventTarget* aEventTarget)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
+ rv = tee->SetSource(aSource);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = tee->SetSink(aSink);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = tee->SetEventTarget(aEventTarget);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ tee.forget(aResult);
+ return rv;
+}
+
+nsresult
+NS_NewInputStreamTee(nsIInputStream** aResult,
+ nsIInputStream* aSource,
+ nsIOutputStream* aSink)
+{
+ return NS_NewInputStreamTeeAsync(aResult, aSource, aSink, nullptr);
+}
+
+#undef LOG
diff --git a/xpcom/io/nsLinebreakConverter.cpp b/xpcom/io/nsLinebreakConverter.cpp
new file mode 100644
index 000000000..007685a6a
--- /dev/null
+++ b/xpcom/io/nsLinebreakConverter.cpp
@@ -0,0 +1,488 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsLinebreakConverter.h"
+
+#include "nsMemory.h"
+#include "nsCRT.h"
+
+
+/*----------------------------------------------------------------------------
+ GetLinebreakString
+
+ Could make this inline
+----------------------------------------------------------------------------*/
+static const char*
+GetLinebreakString(nsLinebreakConverter::ELinebreakType aBreakType)
+{
+ static const char* const sLinebreaks[] = {
+ "", // any
+ NS_LINEBREAK, // platform
+ LFSTR, // content
+ CRLF, // net
+ CRSTR, // Mac
+ LFSTR, // Unix
+ CRLF, // Windows
+ " ", // space
+ nullptr
+ };
+
+ return sLinebreaks[aBreakType];
+}
+
+
+/*----------------------------------------------------------------------------
+ AppendLinebreak
+
+ Wee inline method to append a line break. Modifies ioDest.
+----------------------------------------------------------------------------*/
+template<class T>
+void
+AppendLinebreak(T*& aIoDest, const char* aLineBreakStr)
+{
+ *aIoDest++ = *aLineBreakStr;
+
+ if (aLineBreakStr[1]) {
+ *aIoDest++ = aLineBreakStr[1];
+ }
+}
+
+/*----------------------------------------------------------------------------
+ CountChars
+
+ Counts occurrences of breakStr in aSrc
+----------------------------------------------------------------------------*/
+template<class T>
+int32_t
+CountLinebreaks(const T* aSrc, int32_t aInLen, const char* aBreakStr)
+{
+ const T* src = aSrc;
+ const T* srcEnd = aSrc + aInLen;
+ int32_t theCount = 0;
+
+ while (src < srcEnd) {
+ if (*src == *aBreakStr) {
+ src++;
+
+ if (aBreakStr[1]) {
+ if (src < srcEnd && *src == aBreakStr[1]) {
+ src++;
+ theCount++;
+ }
+ } else {
+ theCount++;
+ }
+ } else {
+ src++;
+ }
+ }
+
+ return theCount;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertBreaks
+
+ ioLen *includes* a terminating null, if any
+----------------------------------------------------------------------------*/
+template<class T>
+static T*
+ConvertBreaks(const T* aInSrc, int32_t& aIoLen, const char* aSrcBreak,
+ const char* aDestBreak)
+{
+ NS_ASSERTION(aInSrc && aSrcBreak && aDestBreak, "Got a null string");
+
+ T* resultString = nullptr;
+
+ // handle the no conversion case
+ if (nsCRT::strcmp(aSrcBreak, aDestBreak) == 0) {
+ resultString = (T*)malloc(sizeof(T) * aIoLen);
+ if (!resultString) {
+ return nullptr;
+ }
+ memcpy(resultString, aInSrc, sizeof(T) * aIoLen); // includes the null, if any
+ return resultString;
+ }
+
+ int32_t srcBreakLen = strlen(aSrcBreak);
+ int32_t destBreakLen = strlen(aDestBreak);
+
+ // handle the easy case, where the string length does not change, and the
+ // breaks are only 1 char long, i.e. CR <-> LF
+ if (srcBreakLen == destBreakLen && srcBreakLen == 1) {
+ resultString = (T*)malloc(sizeof(T) * aIoLen);
+ if (!resultString) {
+ return nullptr;
+ }
+
+ const T* src = aInSrc;
+ const T* srcEnd = aInSrc + aIoLen; // includes null, if any
+ T* dst = resultString;
+
+ char srcBreakChar = *aSrcBreak; // we know it's one char long already
+ char dstBreakChar = *aDestBreak;
+
+ while (src < srcEnd) {
+ if (*src == srcBreakChar) {
+ *dst++ = dstBreakChar;
+ src++;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+
+ // aIoLen does not change
+ } else {
+ // src and dest termination is different length. Do it a slower way.
+
+ // count linebreaks in src. Assumes that chars in 2-char linebreaks are unique.
+ int32_t numLinebreaks = CountLinebreaks(aInSrc, aIoLen, aSrcBreak);
+
+ int32_t newBufLen =
+ aIoLen - (numLinebreaks * srcBreakLen) + (numLinebreaks * destBreakLen);
+ resultString = (T*)malloc(sizeof(T) * newBufLen);
+ if (!resultString) {
+ return nullptr;
+ }
+
+ const T* src = aInSrc;
+ const T* srcEnd = aInSrc + aIoLen; // includes null, if any
+ T* dst = resultString;
+
+ while (src < srcEnd) {
+ if (*src == *aSrcBreak) {
+ *dst++ = *aDestBreak;
+ if (aDestBreak[1]) {
+ *dst++ = aDestBreak[1];
+ }
+
+ src++;
+ if (src < srcEnd && aSrcBreak[1] && *src == aSrcBreak[1]) {
+ src++;
+ }
+ } else {
+ *dst++ = *src++;
+ }
+ }
+
+ aIoLen = newBufLen;
+ }
+
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertBreaksInSitu
+
+ Convert breaks in situ. Can only do this if the linebreak length
+ does not change.
+----------------------------------------------------------------------------*/
+template<class T>
+static void
+ConvertBreaksInSitu(T* aInSrc, int32_t aInLen, char aSrcBreak, char aDestBreak)
+{
+ T* src = aInSrc;
+ T* srcEnd = aInSrc + aInLen;
+
+ while (src < srcEnd) {
+ if (*src == aSrcBreak) {
+ *src = aDestBreak;
+ }
+
+ src++;
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertUnknownBreaks
+
+ Convert unknown line breaks to the specified break.
+
+ This will convert CRLF pairs to one break, and single CR or LF to a break.
+----------------------------------------------------------------------------*/
+template<class T>
+static T*
+ConvertUnknownBreaks(const T* aInSrc, int32_t& aIoLen, const char* aDestBreak)
+{
+ const T* src = aInSrc;
+ const T* srcEnd = aInSrc + aIoLen; // includes null, if any
+
+ int32_t destBreakLen = strlen(aDestBreak);
+ int32_t finalLen = 0;
+
+ while (src < srcEnd) {
+ if (*src == nsCRT::CR) {
+ if (src < srcEnd && src[1] == nsCRT::LF) {
+ // CRLF
+ finalLen += destBreakLen;
+ src++;
+ } else {
+ // Lone CR
+ finalLen += destBreakLen;
+ }
+ } else if (*src == nsCRT::LF) {
+ // Lone LF
+ finalLen += destBreakLen;
+ } else {
+ finalLen++;
+ }
+ src++;
+ }
+
+ T* resultString = (T*)malloc(sizeof(T) * finalLen);
+ if (!resultString) {
+ return nullptr;
+ }
+
+ src = aInSrc;
+ srcEnd = aInSrc + aIoLen; // includes null, if any
+
+ T* dst = resultString;
+
+ while (src < srcEnd) {
+ if (*src == nsCRT::CR) {
+ if (src < srcEnd && src[1] == nsCRT::LF) {
+ // CRLF
+ AppendLinebreak(dst, aDestBreak);
+ src++;
+ } else {
+ // Lone CR
+ AppendLinebreak(dst, aDestBreak);
+ }
+ } else if (*src == nsCRT::LF) {
+ // Lone LF
+ AppendLinebreak(dst, aDestBreak);
+ } else {
+ *dst++ = *src;
+ }
+ src++;
+ }
+
+ aIoLen = finalLen;
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertLineBreaks
+
+----------------------------------------------------------------------------*/
+char*
+nsLinebreakConverter::ConvertLineBreaks(const char* aSrc,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks,
+ int32_t aSrcLen, int32_t* aOutLen)
+{
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+ if (!aSrc) {
+ return nullptr;
+ }
+
+ int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(aSrc) + 1 : aSrcLen;
+
+ char* resultString;
+ if (aSrcBreaks == eLinebreakAny) {
+ resultString = ConvertUnknownBreaks(aSrc, sourceLen,
+ GetLinebreakString(aDestBreaks));
+ } else
+ resultString = ConvertBreaks(aSrc, sourceLen,
+ GetLinebreakString(aSrcBreaks),
+ GetLinebreakString(aDestBreaks));
+
+ if (aOutLen) {
+ *aOutLen = sourceLen;
+ }
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertLineBreaksInSitu
+
+----------------------------------------------------------------------------*/
+nsresult
+nsLinebreakConverter::ConvertLineBreaksInSitu(char** aIoBuffer,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks,
+ int32_t aSrcLen, int32_t* aOutLen)
+{
+ NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
+ if (!aIoBuffer || !*aIoBuffer) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+
+ int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(*aIoBuffer) + 1 : aSrcLen;
+
+ // can we convert in-place?
+ const char* srcBreaks = GetLinebreakString(aSrcBreaks);
+ const char* dstBreaks = GetLinebreakString(aDestBreaks);
+
+ if (aSrcBreaks != eLinebreakAny &&
+ strlen(srcBreaks) == 1 &&
+ strlen(dstBreaks) == 1) {
+ ConvertBreaksInSitu(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
+ if (aOutLen) {
+ *aOutLen = sourceLen;
+ }
+ } else {
+ char* destBuffer;
+
+ if (aSrcBreaks == eLinebreakAny) {
+ destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
+ } else {
+ destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
+ }
+
+ if (!destBuffer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *aIoBuffer = destBuffer;
+ if (aOutLen) {
+ *aOutLen = sourceLen;
+ }
+ }
+
+ return NS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertUnicharLineBreaks
+
+----------------------------------------------------------------------------*/
+char16_t*
+nsLinebreakConverter::ConvertUnicharLineBreaks(const char16_t* aSrc,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks,
+ int32_t aSrcLen,
+ int32_t* aOutLen)
+{
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+ if (!aSrc) {
+ return nullptr;
+ }
+
+ int32_t bufLen = (aSrcLen == kIgnoreLen) ? NS_strlen(aSrc) + 1 : aSrcLen;
+
+ char16_t* resultString;
+ if (aSrcBreaks == eLinebreakAny) {
+ resultString = ConvertUnknownBreaks(aSrc, bufLen,
+ GetLinebreakString(aDestBreaks));
+ } else
+ resultString = ConvertBreaks(aSrc, bufLen, GetLinebreakString(aSrcBreaks),
+ GetLinebreakString(aDestBreaks));
+
+ if (aOutLen) {
+ *aOutLen = bufLen;
+ }
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertStringLineBreaks
+
+----------------------------------------------------------------------------*/
+nsresult
+nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(
+ char16_t** aIoBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen, int32_t* aOutLen)
+{
+ NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
+ if (!aIoBuffer || !*aIoBuffer) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+
+ int32_t sourceLen =
+ (aSrcLen == kIgnoreLen) ? NS_strlen(*aIoBuffer) + 1 : aSrcLen;
+
+ // can we convert in-place?
+ const char* srcBreaks = GetLinebreakString(aSrcBreaks);
+ const char* dstBreaks = GetLinebreakString(aDestBreaks);
+
+ if ((aSrcBreaks != eLinebreakAny) &&
+ (strlen(srcBreaks) == 1) &&
+ (strlen(dstBreaks) == 1)) {
+ ConvertBreaksInSitu(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
+ if (aOutLen) {
+ *aOutLen = sourceLen;
+ }
+ } else {
+ char16_t* destBuffer;
+
+ if (aSrcBreaks == eLinebreakAny) {
+ destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
+ } else {
+ destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
+ }
+
+ if (!destBuffer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *aIoBuffer = destBuffer;
+ if (aOutLen) {
+ *aOutLen = sourceLen;
+ }
+ }
+
+ return NS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ ConvertStringLineBreaks
+
+----------------------------------------------------------------------------*/
+nsresult
+nsLinebreakConverter::ConvertStringLineBreaks(nsString& aIoString,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks)
+{
+
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+
+ // nothing to do
+ if (aIoString.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+
+ // remember the old buffer in case
+ // we blow it away later
+ nsString::char_iterator stringBuf;
+ if (!aIoString.BeginWriting(stringBuf, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t newLen;
+
+ rv = ConvertUnicharLineBreaksInSitu(&stringBuf,
+ aSrcBreaks, aDestBreaks,
+ aIoString.Length() + 1, &newLen);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (stringBuf != aIoString.get()) {
+ aIoString.Adopt(stringBuf, newLen - 1);
+ }
+
+ return NS_OK;
+}
+
+
+
diff --git a/xpcom/io/nsLinebreakConverter.h b/xpcom/io/nsLinebreakConverter.h
new file mode 100644
index 000000000..a1678ef2d
--- /dev/null
+++ b/xpcom/io/nsLinebreakConverter.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsLinebreakConverter_h_
+#define nsLinebreakConverter_h_
+
+#include "nscore.h"
+#include "nsString.h"
+
+// utility class for converting between different line breaks.
+
+class nsLinebreakConverter
+{
+public:
+
+ // Note: enum must match char* array in GetLinebreakString
+ typedef enum {
+ eLinebreakAny, // any kind of linebreak (i.e. "don't care" source)
+
+ eLinebreakPlatform, // platform linebreak
+ eLinebreakContent, // Content model linebreak (LF)
+ eLinebreakNet, // Form submission linebreak (CRLF)
+
+ eLinebreakMac, // CR
+ eLinebreakUnix, // LF
+ eLinebreakWindows, // CRLF
+
+ eLinebreakSpace // space characters. Only valid as destination type
+
+ } ELinebreakType;
+
+ enum {
+ kIgnoreLen = -1
+ };
+
+ /* ConvertLineBreaks
+ * Convert line breaks in the supplied string, allocating and returning
+ * a new buffer. Returns nullptr on failure.
+ * @param aSrc: the source string. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static char* ConvertLineBreaks(const char* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen, int32_t* aOutLen = nullptr);
+
+
+ /* ConvertUnicharLineBreaks
+ * Convert line breaks in the supplied string, allocating and returning
+ * a new buffer. Returns nullptr on failure.
+ * @param aSrc: the source string. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source, in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static char16_t* ConvertUnicharLineBreaks(const char16_t* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen, int32_t* aOutLen = nullptr);
+
+
+ /* ConvertStringLineBreaks
+ * Convert line breaks in the supplied string, changing the string buffer (i.e. in-place conversion)
+ * @param ioString: the string to be converted.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source, in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ */
+ static nsresult ConvertStringLineBreaks(nsString& aIoString,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks);
+
+
+ /* ConvertLineBreaksInSitu
+ * Convert line breaks in place if possible. NOTE: THIS MAY REALLOCATE THE BUFFER,
+ * BUT IT WON'T FREE THE OLD BUFFER (because it doesn't know how). So be prepared
+ * to keep a copy of the old pointer, and free it if this passes back a new pointer.
+ * ALSO NOTE: DON'T PASS A STATIC STRING POINTER TO THIS FUNCTION.
+ *
+ * @param ioBuffer: the source buffer. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static nsresult ConvertLineBreaksInSitu(char** aIoBuffer,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen,
+ int32_t* aOutLen = nullptr);
+
+
+ /* ConvertUnicharLineBreaksInSitu
+ * Convert line breaks in place if possible. NOTE: THIS MAY REALLOCATE THE BUFFER,
+ * BUT IT WON'T FREE THE OLD BUFFER (because it doesn't know how). So be prepared
+ * to keep a copy of the old pointer, and free it if this passes back a new pointer.
+ *
+ * @param ioBuffer: the source buffer. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static nsresult ConvertUnicharLineBreaksInSitu(char16_t** aIoBuffer,
+ ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen,
+ int32_t* aOutLen = nullptr);
+
+};
+
+#endif // nsLinebreakConverter_h_
diff --git a/xpcom/io/nsLocalFile.h b/xpcom/io/nsLocalFile.h
new file mode 100644
index 000000000..f7bdb86f7
--- /dev/null
+++ b/xpcom/io/nsLocalFile.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 build.
+ */
+
+#ifndef _NS_LOCAL_FILE_H_
+#define _NS_LOCAL_FILE_H_
+
+#include "nscore.h"
+
+#define NS_LOCAL_FILE_CID {0x2e23e220, 0x60be, 0x11d3, {0x8c, 0x4a, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
+
+#define NS_DECL_NSLOCALFILE_UNICODE_METHODS \
+ nsresult AppendUnicode(const char16_t *aNode); \
+ nsresult GetUnicodeLeafName(char16_t **aLeafName); \
+ nsresult SetUnicodeLeafName(const char16_t *aLeafName); \
+ nsresult CopyToUnicode(nsIFile *aNewParentDir, const char16_t *aNewLeafName); \
+ nsresult CopyToFollowingLinksUnicode(nsIFile *aNewParentDir, const char16_t *aNewLeafName); \
+ nsresult MoveToUnicode(nsIFile *aNewParentDir, const char16_t *aNewLeafName); \
+ nsresult GetUnicodeTarget(char16_t **aTarget); \
+ nsresult GetUnicodePath(char16_t **aPath); \
+ nsresult InitWithUnicodePath(const char16_t *aPath); \
+ nsresult AppendRelativeUnicodePath(const char16_t *aRelativePath);
+
+// XPCOMInit needs to know about how we are implemented,
+// so here we will export it. Other users should not depend
+// on this.
+
+#include <errno.h>
+#include "nsILocalFile.h"
+
+#ifdef XP_WIN
+#include "nsLocalFileWin.h"
+#elif defined(XP_UNIX)
+#include "nsLocalFileUnix.h"
+#else
+#error NOT_IMPLEMENTED
+#endif
+
+#define NSRESULT_FOR_RETURN(ret) (((ret) < 0) ? NSRESULT_FOR_ERRNO() : NS_OK)
+
+inline nsresult
+nsresultForErrno(int aErr)
+{
+ switch (aErr) {
+ case 0:
+ return NS_OK;
+#ifdef EDQUOT
+ case EDQUOT: /* Quota exceeded */
+ // FALLTHROUGH to return NS_ERROR_FILE_DISK_FULL
+#endif
+ case ENOSPC:
+ return NS_ERROR_FILE_DISK_FULL;
+#ifdef EISDIR
+ case EISDIR: /* Is a directory. */
+ return NS_ERROR_FILE_IS_DIRECTORY;
+#endif
+ case ENAMETOOLONG:
+ return NS_ERROR_FILE_NAME_TOO_LONG;
+ case ENOEXEC: /* Executable file format error. */
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ case ENOENT:
+ return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
+ case ENOTDIR:
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+#ifdef ELOOP
+ case ELOOP:
+ return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+#endif /* ELOOP */
+#ifdef ENOLINK
+ case ENOLINK:
+ return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+#endif /* ENOLINK */
+ case EEXIST:
+ return NS_ERROR_FILE_ALREADY_EXISTS;
+#ifdef EPERM
+ case EPERM:
+#endif /* EPERM */
+ case EACCES:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+#ifdef EROFS
+ case EROFS: /* Read-only file system. */
+ return NS_ERROR_FILE_READ_ONLY;
+#endif
+ /*
+ * On AIX 4.3, ENOTEMPTY is defined as EEXIST,
+ * so there can't be cases for both without
+ * preprocessing.
+ */
+#if ENOTEMPTY != EEXIST
+ case ENOTEMPTY:
+ return NS_ERROR_FILE_DIR_NOT_EMPTY;
+#endif /* ENOTEMPTY != EEXIST */
+ /* Note that nsIFile.createUnique() returns
+ NS_ERROR_FILE_TOO_BIG when it cannot create a temporary
+ file with a unique filename.
+ See https://developer.mozilla.org/en-US/docs/Table_Of_Errors
+ Other usages of NS_ERROR_FILE_TOO_BIG in the source tree
+ are in line with the POSIX semantics of EFBIG.
+ So this is a reasonably good approximation.
+ */
+ case EFBIG: /* File too large. */
+ return NS_ERROR_FILE_TOO_BIG;
+
+ default:
+ return NS_ERROR_FAILURE;
+ }
+}
+
+#define NSRESULT_FOR_ERRNO() nsresultForErrno(errno)
+
+void NS_StartupLocalFile();
+void NS_ShutdownLocalFile();
+
+#endif
diff --git a/xpcom/io/nsLocalFileCommon.cpp b/xpcom/io/nsLocalFileCommon.cpp
new file mode 100644
index 000000000..8fbb7227d
--- /dev/null
+++ b/xpcom/io/nsLocalFileCommon.cpp
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIServiceManager.h"
+
+#include "nsLocalFile.h" // includes platform-specific headers
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsReadableUtils.h"
+#include "nsPrintfCString.h"
+#include "nsCRT.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsUTF8Utils.h"
+
+#ifdef XP_WIN
+#include <string.h>
+#endif
+
+
+void
+NS_StartupLocalFile()
+{
+ nsLocalFile::GlobalInit();
+}
+
+void
+NS_ShutdownLocalFile()
+{
+ nsLocalFile::GlobalShutdown();
+}
+
+#if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
+NS_IMETHODIMP
+nsLocalFile::InitWithFile(nsIFile* aFile)
+{
+ if (NS_WARN_IF(!aFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoCString path;
+ aFile->GetNativePath(path);
+ if (path.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ return InitWithNativePath(path);
+}
+#endif
+
+#define kMaxFilenameLength 255
+#define kMaxExtensionLength 100
+#define kMaxSequenceNumberLength 5 // "-9999"
+// requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
+
+NS_IMETHODIMP
+nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes)
+{
+ nsresult rv;
+ bool longName;
+
+#ifdef XP_WIN
+ nsAutoString pathName, leafName, rootName, suffix;
+ rv = GetPath(pathName);
+#else
+ nsAutoCString pathName, leafName, rootName, suffix;
+ rv = GetNativePath(pathName);
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ longName = (pathName.Length() + kMaxSequenceNumberLength >
+ kMaxFilenameLength);
+ if (!longName) {
+ rv = Create(aType, aAttributes);
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+ return rv;
+ }
+ }
+
+#ifdef XP_WIN
+ rv = GetLeafName(leafName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ const int32_t lastDot = leafName.RFindChar(char16_t('.'));
+#else
+ rv = GetNativeLeafName(leafName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ const int32_t lastDot = leafName.RFindChar('.');
+#endif
+
+ if (lastDot == kNotFound) {
+ rootName = leafName;
+ } else {
+ suffix = Substring(leafName, lastDot); // include '.'
+ rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
+ }
+
+ if (longName) {
+ int32_t maxRootLength = (kMaxFilenameLength -
+ (pathName.Length() - leafName.Length()) -
+ suffix.Length() - kMaxSequenceNumberLength);
+
+ // We cannot create an item inside a directory whose name is too long.
+ // Also, ensure that at least one character remains after we truncate
+ // the root name, as we don't want to end up with an empty leaf name.
+ if (maxRootLength < 2) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+#ifdef XP_WIN
+ // ensure that we don't cut the name in mid-UTF16-character
+ rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ?
+ maxRootLength - 1 : maxRootLength);
+ SetLeafName(rootName + suffix);
+#else
+ if (NS_IsNativeUTF8()) {
+ // ensure that we don't cut the name in mid-UTF8-character
+ // (assume the name is valid UTF8 to begin with)
+ while (UTF8traits::isInSeq(rootName[maxRootLength])) {
+ --maxRootLength;
+ }
+
+ // Another check to avoid ending up with an empty leaf name.
+ if (maxRootLength == 0 && suffix.IsEmpty()) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ }
+
+ rootName.SetLength(maxRootLength);
+ SetNativeLeafName(rootName + suffix);
+#endif
+ nsresult rvCreate = Create(aType, aAttributes);
+ if (rvCreate != NS_ERROR_FILE_ALREADY_EXISTS) {
+ return rvCreate;
+ }
+ }
+
+ for (int indx = 1; indx < 10000; ++indx) {
+ // start with "Picture-1.jpg" after "Picture.jpg" exists
+#ifdef XP_WIN
+ SetLeafName(rootName +
+ NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) +
+ suffix);
+#else
+ SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
+#endif
+ rv = Create(aType, aAttributes);
+ if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+ return rv;
+ }
+ }
+
+ // The disk is full, sort of
+ return NS_ERROR_FILE_TOO_BIG;
+}
+
+#if defined(XP_WIN)
+static const char16_t kPathSeparatorChar = '\\';
+#elif defined(XP_UNIX)
+static const char16_t kPathSeparatorChar = '/';
+#else
+#error Need to define file path separator for your platform
+#endif
+
+static void
+SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray)
+{
+ if (*aPath == 0) {
+ return;
+ }
+
+ if (*aPath == kPathSeparatorChar) {
+ aPath++;
+ }
+ aNodeArray.AppendElement(aPath);
+
+ for (char16_t* cp = aPath; *cp != 0; ++cp) {
+ if (*cp == kPathSeparatorChar) {
+ *cp++ = 0;
+ if (*cp == 0) {
+ break;
+ }
+ aNodeArray.AppendElement(cp);
+ }
+ }
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult)
+{
+ if (NS_WARN_IF(!aFromFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ //
+ // aResult will be UTF-8 encoded
+ //
+
+ nsresult rv;
+ aResult.Truncate(0);
+
+ nsAutoString thisPath, fromPath;
+ AutoTArray<char16_t*, 32> thisNodes;
+ AutoTArray<char16_t*, 32> fromNodes;
+
+ rv = GetPath(thisPath);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = aFromFile->GetPath(fromPath);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // get raw pointer to mutable string buffer
+ char16_t* thisPathPtr;
+ thisPath.BeginWriting(thisPathPtr);
+ char16_t* fromPathPtr;
+ fromPath.BeginWriting(fromPathPtr);
+
+ SplitPath(thisPathPtr, thisNodes);
+ SplitPath(fromPathPtr, fromNodes);
+
+ size_t nodeIndex;
+ for (nodeIndex = 0;
+ nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
+ ++nodeIndex) {
+#ifdef XP_WIN
+ if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
+ char16ptr_t(fromNodes[nodeIndex]))) {
+ break;
+ }
+#else
+ if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
+ break;
+ }
+#endif
+ }
+
+ size_t branchIndex = nodeIndex;
+ for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
+ aResult.AppendLiteral("../");
+ }
+ for (nodeIndex = branchIndex; nodeIndex < thisNodes.Length(); ++nodeIndex) {
+ NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
+ aResult.Append(nodeStr);
+ if (nodeIndex + 1 < thisNodes.Length()) {
+ aResult.Append('/');
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
+ const nsACString& aRelativeDesc)
+{
+ NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
+
+ nsCOMPtr<nsIFile> targetFile;
+ nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ //
+ // aRelativeDesc is UTF-8 encoded
+ //
+
+ nsCString::const_iterator strBegin, strEnd;
+ aRelativeDesc.BeginReading(strBegin);
+ aRelativeDesc.EndReading(strEnd);
+
+ nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
+ nsCString::const_iterator pos(strBegin);
+
+ nsCOMPtr<nsIFile> parentDir;
+ while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
+ rv = targetFile->GetParent(getter_AddRefs(parentDir));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!parentDir) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ targetFile = parentDir;
+
+ nodeBegin = nodeEnd;
+ pos = nodeEnd;
+ nodeEnd = strEnd;
+ }
+
+ nodeBegin = nodeEnd = pos;
+ while (nodeEnd != strEnd) {
+ FindCharInReadable('/', nodeEnd, strEnd);
+ targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
+ if (nodeEnd != strEnd) { // If there's more left in the string, inc over the '/' nodeEnd is on.
+ ++nodeEnd;
+ }
+ nodeBegin = nodeEnd;
+ }
+
+ return InitWithFile(targetFile);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult)
+{
+ return GetRelativeDescriptor(aFromFile, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetRelativePath(nsIFile* aFromFile,
+ const nsACString& aRelativePath)
+{
+ return SetRelativeDescriptor(aFromFile, aRelativePath);
+}
diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp
new file mode 100644
index 000000000..194e5835e
--- /dev/null
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -0,0 +1,2715 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Implementation of nsIFile for "unixy" systems.
+ */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Sprintf.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <locale.h>
+
+#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
+#define USE_LINUX_QUOTACTL
+#include <sys/mount.h>
+#include <sys/quota.h>
+#include <sys/sysmacros.h>
+#ifndef BLOCK_SIZE
+#define BLOCK_SIZE 1024 /* kernel block size */
+#endif
+#endif
+
+#include "xpcom-private.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsLocalFile.h"
+#include "nsIComponentManager.h"
+#include "nsXPIDLString.h"
+#include "prproces.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsISimpleEnumerator.h"
+#include "private/pprio.h"
+#include "prlink.h"
+
+#ifdef MOZ_WIDGET_GTK
+#include "nsIGIOService.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include <Carbon/Carbon.h>
+#include "CocoaFileUtils.h"
+#include "prmem.h"
+#include "plbase64.h"
+
+static nsresult MacErrorMapper(OSErr inErr);
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "GeneratedJNIWrappers.h"
+#include "nsIMIMEService.h"
+#include <linux/magic.h>
+#endif
+
+#ifdef MOZ_ENABLE_CONTENTACTION
+#include <contentaction/contentaction.h>
+#endif
+
+#include "nsNativeCharsetUtils.h"
+#include "nsTraceRefcnt.h"
+#include "nsHashKeys.h"
+
+using namespace mozilla;
+
+#define ENSURE_STAT_CACHE() \
+ PR_BEGIN_MACRO \
+ if (!FillStatCache()) \
+ return NSRESULT_FOR_ERRNO(); \
+ PR_END_MACRO
+
+#define CHECK_mPath() \
+ PR_BEGIN_MACRO \
+ if (mPath.IsEmpty()) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+/* directory enumerator */
+class nsDirEnumeratorUnix final
+ : public nsISimpleEnumerator
+ , public nsIDirectoryEnumerator
+{
+public:
+ nsDirEnumeratorUnix();
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsIDirectoryEnumerator interface
+ NS_DECL_NSIDIRECTORYENUMERATOR
+
+ NS_IMETHOD Init(nsLocalFile* aParent, bool aIgnored);
+
+private:
+ ~nsDirEnumeratorUnix();
+
+protected:
+ NS_IMETHOD GetNextEntry();
+
+ DIR* mDir;
+ struct dirent* mEntry;
+ nsCString mParentPath;
+};
+
+nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
+ mDir(nullptr),
+ mEntry(nullptr)
+{
+}
+
+nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
+{
+ Close();
+}
+
+NS_IMPL_ISUPPORTS(nsDirEnumeratorUnix, nsISimpleEnumerator,
+ nsIDirectoryEnumerator)
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::Init(nsLocalFile* aParent,
+ bool aResolveSymlinks /*ignored*/)
+{
+ nsAutoCString dirPath;
+ if (NS_FAILED(aParent->GetNativePath(dirPath)) ||
+ dirPath.IsEmpty()) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ if (NS_FAILED(aParent->GetNativePath(mParentPath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mDir = opendir(dirPath.get());
+ if (!mDir) {
+ return NSRESULT_FOR_ERRNO();
+ }
+ return GetNextEntry();
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::HasMoreElements(bool* aResult)
+{
+ *aResult = mDir && mEntry;
+ if (!*aResult) {
+ Close();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNext(nsISupports** aResult)
+{
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = GetNextFile(getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ NS_IF_ADDREF(*aResult = file);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNextEntry()
+{
+ do {
+ errno = 0;
+ mEntry = readdir(mDir);
+
+ // end of dir or error
+ if (!mEntry) {
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ // keep going past "." and ".."
+ } while (mEntry->d_name[0] == '.' &&
+ (mEntry->d_name[1] == '\0' || // .\0
+ (mEntry->d_name[1] == '.' &&
+ mEntry->d_name[2] == '\0'))); // ..\0
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNextFile(nsIFile** aResult)
+{
+ nsresult rv;
+ if (!mDir || !mEntry) {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file = new nsLocalFile();
+
+ if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
+ NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) {
+ return rv;
+ }
+
+ file.forget(aResult);
+ return GetNextEntry();
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::Close()
+{
+ if (mDir) {
+ closedir(mDir);
+ mDir = nullptr;
+ }
+ return NS_OK;
+}
+
+nsLocalFile::nsLocalFile()
+{
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& aOther)
+ : mPath(aOther.mPath)
+{
+}
+
+#ifdef MOZ_WIDGET_COCOA
+NS_IMPL_ISUPPORTS(nsLocalFile,
+ nsILocalFileMac,
+ nsILocalFile,
+ nsIFile,
+ nsIHashable)
+#else
+NS_IMPL_ISUPPORTS(nsLocalFile,
+ nsILocalFile,
+ nsIFile,
+ nsIHashable)
+#endif
+
+nsresult
+nsLocalFile::nsLocalFileConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(!aInstancePtr)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ *aInstancePtr = nullptr;
+
+ nsCOMPtr<nsIFile> inst = new nsLocalFile();
+ return inst->QueryInterface(aIID, aInstancePtr);
+}
+
+bool
+nsLocalFile::FillStatCache()
+{
+ if (STAT(mPath.get(), &mCachedStat) == -1) {
+ // try lstat it may be a symlink
+ if (LSTAT(mPath.get(), &mCachedStat) == -1) {
+ return false;
+ }
+ }
+ return true;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile** aFile)
+{
+ // Just copy-construct ourselves
+ RefPtr<nsLocalFile> copy = new nsLocalFile(*this);
+ copy.forget(aFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
+{
+ if (aFilePath.EqualsLiteral("~") ||
+ Substring(aFilePath, 0, 2).EqualsLiteral("~/")) {
+ nsCOMPtr<nsIFile> homeDir;
+ nsAutoCString homePath;
+ if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
+ getter_AddRefs(homeDir))) ||
+ NS_FAILED(homeDir->GetNativePath(homePath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPath = homePath;
+ if (aFilePath.Length() > 2) {
+ mPath.Append(Substring(aFilePath, 1, aFilePath.Length() - 1));
+ }
+ } else {
+ if (aFilePath.IsEmpty() || aFilePath.First() != '/') {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ mPath = aFilePath;
+ }
+
+ // trim off trailing slashes
+ ssize_t len = mPath.Length();
+ while ((len > 1) && (mPath[len - 1] == '/')) {
+ --len;
+ }
+ mPath.SetLength(len);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CreateAllAncestors(uint32_t aPermissions)
+{
+ // <jband> I promise to play nice
+ char* buffer = mPath.BeginWriting();
+ char* slashp = buffer;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: before: %s\n", buffer);
+#endif
+
+ while ((slashp = strchr(slashp + 1, '/'))) {
+ /*
+ * Sequences of '/' are equivalent to a single '/'.
+ */
+ if (slashp[1] == '/') {
+ continue;
+ }
+
+ /*
+ * If the path has a trailing slash, don't make the last component,
+ * because we'll get EEXIST in Create when we try to build the final
+ * component again, and it's easier to condition the logic here than
+ * there.
+ */
+ if (slashp[1] == '\0') {
+ break;
+ }
+
+ /* Temporarily NUL-terminate here */
+ *slashp = '\0';
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
+#endif
+ int mkdir_result = mkdir(buffer, aPermissions);
+ int mkdir_errno = errno;
+ if (mkdir_result == -1) {
+ /*
+ * Always set |errno| to EEXIST if the dir already exists
+ * (we have to do this here since the errno value is not consistent
+ * in all cases - various reasons like different platform,
+ * automounter-controlled dir, etc. can affect it (see bug 125489
+ * for details)).
+ */
+ if (access(buffer, F_OK) == 0) {
+ mkdir_errno = EEXIST;
+ }
+ }
+
+ /* Put the / back before we (maybe) return */
+ *slashp = '/';
+
+ /*
+ * We could get EEXIST for an existing file -- not directory --
+ * with the name of one of our ancestors, but that's OK: we'll get
+ * ENOTDIR when we try to make the next component in the path,
+ * either here on back in Create, and error out appropriately.
+ */
+ if (mkdir_result == -1 && mkdir_errno != EEXIST) {
+ return nsresultForErrno(mkdir_errno);
+ }
+ }
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: after: %s\n", buffer);
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
+ PRFileDesc** aResult)
+{
+ *aResult = PR_Open(mPath.get(), aFlags, aMode);
+ if (!*aResult) {
+ return NS_ErrorAccordingToNSPR();
+ }
+
+ if (aFlags & DELETE_ON_CLOSE) {
+ PR_Delete(mPath.get());
+ }
+
+#if defined(HAVE_POSIX_FADVISE)
+ if (aFlags & OS_READAHEAD) {
+ posix_fadvise(PR_FileDesc2NativeHandle(*aResult), 0, 0,
+ POSIX_FADV_SEQUENTIAL);
+ }
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult)
+{
+ *aResult = fopen(mPath.get(), aMode);
+ if (!*aResult) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static int
+do_create(const char* aPath, int aFlags, mode_t aMode, PRFileDesc** aResult)
+{
+ *aResult = PR_Open(aPath, aFlags, aMode);
+ return *aResult ? 0 : -1;
+}
+
+static int
+do_mkdir(const char* aPath, int aFlags, mode_t aMode, PRFileDesc** aResult)
+{
+ *aResult = nullptr;
+ return mkdir(aPath, aMode);
+}
+
+nsresult
+nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
+ uint32_t aPermissions, PRFileDesc** aResult)
+{
+ if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) {
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+ }
+
+ int (*createFunc)(const char*, int, mode_t, PRFileDesc**) =
+ (aType == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
+
+ int result = createFunc(mPath.get(), aFlags, aPermissions, aResult);
+ if (result == -1 && errno == ENOENT) {
+ /*
+ * If we failed because of missing ancestor components, try to create
+ * them and then retry the original creation.
+ *
+ * Ancestor directories get the same permissions as the file we're
+ * creating, with the X bit set for each of (user,group,other) with
+ * an R bit in the original permissions. If you want to do anything
+ * fancy like setgid or sticky bits, do it by hand.
+ */
+ int dirperm = aPermissions;
+ if (aPermissions & S_IRUSR) {
+ dirperm |= S_IXUSR;
+ }
+ if (aPermissions & S_IRGRP) {
+ dirperm |= S_IXGRP;
+ }
+ if (aPermissions & S_IROTH) {
+ dirperm |= S_IXOTH;
+ }
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", aPermissions,
+ dirperm);
+#endif
+
+ if (NS_FAILED(CreateAllAncestors(dirperm))) {
+ return NS_ERROR_FAILURE;
+ }
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
+#endif
+ result = createFunc(mPath.get(), aFlags, aPermissions, aResult);
+ }
+ return NSRESULT_FOR_RETURN(result);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Create(uint32_t aType, uint32_t aPermissions)
+{
+ PRFileDesc* junk = nullptr;
+ nsresult rv = CreateAndKeepOpen(aType,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
+ PR_EXCL,
+ aPermissions,
+ &junk);
+ if (junk) {
+ PR_Close(junk);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString& aFragment)
+{
+ if (aFragment.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // only one component of path can be appended
+ nsACString::const_iterator begin, end;
+ if (FindCharInReadable('/', aFragment.BeginReading(begin),
+ aFragment.EndReading(end))) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ return AppendRelativeNativePath(aFragment);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString& aFragment)
+{
+ if (aFragment.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // No leading '/'
+ if (aFragment.First() == '/') {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ if (!mPath.EqualsLiteral("/")) {
+ mPath.Append('/');
+ }
+ mPath.Append(aFragment);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ char resolved_path[PATH_MAX] = "";
+ char* resolved_path_ptr = nullptr;
+
+ resolved_path_ptr = realpath(mPath.get(), resolved_path);
+
+ // if there is an error, the return is null.
+ if (!resolved_path_ptr) {
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ mPath = resolved_path;
+ return NS_OK;
+}
+
+void
+nsLocalFile::LocateNativeLeafName(nsACString::const_iterator& aBegin,
+ nsACString::const_iterator& aEnd)
+{
+ // XXX perhaps we should cache this??
+
+ mPath.BeginReading(aBegin);
+ mPath.EndReading(aEnd);
+
+ nsACString::const_iterator it = aEnd;
+ nsACString::const_iterator stop = aBegin;
+ --stop;
+ while (--it != stop) {
+ if (*it == '/') {
+ aBegin = ++it;
+ return;
+ }
+ }
+ // else, the entire path is the leaf name (which means this
+ // isn't an absolute path... unexpected??)
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString& aLeafName)
+{
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ aLeafName = Substring(begin, end);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString& aLeafName)
+{
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString& aResult)
+{
+ aResult = mPath;
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::GetNativeTargetPathName(nsIFile* aNewParent,
+ const nsACString& aNewName,
+ nsACString& aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> oldParent;
+
+ if (!aNewParent) {
+ if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent)))) {
+ return rv;
+ }
+ aNewParent = oldParent.get();
+ } else {
+ // check to see if our target directory exists
+ bool targetExists;
+ if (NS_FAILED(rv = aNewParent->Exists(&targetExists))) {
+ return rv;
+ }
+
+ if (!targetExists) {
+ // XXX create the new directory with some permissions
+ rv = aNewParent->Create(DIRECTORY_TYPE, 0755);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ // make sure that the target is actually a directory
+ bool targetIsDirectory;
+ if (NS_FAILED(rv = aNewParent->IsDirectory(&targetIsDirectory))) {
+ return rv;
+ }
+ if (!targetIsDirectory) {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+ }
+
+ nsACString::const_iterator nameBegin, nameEnd;
+ if (!aNewName.IsEmpty()) {
+ aNewName.BeginReading(nameBegin);
+ aNewName.EndReading(nameEnd);
+ } else {
+ LocateNativeLeafName(nameBegin, nameEnd);
+ }
+
+ nsAutoCString dirName;
+ if (NS_FAILED(rv = aNewParent->GetNativePath(dirName))) {
+ return rv;
+ }
+
+ aResult = dirName + NS_LITERAL_CSTRING("/") + Substring(nameBegin, nameEnd);
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::CopyDirectoryTo(nsIFile* aNewParent)
+{
+ nsresult rv;
+ /*
+ * dirCheck is used for various boolean test results such as from Equals,
+ * Exists, isDir, etc.
+ */
+ bool dirCheck, isSymlink;
+ uint32_t oldPerms;
+
+ if (NS_FAILED(rv = IsDirectory(&dirCheck))) {
+ return rv;
+ }
+ if (!dirCheck) {
+ return CopyToNative(aNewParent, EmptyCString());
+ }
+
+ if (NS_FAILED(rv = Equals(aNewParent, &dirCheck))) {
+ return rv;
+ }
+ if (dirCheck) {
+ // can't copy dir to itself
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) {
+ return rv;
+ }
+ // get the dirs old permissions
+ if (NS_FAILED(rv = GetPermissions(&oldPerms))) {
+ return rv;
+ }
+ if (!dirCheck) {
+ if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) {
+ return rv;
+ }
+ } else { // dir exists lets try to use leaf
+ nsAutoCString leafName;
+ if (NS_FAILED(rv = GetNativeLeafName(leafName))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = aNewParent->AppendNative(leafName))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) {
+ return rv;
+ }
+ if (dirCheck) {
+ return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
+ }
+ if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) {
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> dirIterator;
+ if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator)))) {
+ return rv;
+ }
+
+ bool hasMore = false;
+ while (dirIterator->HasMoreElements(&hasMore), hasMore) {
+ nsCOMPtr<nsISupports> supports;
+ nsCOMPtr<nsIFile> entry;
+ rv = dirIterator->GetNext(getter_AddRefs(supports));
+ entry = do_QueryInterface(supports);
+ if (NS_FAILED(rv) || !entry) {
+ continue;
+ }
+ if (NS_FAILED(rv = entry->IsSymlink(&isSymlink))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = entry->IsDirectory(&dirCheck))) {
+ return rv;
+ }
+ if (dirCheck && !isSymlink) {
+ nsCOMPtr<nsIFile> destClone;
+ rv = aNewParent->Clone(getter_AddRefs(destClone));
+ if (NS_SUCCEEDED(rv)) {
+ if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) {
+#ifdef DEBUG
+ nsresult rv2;
+ nsAutoCString pathName;
+ if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) {
+ return rv2;
+ }
+ printf("Operation not supported: %s\n", pathName.get());
+#endif
+ if (rv == NS_ERROR_OUT_OF_MEMORY) {
+ return rv;
+ }
+ continue;
+ }
+ }
+ } else {
+ if (NS_FAILED(rv = entry->CopyToNative(aNewParent, EmptyCString()))) {
+#ifdef DEBUG
+ nsresult rv2;
+ nsAutoCString pathName;
+ if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) {
+ return rv2;
+ }
+ printf("Operation not supported: %s\n", pathName.get());
+#endif
+ if (rv == NS_ERROR_OUT_OF_MEMORY) {
+ return rv;
+ }
+ continue;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile* aNewParent, const nsACString& aNewName)
+{
+ nsresult rv;
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // we copy the parent here so 'aNewParent' remains immutable
+ nsCOMPtr <nsIFile> workParent;
+ if (aNewParent) {
+ if (NS_FAILED(rv = aNewParent->Clone(getter_AddRefs(workParent)))) {
+ return rv;
+ }
+ } else {
+ if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent)))) {
+ return rv;
+ }
+ }
+
+ // check to see if we are a directory or if we are a file
+ bool isDirectory;
+ if (NS_FAILED(rv = IsDirectory(&isDirectory))) {
+ return rv;
+ }
+
+ nsAutoCString newPathName;
+ if (isDirectory) {
+ if (!aNewName.IsEmpty()) {
+ if (NS_FAILED(rv = workParent->AppendNative(aNewName))) {
+ return rv;
+ }
+ } else {
+ if (NS_FAILED(rv = GetNativeLeafName(newPathName))) {
+ return rv;
+ }
+ if (NS_FAILED(rv = workParent->AppendNative(newPathName))) {
+ return rv;
+ }
+ }
+ if (NS_FAILED(rv = CopyDirectoryTo(workParent))) {
+ return rv;
+ }
+ } else {
+ rv = GetNativeTargetPathName(workParent, aNewName, newPathName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+#ifdef DEBUG_blizzard
+ printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
+#endif
+
+ // actually create the file.
+ nsLocalFile* newFile = new nsLocalFile();
+ nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
+
+ rv = newFile->InitWithNativePath(newPathName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // get the old permissions
+ uint32_t myPerms;
+ GetPermissions(&myPerms);
+
+ // Create the new file with the old file's permissions, even if write
+ // permission is missing. We can't create with write permission and
+ // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
+ // But we can write to a read-only file on all Unix filesystems if we
+ // open it successfully for writing.
+
+ PRFileDesc* newFD;
+ rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ myPerms,
+ &newFD);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // open the old file, too
+ bool specialFile;
+ if (NS_FAILED(rv = IsSpecial(&specialFile))) {
+ PR_Close(newFD);
+ return rv;
+ }
+ if (specialFile) {
+#ifdef DEBUG
+ printf("Operation not supported: %s\n", mPath.get());
+#endif
+ // make sure to clean up properly
+ PR_Close(newFD);
+ return NS_OK;
+ }
+
+ PRFileDesc* oldFD;
+ rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
+ if (NS_FAILED(rv)) {
+ // make sure to clean up properly
+ PR_Close(newFD);
+ return rv;
+ }
+
+#ifdef DEBUG_blizzard
+ int32_t totalRead = 0;
+ int32_t totalWritten = 0;
+#endif
+ char buf[BUFSIZ];
+ int32_t bytesRead;
+
+ // record PR_Write() error for better error message later.
+ nsresult saved_write_error = NS_OK;
+ nsresult saved_read_error = NS_OK;
+ nsresult saved_read_close_error = NS_OK;
+ nsresult saved_write_close_error = NS_OK;
+
+ // DONE: Does PR_Read() return bytesRead < 0 for error?
+ // Yes., The errors from PR_Read are not so common and
+ // the value may not have correspondence in NS_ERROR_*, but
+ // we do catch it still, immediately after while() loop.
+ // We can differentiate errors pf PR_Read and PR_Write by
+ // looking at saved_write_error value. If PR_Write error occurs (and not
+ // PR_Read() error), save_write_error is not NS_OK.
+
+ while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
+#ifdef DEBUG_blizzard
+ totalRead += bytesRead;
+#endif
+
+ // PR_Write promises never to do a short write
+ int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
+ if (bytesWritten < 0) {
+ saved_write_error = NSRESULT_FOR_ERRNO();
+ bytesRead = -1;
+ break;
+ }
+ NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
+
+#ifdef DEBUG_blizzard
+ totalWritten += bytesWritten;
+#endif
+ }
+
+ // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR,
+ // we are better off to prepare for retrying. But we need confirmation if
+ // EINTR is returned.
+
+ // Record error if PR_Read() failed.
+ // Must be done before any other I/O which may reset errno.
+ if (bytesRead < 0 && saved_write_error == NS_OK) {
+ saved_read_error = NSRESULT_FOR_ERRNO();
+ }
+
+#ifdef DEBUG_blizzard
+ printf("read %d bytes, wrote %d bytes\n",
+ totalRead, totalWritten);
+#endif
+
+ // DONE: Errors of close can occur. Read man page of
+ // close(2);
+ // This is likely to happen if the file system is remote file
+ // system (NFS, CIFS, etc.) and network outage occurs.
+ // At least, we should tell the user that filesystem/disk is
+ // hosed (possibly due to network error, hard disk failure,
+ // etc.) so that users can take remedial action.
+
+ // close the files
+ if (PR_Close(newFD) < 0) {
+ saved_write_close_error = NSRESULT_FOR_ERRNO();
+#if DEBUG
+ // This error merits printing.
+ fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno);
+#endif
+ }
+
+ if (PR_Close(oldFD) < 0) {
+ saved_read_close_error = NSRESULT_FOR_ERRNO();
+#if DEBUG
+ fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno);
+#endif
+ }
+
+ // Let us report the failure to write and read.
+ // check for write/read error after cleaning up
+ if (bytesRead < 0) {
+ if (saved_write_error != NS_OK) {
+ return saved_write_error;
+ } else if (saved_read_error != NS_OK) {
+ return saved_read_error;
+ }
+#if DEBUG
+ else { // sanity check. Die and debug.
+ MOZ_ASSERT(0);
+ }
+#endif
+ }
+
+ if (saved_write_close_error != NS_OK) {
+ return saved_write_close_error;
+ }
+ if (saved_read_close_error != NS_OK) {
+ return saved_read_close_error;
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile* aNewParent,
+ const nsACString& aNewName)
+{
+ return CopyToNative(aNewParent, aNewName);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile* aNewParent, const nsACString& aNewName)
+{
+ nsresult rv;
+
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // check to make sure that we have a new parent
+ nsAutoCString newPathName;
+ rv = GetNativeTargetPathName(aNewParent, aNewName, newPathName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // try for atomic rename, falling back to copy/delete
+ if (rename(mPath.get(), newPathName.get()) < 0) {
+ if (errno == EXDEV) {
+ rv = CopyToNative(aNewParent, aNewName);
+ if (NS_SUCCEEDED(rv)) {
+ rv = Remove(true);
+ }
+ } else {
+ rv = NSRESULT_FOR_ERRNO();
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ // Adjust this
+ mPath = newPathName;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(bool aRecursive)
+{
+ CHECK_mPath();
+ ENSURE_STAT_CACHE();
+
+ bool isSymLink;
+
+ nsresult rv = IsSymlink(&isSymLink);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (isSymLink || !S_ISDIR(mCachedStat.st_mode)) {
+ return NSRESULT_FOR_RETURN(unlink(mPath.get()));
+ }
+
+ if (aRecursive) {
+ nsDirEnumeratorUnix* dir = new nsDirEnumeratorUnix();
+
+ nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
+
+ rv = dir->Init(this, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool more;
+ while (dir->HasMoreElements(&more), more) {
+ nsCOMPtr<nsISupports> item;
+ rv = dir->GetNext(getter_AddRefs(item));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = file->Remove(aRecursive);
+
+#ifdef ANDROID
+ // See bug 580434 - Bionic gives us just deleted files
+ if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
+ continue;
+ }
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+
+ return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRTime* aLastModTime)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aLastModTime)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ PRFileInfo64 info;
+ if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS) {
+ return NSRESULT_FOR_ERRNO();
+ }
+ PRTime modTime = info.modifyTime;
+ if (modTime == 0) {
+ *aLastModTime = 0;
+ } else {
+ *aLastModTime = modTime / PR_USEC_PER_MSEC;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRTime aLastModTime)
+{
+ CHECK_mPath();
+
+ int result;
+ if (aLastModTime != 0) {
+ ENSURE_STAT_CACHE();
+ struct utimbuf ut;
+ ut.actime = mCachedStat.st_atime;
+
+ // convert milliseconds to seconds since the unix epoch
+ ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
+ result = utime(mPath.get(), &ut);
+ } else {
+ result = utime(mPath.get(), nullptr);
+ }
+ return NSRESULT_FOR_RETURN(result);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModTimeOfLink)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aLastModTimeOfLink)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ struct STAT sbuf;
+ if (LSTAT(mPath.get(), &sbuf) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+ *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
+
+ return NS_OK;
+}
+
+/*
+ * utime(2) may or may not dereference symlinks, joy.
+ */
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
+{
+ return SetLastModifiedTime(aLastModTimeOfLink);
+}
+
+/*
+ * Only send back permissions bits: maybe we want to send back the whole
+ * mode_t to permit checks against other file types?
+ */
+
+#define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(uint32_t* aPermissions)
+{
+ if (NS_WARN_IF(!aPermissions)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ ENSURE_STAT_CACHE();
+ *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissionsOfLink)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aPermissionsOfLink)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ struct STAT sbuf;
+ if (LSTAT(mPath.get(), &sbuf) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+ *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(uint32_t aPermissions)
+{
+ CHECK_mPath();
+
+ /*
+ * Race condition here: we should use fchmod instead, there's no way to
+ * guarantee the name still refers to the same file.
+ */
+ if (chmod(mPath.get(), aPermissions) >= 0) {
+ return NS_OK;
+ }
+#if defined(ANDROID) && defined(STATFS)
+ // For the time being, this is restricted for use by Android, but we
+ // will figure out what to do for all platforms in bug 638503
+ struct STATFS sfs;
+ if (STATFS(mPath.get(), &sfs) < 0) {
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ // if this is a FAT file system we can't set file permissions
+ if (sfs.f_type == MSDOS_SUPER_MAGIC) {
+ return NS_OK;
+ }
+#endif
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
+{
+ // There isn't a consistent mechanism for doing this on UNIX platforms. We
+ // might want to carefully implement this in the future though.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(int64_t* aFileSize)
+{
+ if (NS_WARN_IF(!aFileSize)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aFileSize = 0;
+ ENSURE_STAT_CACHE();
+
+ if (!S_ISDIR(mCachedStat.st_mode)) {
+ *aFileSize = (int64_t)mCachedStat.st_size;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(int64_t aFileSize)
+{
+ CHECK_mPath();
+
+#if defined(ANDROID)
+ /* no truncate on bionic */
+ int fd = open(mPath.get(), O_WRONLY);
+ if (fd == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ int ret = ftruncate(fd, (off_t)aFileSize);
+ close(fd);
+
+ if (ret == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+#elif defined(HAVE_TRUNCATE64)
+ if (truncate64(mPath.get(), (off64_t)aFileSize) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+#else
+ off_t size = (off_t)aFileSize;
+ if (truncate(mPath.get(), size) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aFileSize)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ struct STAT sbuf;
+ if (LSTAT(mPath.get(), &sbuf) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ *aFileSize = (int64_t)sbuf.st_size;
+ return NS_OK;
+}
+
+#if defined(USE_LINUX_QUOTACTL)
+/*
+ * Searches /proc/self/mountinfo for given device (Major:Minor),
+ * returns exported name from /dev
+ *
+ * Fails when /proc/self/mountinfo or diven device don't exist.
+ */
+static bool
+GetDeviceName(int aDeviceMajor, int aDeviceMinor, nsACString& aDeviceName)
+{
+ bool ret = false;
+
+ const int kMountInfoLineLength = 200;
+ const int kMountInfoDevPosition = 6;
+
+ char mountinfoLine[kMountInfoLineLength];
+ char deviceNum[kMountInfoLineLength];
+
+ SprintfLiteral(deviceNum, "%d:%d", aDeviceMajor, aDeviceMinor);
+
+ FILE* f = fopen("/proc/self/mountinfo", "rt");
+ if (!f) {
+ return ret;
+ }
+
+ // Expects /proc/self/mountinfo in format:
+ // 'ID ID major:minor root mountpoint flags - type devicename flags'
+ while (fgets(mountinfoLine, kMountInfoLineLength, f)) {
+ char* p_dev = strstr(mountinfoLine, deviceNum);
+
+ for (int i = 0; i < kMountInfoDevPosition && p_dev; ++i) {
+ p_dev = strchr(p_dev, ' ');
+ if (p_dev) {
+ p_dev++;
+ }
+ }
+
+ if (p_dev) {
+ char* p_dev_end = strchr(p_dev, ' ');
+ if (p_dev_end) {
+ *p_dev_end = '\0';
+ aDeviceName.Assign(p_dev);
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ fclose(f);
+ return ret;
+}
+#endif
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable)
+{
+ if (NS_WARN_IF(!aDiskSpaceAvailable)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // These systems have the operations necessary to check disk space.
+
+#ifdef STATFS
+
+ // check to make sure that mPath is properly initialized
+ CHECK_mPath();
+
+ struct STATFS fs_buf;
+
+ /*
+ * Members of the STATFS struct that you should know about:
+ * F_BSIZE = block size on disk.
+ * f_bavail = number of free blocks available to a non-superuser.
+ * f_bfree = number of total free blocks in file system.
+ */
+
+ if (STATFS(mPath.get(), &fs_buf) < 0) {
+ // The call to STATFS failed.
+#ifdef DEBUG
+ printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
+ *aDiskSpaceAvailable = (int64_t)fs_buf.F_BSIZE * fs_buf.f_bavail;
+
+#ifdef DEBUG_DISK_SPACE
+ printf("DiskSpaceAvailable: %lu bytes\n",
+ *aDiskSpaceAvailable);
+#endif
+
+#if defined(USE_LINUX_QUOTACTL)
+
+ if (!FillStatCache()) {
+ // Return available size from statfs
+ return NS_OK;
+ }
+
+ nsCString deviceName;
+ if (!GetDeviceName(major(mCachedStat.st_dev),
+ minor(mCachedStat.st_dev),
+ deviceName)) {
+ return NS_OK;
+ }
+
+ struct dqblk dq;
+ if (!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(),
+ getuid(), (caddr_t)&dq)
+#ifdef QIF_BLIMITS
+ && dq.dqb_valid & QIF_BLIMITS
+#endif
+ && dq.dqb_bhardlimit) {
+ int64_t QuotaSpaceAvailable = 0;
+ // dqb_bhardlimit is count of BLOCK_SIZE blocks, dqb_curspace is bytes
+ if ((BLOCK_SIZE * dq.dqb_bhardlimit) > dq.dqb_curspace)
+ QuotaSpaceAvailable = int64_t(BLOCK_SIZE * dq.dqb_bhardlimit - dq.dqb_curspace);
+ if (QuotaSpaceAvailable < *aDiskSpaceAvailable) {
+ *aDiskSpaceAvailable = QuotaSpaceAvailable;
+ }
+ }
+#endif
+
+ return NS_OK;
+
+#else
+ /*
+ * This platform doesn't have statfs or statvfs. I'm sure that there's
+ * a way to check for free disk space on platforms that don't have statfs
+ * (I'm SURE they have df, for example).
+ *
+ * Until we figure out how to do that, lets be honest and say that this
+ * command isn't implemented properly for these platforms yet.
+ */
+#ifdef DEBUG
+ printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
+#endif
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+#endif /* STATFS */
+
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile** aParent)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aParent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aParent = nullptr;
+
+ // if '/' we are at the top of the volume, return null
+ if (mPath.EqualsLiteral("/")) {
+ return NS_OK;
+ }
+
+ // <brendan, after jband> I promise to play nice
+ char* buffer = mPath.BeginWriting();
+ // find the last significant slash in buffer
+ char* slashp = strrchr(buffer, '/');
+ NS_ASSERTION(slashp, "non-canonical path?");
+ if (!slashp) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ // for the case where we are at '/'
+ if (slashp == buffer) {
+ slashp++;
+ }
+
+ // temporarily terminate buffer at the last significant slash
+ char c = *slashp;
+ *slashp = '\0';
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
+ getter_AddRefs(localFile));
+
+ // make buffer whole again
+ *slashp = c;
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ localFile.forget(aParent);
+ return NS_OK;
+}
+
+/*
+ * The results of Exists, isWritable and isReadable are not cached.
+ */
+
+
+NS_IMETHODIMP
+nsLocalFile::Exists(bool* aResult)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = (access(mPath.get(), F_OK) == 0);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(bool* aResult)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = (access(mPath.get(), W_OK) == 0);
+ if (*aResult || errno == EACCES) {
+ return NS_OK;
+ }
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(bool* aResult)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aResult = (access(mPath.get(), R_OK) == 0);
+ if (*aResult || errno == EACCES) {
+ return NS_OK;
+ }
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(bool* aResult)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Check extension (bug 663899). On certain platforms, the file
+ // extension may cause the OS to treat it as executable regardless of
+ // the execute bit, such as .jar on Mac OS X. We borrow the code from
+ // nsLocalFileWin, slightly modified.
+
+ // Don't be fooled by symlinks.
+ bool symLink;
+ nsresult rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoString path;
+ if (symLink) {
+ GetTarget(path);
+ } else {
+ GetPath(path);
+ }
+
+ int32_t dotIdx = path.RFindChar(char16_t('.'));
+ if (dotIdx != kNotFound) {
+ // Convert extension to lower case.
+ char16_t* p = path.BeginWriting();
+ for (p += dotIdx + 1; *p; ++p) {
+ *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
+ }
+
+ // Search for any of the set of executable extensions.
+ static const char* const executableExts[] = {
+ "air", // Adobe AIR installer
+ "jar" // java application bundle
+ };
+ nsDependentSubstring ext = Substring(path, dotIdx + 1);
+ for (size_t i = 0; i < ArrayLength(executableExts); i++) {
+ if (ext.EqualsASCII(executableExts[i])) {
+ // Found a match. Set result and quit.
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+ }
+
+ // On OS X, then query Launch Services.
+#ifdef MOZ_WIDGET_COCOA
+ // Certain Mac applications, such as Classic applications, which
+ // run under Rosetta, might not have the +x mode bit but are still
+ // considered to be executable by Launch Services (bug 646748).
+ CFURLRef url;
+ if (NS_FAILED(GetCFURL(&url))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
+ LSItemInfoRecord theInfo;
+ OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
+ ::CFRelease(url);
+ if (result == noErr) {
+ if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+#endif
+
+ // Then check the execute bit.
+ *aResult = (access(mPath.get(), X_OK) == 0);
+#ifdef SOLARIS
+ // On Solaris, access will always return 0 for root user, however
+ // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
+ // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
+ if (*aResult) {
+ struct STAT buf;
+
+ *aResult = (STAT(mPath.get(), &buf) == 0);
+ if (*aResult || errno == EACCES) {
+ *aResult = *aResult && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
+ return NS_OK;
+ }
+
+ return NSRESULT_FOR_ERRNO();
+ }
+#endif
+ if (*aResult || errno == EACCES) {
+ return NS_OK;
+ }
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+ ENSURE_STAT_CACHE();
+ *aResult = S_ISDIR(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+ ENSURE_STAT_CACHE();
+ *aResult = S_ISREG(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ *aResult = (*begin == '.');
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ CHECK_mPath();
+
+ struct STAT symStat;
+ if (LSTAT(mPath.get(), &symStat) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+ *aResult = S_ISLNK(symStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ ENSURE_STAT_CACHE();
+ *aResult = S_ISCHR(mCachedStat.st_mode) ||
+ S_ISBLK(mCachedStat.st_mode) ||
+#ifdef S_ISSOCK
+ S_ISSOCK(mCachedStat.st_mode) ||
+#endif
+ S_ISFIFO(mCachedStat.st_mode);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile* aInFile, bool* aResult)
+{
+ if (NS_WARN_IF(!aInFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+
+ nsAutoCString inPath;
+ nsresult rv = aInFile->GetNativePath(inPath);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We don't need to worry about "/foo/" vs. "/foo" here
+ // because trailing slashes are stripped on init.
+ *aResult = !strcmp(inPath.get(), mPath.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile* aInFile, bool* aResult)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aInFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoCString inPath;
+ nsresult rv;
+
+ if (NS_FAILED(rv = aInFile->GetNativePath(inPath))) {
+ return rv;
+ }
+
+ *aResult = false;
+
+ ssize_t len = mPath.Length();
+ if (strncmp(mPath.get(), inPath.get(), len) == 0) {
+ // Now make sure that the |aInFile|'s path has a separator at len,
+ // which implies that it has more components after len.
+ if (inPath[len] == '/') {
+ *aResult = true;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString& aResult)
+{
+ CHECK_mPath();
+ aResult.Truncate();
+
+ struct STAT symStat;
+ if (LSTAT(mPath.get(), &symStat) == -1) {
+ return NSRESULT_FOR_ERRNO();
+ }
+
+ if (!S_ISLNK(symStat.st_mode)) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ int32_t size = (int32_t)symStat.st_size;
+ char* target = (char*)moz_xmalloc(size + 1);
+ if (!target) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (readlink(mPath.get(), target, (size_t)size) < 0) {
+ free(target);
+ return NSRESULT_FOR_ERRNO();
+ }
+ target[size] = '\0';
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIFile> self(this);
+ int32_t maxLinks = 40;
+ while (true) {
+ if (maxLinks-- == 0) {
+ rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+ break;
+ }
+
+ if (target[0] != '/') {
+ nsCOMPtr<nsIFile> parent;
+ if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent)))) {
+ break;
+ }
+ if (NS_FAILED(rv = parent->AppendRelativeNativePath(nsDependentCString(target)))) {
+ break;
+ }
+ if (NS_FAILED(rv = parent->GetNativePath(aResult))) {
+ break;
+ }
+ self = parent;
+ } else {
+ aResult = target;
+ }
+
+ const nsPromiseFlatCString& flatRetval = PromiseFlatCString(aResult);
+
+ // Any failure in testing the current target we'll just interpret
+ // as having reached our destiny.
+ if (LSTAT(flatRetval.get(), &symStat) == -1) {
+ break;
+ }
+
+ // And of course we're done if it isn't a symlink.
+ if (!S_ISLNK(symStat.st_mode)) {
+ break;
+ }
+
+ int32_t newSize = (int32_t)symStat.st_size;
+ if (newSize > size) {
+ char* newTarget = (char*)moz_xrealloc(target, newSize + 1);
+ if (!newTarget) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ target = newTarget;
+ size = newSize;
+ }
+
+ int32_t linkLen = readlink(flatRetval.get(), target, size);
+ if (linkLen == -1) {
+ rv = NSRESULT_FOR_ERRNO();
+ break;
+ }
+ target[linkLen] = '\0';
+ }
+
+ free(target);
+
+ if (NS_FAILED(rv)) {
+ aResult.Truncate();
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(bool* aFollowLinks)
+{
+ *aFollowLinks = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(bool aFollowLinks)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator** aEntries)
+{
+ RefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();
+
+ nsresult rv = dir->Init(this, false);
+ if (NS_FAILED(rv)) {
+ *aEntries = nullptr;
+ } else {
+ dir.forget(aEntries);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary** aResult)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcnt::SetActivityIsLegal(false);
+#endif
+
+ *aResult = PR_LoadLibrary(mPath.get());
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcnt::SetActivityIsLegal(true);
+#endif
+
+ if (!*aResult) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
+{
+ return GetNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
+{
+#ifdef MOZ_WIDGET_COCOA
+ if (aPersistentDescriptor.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Support pathnames as user-supplied descriptors if they begin with '/'
+ // or '~'. These characters do not collide with the base64 set used for
+ // encoding alias records.
+ char first = aPersistentDescriptor.First();
+ if (first == '/' || first == '~') {
+ return InitWithNativePath(aPersistentDescriptor);
+ }
+
+ uint32_t dataSize = aPersistentDescriptor.Length();
+ char* decodedData = PL_Base64Decode(
+ PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
+ if (!decodedData) {
+ NS_ERROR("SetPersistentDescriptor was given bad data");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Cast to an alias record and resolve.
+ AliasRecord aliasHeader = *(AliasPtr)decodedData;
+ int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
+ if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
+ PR_Free(decodedData);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = NS_OK;
+
+ // Move the now-decoded data into the Handle.
+ // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
+ Handle newHandle = nullptr;
+ if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ PR_Free(decodedData);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ Boolean changed;
+ FSRef resolvedFSRef;
+ OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef,
+ &changed);
+
+ rv = MacErrorMapper(err);
+ DisposeHandle(newHandle);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return InitWithFSRef(&resolvedFSRef);
+#else
+ return InitWithNativePath(aPersistentDescriptor);
+#endif
+}
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+#ifdef MOZ_WIDGET_GTK
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+ if (!giovfs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ bool isDirectory;
+ if (NS_FAILED(IsDirectory(&isDirectory))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (isDirectory) {
+ return giovfs->ShowURIForInput(mPath);
+ } else if (NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) {
+ return NS_OK;
+ } else {
+ nsCOMPtr<nsIFile> parentDir;
+ nsAutoCString dirPath;
+ if (NS_FAILED(GetParent(getter_AddRefs(parentDir)))) {
+ return NS_ERROR_FAILURE;
+ }
+ if (NS_FAILED(parentDir->GetNativePath(dirPath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return giovfs->ShowURIForInput(dirPath);
+ }
+#elif defined(MOZ_WIDGET_COCOA)
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+#ifdef MOZ_WIDGET_GTK
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+ if (!giovfs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return giovfs->ShowURIForInput(mPath);
+#elif defined(MOZ_ENABLE_CONTENTACTION)
+ QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
+ ContentAction::Action action =
+ ContentAction::Action::defaultActionForFile(uri);
+
+ if (action.isValid()) {
+ action.trigger();
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+#elif defined(MOZ_WIDGET_ANDROID)
+ // Try to get a mimetype, if this fails just use the file uri alone
+ nsresult rv;
+ nsAutoCString type;
+ nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = mimeService->GetTypeFromFile(this, type);
+ }
+
+ nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath;
+ return java::GeckoAppShell::OpenUriExternal(
+ NS_ConvertUTF8toUTF16(fileUri),
+ NS_ConvertUTF8toUTF16(type),
+ EmptyString(),
+ EmptyString(),
+ EmptyString(),
+ EmptyString()) ? NS_OK : NS_ERROR_FAILURE;
+#elif defined(MOZ_WIDGET_COCOA)
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::OpenURL(url);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowSymlinks,
+ nsIFile** aResult)
+{
+ RefPtr<nsLocalFile> file = new nsLocalFile();
+
+ file->SetFollowLinks(aFollowSymlinks);
+
+ if (!aPath.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(aPath);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ file.forget(aResult);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// unicode support
+//-----------------------------------------------------------------------------
+
+#define SET_UCS(func, ucsArg) \
+ { \
+ nsAutoCString buf; \
+ nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ return (func)(buf); \
+ }
+
+#define GET_UCS(func, ucsArg) \
+ { \
+ nsAutoCString buf; \
+ nsresult rv = (func)(buf); \
+ if (NS_FAILED(rv)) return rv; \
+ return NS_CopyNativeToUnicode(buf, ucsArg); \
+ }
+
+#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
+ { \
+ nsAutoCString buf; \
+ nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ return (func)(opaqueArg, buf); \
+ }
+
+// Unicode interface Wrapper
+nsresult
+nsLocalFile::InitWithPath(const nsAString& aFilePath)
+{
+ SET_UCS(InitWithNativePath, aFilePath);
+}
+nsresult
+nsLocalFile::Append(const nsAString& aNode)
+{
+ SET_UCS(AppendNative, aNode);
+}
+nsresult
+nsLocalFile::AppendRelativePath(const nsAString& aNode)
+{
+ SET_UCS(AppendRelativeNativePath, aNode);
+}
+nsresult
+nsLocalFile::GetLeafName(nsAString& aLeafName)
+{
+ GET_UCS(GetNativeLeafName, aLeafName);
+}
+nsresult
+nsLocalFile::SetLeafName(const nsAString& aLeafName)
+{
+ SET_UCS(SetNativeLeafName, aLeafName);
+}
+nsresult
+nsLocalFile::GetPath(nsAString& aResult)
+{
+ return NS_CopyNativeToUnicode(mPath, aResult);
+}
+nsresult
+nsLocalFile::CopyTo(nsIFile* aNewParentDir, const nsAString& aNewName)
+{
+ SET_UCS_2ARGS_2(CopyToNative , aNewParentDir, aNewName);
+}
+nsresult
+nsLocalFile::CopyToFollowingLinks(nsIFile* aNewParentDir,
+ const nsAString& aNewName)
+{
+ SET_UCS_2ARGS_2(CopyToFollowingLinksNative , aNewParentDir, aNewName);
+}
+nsresult
+nsLocalFile::MoveTo(nsIFile* aNewParentDir, const nsAString& aNewName)
+{
+ SET_UCS_2ARGS_2(MoveToNative, aNewParentDir, aNewName);
+}
+
+NS_IMETHODIMP
+nsLocalFile::RenameTo(nsIFile* aNewParentDir, const nsAString& aNewName)
+{
+ SET_UCS_2ARGS_2(RenameToNative, aNewParentDir, aNewName);
+}
+
+NS_IMETHODIMP
+nsLocalFile::RenameToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
+{
+ nsresult rv;
+
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // check to make sure that we have a new parent
+ nsAutoCString newPathName;
+ rv = GetNativeTargetPathName(aNewParentDir, aNewName, newPathName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // try for atomic rename
+ if (rename(mPath.get(), newPathName.get()) < 0) {
+ if (errno == EXDEV) {
+ rv = NS_ERROR_FILE_ACCESS_DENIED;
+ } else {
+ rv = NSRESULT_FOR_ERRNO();
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::GetTarget(nsAString& aResult)
+{
+ GET_UCS(GetNativeTarget, aResult);
+}
+
+// nsIHashable
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIHashable* aOther, bool* aResult)
+{
+ nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
+ if (!otherFile) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ return Equals(otherFile, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetHashCode(uint32_t* aResult)
+{
+ *aResult = HashString(mPath);
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks, nsIFile** aResult)
+{
+ nsAutoCString buf;
+ nsresult rv = NS_CopyUnicodeToNative(aPath, buf);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_NewNativeLocalFile(buf, aFollowLinks, aResult);
+}
+
+//-----------------------------------------------------------------------------
+// global init/shutdown
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+}
+
+// nsILocalFileMac
+
+#ifdef MOZ_WIDGET_COCOA
+
+static nsresult MacErrorMapper(OSErr inErr)
+{
+ nsresult outErr;
+
+ switch (inErr) {
+ case noErr:
+ outErr = NS_OK;
+ break;
+
+ case fnfErr:
+ case afpObjectNotFound:
+ case afpDirNotFound:
+ outErr = NS_ERROR_FILE_NOT_FOUND;
+ break;
+
+ case dupFNErr:
+ case afpObjectExists:
+ outErr = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+
+ case dskFulErr:
+ case afpDiskFull:
+ outErr = NS_ERROR_FILE_DISK_FULL;
+ break;
+
+ case fLckdErr:
+ case afpVolLocked:
+ outErr = NS_ERROR_FILE_IS_LOCKED;
+ break;
+
+ case afpAccessDenied:
+ outErr = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+
+ case afpDirNotEmpty:
+ outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+
+ // Can't find good map for some
+ case bdNamErr:
+ outErr = NS_ERROR_FAILURE;
+ break;
+
+ default:
+ outErr = NS_ERROR_FAILURE;
+ break;
+ }
+
+ return outErr;
+}
+
+static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
+{
+ // first see if the conversion would succeed and find the length of the result
+ CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
+ CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
+ kCFStringEncodingUTF8, 0, false,
+ nullptr, 0, &usedBufLen);
+ if (charsConverted == inStrLen) {
+ // all characters converted, do the actual conversion
+ aOutStr.SetLength(usedBufLen);
+ if (aOutStr.Length() != (unsigned int)usedBufLen) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ UInt8* buffer = (UInt8*)aOutStr.BeginWriting();
+ ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
+ 0, false, buffer, usedBufLen, &usedBufLen);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
+{
+ UInt8 path[PATH_MAX];
+ if (::CFURLGetFileSystemRepresentation(aCFURL, true, path, PATH_MAX)) {
+ nsDependentCString nativePath((char*)path);
+ return InitWithNativePath(nativePath);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithFSRef(const FSRef* aFSRef)
+{
+ if (NS_WARN_IF(!aFSRef)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
+ if (newURLRef) {
+ nsresult rv = InitWithCFURL(newURLRef);
+ ::CFRelease(newURLRef);
+ return rv;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetCFURL(CFURLRef* aResult)
+{
+ CHECK_mPath();
+
+ bool isDir;
+ IsDirectory(&isDir);
+ *aResult = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8*)mPath.get(),
+ mPath.Length(),
+ isDir);
+
+ return (*aResult ? NS_OK : NS_ERROR_FAILURE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFSRef(FSRef* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef url = nullptr;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ if (::CFURLGetFSRef(url, aResult)) {
+ rv = NS_OK;
+ }
+ ::CFRelease(url);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFSSpec(FSSpec* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ FSRef fsRef;
+ nsresult rv = GetFSRef(&fsRef);
+ if (NS_SUCCEEDED(rv)) {
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr,
+ aResult, nullptr);
+ return MacErrorMapper(err);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeWithResFork(int64_t* aFileSizeWithResFork)
+{
+ if (NS_WARN_IF(!aFileSizeWithResFork)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ FSRef fsRef;
+ nsresult rv = GetFSRef(&fsRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
+ &catalogInfo, nullptr, nullptr, nullptr);
+ if (err != noErr) {
+ return MacErrorMapper(err);
+ }
+
+ *aFileSizeWithResFork =
+ catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileType(OSType* aFileType)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileType(OSType aFileType)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileCreator(OSType* aFileCreator)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileCreator(OSType aFileCreator)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::LaunchWithDoc(nsIFile* aDocToLoad, bool aLaunchInBackground)
+{
+ bool isExecutable;
+ nsresult rv = IsExecutable(&isExecutable);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!isExecutable) {
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+
+ FSRef appFSRef, docFSRef;
+ rv = GetFSRef(&appFSRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (aDocToLoad) {
+ nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
+ rv = macDoc->GetFSRef(&docFSRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (aLaunchInBackground) {
+ theLaunchFlags |= kLSLaunchDontSwitch;
+ }
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appFSRef;
+ if (aDocToLoad) {
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docFSRef;
+ }
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
+ if (err != noErr) {
+ return MacErrorMapper(err);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenDocWithApp(nsIFile* aAppToOpenWith, bool aLaunchInBackground)
+{
+ FSRef docFSRef;
+ nsresult rv = GetFSRef(&docFSRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!aAppToOpenWith) {
+ OSErr err = ::LSOpenFSRef(&docFSRef, nullptr);
+ return MacErrorMapper(err);
+ }
+
+ nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
+ if (!appFileMac) {
+ return rv;
+ }
+
+ bool isExecutable;
+ rv = appFileMac->IsExecutable(&isExecutable);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!isExecutable) {
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+
+ FSRef appFSRef;
+ rv = appFileMac->GetFSRef(&appFSRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (aLaunchInBackground) {
+ theLaunchFlags |= kLSLaunchDontSwitch;
+ }
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appFSRef;
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docFSRef;
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
+ if (err != noErr) {
+ return MacErrorMapper(err);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsPackage(bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+
+ CFURLRef url;
+ nsresult rv = GetCFURL(&url);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ LSItemInfoRecord info;
+ OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
+
+ ::CFRelease(url);
+
+ if (status != noErr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = !!(info.flags & kLSItemInfoIsPackage);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleDisplayName(nsAString& aOutBundleName)
+{
+ bool isPackage = false;
+ nsresult rv = IsPackage(&isPackage);
+ if (NS_FAILED(rv) || !isPackage) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString name;
+ rv = GetLeafName(name);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ int32_t length = name.Length();
+ if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
+ // 4 characters in ".app"
+ aOutBundleName = Substring(name, 0, length - 4);
+ } else {
+ aOutBundleName = name;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleIdentifier(nsACString& aOutBundleIdentifier)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef urlRef;
+ if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
+ CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef);
+ if (bundle) {
+ CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
+ if (bundleIdentifier) {
+ rv = CFStringReftoUTF8(bundleIdentifier, aOutBundleIdentifier);
+ }
+ ::CFRelease(bundle);
+ }
+ ::CFRelease(urlRef);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleContentsLastModifiedTime(int64_t* aLastModTime)
+{
+ CHECK_mPath();
+ if (NS_WARN_IF(!aLastModTime)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ bool isPackage = false;
+ nsresult rv = IsPackage(&isPackage);
+ if (NS_FAILED(rv) || !isPackage) {
+ return GetLastModifiedTime(aLastModTime);
+ }
+
+ nsAutoCString infoPlistPath(mPath);
+ infoPlistPath.AppendLiteral("/Contents/Info.plist");
+ PRFileInfo64 info;
+ if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
+ return GetLastModifiedTime(aLastModTime);
+ }
+ int64_t modTime = int64_t(info.modifyTime);
+ if (modTime == 0) {
+ *aLastModTime = 0;
+ } else {
+ *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile* aFile)
+{
+ if (NS_WARN_IF(!aFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoCString nativePath;
+ nsresult rv = aFile->GetNativePath(nativePath);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return InitWithNativePath(nativePath);
+}
+
+nsresult
+NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks,
+ nsILocalFileMac** aResult)
+{
+ RefPtr<nsLocalFile> file = new nsLocalFile();
+
+ file->SetFollowLinks(aFollowLinks);
+
+ nsresult rv = file->InitWithFSRef(aFSRef);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ file.forget(aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks,
+ nsILocalFileMac** aResult)
+{
+ RefPtr<nsLocalFile> file = new nsLocalFile();
+
+ file->SetFollowLinks(aFollowLinks);
+
+ nsresult rv = file->InitWithCFURL(aURL);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ file.forget(aResult);
+ return NS_OK;
+}
+
+#endif
diff --git a/xpcom/io/nsLocalFileUnix.h b/xpcom/io/nsLocalFileUnix.h
new file mode 100644
index 000000000..9a3e7d6af
--- /dev/null
+++ b/xpcom/io/nsLocalFileUnix.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of nsIFile for ``Unixy'' systems.
+ */
+
+#ifndef _nsLocalFileUNIX_H_
+#define _nsLocalFileUNIX_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "nscore.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
+#include "mozilla/Attributes.h"
+#ifdef MOZ_WIDGET_COCOA
+#include "nsILocalFileMac.h"
+#endif
+
+/**
+ * we need these for statfs()
+ */
+#ifdef HAVE_SYS_STATVFS_H
+ #if defined(__osf__) && defined(__DECCXX)
+ extern "C" int statvfs(const char *, struct statvfs *);
+ #endif
+ #include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+ #include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_SYS_VFS_H
+ #include <sys/vfs.h>
+#endif
+
+#ifdef HAVE_SYS_MOUNT_H
+ #include <sys/param.h>
+ #include <sys/mount.h>
+#endif
+
+#if defined(HAVE_STATVFS64) && (!defined(LINUX) && !defined(__osf__))
+ #define STATFS statvfs64
+ #define F_BSIZE f_frsize
+#elif defined(HAVE_STATVFS) && (!defined(LINUX) && !defined(__osf__))
+ #define STATFS statvfs
+ #define F_BSIZE f_frsize
+#elif defined(HAVE_STATFS64)
+ #define STATFS statfs64
+ #define F_BSIZE f_bsize
+#elif defined(HAVE_STATFS)
+ #define STATFS statfs
+ #define F_BSIZE f_bsize
+#endif
+
+// stat64 and lstat64 are deprecated on OS X. Normal stat and lstat are
+// 64-bit by default on OS X 10.6+.
+#if defined(HAVE_STAT64) && defined(HAVE_LSTAT64) && !defined(XP_DARWIN)
+ #if defined (AIX)
+ #if defined STAT
+ #undef STAT
+ #endif
+ #endif
+ #define STAT stat64
+ #define LSTAT lstat64
+ #define HAVE_STATS64 1
+#else
+ #define STAT stat
+ #define LSTAT lstat
+#endif
+
+
+class nsLocalFile final
+#ifdef MOZ_WIDGET_COCOA
+ : public nsILocalFileMac
+#else
+ : public nsILocalFile
+#endif
+ , public nsIHashable
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static nsresult nsLocalFileConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIFILE
+ NS_DECL_NSILOCALFILE
+#ifdef MOZ_WIDGET_COCOA
+ NS_DECL_NSILOCALFILEMAC
+#endif
+ NS_DECL_NSIHASHABLE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ nsLocalFile(const nsLocalFile& aOther);
+ ~nsLocalFile()
+ {
+ }
+
+protected:
+ // This stat cache holds the *last stat* - it does not invalidate.
+ // Call "FillStatCache" whenever you want to stat our file.
+ struct STAT mCachedStat;
+ nsCString mPath;
+
+ void LocateNativeLeafName(nsACString::const_iterator&,
+ nsACString::const_iterator&);
+
+ nsresult CopyDirectoryTo(nsIFile* aNewParent);
+ nsresult CreateAllAncestors(uint32_t aPermissions);
+ nsresult GetNativeTargetPathName(nsIFile* aNewParent,
+ const nsACString& aNewName,
+ nsACString& aResult);
+
+ bool FillStatCache();
+
+ nsresult CreateAndKeepOpen(uint32_t aType, int aFlags,
+ uint32_t aPermissions, PRFileDesc** aResult);
+};
+
+#endif /* _nsLocalFileUNIX_H_ */
diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp
new file mode 100644
index 000000000..3a7e570f5
--- /dev/null
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -0,0 +1,3787 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/WindowsVersion.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsMemory.h"
+#include "GeckoProfiler.h"
+
+#include "nsLocalFile.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsNativeCharsetUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIComponentManager.h"
+#include "prio.h"
+#include "private/pprio.h" // To get PR_ImportFile
+#include "prprf.h"
+#include "prmem.h"
+#include "nsHashKeys.h"
+
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+
+#include <direct.h>
+#include <windows.h>
+#include <shlwapi.h>
+#include <aclapi.h>
+
+#include "shellapi.h"
+#include "shlguid.h"
+
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <mbstring.h>
+
+#include "nsXPIDLString.h"
+#include "prproces.h"
+#include "prlink.h"
+
+#include "mozilla/Mutex.h"
+#include "SpecialSystemDirectory.h"
+
+#include "nsTraceRefcnt.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#include "nsIWindowMediator.h"
+#include "mozIDOMWindow.h"
+#include "nsPIDOMWindow.h"
+#include "nsIWidget.h"
+#include "mozilla/WidgetUtils.h"
+
+using namespace mozilla;
+
+#define CHECK_mWorkingPath() \
+ PR_BEGIN_MACRO \
+ if (mWorkingPath.IsEmpty()) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+// CopyFileEx only supports unbuffered I/O in Windows Vista and above
+#ifndef COPY_FILE_NO_BUFFERING
+#define COPY_FILE_NO_BUFFERING 0x00001000
+#endif
+
+#ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
+#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
+#endif
+
+#ifndef DRIVE_REMOTE
+#define DRIVE_REMOTE 4
+#endif
+
+static HWND
+GetMostRecentNavigatorHWND()
+{
+ nsresult rv;
+ nsCOMPtr<nsIWindowMediator> winMediator(
+ do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> navWin;
+ rv = winMediator->GetMostRecentWindow(u"navigator:browser",
+ getter_AddRefs(navWin));
+ if (NS_FAILED(rv) || !navWin) {
+ return nullptr;
+ }
+
+ nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
+ nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(win);
+ if (!widget) {
+ return nullptr;
+ }
+
+ return reinterpret_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
+}
+
+
+/**
+ * A runnable to dispatch back to the main thread when
+ * AsyncRevealOperation completes.
+*/
+class AsyncLocalFileWinDone : public Runnable
+{
+public:
+ AsyncLocalFileWinDone() :
+ mWorkerThread(do_GetCurrentThread())
+ {
+ // Objects of this type must only be created on worker threads
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ NS_IMETHOD Run() override
+ {
+ // This event shuts down the worker thread and so must be main thread.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // If we don't destroy the thread when we're done with it, it will hang
+ // around forever... and that is bad!
+ mWorkerThread->Shutdown();
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mWorkerThread;
+};
+
+/**
+ * A runnable to dispatch from the main thread when an async operation should
+ * be performed.
+*/
+class AsyncRevealOperation : public Runnable
+{
+public:
+ explicit AsyncRevealOperation(const nsAString& aResolvedPath)
+ : mResolvedPath(aResolvedPath)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(!NS_IsMainThread(),
+ "AsyncRevealOperation should not be run on the main thread!");
+
+ bool doCoUninitialize = SUCCEEDED(
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
+ Reveal();
+ if (doCoUninitialize) {
+ CoUninitialize();
+ }
+
+ // Send the result back to the main thread so that this thread can be
+ // cleanly shut down
+ nsCOMPtr<nsIRunnable> resultrunnable = new AsyncLocalFileWinDone();
+ NS_DispatchToMainThread(resultrunnable);
+ return NS_OK;
+ }
+
+private:
+ // Reveals the path in explorer.
+ nsresult Reveal()
+ {
+ DWORD attributes = GetFileAttributesW(mResolvedPath.get());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ HRESULT hr;
+ if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ // We have a directory so we should open the directory itself.
+ LPITEMIDLIST dir = ILCreateFromPathW(mResolvedPath.get());
+ if (!dir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LPCITEMIDLIST selection[] = { dir };
+ UINT count = ArrayLength(selection);
+
+ //Perform the open of the directory.
+ hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
+ CoTaskMemFree(dir);
+ } else {
+ int32_t len = mResolvedPath.Length();
+ // We don't currently handle UNC long paths of the form \\?\ anywhere so
+ // this should be fine.
+ if (len > MAX_PATH) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+ WCHAR parentDirectoryPath[MAX_PATH + 1] = { 0 };
+ wcsncpy(parentDirectoryPath, mResolvedPath.get(), MAX_PATH);
+ PathRemoveFileSpecW(parentDirectoryPath);
+
+ // We have a file so we should open the parent directory.
+ LPITEMIDLIST dir = ILCreateFromPathW(parentDirectoryPath);
+ if (!dir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set the item in the directory to select to the file we want to reveal.
+ LPITEMIDLIST item = ILCreateFromPathW(mResolvedPath.get());
+ if (!item) {
+ CoTaskMemFree(dir);
+ return NS_ERROR_FAILURE;
+ }
+
+ LPCITEMIDLIST selection[] = { item };
+ UINT count = ArrayLength(selection);
+
+ //Perform the selection of the file.
+ hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
+
+ CoTaskMemFree(dir);
+ CoTaskMemFree(item);
+ }
+
+ return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ // Stores the path to perform the operation on
+ nsString mResolvedPath;
+};
+
+class nsDriveEnumerator : public nsISimpleEnumerator
+{
+public:
+ nsDriveEnumerator();
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+ nsresult Init();
+private:
+ virtual ~nsDriveEnumerator();
+
+ /* mDrives stores the null-separated drive names.
+ * Init sets them.
+ * HasMoreElements checks mStartOfCurrentDrive.
+ * GetNext advances mStartOfCurrentDrive.
+ */
+ nsString mDrives;
+ nsAString::const_iterator mStartOfCurrentDrive;
+ nsAString::const_iterator mEndOfDrivesString;
+};
+
+//----------------------------------------------------------------------------
+// short cut resolver
+//----------------------------------------------------------------------------
+class ShortcutResolver
+{
+public:
+ ShortcutResolver();
+ // nonvirtual since we're not subclassed
+ ~ShortcutResolver();
+
+ nsresult Init();
+ nsresult Resolve(const WCHAR* aIn, WCHAR* aOut);
+ nsresult SetShortcut(bool aUpdateExisting,
+ const WCHAR* aShortcutPath,
+ const WCHAR* aTargetPath,
+ const WCHAR* aWorkingDir,
+ const WCHAR* aArgs,
+ const WCHAR* aDescription,
+ const WCHAR* aIconFile,
+ int32_t aIconIndex);
+
+private:
+ Mutex mLock;
+ RefPtr<IPersistFile> mPersistFile;
+ RefPtr<IShellLinkW> mShellLink;
+};
+
+ShortcutResolver::ShortcutResolver() :
+ mLock("ShortcutResolver.mLock")
+{
+ CoInitialize(nullptr);
+}
+
+ShortcutResolver::~ShortcutResolver()
+{
+ CoUninitialize();
+}
+
+nsresult
+ShortcutResolver::Init()
+{
+ // Get a pointer to the IPersistFile interface.
+ if (FAILED(CoCreateInstance(CLSID_ShellLink,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW,
+ getter_AddRefs(mShellLink))) ||
+ FAILED(mShellLink->QueryInterface(IID_IPersistFile,
+ getter_AddRefs(mPersistFile)))) {
+ mShellLink = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+// |out| must be an allocated buffer of size MAX_PATH
+nsresult
+ShortcutResolver::Resolve(const WCHAR* aIn, WCHAR* aOut)
+{
+ if (!mShellLink) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MutexAutoLock lock(mLock);
+
+ if (FAILED(mPersistFile->Load(aIn, STGM_READ)) ||
+ FAILED(mShellLink->Resolve(nullptr, SLR_NO_UI)) ||
+ FAILED(mShellLink->GetPath(aOut, MAX_PATH, nullptr, SLGP_UNCPRIORITY))) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+nsresult
+ShortcutResolver::SetShortcut(bool aUpdateExisting,
+ const WCHAR* aShortcutPath,
+ const WCHAR* aTargetPath,
+ const WCHAR* aWorkingDir,
+ const WCHAR* aArgs,
+ const WCHAR* aDescription,
+ const WCHAR* aIconPath,
+ int32_t aIconIndex)
+{
+ if (!mShellLink) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aShortcutPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MutexAutoLock lock(mLock);
+
+ if (aUpdateExisting) {
+ if (FAILED(mPersistFile->Load(aShortcutPath, STGM_READWRITE))) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ if (!aTargetPath) {
+ return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
+ }
+
+ // Since we reuse our IPersistFile, we have to clear out any values that
+ // may be left over from previous calls to SetShortcut.
+ if (FAILED(mShellLink->SetWorkingDirectory(L"")) ||
+ FAILED(mShellLink->SetArguments(L"")) ||
+ FAILED(mShellLink->SetDescription(L"")) ||
+ FAILED(mShellLink->SetIconLocation(L"", 0))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (aTargetPath && FAILED(mShellLink->SetPath(aTargetPath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aWorkingDir && FAILED(mShellLink->SetWorkingDirectory(aWorkingDir))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aArgs && FAILED(mShellLink->SetArguments(aArgs))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aDescription && FAILED(mShellLink->SetDescription(aDescription))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aIconPath && FAILED(mShellLink->SetIconLocation(aIconPath, aIconIndex))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (FAILED(mPersistFile->Save(aShortcutPath,
+ TRUE))) {
+ // Second argument indicates whether the file path specified in the
+ // first argument should become the "current working file" for this
+ // IPersistFile
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static ShortcutResolver* gResolver = nullptr;
+
+static nsresult
+NS_CreateShortcutResolver()
+{
+ gResolver = new ShortcutResolver();
+ return gResolver->Init();
+}
+
+static void
+NS_DestroyShortcutResolver()
+{
+ delete gResolver;
+ gResolver = nullptr;
+}
+
+
+//-----------------------------------------------------------------------------
+// static helper functions
+//-----------------------------------------------------------------------------
+
+// certainly not all the error that can be
+// encountered, but many of them common ones
+static nsresult
+ConvertWinError(DWORD aWinErr)
+{
+ nsresult rv;
+
+ switch (aWinErr) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_NOT_READY:
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_NOT_SAME_DEVICE:
+ rv = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+ case ERROR_SHARING_VIOLATION: // CreateFile without sharing flags
+ case ERROR_LOCK_VIOLATION: // LockFile, LockFileEx
+ rv = NS_ERROR_FILE_IS_LOCKED;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_ARENA_TRASHED:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ case ERROR_CURRENT_DIRECTORY:
+ rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+ case ERROR_WRITE_PROTECT:
+ rv = NS_ERROR_FILE_READ_ONLY;
+ break;
+ case ERROR_HANDLE_DISK_FULL:
+ rv = NS_ERROR_FILE_TOO_BIG;
+ break;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ rv = NS_ERROR_FILE_NAME_TOO_LONG;
+ break;
+ case ERROR_DIRECTORY:
+ rv = NS_ERROR_FILE_NOT_DIRECTORY;
+ break;
+ case 0:
+ rv = NS_OK;
+ break;
+ default:
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+// as suggested in the MSDN documentation on SetFilePointer
+static __int64
+MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = aDistance;
+ li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+static bool
+IsShortcutPath(const nsAString& aPath)
+{
+ // Under Windows, the shortcuts are just files with a ".lnk" extension.
+ // Note also that we don't resolve links in the middle of paths.
+ // i.e. "c:\foo.lnk\bar.txt" is invalid.
+ MOZ_ASSERT(!aPath.IsEmpty(), "don't pass an empty string");
+ int32_t len = aPath.Length();
+ return len >= 4 && (StringTail(aPath, 4).LowerCaseEqualsASCII(".lnk"));
+}
+
+//-----------------------------------------------------------------------------
+// We need the following three definitions to make |OpenFile| convert a file
+// handle to an NSPR file descriptor correctly when |O_APPEND| flag is
+// specified. It is defined in a private header of NSPR (primpl.h) we can't
+// include. As a temporary workaround until we decide how to extend
+// |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
+// and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
+// of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
+// Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
+// In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary
+// workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc|
+// need to be changed to match the definitions for WinNT.
+//-----------------------------------------------------------------------------
+typedef enum
+{
+ _PR_TRI_TRUE = 1,
+ _PR_TRI_FALSE = 0,
+ _PR_TRI_UNKNOWN = -1
+} _PRTriStateBool;
+
+struct _MDFileDesc
+{
+ PROsfd osfd;
+};
+
+struct PRFilePrivate
+{
+ int32_t state;
+ bool nonblocking;
+ _PRTriStateBool inheritable;
+ PRFileDesc* next;
+ int lockCount; /* 0: not locked
+ * -1: a native lockfile call is in progress
+ * > 0: # times the file is locked */
+ bool appendMode;
+ _MDFileDesc md;
+};
+
+//-----------------------------------------------------------------------------
+// Six static methods defined below (OpenFile, FileTimeToPRTime, GetFileInfo,
+// OpenDir, CloseDir, ReadDir) should go away once the corresponding
+// UTF-16 APIs are implemented on all the supported platforms (or at least
+// Windows 9x/ME) in NSPR. Currently, they're only implemented on
+// Windows NT4 or later. (bug 330665)
+//-----------------------------------------------------------------------------
+
+// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
+// PR_Open and _PR_MD_OPEN
+nsresult
+OpenFile(const nsAFlatString& aName,
+ int aOsflags,
+ int aMode,
+ bool aShareDelete,
+ PRFileDesc** aFd)
+{
+ int32_t access = 0;
+
+ int32_t shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ int32_t disposition = 0;
+ int32_t attributes = 0;
+
+ if (aShareDelete) {
+ shareMode |= FILE_SHARE_DELETE;
+ }
+
+ if (aOsflags & PR_SYNC) {
+ attributes = FILE_FLAG_WRITE_THROUGH;
+ }
+ if (aOsflags & PR_RDONLY || aOsflags & PR_RDWR) {
+ access |= GENERIC_READ;
+ }
+ if (aOsflags & PR_WRONLY || aOsflags & PR_RDWR) {
+ access |= GENERIC_WRITE;
+ }
+
+ if (aOsflags & PR_CREATE_FILE && aOsflags & PR_EXCL) {
+ disposition = CREATE_NEW;
+ } else if (aOsflags & PR_CREATE_FILE) {
+ if (aOsflags & PR_TRUNCATE) {
+ disposition = CREATE_ALWAYS;
+ } else {
+ disposition = OPEN_ALWAYS;
+ }
+ } else {
+ if (aOsflags & PR_TRUNCATE) {
+ disposition = TRUNCATE_EXISTING;
+ } else {
+ disposition = OPEN_EXISTING;
+ }
+ }
+
+ if (aOsflags & nsIFile::DELETE_ON_CLOSE) {
+ attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ }
+
+ if (aOsflags & nsIFile::OS_READAHEAD) {
+ attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+ }
+
+ // If no write permissions are requested, and if we are possibly creating
+ // the file, then set the new file as read only.
+ // The flag has no effect if we happen to open the file.
+ if (!(aMode & (PR_IWUSR | PR_IWGRP | PR_IWOTH)) &&
+ disposition != OPEN_EXISTING) {
+ attributes |= FILE_ATTRIBUTE_READONLY;
+ }
+
+ HANDLE file = ::CreateFileW(aName.get(), access, shareMode,
+ nullptr, disposition, attributes, nullptr);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ *aFd = nullptr;
+ return ConvertWinError(GetLastError());
+ }
+
+ *aFd = PR_ImportFile((PROsfd) file);
+ if (*aFd) {
+ // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
+ // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
+ (*aFd)->secret->appendMode = (PR_APPEND & aOsflags) ? true : false;
+ return NS_OK;
+ }
+
+ nsresult rv = NS_ErrorAccordingToNSPR();
+
+ CloseHandle(file);
+
+ return rv;
+}
+
+// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
+// PR_FileTimeToPRTime and _PR_FileTimeToPRTime
+static void
+FileTimeToPRTime(const FILETIME* aFiletime, PRTime* aPrtm)
+{
+#ifdef __GNUC__
+ const PRTime _pr_filetime_offset = 116444736000000000LL;
+#else
+ const PRTime _pr_filetime_offset = 116444736000000000i64;
+#endif
+
+ MOZ_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ ::CopyMemory(aPrtm, aFiletime, sizeof(PRTime));
+#ifdef __GNUC__
+ *aPrtm = (*aPrtm - _pr_filetime_offset) / 10LL;
+#else
+ *aPrtm = (*aPrtm - _pr_filetime_offset) / 10i64;
+#endif
+}
+
+// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
+// changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
+static nsresult
+GetFileInfo(const nsAFlatString& aName, PRFileInfo64* aInfo)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+
+ if (aName.IsEmpty() || aName.FindCharInSet(u"?*") != kNotFound) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (!::GetFileAttributesExW(aName.get(), GetFileExInfoStandard, &fileData)) {
+ return ConvertWinError(GetLastError());
+ }
+
+ if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ aInfo->type = PR_FILE_DIRECTORY;
+ } else {
+ aInfo->type = PR_FILE_FILE;
+ }
+
+ aInfo->size = fileData.nFileSizeHigh;
+ aInfo->size = (aInfo->size << 32) + fileData.nFileSizeLow;
+
+ FileTimeToPRTime(&fileData.ftLastWriteTime, &aInfo->modifyTime);
+
+ if (0 == fileData.ftCreationTime.dwLowDateTime &&
+ 0 == fileData.ftCreationTime.dwHighDateTime) {
+ aInfo->creationTime = aInfo->modifyTime;
+ } else {
+ FileTimeToPRTime(&fileData.ftCreationTime, &aInfo->creationTime);
+ }
+
+ return NS_OK;
+}
+
+struct nsDir
+{
+ HANDLE handle;
+ WIN32_FIND_DATAW data;
+ bool firstEntry;
+};
+
+static nsresult
+OpenDir(const nsAFlatString& aName, nsDir** aDir)
+{
+ if (NS_WARN_IF(!aDir)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aDir = nullptr;
+ if (aName.Length() + 3 >= MAX_PATH) {
+ return NS_ERROR_FILE_NAME_TOO_LONG;
+ }
+
+ nsDir* d = new nsDir();
+ nsAutoString filename(aName);
+
+ // If |aName| ends in a slash or backslash, do not append another backslash.
+ if (filename.Last() == L'/' || filename.Last() == L'\\') {
+ filename.Append('*');
+ } else {
+ filename.AppendLiteral("\\*");
+ }
+
+ filename.ReplaceChar(L'/', L'\\');
+
+ // FindFirstFileW Will have a last error of ERROR_DIRECTORY if
+ // <file_path>\* is passed in. If <unknown_path>\* is passed in then
+ // ERROR_PATH_NOT_FOUND will be the last error.
+ d->handle = ::FindFirstFileW(filename.get(), &(d->data));
+
+ if (d->handle == INVALID_HANDLE_VALUE) {
+ delete d;
+ return ConvertWinError(GetLastError());
+ }
+ d->firstEntry = true;
+
+ *aDir = d;
+ return NS_OK;
+}
+
+static nsresult
+ReadDir(nsDir* aDir, PRDirFlags aFlags, nsString& aName)
+{
+ aName.Truncate();
+ if (NS_WARN_IF(!aDir)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ while (1) {
+ BOOL rv;
+ if (aDir->firstEntry) {
+ aDir->firstEntry = false;
+ rv = 1;
+ } else {
+ rv = ::FindNextFileW(aDir->handle, &(aDir->data));
+ }
+
+ if (rv == 0) {
+ break;
+ }
+
+ const wchar_t* fileName;
+ fileName = (aDir)->data.cFileName;
+
+ if ((aFlags & PR_SKIP_DOT) &&
+ (fileName[0] == L'.') && (fileName[1] == L'\0')) {
+ continue;
+ }
+ if ((aFlags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == L'.') && (fileName[1] == L'.') &&
+ (fileName[2] == L'\0')) {
+ continue;
+ }
+
+ DWORD attrib = aDir->data.dwFileAttributes;
+ if ((aFlags & PR_SKIP_HIDDEN) && (attrib & FILE_ATTRIBUTE_HIDDEN)) {
+ continue;
+ }
+
+ aName = fileName;
+ return NS_OK;
+ }
+
+ DWORD err = GetLastError();
+ return err == ERROR_NO_MORE_FILES ? NS_OK : ConvertWinError(err);
+}
+
+static nsresult
+CloseDir(nsDir*& aDir)
+{
+ if (NS_WARN_IF(!aDir)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ BOOL isOk = FindClose(aDir->handle);
+ delete aDir;
+ aDir = nullptr;
+ return isOk ? NS_OK : ConvertWinError(GetLastError());
+}
+
+//-----------------------------------------------------------------------------
+// nsDirEnumerator
+//-----------------------------------------------------------------------------
+
+class nsDirEnumerator final
+ : public nsISimpleEnumerator
+ , public nsIDirectoryEnumerator
+{
+private:
+ ~nsDirEnumerator()
+ {
+ Close();
+ }
+
+public:
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator() : mDir(nullptr)
+ {
+ }
+
+ nsresult Init(nsIFile* aParent)
+ {
+ nsAutoString filepath;
+ aParent->GetTarget(filepath);
+
+ if (filepath.IsEmpty()) {
+ aParent->GetPath(filepath);
+ }
+
+ if (filepath.IsEmpty()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // IsDirectory is not needed here because OpenDir will return
+ // NS_ERROR_FILE_NOT_DIRECTORY if the passed in path is a file.
+ nsresult rv = OpenDir(filepath, &mDir);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mParent = aParent;
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(bool* aResult)
+ {
+ nsresult rv;
+ if (!mNext && mDir) {
+ nsString name;
+ rv = ReadDir(mDir, PR_SKIP_BOTH, name);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (name.IsEmpty()) {
+ // end of dir entries
+ if (NS_FAILED(CloseDir(mDir))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = false;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ rv = mParent->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = file->Append(name);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mNext = do_QueryInterface(file);
+ }
+ *aResult = mNext != nullptr;
+ if (!*aResult) {
+ Close();
+ }
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports** aResult)
+ {
+ nsresult rv;
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aResult = mNext; // might return nullptr
+ NS_IF_ADDREF(*aResult);
+
+ mNext = nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNextFile(nsIFile** aResult)
+ {
+ *aResult = nullptr;
+ bool hasMore = false;
+ nsresult rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv) || !hasMore) {
+ return rv;
+ }
+ *aResult = mNext;
+ NS_IF_ADDREF(*aResult);
+ mNext = nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD Close()
+ {
+ if (mDir) {
+ nsresult rv = CloseDir(mDir);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "close failed");
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+ }
+
+protected:
+ nsDir* mDir;
+ nsCOMPtr<nsIFile> mParent;
+ nsCOMPtr<nsIFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <public>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile()
+ : mDirty(true)
+ , mResolveDirty(true)
+ , mFollowSymlinks(false)
+{
+}
+
+nsresult
+nsLocalFile::nsLocalFileConstructor(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(!aInstancePtr)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsLocalFile* inst = new nsLocalFile();
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv)) {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsLocalFile,
+ nsILocalFile,
+ nsIFile,
+ nsILocalFileWin,
+ nsIHashable)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <private>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile(const nsLocalFile& aOther)
+ : mDirty(true)
+ , mResolveDirty(true)
+ , mFollowSymlinks(aOther.mFollowSymlinks)
+ , mWorkingPath(aOther.mWorkingPath)
+{
+}
+
+// Resolve the shortcut file from mWorkingPath and write the path
+// it points to into mResolvedPath.
+nsresult
+nsLocalFile::ResolveShortcut()
+{
+ // we can't do anything without the resolver
+ if (!gResolver) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mResolvedPath.SetLength(MAX_PATH);
+ if (mResolvedPath.Length() != MAX_PATH) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ wchar_t* resolvedPath = wwc(mResolvedPath.BeginWriting());
+
+ // resolve this shortcut
+ nsresult rv = gResolver->Resolve(mWorkingPath.get(), resolvedPath);
+
+ size_t len = NS_FAILED(rv) ? 0 : wcslen(resolvedPath);
+ mResolvedPath.SetLength(len);
+
+ return rv;
+}
+
+// Resolve any shortcuts and stat the resolved path. After a successful return
+// the path is guaranteed valid and the members of mFileInfo64 can be used.
+nsresult
+nsLocalFile::ResolveAndStat()
+{
+ // if we aren't dirty then we are already done
+ if (!mDirty) {
+ return NS_OK;
+ }
+
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+ // we can't resolve/stat anything that isn't a valid NSPR addressable path
+ if (mWorkingPath.IsEmpty()) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ // this is usually correct
+ mResolvedPath.Assign(mWorkingPath);
+
+ // slutty hack designed to work around bug 134796 until it is fixed
+ nsAutoString nsprPath(mWorkingPath.get());
+ if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == L':') {
+ nsprPath.Append('\\');
+ }
+
+ // first we will see if the working path exists. If it doesn't then
+ // there is nothing more that can be done
+ nsresult rv = GetFileInfo(nsprPath, &mFileInfo64);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // if this isn't a shortcut file or we aren't following symlinks then we're done
+ if (!mFollowSymlinks ||
+ mFileInfo64.type != PR_FILE_FILE ||
+ !IsShortcutPath(mWorkingPath)) {
+ mDirty = false;
+ mResolveDirty = false;
+ return NS_OK;
+ }
+
+ // we need to resolve this shortcut to what it points to, this will
+ // set mResolvedPath. Even if it fails we need to have the resolved
+ // path equal to working path for those functions that always use
+ // the resolved path.
+ rv = ResolveShortcut();
+ if (NS_FAILED(rv)) {
+ mResolvedPath.Assign(mWorkingPath);
+ return rv;
+ }
+ mResolveDirty = false;
+
+ // get the details of the resolved path
+ rv = GetFileInfo(mResolvedPath, &mFileInfo64);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mDirty = false;
+ return NS_OK;
+}
+
+/**
+ * Fills the mResolvedPath member variable with the file or symlink target
+ * if follow symlinks is on. This is a copy of the Resolve parts from
+ * ResolveAndStat. ResolveAndStat is much slower though because of the stat.
+ *
+ * @return NS_OK on success.
+*/
+nsresult
+nsLocalFile::Resolve()
+{
+ // if we aren't dirty then we are already done
+ if (!mResolveDirty) {
+ return NS_OK;
+ }
+
+ // we can't resolve/stat anything that isn't a valid NSPR addressable path
+ if (mWorkingPath.IsEmpty()) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ // this is usually correct
+ mResolvedPath.Assign(mWorkingPath);
+
+ // if this isn't a shortcut file or we aren't following symlinks then
+ // we're done.
+ if (!mFollowSymlinks ||
+ !IsShortcutPath(mWorkingPath)) {
+ mResolveDirty = false;
+ return NS_OK;
+ }
+
+ // we need to resolve this shortcut to what it points to, this will
+ // set mResolvedPath. Even if it fails we need to have the resolved
+ // path equal to working path for those functions that always use
+ // the resolved path.
+ nsresult rv = ResolveShortcut();
+ if (NS_FAILED(rv)) {
+ mResolvedPath.Assign(mWorkingPath);
+ return rv;
+ }
+
+ mResolveDirty = false;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsIFile,nsILocalFile
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile** aFile)
+{
+ // Just copy-construct ourselves
+ RefPtr<nsLocalFile> file = new nsLocalFile(*this);
+ file.forget(aFile);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithFile(nsIFile* aFile)
+{
+ if (NS_WARN_IF(!aFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoString path;
+ aFile->GetPath(path);
+ if (path.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ return InitWithPath(path);
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithPath(const nsAString& aFilePath)
+{
+ MakeDirty();
+
+ nsAString::const_iterator begin, end;
+ aFilePath.BeginReading(begin);
+ aFilePath.EndReading(end);
+
+ // input string must not be empty
+ if (begin == end) {
+ return NS_ERROR_FAILURE;
+ }
+
+ char16_t firstChar = *begin;
+ char16_t secondChar = *(++begin);
+
+ // just do a sanity check. if it has any forward slashes, it is not a Native path
+ // on windows. Also, it must have a colon at after the first char.
+ if (FindCharInReadable(L'/', begin, end)) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ if (secondChar != L':' && (secondChar != L'\\' || firstChar != L'\\')) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ if (secondChar == L':') {
+ // Make sure we have a valid drive, later code assumes the drive letter
+ // is a single char a-z or A-Z.
+ if (PathGetDriveNumberW(aFilePath.Data()) == -1) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ }
+
+ mWorkingPath = aFilePath;
+ // kill any trailing '\'
+ if (mWorkingPath.Last() == L'\\') {
+ mWorkingPath.Truncate(mWorkingPath.Length() - 1);
+ }
+
+ return NS_OK;
+
+}
+
+// Strip a handler command string of its quotes and parameters.
+static void
+CleanupHandlerPath(nsString& aPath)
+{
+ // Example command strings passed into this routine:
+
+ // 1) C:\Program Files\Company\some.exe -foo -bar
+ // 2) C:\Program Files\Company\some.dll
+ // 3) C:\Windows\some.dll,-foo -bar
+ // 4) C:\Windows\some.cpl,-foo -bar
+
+ int32_t lastCommaPos = aPath.RFindChar(',');
+ if (lastCommaPos != kNotFound)
+ aPath.Truncate(lastCommaPos);
+
+ aPath.Append(' ');
+
+ // case insensitive
+ uint32_t index = aPath.Find(".exe ", true);
+ if (index == kNotFound)
+ index = aPath.Find(".dll ", true);
+ if (index == kNotFound)
+ index = aPath.Find(".cpl ", true);
+
+ if (index != kNotFound)
+ aPath.Truncate(index + 4);
+ aPath.Trim(" ", true, true);
+}
+
+// Strip the windows host process bootstrap executable rundll32.exe
+// from a handler's command string if it exists.
+static void
+StripRundll32(nsString& aCommandString)
+{
+ // Example rundll formats:
+ // C:\Windows\System32\rundll32.exe "path to dll"
+ // rundll32.exe "path to dll"
+ // C:\Windows\System32\rundll32.exe "path to dll", var var
+ // rundll32.exe "path to dll", var var
+
+ NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
+ NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 ");
+
+ // case insensitive
+ int32_t strLen = rundllSegment.Length();
+ int32_t index = aCommandString.Find(rundllSegment, true);
+ if (index == kNotFound) {
+ strLen = rundllSegmentShort.Length();
+ index = aCommandString.Find(rundllSegmentShort, true);
+ }
+
+ if (index != kNotFound) {
+ uint32_t rundllSegmentLength = index + strLen;
+ aCommandString.Cut(0, rundllSegmentLength);
+ }
+}
+
+// Returns the fully qualified path to an application handler based on
+// a parameterized command string. Note this routine should not be used
+// to launch the associated application as it strips parameters and
+// rundll.exe from the string. Designed for retrieving display information
+// on a particular handler.
+/* static */ bool
+nsLocalFile::CleanupCmdHandlerPath(nsAString& aCommandHandler)
+{
+ nsAutoString handlerCommand(aCommandHandler);
+
+ // Straight command path:
+ //
+ // %SystemRoot%\system32\NOTEPAD.EXE var
+ // "C:\Program Files\iTunes\iTunes.exe" var var
+ // C:\Program Files\iTunes\iTunes.exe var var
+ //
+ // Example rundll handlers:
+ //
+ // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var
+ // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
+ // C:\Windows\System32\rundll32.exe "path to dll", var var
+ // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo
+ // Viewer.dll", var var
+
+ // Expand environment variables so we have full path strings.
+ uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
+ L"", 0);
+ if (bufLength == 0) // Error
+ return false;
+
+ auto destination = mozilla::MakeUniqueFallible<wchar_t[]>(bufLength);
+ if (!destination)
+ return false;
+ if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination.get(),
+ bufLength))
+ return false;
+
+ handlerCommand.Assign(destination.get());
+
+ // Remove quotes around paths
+ handlerCommand.StripChars("\"");
+
+ // Strip windows host process bootstrap so we can get to the actual
+ // handler.
+ StripRundll32(handlerCommand);
+
+ // Trim any command parameters so that we have a native path we can
+ // initialize a local file with.
+ CleanupHandlerPath(handlerCommand);
+
+ aCommandHandler.Assign(handlerCommand);
+ return true;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::InitWithCommandLine(const nsAString& aCommandLine)
+{
+ nsAutoString commandLine(aCommandLine);
+ if (!CleanupCmdHandlerPath(commandLine)) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ return InitWithPath(commandLine);
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
+ PRFileDesc** aResult)
+{
+ nsresult rv = OpenNSPRFileDescMaybeShareDelete(aFlags, aMode, false, aResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
+ return rv;
+ }
+
+ *aResult = _wfopen(mResolvedPath.get(), NS_ConvertASCIItoUTF16(aMode).get());
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+
+
+NS_IMETHODIMP
+nsLocalFile::Create(uint32_t aType, uint32_t aAttributes)
+{
+ if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) {
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+ }
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
+ return rv;
+ }
+
+ // create directories to target
+ //
+ // A given local file can be either one of these forms:
+ //
+ // - normal: X:\some\path\on\this\drive
+ // ^--- start here
+ //
+ // - UNC path: \\machine\volume\some\path\on\this\drive
+ // ^--- start here
+ //
+ // Skip the first 'X:\' for the first form, and skip the first full
+ // '\\machine\volume\' segment for the second form.
+
+ wchar_t* path = wwc(mResolvedPath.BeginWriting());
+
+ if (path[0] == L'\\' && path[1] == L'\\') {
+ // dealing with a UNC path here; skip past '\\machine\'
+ path = wcschr(path + 2, L'\\');
+ if (!path) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+ ++path;
+ }
+
+ // search for first slash after the drive (or volume) name
+ wchar_t* slash = wcschr(path, L'\\');
+
+ nsresult directoryCreateError = NS_OK;
+ if (slash) {
+ // skip the first '\\'
+ ++slash;
+ slash = wcschr(slash, L'\\');
+
+ while (slash) {
+ *slash = L'\0';
+
+ if (!::CreateDirectoryW(mResolvedPath.get(), nullptr)) {
+ rv = ConvertWinError(GetLastError());
+ if (NS_ERROR_FILE_NOT_FOUND == rv &&
+ NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
+ // If a previous CreateDirectory failed due to access, return that.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ // perhaps the base path already exists, or perhaps we don't have
+ // permissions to create the directory. NOTE: access denied could
+ // occur on a parent directory even though it exists.
+ else if (rv != NS_ERROR_FILE_ALREADY_EXISTS &&
+ rv != NS_ERROR_FILE_ACCESS_DENIED) {
+ return rv;
+ }
+
+ directoryCreateError = rv;
+ }
+ *slash = L'\\';
+ ++slash;
+ slash = wcschr(slash, L'\\');
+ }
+ }
+
+ if (aType == NORMAL_FILE_TYPE) {
+ PRFileDesc* file;
+ rv = OpenFile(mResolvedPath,
+ PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL,
+ aAttributes, false, &file);
+ if (file) {
+ PR_Close(file);
+ }
+
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ // need to return already-exists for directories (bug 452217)
+ bool isdir;
+ if (NS_SUCCEEDED(IsDirectory(&isdir)) && isdir) {
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ }
+ } else if (NS_ERROR_FILE_NOT_FOUND == rv &&
+ NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
+ // If a previous CreateDirectory failed due to access, return that.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ return rv;
+ }
+
+ if (aType == DIRECTORY_TYPE) {
+ if (!::CreateDirectoryW(mResolvedPath.get(), nullptr)) {
+ rv = ConvertWinError(GetLastError());
+ if (NS_ERROR_FILE_NOT_FOUND == rv &&
+ NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
+ // If a previous CreateDirectory failed due to access, return that.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Append(const nsAString& aNode)
+{
+ // append this path, multiple components are not permitted
+ return AppendInternal(PromiseFlatString(aNode), false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativePath(const nsAString& aNode)
+{
+ // append this path, multiple components are permitted
+ return AppendInternal(PromiseFlatString(aNode), true);
+}
+
+
+nsresult
+nsLocalFile::AppendInternal(const nsAFlatString& aNode,
+ bool aMultipleComponents)
+{
+ if (aNode.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // check the relative path for validity
+ if (aNode.First() == L'\\' || // can't start with an '\'
+ aNode.Contains(L'/') || // can't contain /
+ aNode.EqualsASCII("..")) { // can't be ..
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ if (aMultipleComponents) {
+ // can't contain .. as a path component. Ensure that the valid components
+ // "foo..foo", "..foo", and "foo.." are not falsely detected,
+ // but the invalid paths "..\", "foo\..", "foo\..\foo",
+ // "..\foo", etc are.
+ NS_NAMED_LITERAL_STRING(doubleDot, "\\..");
+ nsAString::const_iterator start, end, offset;
+ aNode.BeginReading(start);
+ aNode.EndReading(end);
+ offset = end;
+ while (FindInReadable(doubleDot, start, offset)) {
+ if (offset == end || *offset == L'\\') {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ start = offset;
+ offset = end;
+ }
+
+ // catches the remaining cases of prefixes
+ if (StringBeginsWith(aNode, NS_LITERAL_STRING("..\\"))) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ }
+ // single components can't contain '\'
+ else if (aNode.Contains(L'\\')) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ MakeDirty();
+
+ mWorkingPath.Append('\\');
+ mWorkingPath.Append(aNode);
+
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::OpenNSPRFileDescMaybeShareDelete(int32_t aFlags,
+ int32_t aMode,
+ bool aShareDelete,
+ PRFileDesc** aResult)
+{
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return OpenFile(mResolvedPath, aFlags, aMode, aShareDelete, aResult);
+}
+
+#define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
+ (u) - (L'a' - L'A') : (u))
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ // XXX See bug 187957 comment 18 for possible problems with this implementation.
+
+ if (mWorkingPath.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsAutoString path(mWorkingPath);
+
+ // find the index of the root backslash for the path. Everything before
+ // this is considered fully normalized and cannot be ascended beyond
+ // using ".." For a local drive this is the first slash (e.g. "c:\").
+ // For a UNC path it is the slash following the share name
+ // (e.g. "\\server\share\").
+ int32_t rootIdx = 2; // default to local drive
+ if (path.First() == L'\\') { // if a share then calculate the rootIdx
+ rootIdx = path.FindChar(L'\\', 2); // skip \\ in front of the server
+ if (rootIdx == kNotFound) {
+ return NS_OK; // already normalized
+ }
+ rootIdx = path.FindChar(L'\\', rootIdx + 1);
+ if (rootIdx == kNotFound) {
+ return NS_OK; // already normalized
+ }
+ } else if (path.CharAt(rootIdx) != L'\\') {
+ // The path has been specified relative to the current working directory
+ // for that drive. To normalize it, the current working directory for
+ // that drive needs to be inserted before the supplied relative path
+ // which will provide an absolute path (and the rootIdx will still be 2).
+ WCHAR cwd[MAX_PATH];
+ WCHAR* pcwd = cwd;
+ int drive = TOUPPER(path.First()) - 'A' + 1;
+ /* We need to worry about IPH, for details read bug 419326.
+ * _getdrives - http://msdn2.microsoft.com/en-us/library/xdhk0xd2.aspx
+ * uses a bitmask, bit 0 is 'a:'
+ * _chdrive - http://msdn2.microsoft.com/en-us/library/0d1409hb.aspx
+ * _getdcwd - http://msdn2.microsoft.com/en-us/library/7t2zk3s4.aspx
+ * take an int, 1 is 'a:'.
+ *
+ * Because of this, we need to do some math. Subtract 1 to convert from
+ * _chdrive/_getdcwd format to _getdrives drive numbering.
+ * Shift left x bits to convert from integer indexing to bitfield indexing.
+ * And of course, we need to find out if the drive is in the bitmask.
+ *
+ * If we're really unlucky, we can still lose, but only if the user
+ * manages to eject the drive between our call to _getdrives() and
+ * our *calls* to _wgetdcwd.
+ */
+ if (!((1 << (drive - 1)) & _getdrives())) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+ if (!_wgetdcwd(drive, pcwd, MAX_PATH)) {
+ pcwd = _wgetdcwd(drive, 0, 0);
+ }
+ if (!pcwd) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ nsAutoString currentDir(pcwd);
+ if (pcwd != cwd) {
+ free(pcwd);
+ }
+
+ if (currentDir.Last() == '\\') {
+ path.Replace(0, 2, currentDir);
+ } else {
+ path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
+ }
+ }
+ NS_POSTCONDITION(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid");
+ NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
+
+ // if there is nothing following the root path then it is already normalized
+ if (rootIdx + 1 == (int32_t)path.Length()) {
+ return NS_OK;
+ }
+
+ // assign the root
+ const char16_t* pathBuffer = path.get(); // simplify access to the buffer
+ mWorkingPath.SetCapacity(path.Length()); // it won't ever grow longer
+ mWorkingPath.Assign(pathBuffer, rootIdx);
+
+ // Normalize the path components. The actions taken are:
+ //
+ // "\\" condense to single backslash
+ // "." remove from path
+ // ".." up a directory
+ // "..." remove from path (any number of dots > 2)
+ //
+ // The last form is something that Windows 95 and 98 supported and
+ // is a shortcut for changing up multiple directories. Windows XP
+ // and ilk ignore it in a path, as is done here.
+ int32_t len, begin, end = rootIdx;
+ while (end < (int32_t)path.Length()) {
+ // find the current segment (text between the backslashes) to
+ // be examined, this will set the following variables:
+ // begin == index of first char in segment
+ // end == index 1 char after last char in segment
+ // len == length of segment
+ begin = end + 1;
+ end = path.FindChar('\\', begin);
+ if (end == kNotFound) {
+ end = path.Length();
+ }
+ len = end - begin;
+
+ // ignore double backslashes
+ if (len == 0) {
+ continue;
+ }
+
+ // len != 0, and interesting paths always begin with a dot
+ if (pathBuffer[begin] == '.') {
+ // ignore single dots
+ if (len == 1) {
+ continue;
+ }
+
+ // handle multiple dots
+ if (len >= 2 && pathBuffer[begin + 1] == L'.') {
+ // back up a path component on double dot
+ if (len == 2) {
+ int32_t prev = mWorkingPath.RFindChar('\\');
+ if (prev >= rootIdx) {
+ mWorkingPath.Truncate(prev);
+ }
+ continue;
+ }
+
+ // length is > 2 and the first two characters are dots.
+ // if the rest of the string is dots, then ignore it.
+ int idx = len - 1;
+ for (; idx >= 2; --idx) {
+ if (pathBuffer[begin + idx] != L'.') {
+ break;
+ }
+ }
+
+ // this is true if the loop above didn't break
+ // and all characters in this segment are dots.
+ if (idx < 2) {
+ continue;
+ }
+ }
+ }
+
+ // add the current component to the path, including the preceding backslash
+ mWorkingPath.Append(pathBuffer + begin - 1, len + 1);
+ }
+
+ // kill trailing dots and spaces.
+ int32_t filePathLen = mWorkingPath.Length() - 1;
+ while (filePathLen > 0 && (mWorkingPath[filePathLen] == L' ' ||
+ mWorkingPath[filePathLen] == L'.')) {
+ mWorkingPath.Truncate(filePathLen--);
+ }
+
+ MakeDirty();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLeafName(nsAString& aLeafName)
+{
+ aLeafName.Truncate();
+
+ if (mWorkingPath.IsEmpty()) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ int32_t offset = mWorkingPath.RFindChar(L'\\');
+
+ // if the working path is just a node without any lashes.
+ if (offset == kNotFound) {
+ aLeafName = mWorkingPath;
+ } else {
+ aLeafName = Substring(mWorkingPath, offset + 1);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLeafName(const nsAString& aLeafName)
+{
+ MakeDirty();
+
+ if (mWorkingPath.IsEmpty()) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ int32_t offset = mWorkingPath.RFindChar(L'\\');
+ if (offset) {
+ mWorkingPath.Truncate(offset + 1);
+ }
+ mWorkingPath.Append(aLeafName);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetPath(nsAString& aResult)
+{
+ aResult = mWorkingPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetCanonicalPath(nsAString& aResult)
+{
+ EnsureShortPath();
+ aResult.Assign(mShortWorkingPath);
+ return NS_OK;
+}
+
+typedef struct
+{
+ WORD wLanguage;
+ WORD wCodePage;
+} LANGANDCODEPAGE;
+
+NS_IMETHODIMP
+nsLocalFile::GetVersionInfoField(const char* aField, nsAString& aResult)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = NS_ERROR_FAILURE;
+
+ const WCHAR* path =
+ mFollowSymlinks ? mResolvedPath.get() : mWorkingPath.get();
+
+ DWORD dummy;
+ DWORD size = ::GetFileVersionInfoSizeW(path, &dummy);
+ if (!size) {
+ return rv;
+ }
+
+ void* ver = moz_xcalloc(size, 1);
+ if (::GetFileVersionInfoW(path, 0, size, ver)) {
+ LANGANDCODEPAGE* translate = nullptr;
+ UINT pageCount;
+ BOOL queryResult = ::VerQueryValueW(ver, L"\\VarFileInfo\\Translation",
+ (void**)&translate, &pageCount);
+ if (queryResult && translate) {
+ for (int32_t i = 0; i < 2; ++i) {
+ wchar_t subBlock[MAX_PATH];
+ _snwprintf(subBlock, MAX_PATH,
+ L"\\StringFileInfo\\%04x%04x\\%s",
+ (i == 0 ? translate[0].wLanguage : ::GetUserDefaultLangID()),
+ translate[0].wCodePage,
+ NS_ConvertASCIItoUTF16(
+ nsDependentCString(aField)).get());
+ subBlock[MAX_PATH - 1] = 0;
+ LPVOID value = nullptr;
+ UINT size;
+ queryResult = ::VerQueryValueW(ver, subBlock, &value, &size);
+ if (queryResult && value) {
+ aResult.Assign(static_cast<char16_t*>(value));
+ if (!aResult.IsEmpty()) {
+ rv = NS_OK;
+ break;
+ }
+ }
+ }
+ }
+ }
+ free(ver);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetShortcut(nsIFile* aTargetFile,
+ nsIFile* aWorkingDir,
+ const char16_t* aArgs,
+ const char16_t* aDescription,
+ nsIFile* aIconFile,
+ int32_t aIconIndex)
+{
+ bool exists;
+ nsresult rv = this->Exists(&exists);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ const WCHAR* targetFilePath = nullptr;
+ const WCHAR* workingDirPath = nullptr;
+ const WCHAR* iconFilePath = nullptr;
+
+ nsAutoString targetFilePathAuto;
+ if (aTargetFile) {
+ rv = aTargetFile->GetPath(targetFilePathAuto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ targetFilePath = targetFilePathAuto.get();
+ }
+
+ nsAutoString workingDirPathAuto;
+ if (aWorkingDir) {
+ rv = aWorkingDir->GetPath(workingDirPathAuto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ workingDirPath = workingDirPathAuto.get();
+ }
+
+ nsAutoString iconPathAuto;
+ if (aIconFile) {
+ rv = aIconFile->GetPath(iconPathAuto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ iconFilePath = iconPathAuto.get();
+ }
+
+ rv = gResolver->SetShortcut(exists,
+ mWorkingPath.get(),
+ targetFilePath,
+ workingDirPath,
+ char16ptr_t(aArgs),
+ char16ptr_t(aDescription),
+ iconFilePath,
+ iconFilePath ? aIconIndex : 0);
+ if (targetFilePath && NS_SUCCEEDED(rv)) {
+ MakeDirty();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDescShareDelete(int32_t aFlags,
+ int32_t aMode,
+ PRFileDesc** aResult)
+{
+ nsresult rv = OpenNSPRFileDescMaybeShareDelete(aFlags, aMode, true, aResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Determines if the drive type for the specified file is rmeote or local.
+ *
+ * @param path The path of the file to check
+ * @param remote Out parameter, on function success holds true if the specified
+ * file path is remote, or false if the file path is local.
+ * @return true on success. The return value implies absolutely nothing about
+ * wether the file is local or remote.
+*/
+static bool
+IsRemoteFilePath(LPCWSTR aPath, bool& aRemote)
+{
+ // Obtain the parent directory path and make sure it ends with
+ // a trailing backslash.
+ WCHAR dirPath[MAX_PATH + 1] = { 0 };
+ wcsncpy(dirPath, aPath, MAX_PATH);
+ if (!PathRemoveFileSpecW(dirPath)) {
+ return false;
+ }
+ size_t len = wcslen(dirPath);
+ // In case the dirPath holds exaclty MAX_PATH and remains unchanged, we
+ // recheck the required length here since we need to terminate it with
+ // a backslash.
+ if (len >= MAX_PATH) {
+ return false;
+ }
+
+ dirPath[len] = L'\\';
+ dirPath[len + 1] = L'\0';
+ UINT driveType = GetDriveTypeW(dirPath);
+ aRemote = driveType == DRIVE_REMOTE;
+ return true;
+}
+
+nsresult
+nsLocalFile::CopySingleFile(nsIFile* aSourceFile, nsIFile* aDestParent,
+ const nsAString& aNewName, uint32_t aOptions)
+{
+ nsresult rv = NS_OK;
+ nsAutoString filePath;
+
+ bool move = aOptions & (Move | Rename);
+
+ // get the path that we are going to copy to.
+ // Since windows does not know how to auto
+ // resolve shortcuts, we must work with the
+ // target.
+ nsAutoString destPath;
+ aDestParent->GetTarget(destPath);
+
+ destPath.Append('\\');
+
+ if (aNewName.IsEmpty()) {
+ nsAutoString aFileName;
+ aSourceFile->GetLeafName(aFileName);
+ destPath.Append(aFileName);
+ } else {
+ destPath.Append(aNewName);
+ }
+
+
+ if (aOptions & FollowSymlinks) {
+ rv = aSourceFile->GetTarget(filePath);
+ if (filePath.IsEmpty()) {
+ rv = aSourceFile->GetPath(filePath);
+ }
+ } else {
+ rv = aSourceFile->GetPath(filePath);
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Pass the flag COPY_FILE_NO_BUFFERING to CopyFileEx as we may be copying
+ // to a SMBV2 remote drive. Without this parameter subsequent append mode
+ // file writes can cause the resultant file to become corrupt. We only need to do
+ // this if the major version of Windows is > 5(Only Windows Vista and above
+ // can support SMBV2). With a 7200RPM hard drive:
+ // Copying a 1KB file with COPY_FILE_NO_BUFFERING takes about 30-60ms.
+ // Copying a 1KB file without COPY_FILE_NO_BUFFERING takes < 1ms.
+ // So we only use COPY_FILE_NO_BUFFERING when we have a remote drive.
+ int copyOK;
+ DWORD dwCopyFlags = COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
+ if (IsVistaOrLater()) {
+ bool path1Remote, path2Remote;
+ if (!IsRemoteFilePath(filePath.get(), path1Remote) ||
+ !IsRemoteFilePath(destPath.get(), path2Remote) ||
+ path1Remote || path2Remote) {
+ dwCopyFlags |= COPY_FILE_NO_BUFFERING;
+ }
+ }
+
+ if (!move) {
+ copyOK = ::CopyFileExW(filePath.get(), destPath.get(), nullptr,
+ nullptr, nullptr, dwCopyFlags);
+ } else {
+ copyOK = ::MoveFileExW(filePath.get(), destPath.get(),
+ MOVEFILE_REPLACE_EXISTING);
+
+ // Check if copying the source file to a different volume,
+ // as this could be an SMBV2 mapped drive.
+ if (!copyOK && GetLastError() == ERROR_NOT_SAME_DEVICE) {
+ if (aOptions & Rename) {
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ copyOK = CopyFileExW(filePath.get(), destPath.get(), nullptr,
+ nullptr, nullptr, dwCopyFlags);
+
+ if (copyOK) {
+ DeleteFileW(filePath.get());
+ }
+ }
+ }
+
+ if (!copyOK) { // CopyFileEx and MoveFileEx return zero at failure.
+ rv = ConvertWinError(GetLastError());
+ } else if (move && !(aOptions & SkipNtfsAclReset)) {
+ // Set security permissions to inherit from parent.
+ // Note: propagates to all children: slow for big file trees
+ PACL pOldDACL = nullptr;
+ PSECURITY_DESCRIPTOR pSD = nullptr;
+ ::GetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, &pOldDACL, nullptr, &pSD);
+ if (pOldDACL)
+ ::SetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION |
+ UNPROTECTED_DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, pOldDACL, nullptr);
+ if (pSD) {
+ LocalFree((HLOCAL)pSD);
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::CopyMove(nsIFile* aParentDir, const nsAString& aNewName,
+ uint32_t aOptions)
+{
+ bool move = aOptions & (Move | Rename);
+ bool followSymlinks = aOptions & FollowSymlinks;
+
+ nsCOMPtr<nsIFile> newParentDir = aParentDir;
+ // check to see if this exists, otherwise return an error.
+ // we will check this by resolving. If the user wants us
+ // to follow links, then we are talking about the target,
+ // hence we can use the |FollowSymlinks| option.
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!newParentDir) {
+ // no parent was specified. We must rename.
+ if (aNewName.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ rv = GetParent(getter_AddRefs(newParentDir));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ if (!newParentDir) {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+
+ // make sure it exists and is a directory. Create it if not there.
+ bool exists;
+ newParentDir->Exists(&exists);
+ if (!exists) {
+ rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ bool isDir;
+ newParentDir->IsDirectory(&isDir);
+ if (!isDir) {
+ if (followSymlinks) {
+ bool isLink;
+ newParentDir->IsSymlink(&isLink);
+ if (isLink) {
+ nsAutoString target;
+ newParentDir->GetTarget(target);
+
+ nsCOMPtr<nsIFile> realDest = new nsLocalFile();
+ rv = realDest->InitWithPath(target);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return CopyMove(realDest, aNewName, aOptions);
+ }
+ } else {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+ }
+
+ // Try different ways to move/copy files/directories
+ bool done = false;
+ bool isDir;
+ IsDirectory(&isDir);
+ bool isSymlink;
+ IsSymlink(&isSymlink);
+
+ // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
+ if (move || !isDir || (isSymlink && !followSymlinks)) {
+ // Copy/Move single file, or move a directory
+ if (!aParentDir) {
+ aOptions |= SkipNtfsAclReset;
+ }
+ rv = CopySingleFile(this, newParentDir, aNewName, aOptions);
+ done = NS_SUCCEEDED(rv);
+ // If we are moving a directory and that fails, fallback on directory
+ // enumeration. See bug 231300 for details.
+ if (!done && !(move && isDir)) {
+ return rv;
+ }
+ }
+
+ // Not able to copy or move directly, so enumerate it
+ if (!done) {
+ // create a new target destination in the new parentDir;
+ nsCOMPtr<nsIFile> target;
+ rv = newParentDir->Clone(getter_AddRefs(target));
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoString allocatedNewName;
+ if (aNewName.IsEmpty()) {
+ bool isLink;
+ IsSymlink(&isLink);
+ if (isLink) {
+ nsAutoString temp;
+ GetTarget(temp);
+ int32_t offset = temp.RFindChar(L'\\');
+ if (offset == kNotFound) {
+ allocatedNewName = temp;
+ } else {
+ allocatedNewName = Substring(temp, offset + 1);
+ }
+ } else {
+ GetLeafName(allocatedNewName);// this should be the leaf name of the
+ }
+ } else {
+ allocatedNewName = aNewName;
+ }
+
+ rv = target->Append(allocatedNewName);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ allocatedNewName.Truncate();
+
+ // check if the destination directory already exists
+ target->Exists(&exists);
+ if (!exists) {
+ // if the destination directory cannot be created, return an error
+ rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ // check if the destination directory is writable and empty
+ bool isWritable;
+
+ target->IsWritable(&isWritable);
+ if (!isWritable) {
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> targetIterator;
+ rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool more;
+ targetIterator->HasMoreElements(&more);
+ // return error if target directory is not empty
+ if (more) {
+ return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ }
+ }
+
+ RefPtr<nsDirEnumerator> dirEnum = new nsDirEnumerator();
+
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("dirEnum initialization failed");
+ return rv;
+ }
+
+ bool more = false;
+ while (NS_SUCCEEDED(dirEnum->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIFile> file;
+ dirEnum->GetNext(getter_AddRefs(item));
+ file = do_QueryInterface(item);
+ if (file) {
+ bool isDir, isLink;
+
+ file->IsDirectory(&isDir);
+ file->IsSymlink(&isLink);
+
+ if (move) {
+ if (followSymlinks) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = file->MoveTo(target, EmptyString());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ if (followSymlinks) {
+ rv = file->CopyToFollowingLinks(target, EmptyString());
+ } else {
+ rv = file->CopyTo(target, EmptyString());
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ }
+ // we've finished moving all the children of this directory
+ // in the new directory. so now delete the directory
+ // note, we don't need to do a recursive delete.
+ // MoveTo() is recursive. At this point,
+ // we've already moved the children of the current folder
+ // to the new location. nothing should be left in the folder.
+ if (move) {
+ rv = Remove(false /* recursive */);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+
+
+ // If we moved, we want to adjust this.
+ if (move) {
+ MakeDirty();
+
+ nsAutoString newParentPath;
+ newParentDir->GetPath(newParentPath);
+
+ if (newParentPath.IsEmpty()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aNewName.IsEmpty()) {
+ nsAutoString aFileName;
+ GetLeafName(aFileName);
+
+ InitWithPath(newParentPath);
+ Append(aFileName);
+ } else {
+ InitWithPath(newParentPath);
+ Append(aNewName);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyTo(nsIFile* aNewParentDir, const nsAString& aNewName)
+{
+ return CopyMove(aNewParentDir, aNewName, 0);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinks(nsIFile* aNewParentDir,
+ const nsAString& aNewName)
+{
+ return CopyMove(aNewParentDir, aNewName, FollowSymlinks);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveTo(nsIFile* aNewParentDir, const nsAString& aNewName)
+{
+ return CopyMove(aNewParentDir, aNewName, Move);
+}
+
+NS_IMETHODIMP
+nsLocalFile::RenameTo(nsIFile* aNewParentDir, const nsAString& aNewName)
+{
+ nsCOMPtr<nsIFile> targetParentDir = aNewParentDir;
+ // check to see if this exists, otherwise return an error.
+ // we will check this by resolving. If the user wants us
+ // to follow links, then we are talking about the target,
+ // hence we can use the |followSymlinks| parameter.
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!targetParentDir) {
+ // no parent was specified. We must rename.
+ if (aNewName.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ rv = GetParent(getter_AddRefs(targetParentDir));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ if (!targetParentDir) {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+
+ // make sure it exists and is a directory. Create it if not there.
+ bool exists;
+ targetParentDir->Exists(&exists);
+ if (!exists) {
+ rv = targetParentDir->Create(DIRECTORY_TYPE, 0644);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ bool isDir;
+ targetParentDir->IsDirectory(&isDir);
+ if (!isDir) {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+
+ uint32_t options = Rename;
+ if (!aNewParentDir) {
+ options |= SkipNtfsAclReset;
+ }
+ // Move single file, or move a directory
+ return CopySingleFile(this, targetParentDir, aNewName, options);
+}
+
+NS_IMETHODIMP
+nsLocalFile::RenameToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aNewName, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return RenameTo(aNewParentDir, tmp);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary** aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ bool isFile;
+ nsresult rv = IsFile(&isFile);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!isFile) {
+ return NS_ERROR_FILE_IS_DIRECTORY;
+ }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcnt::SetActivityIsLegal(false);
+#endif
+
+ PRLibSpec libSpec;
+ libSpec.value.pathname_u = mResolvedPath.get();
+ libSpec.type = PR_LibSpec_PathnameU;
+ *aResult = PR_LoadLibraryWithFlags(libSpec, 0);
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcnt::SetActivityIsLegal(true);
+#endif
+
+ if (*aResult) {
+ return NS_OK;
+ }
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(bool aRecursive)
+{
+ // NOTE:
+ //
+ // if the working path points to a shortcut, then we will only
+ // delete the shortcut itself. even if the shortcut points to
+ // a directory, we will not recurse into that directory or
+ // delete that directory itself. likewise, if the shortcut
+ // points to a normal file, we will not delete the real file.
+ // this is done to be consistent with the other platforms that
+ // behave this way. we do this even if the followLinks attribute
+ // is set to true. this helps protect against misuse that could
+ // lead to security bugs (e.g., bug 210588).
+ //
+ // Since shortcut files are no longer permitted to be used as unix-like
+ // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
+ // this processing is a lot simpler. Even if the shortcut file is
+ // pointing to a directory, only the mWorkingPath value is used and so
+ // only the shortcut file will be deleted.
+
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ bool isDir, isLink;
+ nsresult rv;
+
+ isDir = false;
+ rv = IsSymlink(&isLink);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // only check to see if we have a directory if it isn't a link
+ if (!isLink) {
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ if (isDir) {
+ if (aRecursive) {
+ RefPtr<nsDirEnumerator> dirEnum = new nsDirEnumerator();
+
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool more = false;
+ while (NS_SUCCEEDED(dirEnum->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> item;
+ dirEnum->GetNext(getter_AddRefs(item));
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item);
+ if (file) {
+ file->Remove(aRecursive);
+ }
+ }
+ }
+ if (RemoveDirectoryW(mWorkingPath.get()) == 0) {
+ return ConvertWinError(GetLastError());
+ }
+ } else {
+ if (DeleteFileW(mWorkingPath.get()) == 0) {
+ return ConvertWinError(GetLastError());
+ }
+ }
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRTime* aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aLastModifiedTime)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // get the modified time of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as GetLastModifiedTimeOfLink)
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // microseconds -> milliseconds
+ *aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aLastModifiedTime)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ PRFileInfo64 info;
+ nsresult rv = GetFileInfo(mWorkingPath, &info);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // microseconds -> milliseconds
+ *aLastModifiedTime = info.modifyTime / PR_USEC_PER_MSEC;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // set the modified time of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as SetLastModifiedTimeOfLink)
+
+ rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
+ if (NS_SUCCEEDED(rv)) {
+ MakeDirty();
+ }
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModifiedTime)
+{
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
+ if (NS_SUCCEEDED(rv)) {
+ MakeDirty();
+ }
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::SetModDate(PRTime aLastModifiedTime, const wchar_t* aFilePath)
+{
+ // The FILE_FLAG_BACKUP_SEMANTICS is required in order to change the
+ // modification time for directories.
+ HANDLE file = ::CreateFileW(aFilePath, // pointer to name of the file
+ GENERIC_WRITE, // access (write) mode
+ 0, // share mode
+ nullptr, // pointer to security attributes
+ OPEN_EXISTING, // how to create
+ FILE_FLAG_BACKUP_SEMANTICS, // file attributes
+ nullptr);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ return ConvertWinError(GetLastError());
+ }
+
+ FILETIME ft;
+ SYSTEMTIME st;
+ PRExplodedTime pret;
+
+ // PR_ExplodeTime expects usecs...
+ PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_GMTParameters, &pret);
+ st.wYear = pret.tm_year;
+ st.wMonth = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
+ st.wDayOfWeek = pret.tm_wday;
+ st.wDay = pret.tm_mday;
+ st.wHour = pret.tm_hour;
+ st.wMinute = pret.tm_min;
+ st.wSecond = pret.tm_sec;
+ st.wMilliseconds = pret.tm_usec / 1000;
+
+ nsresult rv = NS_OK;
+ // if at least one of these fails...
+ if (!(SystemTimeToFileTime(&st, &ft) != 0 &&
+ SetFileTime(file, nullptr, &ft, &ft) != 0)) {
+ rv = ConvertWinError(GetLastError());
+ }
+
+ CloseHandle(file);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(uint32_t* aPermissions)
+{
+ if (NS_WARN_IF(!aPermissions)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // get the permissions of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as GetPermissionsOfLink)
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool isWritable, isExecutable;
+ IsWritable(&isWritable);
+ IsExecutable(&isExecutable);
+
+ *aPermissions = PR_IRUSR | PR_IRGRP | PR_IROTH; // all read
+ if (isWritable) {
+ *aPermissions |= PR_IWUSR | PR_IWGRP | PR_IWOTH; // all write
+ }
+ if (isExecutable) {
+ *aPermissions |= PR_IXUSR | PR_IXGRP | PR_IXOTH; // all execute
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissions)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aPermissions)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link. It is not
+ // possible for a link file to be executable.
+
+ DWORD word = ::GetFileAttributesW(mWorkingPath.get());
+ if (word == INVALID_FILE_ATTRIBUTES) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ bool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
+ *aPermissions = PR_IRUSR | PR_IRGRP | PR_IROTH; // all read
+ if (isWritable) {
+ *aPermissions |= PR_IWUSR | PR_IWGRP | PR_IWOTH; // all write
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(uint32_t aPermissions)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ // set the permissions of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as SetPermissionsOfLink)
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // windows only knows about the following permissions
+ int mode = 0;
+ if (aPermissions & (PR_IRUSR | PR_IRGRP | PR_IROTH)) { // any read
+ mode |= _S_IREAD;
+ }
+ if (aPermissions & (PR_IWUSR | PR_IWGRP | PR_IWOTH)) { // any write
+ mode |= _S_IWRITE;
+ }
+
+ if (_wchmod(mResolvedPath.get(), mode) == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
+{
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ // windows only knows about the following permissions
+ int mode = 0;
+ if (aPermissions & (PR_IRUSR | PR_IRGRP | PR_IROTH)) { // any read
+ mode |= _S_IREAD;
+ }
+ if (aPermissions & (PR_IWUSR | PR_IWGRP | PR_IWOTH)) { // any write
+ mode |= _S_IWRITE;
+ }
+
+ if (_wchmod(mWorkingPath.get(), mode) == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(int64_t* aFileSize)
+{
+ if (NS_WARN_IF(!aFileSize)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aFileSize = mFileInfo64.size;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aFileSize)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ PRFileInfo64 info;
+ if (NS_FAILED(GetFileInfo(mWorkingPath, &info))) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ *aFileSize = info.size;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(int64_t aFileSize)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ HANDLE hFile = ::CreateFileW(mResolvedPath.get(),// pointer to name of the file
+ GENERIC_WRITE, // access (write) mode
+ FILE_SHARE_READ, // share mode
+ nullptr, // pointer to security attributes
+ OPEN_EXISTING, // how to create
+ FILE_ATTRIBUTE_NORMAL, // file attributes
+ nullptr);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return ConvertWinError(GetLastError());
+ }
+
+ // seek the file pointer to the new, desired end of file
+ // and then truncate the file at that position
+ rv = NS_ERROR_FAILURE;
+ aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
+ if (aFileSize != -1 && SetEndOfFile(hFile)) {
+ MakeDirty();
+ rv = NS_OK;
+ }
+
+ CloseHandle(hFile);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aDiskSpaceAvailable)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ ResolveAndStat();
+
+ if (mFileInfo64.type == PR_FILE_FILE) {
+ // Since GetDiskFreeSpaceExW works only on directories, use the parent.
+ nsCOMPtr<nsIFile> parent;
+ if (NS_SUCCEEDED(GetParent(getter_AddRefs(parent))) && parent) {
+ return parent->GetDiskSpaceAvailable(aDiskSpaceAvailable);
+ }
+ }
+
+ ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
+ if (::GetDiskFreeSpaceExW(mResolvedPath.get(), &liFreeBytesAvailableToCaller,
+ &liTotalNumberOfBytes, nullptr)) {
+ *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
+ return NS_OK;
+ }
+ *aDiskSpaceAvailable = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile** aParent)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aParent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // A two-character path must be a drive such as C:, so it has no parent
+ if (mWorkingPath.Length() == 2) {
+ *aParent = nullptr;
+ return NS_OK;
+ }
+
+ int32_t offset = mWorkingPath.RFindChar(char16_t('\\'));
+ // adding this offset check that was removed in bug 241708 fixes mail
+ // directories that aren't relative to/underneath the profile dir.
+ // e.g., on a different drive. Before you remove them, please make
+ // sure local mail directories that aren't underneath the profile dir work.
+ if (offset == kNotFound) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ // A path of the form \\NAME is a top-level path and has no parent
+ if (offset == 1 && mWorkingPath[0] == L'\\') {
+ *aParent = nullptr;
+ return NS_OK;
+ }
+
+ nsAutoString parentPath(mWorkingPath);
+
+ if (offset > 0) {
+ parentPath.Truncate(offset);
+ } else {
+ parentPath.AssignLiteral("\\\\.");
+ }
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_NewLocalFile(parentPath, mFollowSymlinks,
+ getter_AddRefs(localFile));
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ localFile.forget(aParent);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Exists(bool* aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+
+ MakeDirty();
+ nsresult rv = ResolveAndStat();
+ *aResult = NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_IS_LOCKED;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(bool* aIsWritable)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ // The read-only attribute on a FAT directory only means that it can't
+ // be deleted. It is still possible to modify the contents of the directory.
+ nsresult rv = IsDirectory(aIsWritable);
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ *aIsWritable = true;
+ return NS_OK;
+ } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
+ // If the file is normally allowed write access
+ // we should still return that the file is writable.
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (*aIsWritable) {
+ return NS_OK;
+ }
+
+ // writable if the file doesn't have the readonly attribute
+ rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ *aIsWritable = false;
+ return NS_OK;
+ } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
+ // If the file is normally allowed write access
+ // we should still return that the file is writable.
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aIsWritable = !*aIsWritable;
+
+ // If the read only attribute is not set, check to make sure
+ // we can open the file with write access.
+ if (*aIsWritable) {
+ PRFileDesc* file;
+ rv = OpenFile(mResolvedPath, PR_WRONLY, 0, false, &file);
+ if (NS_SUCCEEDED(rv)) {
+ PR_Close(file);
+ } else if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ *aIsWritable = false;
+ } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
+ // If it is locked and read only we would have
+ // gotten access denied
+ *aIsWritable = true;
+ } else {
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(bool* aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aResult = true;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(bool* aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aResult = false;
+
+ nsresult rv;
+
+ // only files can be executables
+ bool isFile;
+ rv = IsFile(&isFile);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!isFile) {
+ return NS_OK;
+ }
+
+ //TODO: shouldn't we be checking mFollowSymlinks here?
+ bool symLink;
+ rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoString path;
+ if (symLink) {
+ GetTarget(path);
+ } else {
+ GetPath(path);
+ }
+
+ // kill trailing dots and spaces.
+ int32_t filePathLen = path.Length() - 1;
+ while (filePathLen > 0 && (path[filePathLen] == L' ' ||
+ path[filePathLen] == L'.')) {
+ path.Truncate(filePathLen--);
+ }
+
+ // Get extension.
+ int32_t dotIdx = path.RFindChar(char16_t('.'));
+ if (dotIdx != kNotFound) {
+ // Convert extension to lower case.
+ char16_t* p = path.BeginWriting();
+ for (p += dotIdx + 1; *p; ++p) {
+ *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
+ }
+
+ // Search for any of the set of executable extensions.
+ static const char* const executableExts[] = {
+ "ad",
+ "ade", // access project extension
+ "adp",
+ "air", // Adobe AIR installer
+ "app", // executable application
+ "application", // from bug 348763
+ "asp",
+ "bas",
+ "bat",
+ "chm",
+ "cmd",
+ "com",
+ "cpl",
+ "crt",
+ "exe",
+ "fxp", // FoxPro compiled app
+ "hlp",
+ "hta",
+ "inf",
+ "ins",
+ "isp",
+ "jar", // java application bundle
+ "js",
+ "jse",
+ "lnk",
+ "mad", // Access Module Shortcut
+ "maf", // Access
+ "mag", // Access Diagram Shortcut
+ "mam", // Access Macro Shortcut
+ "maq", // Access Query Shortcut
+ "mar", // Access Report Shortcut
+ "mas", // Access Stored Procedure
+ "mat", // Access Table Shortcut
+ "mau", // Media Attachment Unit
+ "mav", // Access View Shortcut
+ "maw", // Access Data Access Page
+ "mda", // Access Add-in, MDA Access 2 Workgroup
+ "mdb",
+ "mde",
+ "mdt", // Access Add-in Data
+ "mdw", // Access Workgroup Information
+ "mdz", // Access Wizard Template
+ "msc",
+ "msh", // Microsoft Shell
+ "mshxml", // Microsoft Shell
+ "msi",
+ "msp",
+ "mst",
+ "ops", // Office Profile Settings
+ "pcd",
+ "pif",
+ "plg", // Developer Studio Build Log
+ "prf", // windows system file
+ "prg",
+ "pst",
+ "reg",
+ "scf", // Windows explorer command
+ "scr",
+ "sct",
+ "shb",
+ "shs",
+ "url",
+ "vb",
+ "vbe",
+ "vbs",
+ "vsd",
+ "vsmacros", // Visual Studio .NET Binary-based Macro Project
+ "vss",
+ "vst",
+ "vsw",
+ "ws",
+ "wsc",
+ "wsf",
+ "wsh"
+ };
+ nsDependentSubstring ext = Substring(path, dotIdx + 1);
+ for (size_t i = 0; i < ArrayLength(executableExts); ++i) {
+ if (ext.EqualsASCII(executableExts[i])) {
+ // Found a match. Set result and quit.
+ *aResult = true;
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(bool* aResult)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(bool* aResult)
+{
+ nsresult rv = HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, aResult);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = !*aResult;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(bool* aResult)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, aResult);
+}
+
+nsresult
+nsLocalFile::HasFileAttribute(DWORD aFileAttrib, bool* aResult)
+{
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ DWORD attributes = GetFileAttributesW(mResolvedPath.get());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ return ConvertWinError(GetLastError());
+ }
+
+ *aResult = ((attributes & aFileAttrib) != 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(bool* aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // unless it is a valid shortcut path it's not a symlink
+ if (!IsShortcutPath(mWorkingPath)) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ // we need to know if this is a file or directory
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We should not check mFileInfo64.type here for PR_FILE_FILE because lnk
+ // files can point to directories or files. Important security checks
+ // depend on correctly identifying lnk files. mFileInfo64 now holds info
+ // about the target of the lnk file, not the actual lnk file!
+ *aResult = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(bool* aResult)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile* aInFile, bool* aResult)
+{
+ if (NS_WARN_IF(!aInFile)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!aResult)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ EnsureShortPath();
+
+ nsCOMPtr<nsILocalFileWin> lf(do_QueryInterface(aInFile));
+ if (!lf) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ nsAutoString inFilePath;
+ lf->GetCanonicalPath(inFilePath);
+
+ // Ok : Win9x
+ *aResult = _wcsicmp(mShortWorkingPath.get(), inFilePath.get()) == 0;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile* aInFile, bool* aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ *aResult = false;
+
+ nsAutoString myFilePath;
+ if (NS_FAILED(GetTarget(myFilePath))) {
+ GetPath(myFilePath);
+ }
+
+ uint32_t myFilePathLen = myFilePath.Length();
+
+ nsAutoString inFilePath;
+ if (NS_FAILED(aInFile->GetTarget(inFilePath))) {
+ aInFile->GetPath(inFilePath);
+ }
+
+ // make sure that the |aInFile|'s path has a trailing separator.
+ if (inFilePath.Length() >= myFilePathLen &&
+ inFilePath[myFilePathLen] == L'\\') {
+ if (_wcsnicmp(myFilePath.get(), inFilePath.get(), myFilePathLen) == 0) {
+ *aResult = true;
+ }
+
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetTarget(nsAString& aResult)
+{
+ aResult.Truncate();
+#if STRICT_FAKE_SYMLINKS
+ bool symLink;
+
+ nsresult rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!symLink) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+#endif
+ ResolveAndStat();
+
+ aResult = mResolvedPath;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(bool* aFollowLinks)
+{
+ *aFollowLinks = mFollowSymlinks;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(bool aFollowLinks)
+{
+ MakeDirty();
+ mFollowSymlinks = aFollowLinks;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator** aEntries)
+{
+ nsresult rv;
+
+ *aEntries = nullptr;
+ if (mWorkingPath.EqualsLiteral("\\\\.")) {
+ RefPtr<nsDriveEnumerator> drives = new nsDriveEnumerator;
+ rv = drives->Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ drives.forget(aEntries);
+ return NS_OK;
+ }
+
+ RefPtr<nsDirEnumerator> dirEnum = new nsDirEnumerator();
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ dirEnum.forget(aEntries);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
+{
+ CopyUTF16toUTF8(mWorkingPath, aPersistentDescriptor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
+{
+ if (IsUTF8(aPersistentDescriptor)) {
+ return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor));
+ } else {
+ return InitWithNativePath(aPersistentDescriptor);
+ }
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileAttributesWin(uint32_t* aAttribs)
+{
+ *aAttribs = 0;
+ DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
+ if (dwAttrs == INVALID_FILE_ATTRIBUTES) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ if (!(dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) {
+ *aAttribs |= WFA_SEARCH_INDEXED;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileAttributesWin(uint32_t aAttribs)
+{
+ DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
+ if (dwAttrs == INVALID_FILE_ATTRIBUTES) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ if (aAttribs & WFA_SEARCH_INDEXED) {
+ dwAttrs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ } else {
+ dwAttrs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ }
+
+ if (aAttribs & WFA_READONLY) {
+ dwAttrs |= FILE_ATTRIBUTE_READONLY;
+ } else if ((aAttribs & WFA_READWRITE) &&
+ (dwAttrs & FILE_ATTRIBUTE_READONLY)) {
+ dwAttrs &= ~FILE_ATTRIBUTE_READONLY;
+ }
+
+ if (SetFileAttributesW(mWorkingPath.get(), dwAttrs) == 0) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ // This API should be main thread only
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // make sure mResolvedPath is set
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
+ return rv;
+ }
+
+ // To create a new thread, get the thread manager
+ nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
+ nsCOMPtr<nsIThread> mythread;
+ rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIRunnable> runnable = new AsyncRevealOperation(mResolvedPath);
+
+ // After the dispatch, the result runnable will shut down the worker
+ // thread, so we can let it go.
+ mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ // This API should be main thread only
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // make sure mResolvedPath is set
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // use the app registry name to launch a shell execute....
+ SHELLEXECUTEINFOW seinfo;
+ memset(&seinfo, 0, sizeof(seinfo));
+ seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+ seinfo.fMask = SEE_MASK_ASYNCOK;
+ seinfo.hwnd = GetMostRecentNavigatorHWND();
+ seinfo.lpVerb = nullptr;
+ seinfo.lpFile = mResolvedPath.get();
+ seinfo.lpParameters = nullptr;
+ seinfo.lpDirectory = nullptr;
+ seinfo.nShow = SW_SHOWNORMAL;
+
+ // Use the directory of the file we're launching as the working
+ // directory. That way if we have a self extracting EXE it won't
+ // suggest to extract to the install directory.
+ WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
+ wcsncpy(workingDirectory, mResolvedPath.get(), MAX_PATH);
+ if (PathRemoveFileSpecW(workingDirectory)) {
+ seinfo.lpDirectory = workingDirectory;
+ } else {
+ NS_WARNING("Could not set working directory for launched file.");
+ }
+
+ if (ShellExecuteExW(&seinfo)) {
+ return NS_OK;
+ }
+ DWORD r = GetLastError();
+ // if the file has no association, we launch windows'
+ // "what do you want to do" dialog
+ if (r == SE_ERR_NOASSOC) {
+ nsAutoString shellArg;
+ shellArg.AssignLiteral(u"shell32.dll,OpenAs_RunDLL ");
+ shellArg.Append(mResolvedPath);
+ seinfo.lpFile = L"RUNDLL32.EXE";
+ seinfo.lpParameters = shellArg.get();
+ if (ShellExecuteExW(&seinfo)) {
+ return NS_OK;
+ }
+ r = GetLastError();
+ }
+ if (r < 32) {
+ switch (r) {
+ case 0:
+ case SE_ERR_OOM:
+ return NS_ERROR_OUT_OF_MEMORY;
+ case ERROR_FILE_NOT_FOUND:
+ return NS_ERROR_FILE_NOT_FOUND;
+ case ERROR_PATH_NOT_FOUND:
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ case ERROR_BAD_FORMAT:
+ return NS_ERROR_FILE_CORRUPTED;
+ case SE_ERR_ACCESSDENIED:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ case SE_ERR_ASSOCINCOMPLETE:
+ case SE_ERR_NOASSOC:
+ return NS_ERROR_UNEXPECTED;
+ case SE_ERR_DDEBUSY:
+ case SE_ERR_DDEFAIL:
+ case SE_ERR_DDETIMEOUT:
+ return NS_ERROR_NOT_AVAILABLE;
+ case SE_ERR_DLLNOTFOUND:
+ return NS_ERROR_FAILURE;
+ case SE_ERR_SHARE:
+ return NS_ERROR_FILE_IS_LOCKED;
+ default:
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks, nsIFile** aResult)
+{
+ RefPtr<nsLocalFile> file = new nsLocalFile();
+
+ file->SetFollowLinks(aFollowLinks);
+
+ if (!aPath.IsEmpty()) {
+ nsresult rv = file->InitWithPath(aPath);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ file.forget(aResult);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Native (lossy) interface
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aFilePath, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return InitWithPath(tmp);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString& aNode)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aNode, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return Append(tmp);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString& aNode)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aNode, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return AppendRelativePath(tmp);
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString& aLeafName)
+{
+ //NS_WARNING("This API is lossy. Use GetLeafName !");
+ nsAutoString tmp;
+ nsresult rv = GetLeafName(tmp);
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_CopyUnicodeToNative(tmp, aLeafName);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString& aLeafName)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aLeafName, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return SetLeafName(tmp);
+ }
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString& aResult)
+{
+ //NS_WARNING("This API is lossy. Use GetPath !");
+ nsAutoString tmp;
+ nsresult rv = GetPath(tmp);
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_CopyUnicodeToNative(tmp, aResult);
+ }
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeCanonicalPath(nsACString& aResult)
+{
+ NS_WARNING("This method is lossy. Use GetCanonicalPath !");
+ EnsureShortPath();
+ NS_CopyUnicodeToNative(mShortWorkingPath, aResult);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (aNewName.IsEmpty()) {
+ return CopyTo(aNewParentDir, EmptyString());
+ }
+
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aNewName, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return CopyTo(aNewParentDir, tmp);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile* aNewParentDir,
+ const nsACString& aNewName)
+{
+ if (aNewName.IsEmpty()) {
+ return CopyToFollowingLinks(aNewParentDir, EmptyString());
+ }
+
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aNewName, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return CopyToFollowingLinks(aNewParentDir, tmp);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (aNewName.IsEmpty()) {
+ return MoveTo(aNewParentDir, EmptyString());
+ }
+
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aNewName, tmp);
+ if (NS_SUCCEEDED(rv)) {
+ return MoveTo(aNewParentDir, tmp);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString& aResult)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_WARNING("This API is lossy. Use GetTarget !");
+ nsAutoString tmp;
+ nsresult rv = GetTarget(tmp);
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_CopyUnicodeToNative(tmp, aResult);
+ }
+
+ return rv;
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowLinks,
+ nsIFile** aResult)
+{
+ nsAutoString buf;
+ nsresult rv = NS_CopyNativeToUnicode(aPath, buf);
+ if (NS_FAILED(rv)) {
+ *aResult = nullptr;
+ return rv;
+ }
+ return NS_NewLocalFile(buf, aFollowLinks, aResult);
+}
+
+void
+nsLocalFile::EnsureShortPath()
+{
+ if (!mShortWorkingPath.IsEmpty()) {
+ return;
+ }
+
+ WCHAR shortPath[MAX_PATH + 1];
+ DWORD lengthNeeded = ::GetShortPathNameW(mWorkingPath.get(), shortPath,
+ ArrayLength(shortPath));
+ // If an error occurred then lengthNeeded is set to 0 or the length of the
+ // needed buffer including null termination. If it succeeds the number of
+ // wide characters not including null termination is returned.
+ if (lengthNeeded != 0 && lengthNeeded < ArrayLength(shortPath)) {
+ mShortWorkingPath.Assign(shortPath);
+ } else {
+ mShortWorkingPath.Assign(mWorkingPath);
+ }
+}
+
+// nsIHashable
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIHashable* aOther, bool* aResult)
+{
+ nsCOMPtr<nsIFile> otherfile(do_QueryInterface(aOther));
+ if (!otherfile) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ return Equals(otherfile, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetHashCode(uint32_t* aResult)
+{
+ // In order for short and long path names to hash to the same value we
+ // always hash on the short pathname.
+ EnsureShortPath();
+
+ *aResult = HashString(mShortWorkingPath);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <static members>
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+ DebugOnly<nsresult> rv = NS_CreateShortcutResolver();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+ NS_DestroyShortcutResolver();
+}
+
+NS_IMPL_ISUPPORTS(nsDriveEnumerator, nsISimpleEnumerator)
+
+nsDriveEnumerator::nsDriveEnumerator()
+{
+}
+
+nsDriveEnumerator::~nsDriveEnumerator()
+{
+}
+
+nsresult
+nsDriveEnumerator::Init()
+{
+ /* If the length passed to GetLogicalDriveStrings is smaller
+ * than the length of the string it would return, it returns
+ * the length required for the string. */
+ DWORD length = GetLogicalDriveStringsW(0, 0);
+ /* The string is null terminated */
+ if (!mDrives.SetLength(length + 1, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!GetLogicalDriveStringsW(length, wwc(mDrives.BeginWriting()))) {
+ return NS_ERROR_FAILURE;
+ }
+ mDrives.BeginReading(mStartOfCurrentDrive);
+ mDrives.EndReading(mEndOfDrivesString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDriveEnumerator::HasMoreElements(bool* aHasMore)
+{
+ *aHasMore = *mStartOfCurrentDrive != L'\0';
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDriveEnumerator::GetNext(nsISupports** aNext)
+{
+ /* GetLogicalDrives stored in mDrives is a concatenation
+ * of null terminated strings, followed by a null terminator.
+ * mStartOfCurrentDrive is an iterator pointing at the first
+ * character of the current drive. */
+ if (*mStartOfCurrentDrive == L'\0') {
+ *aNext = nullptr;
+ return NS_OK;
+ }
+
+ nsAString::const_iterator driveEnd = mStartOfCurrentDrive;
+ FindCharInReadable(L'\0', driveEnd, mEndOfDrivesString);
+ nsString drive(Substring(mStartOfCurrentDrive, driveEnd));
+ mStartOfCurrentDrive = ++driveEnd;
+
+ nsIFile* file;
+ nsresult rv = NS_NewLocalFile(drive, false, &file);
+
+ *aNext = file;
+ return rv;
+}
diff --git a/xpcom/io/nsLocalFileWin.h b/xpcom/io/nsLocalFileWin.h
new file mode 100644
index 000000000..abef2c106
--- /dev/null
+++ b/xpcom/io/nsLocalFileWin.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsLocalFileWIN_H_
+#define _nsLocalFileWIN_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsIFactory.h"
+#include "nsILocalFileWin.h"
+#include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
+#include "prio.h"
+
+#include "mozilla/Attributes.h"
+
+#include "windows.h"
+#include "shlobj.h"
+
+#include <sys/stat.h>
+
+class nsLocalFile final
+ : public nsILocalFileWin
+ , public nsIHashable
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static nsresult nsLocalFileConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIFile interface
+ NS_DECL_NSIFILE
+
+ // nsILocalFile interface
+ NS_DECL_NSILOCALFILE
+
+ // nsILocalFileWin interface
+ NS_DECL_NSILOCALFILEWIN
+
+ // nsIHashable interface
+ NS_DECL_NSIHASHABLE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+ // Removes registry command handler parameters, quotes, and expands environment strings.
+ static bool CleanupCmdHandlerPath(nsAString& aCommandHandler);
+
+private:
+ // CopyMove and CopySingleFile constants for |options| parameter:
+ enum CopyFileOption {
+ FollowSymlinks = 1u << 0,
+ Move = 1u << 1,
+ SkipNtfsAclReset = 1u << 2,
+ Rename = 1u << 3
+ };
+
+ nsLocalFile(const nsLocalFile& aOther);
+ ~nsLocalFile()
+ {
+ }
+
+ bool mDirty; // cached information can only be used when this is false
+ bool mResolveDirty;
+ bool mFollowSymlinks; // should we follow symlinks when working on this file
+
+ // this string will always be in native format!
+ nsString mWorkingPath;
+
+ // this will be the resolved path of shortcuts, it will *NEVER*
+ // be returned to the user
+ nsString mResolvedPath;
+
+ // this string, if not empty, is the *short* pathname that represents
+ // mWorkingPath
+ nsString mShortWorkingPath;
+
+ PRFileInfo64 mFileInfo64;
+
+ void MakeDirty()
+ {
+ mDirty = true;
+ mResolveDirty = true;
+ mShortWorkingPath.Truncate();
+ }
+
+ nsresult ResolveAndStat();
+ nsresult Resolve();
+ nsresult ResolveShortcut();
+
+ void EnsureShortPath();
+
+ nsresult CopyMove(nsIFile* aNewParentDir, const nsAString& aNewName,
+ uint32_t aOptions);
+ nsresult CopySingleFile(nsIFile* aSource, nsIFile* aDest,
+ const nsAString& aNewName, uint32_t aOptions);
+
+ nsresult SetModDate(int64_t aLastModifiedTime, const wchar_t* aFilePath);
+ nsresult HasFileAttribute(DWORD aFileAttrib, bool* aResult);
+ nsresult AppendInternal(const nsAFlatString& aNode,
+ bool aMultipleComponents);
+
+ nsresult OpenNSPRFileDescMaybeShareDelete(int32_t aFlags,
+ int32_t aMode,
+ bool aShareDelete,
+ PRFileDesc** aResult);
+};
+
+#endif
diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp
new file mode 100644
index 000000000..4aa397c37
--- /dev/null
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -0,0 +1,835 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+#include "mozilla/Attributes.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Mutex.h"
+
+#include "base/basictypes.h"
+
+#include "nsMultiplexInputStream.h"
+#include "nsICloneableInputStream.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+
+using mozilla::DeprecatedAbs;
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::Some;
+
+class nsMultiplexInputStream final
+ : public nsIMultiplexInputStream
+ , public nsISeekableStream
+ , public nsIIPCSerializableInputStream
+ , public nsICloneableInputStream
+{
+public:
+ nsMultiplexInputStream();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIMULTIPLEXINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+ NS_DECL_NSICLONEABLEINPUTSTREAM
+
+private:
+ ~nsMultiplexInputStream()
+ {
+ }
+
+ struct MOZ_STACK_CLASS ReadSegmentsState
+ {
+ nsCOMPtr<nsIInputStream> mThisStream;
+ uint32_t mOffset;
+ nsWriteSegmentFun mWriter;
+ void* mClosure;
+ bool mDone;
+ };
+
+ static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
+ const char* aFromRawSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t* aWriteCount);
+
+ Mutex mLock; // Protects access to all data members.
+ nsTArray<nsCOMPtr<nsIInputStream>> mStreams;
+ uint32_t mCurrentStream;
+ bool mStartedReadingCurrent;
+ nsresult mStatus;
+};
+
+NS_IMPL_ADDREF(nsMultiplexInputStream)
+NS_IMPL_RELEASE(nsMultiplexInputStream)
+
+NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE,
+ NS_MULTIPLEXINPUTSTREAM_CID)
+
+NS_IMPL_QUERY_INTERFACE_CI(nsMultiplexInputStream,
+ nsIMultiplexInputStream,
+ nsIInputStream,
+ nsISeekableStream,
+ nsIIPCSerializableInputStream,
+ nsICloneableInputStream)
+NS_IMPL_CI_INTERFACE_GETTER(nsMultiplexInputStream,
+ nsIMultiplexInputStream,
+ nsIInputStream,
+ nsISeekableStream)
+
+static nsresult
+AvailableMaybeSeek(nsIInputStream* aStream, uint64_t* aResult)
+{
+ nsresult rv = aStream->Available(aResult);
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ // Blindly seek to the current position if Available() returns
+ // NS_BASE_STREAM_CLOSED.
+ // If nsIFileInputStream is closed in Read() due to CLOSE_ON_EOF flag,
+ // Seek() could reopen the file if REOPEN_ON_REWIND flag is set.
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
+ if (seekable) {
+ nsresult rvSeek = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
+ if (NS_SUCCEEDED(rvSeek)) {
+ rv = aStream->Available(aResult);
+ }
+ }
+ }
+ return rv;
+}
+
+static nsresult
+TellMaybeSeek(nsISeekableStream* aSeekable, int64_t* aResult)
+{
+ nsresult rv = aSeekable->Tell(aResult);
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ // Blindly seek to the current position if Tell() returns
+ // NS_BASE_STREAM_CLOSED.
+ // If nsIFileInputStream is closed in Read() due to CLOSE_ON_EOF flag,
+ // Seek() could reopen the file if REOPEN_ON_REWIND flag is set.
+ nsresult rvSeek = aSeekable->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
+ if (NS_SUCCEEDED(rvSeek)) {
+ rv = aSeekable->Tell(aResult);
+ }
+ }
+ return rv;
+}
+
+nsMultiplexInputStream::nsMultiplexInputStream()
+ : mLock("nsMultiplexInputStream lock"),
+ mCurrentStream(0),
+ mStartedReadingCurrent(false),
+ mStatus(NS_OK)
+{
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::GetCount(uint32_t* aCount)
+{
+ MutexAutoLock lock(mLock);
+ *aCount = mStreams.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::AppendStream(nsIInputStream* aStream)
+{
+ MutexAutoLock lock(mLock);
+ return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::InsertStream(nsIInputStream* aStream, uint32_t aIndex)
+{
+ MutexAutoLock lock(mLock);
+ mStreams.InsertElementAt(aIndex, aStream);
+ if (mCurrentStream > aIndex ||
+ (mCurrentStream == aIndex && mStartedReadingCurrent)) {
+ ++mCurrentStream;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::RemoveStream(uint32_t aIndex)
+{
+ MutexAutoLock lock(mLock);
+ mStreams.RemoveElementAt(aIndex);
+ if (mCurrentStream > aIndex) {
+ --mCurrentStream;
+ } else if (mCurrentStream == aIndex) {
+ mStartedReadingCurrent = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream** aResult)
+{
+ MutexAutoLock lock(mLock);
+ *aResult = mStreams.SafeElementAt(aIndex, nullptr);
+ if (NS_WARN_IF(!*aResult)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Close()
+{
+ MutexAutoLock lock(mLock);
+ mStatus = NS_BASE_STREAM_CLOSED;
+
+ nsresult rv = NS_OK;
+
+ uint32_t len = mStreams.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ nsresult rv2 = mStreams[i]->Close();
+ // We still want to close all streams, but we should return an error
+ if (NS_FAILED(rv2)) {
+ rv = rv2;
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Available(uint64_t* aResult)
+{
+ MutexAutoLock lock(mLock);
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ uint64_t avail = 0;
+
+ uint32_t len = mStreams.Length();
+ for (uint32_t i = mCurrentStream; i < len; i++) {
+ uint64_t streamAvail;
+ mStatus = AvailableMaybeSeek(mStreams[i], &streamAvail);
+ if (NS_WARN_IF(NS_FAILED(mStatus))) {
+ return mStatus;
+ }
+ avail += streamAvail;
+ }
+ *aResult = avail;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
+{
+ MutexAutoLock lock(mLock);
+ // It is tempting to implement this method in terms of ReadSegments, but
+ // that would prevent this class from being used with streams that only
+ // implement Read (e.g., file streams).
+
+ *aResult = 0;
+
+ if (mStatus == NS_BASE_STREAM_CLOSED) {
+ return NS_OK;
+ }
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ nsresult rv = NS_OK;
+
+ uint32_t len = mStreams.Length();
+ while (mCurrentStream < len && aCount) {
+ uint32_t read;
+ rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
+
+ // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
+ // (This is a bug in those stream implementations)
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
+ rv = NS_OK;
+ read = 0;
+ } else if (NS_FAILED(rv)) {
+ break;
+ }
+
+ if (read == 0) {
+ ++mCurrentStream;
+ mStartedReadingCurrent = false;
+ } else {
+ NS_ASSERTION(aCount >= read, "Read more than requested");
+ *aResult += read;
+ aCount -= read;
+ aBuf += read;
+ mStartedReadingCurrent = true;
+ }
+ }
+ return *aResult ? NS_OK : rv;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t* aResult)
+{
+ MutexAutoLock lock(mLock);
+
+ if (mStatus == NS_BASE_STREAM_CLOSED) {
+ *aResult = 0;
+ return NS_OK;
+ }
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ NS_ASSERTION(aWriter, "missing aWriter");
+
+ nsresult rv = NS_OK;
+ ReadSegmentsState state;
+ state.mThisStream = this;
+ state.mOffset = 0;
+ state.mWriter = aWriter;
+ state.mClosure = aClosure;
+ state.mDone = false;
+
+ uint32_t len = mStreams.Length();
+ while (mCurrentStream < len && aCount) {
+ uint32_t read;
+ rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
+
+ // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
+ // (This is a bug in those stream implementations)
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
+ rv = NS_OK;
+ read = 0;
+ }
+
+ // if |aWriter| decided to stop reading segments...
+ if (state.mDone || NS_FAILED(rv)) {
+ break;
+ }
+
+ // if stream is empty, then advance to the next stream.
+ if (read == 0) {
+ ++mCurrentStream;
+ mStartedReadingCurrent = false;
+ } else {
+ NS_ASSERTION(aCount >= read, "Read more than requested");
+ state.mOffset += read;
+ aCount -= read;
+ mStartedReadingCurrent = true;
+ }
+ }
+
+ // if we successfully read some data, then this call succeeded.
+ *aResult = state.mOffset;
+ return state.mOffset ? NS_OK : rv;
+}
+
+nsresult
+nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
+ const char* aFromRawSegment,
+ uint32_t aToOffset, uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ nsresult rv;
+ ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
+ rv = (state->mWriter)(state->mThisStream,
+ state->mClosure,
+ aFromRawSegment,
+ aToOffset + state->mOffset,
+ aCount,
+ aWriteCount);
+ if (NS_FAILED(rv)) {
+ state->mDone = true;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ MutexAutoLock lock(mLock);
+
+ uint32_t len = mStreams.Length();
+ if (len == 0) {
+ // Claim to be non-blocking, since we won't block the caller.
+ // On the other hand we'll never return NS_BASE_STREAM_WOULD_BLOCK,
+ // so maybe we should claim to be blocking? It probably doesn't
+ // matter in practice.
+ *aNonBlocking = true;
+ return NS_OK;
+ }
+ for (uint32_t i = 0; i < len; ++i) {
+ nsresult rv = mStreams[i]->IsNonBlocking(aNonBlocking);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // If one is non-blocking the entire stream becomes non-blocking
+ // (except that we don't implement nsIAsyncInputStream, so there's
+ // not much for the caller to do if Read returns "would block")
+ if (*aNonBlocking) {
+ return NS_OK;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ MutexAutoLock lock(mLock);
+
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ nsresult rv;
+
+ uint32_t oldCurrentStream = mCurrentStream;
+ bool oldStartedReadingCurrent = mStartedReadingCurrent;
+
+ if (aWhence == NS_SEEK_SET) {
+ int64_t remaining = aOffset;
+ if (aOffset == 0) {
+ mCurrentStream = 0;
+ }
+ for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+ if (!stream) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // See if all remaining streams should be rewound
+ if (remaining == 0) {
+ if (i < oldCurrentStream ||
+ (i == oldCurrentStream && oldStartedReadingCurrent)) {
+ rv = stream->Seek(NS_SEEK_SET, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ // Get position in current stream
+ int64_t streamPos;
+ if (i > oldCurrentStream ||
+ (i == oldCurrentStream && !oldStartedReadingCurrent)) {
+ streamPos = 0;
+ } else {
+ rv = TellMaybeSeek(stream, &streamPos);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ // See if we need to seek current stream forward or backward
+ if (remaining < streamPos) {
+ rv = stream->Seek(NS_SEEK_SET, remaining);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = remaining != 0;
+
+ remaining = 0;
+ } else if (remaining > streamPos) {
+ if (i < oldCurrentStream) {
+ // We're already at end so no need to seek this stream
+ remaining -= streamPos;
+ NS_ASSERTION(remaining >= 0, "Remaining invalid");
+ } else {
+ uint64_t avail;
+ rv = AvailableMaybeSeek(mStreams[i], &avail);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ int64_t newPos = XPCOM_MIN(remaining, streamPos + (int64_t)avail);
+
+ rv = stream->Seek(NS_SEEK_SET, newPos);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining -= newPos;
+ NS_ASSERTION(remaining >= 0, "Remaining invalid");
+ }
+ } else {
+ NS_ASSERTION(remaining == streamPos, "Huh?");
+ remaining = 0;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_CUR && aOffset > 0) {
+ int64_t remaining = aOffset;
+ for (uint32_t i = mCurrentStream; remaining && i < mStreams.Length(); ++i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+
+ uint64_t avail;
+ rv = AvailableMaybeSeek(mStreams[i], &avail);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ int64_t seek = XPCOM_MIN((int64_t)avail, remaining);
+
+ rv = stream->Seek(NS_SEEK_CUR, seek);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining -= seek;
+ }
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_CUR && aOffset < 0) {
+ int64_t remaining = -aOffset;
+ for (uint32_t i = mCurrentStream; remaining && i != (uint32_t)-1; --i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+
+ int64_t pos;
+ rv = TellMaybeSeek(stream, &pos);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ int64_t seek = XPCOM_MIN(pos, remaining);
+
+ rv = stream->Seek(NS_SEEK_CUR, -seek);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = seek != -pos;
+
+ remaining -= seek;
+ }
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_CUR) {
+ NS_ASSERTION(aOffset == 0, "Should have handled all non-zero values");
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_END) {
+ if (aOffset > 0) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ int64_t remaining = aOffset;
+ for (uint32_t i = mStreams.Length() - 1; i != (uint32_t)-1; --i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+
+ // See if all remaining streams should be seeked to end
+ if (remaining == 0) {
+ if (i >= oldCurrentStream) {
+ rv = stream->Seek(NS_SEEK_END, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // Get position in current stream
+ int64_t streamPos;
+ if (i < oldCurrentStream) {
+ streamPos = 0;
+ } else {
+ uint64_t avail;
+ rv = AvailableMaybeSeek(mStreams[i], &avail);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ streamPos = avail;
+ }
+
+ // See if we have enough data in the current stream.
+ if (DeprecatedAbs(remaining) < streamPos) {
+ rv = stream->Seek(NS_SEEK_END, remaining);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining = 0;
+ } else if (DeprecatedAbs(remaining) > streamPos) {
+ if (i > oldCurrentStream ||
+ (i == oldCurrentStream && !oldStartedReadingCurrent)) {
+ // We're already at start so no need to seek this stream
+ remaining += streamPos;
+ } else {
+ int64_t avail;
+ rv = TellMaybeSeek(stream, &avail);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ int64_t newPos = streamPos + XPCOM_MIN(avail, DeprecatedAbs(remaining));
+
+ rv = stream->Seek(NS_SEEK_END, -newPos);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining += newPos;
+ }
+ } else {
+ NS_ASSERTION(remaining == streamPos, "Huh?");
+ remaining = 0;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ // other Seeks not implemented yet
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Tell(int64_t* aResult)
+{
+ MutexAutoLock lock(mLock);
+
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ nsresult rv;
+ int64_t ret64 = 0;
+ uint32_t i, last;
+ last = mStartedReadingCurrent ? mCurrentStream + 1 : mCurrentStream;
+ for (i = 0; i < last; ++i) {
+ nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
+ if (NS_WARN_IF(!stream)) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ int64_t pos;
+ rv = TellMaybeSeek(stream, &pos);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ ret64 += pos;
+ }
+ *aResult = ret64;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::SetEOF()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsMultiplexInputStreamConstructor(nsISupports* aOuter,
+ REFNSIID aIID,
+ void** aResult)
+{
+ *aResult = nullptr;
+
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ RefPtr<nsMultiplexInputStream> inst = new nsMultiplexInputStream();
+
+ return inst->QueryInterface(aIID, aResult);
+}
+
+void
+nsMultiplexInputStream::Serialize(InputStreamParams& aParams,
+ FileDescriptorArray& aFileDescriptors)
+{
+ MutexAutoLock lock(mLock);
+
+ MultiplexInputStreamParams params;
+
+ uint32_t streamCount = mStreams.Length();
+
+ if (streamCount) {
+ InfallibleTArray<InputStreamParams>& streams = params.streams();
+
+ streams.SetCapacity(streamCount);
+ for (uint32_t index = 0; index < streamCount; index++) {
+ InputStreamParams childStreamParams;
+ SerializeInputStream(mStreams[index], childStreamParams,
+ aFileDescriptors);
+
+ streams.AppendElement(childStreamParams);
+ }
+ }
+
+ params.currentStream() = mCurrentStream;
+ params.status() = mStatus;
+ params.startedReadingCurrent() = mStartedReadingCurrent;
+
+ aParams = params;
+}
+
+bool
+nsMultiplexInputStream::Deserialize(const InputStreamParams& aParams,
+ const FileDescriptorArray& aFileDescriptors)
+{
+ if (aParams.type() !=
+ InputStreamParams::TMultiplexInputStreamParams) {
+ NS_ERROR("Received unknown parameters from the other process!");
+ return false;
+ }
+
+ const MultiplexInputStreamParams& params =
+ aParams.get_MultiplexInputStreamParams();
+
+ const InfallibleTArray<InputStreamParams>& streams = params.streams();
+
+ uint32_t streamCount = streams.Length();
+ for (uint32_t index = 0; index < streamCount; index++) {
+ nsCOMPtr<nsIInputStream> stream =
+ DeserializeInputStream(streams[index], aFileDescriptors);
+ if (!stream) {
+ NS_WARNING("Deserialize failed!");
+ return false;
+ }
+
+ if (NS_FAILED(AppendStream(stream))) {
+ NS_WARNING("AppendStream failed!");
+ return false;
+ }
+ }
+
+ mCurrentStream = params.currentStream();
+ mStatus = params.status();
+ mStartedReadingCurrent = params.startedReadingCurrent();
+
+ return true;
+}
+
+Maybe<uint64_t>
+nsMultiplexInputStream::ExpectedSerializedLength()
+{
+ MutexAutoLock lock(mLock);
+
+ bool lengthValueExists = false;
+ uint64_t expectedLength = 0;
+ uint32_t streamCount = mStreams.Length();
+ for (uint32_t index = 0; index < streamCount; index++) {
+ nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStreams[index]);
+ if (!stream) {
+ continue;
+ }
+ Maybe<uint64_t> length = stream->ExpectedSerializedLength();
+ if (length.isNothing()) {
+ continue;
+ }
+ lengthValueExists = true;
+ expectedLength += length.value();
+ }
+ return lengthValueExists ? Some(expectedLength) : Nothing();
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::GetCloneable(bool* aCloneable)
+{
+ MutexAutoLock lock(mLock);
+ //XXXnsm Cloning a multiplex stream which has started reading is not permitted
+ //right now.
+ if (mCurrentStream > 0 || mStartedReadingCurrent) {
+ *aCloneable = false;
+ return NS_OK;
+ }
+
+ uint32_t len = mStreams.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ nsCOMPtr<nsICloneableInputStream> cis = do_QueryInterface(mStreams[i]);
+ if (!cis || !cis->GetCloneable()) {
+ *aCloneable = false;
+ return NS_OK;
+ }
+ }
+
+ *aCloneable = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Clone(nsIInputStream** aClone)
+{
+ MutexAutoLock lock(mLock);
+
+ //XXXnsm Cloning a multiplex stream which has started reading is not permitted
+ //right now.
+ if (mCurrentStream > 0 || mStartedReadingCurrent) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsMultiplexInputStream> clone = new nsMultiplexInputStream();
+
+ nsresult rv;
+ uint32_t len = mStreams.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ nsCOMPtr<nsICloneableInputStream> substream = do_QueryInterface(mStreams[i]);
+ if (NS_WARN_IF(!substream)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIInputStream> clonedSubstream;
+ rv = substream->Clone(getter_AddRefs(clonedSubstream));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = clone->AppendStream(clonedSubstream);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ clone.forget(aClone);
+ return NS_OK;
+}
diff --git a/xpcom/io/nsMultiplexInputStream.h b/xpcom/io/nsMultiplexInputStream.h
new file mode 100644
index 000000000..381051dca
--- /dev/null
+++ b/xpcom/io/nsMultiplexInputStream.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+#ifndef _nsMultiplexInputStream_h_
+#define _nsMultiplexInputStream_h_
+
+#include "nsIMultiplexInputStream.h"
+
+#define NS_MULTIPLEXINPUTSTREAM_CONTRACTID "@mozilla.org/io/multiplex-input-stream;1"
+#define NS_MULTIPLEXINPUTSTREAM_CID \
+ { /* 565e3a2c-1dd2-11b2-8da1-b4cef17e568d */ \
+ 0x565e3a2c, \
+ 0x1dd2, \
+ 0x11b2, \
+ {0x8d, 0xa1, 0xb4, 0xce, 0xf1, 0x7e, 0x56, 0x8d} \
+ }
+
+extern nsresult nsMultiplexInputStreamConstructor(nsISupports* aOuter,
+ REFNSIID aIID,
+ void** aResult);
+
+#endif // _nsMultiplexInputStream_h_
diff --git a/xpcom/io/nsNativeCharsetUtils.cpp b/xpcom/io/nsNativeCharsetUtils.cpp
new file mode 100644
index 000000000..e53307af5
--- /dev/null
+++ b/xpcom/io/nsNativeCharsetUtils.cpp
@@ -0,0 +1,1044 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "xpcom-private.h"
+
+//-----------------------------------------------------------------------------
+// XP_MACOSX or ANDROID
+//-----------------------------------------------------------------------------
+#if defined(XP_MACOSX) || defined(ANDROID)
+
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString& aInput, nsAString& aOutput)
+{
+ CopyUTF8toUTF16(aInput, aOutput);
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString& aInput, nsACString& aOutput)
+{
+ CopyUTF16toUTF8(aInput, aOutput);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// XP_UNIX
+//-----------------------------------------------------------------------------
+#elif defined(XP_UNIX)
+
+#include <stdlib.h> // mbtowc, wctomb
+#include <locale.h> // setlocale
+#include "mozilla/Mutex.h"
+#include "nscore.h"
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+
+using namespace mozilla;
+
+//
+// choose a conversion library. we used to use mbrtowc/wcrtomb under Linux,
+// but that doesn't work for non-BMP characters whether we use '-fshort-wchar'
+// or not (see bug 206811 and
+// news://news.mozilla.org:119/bajml3$fvr1@ripley.netscape.com). we now use
+// iconv for all platforms where nltypes.h and nllanginfo.h are present
+// along with iconv.
+//
+#if defined(HAVE_ICONV) && defined(HAVE_NL_TYPES_H) && defined(HAVE_LANGINFO_CODESET)
+#define USE_ICONV 1
+#else
+#define USE_STDCONV 1
+#endif
+
+static void
+isolatin1_to_utf16(const char** aInput, uint32_t* aInputLeft,
+ char16_t** aOutput, uint32_t* aOutputLeft)
+{
+ while (*aInputLeft && *aOutputLeft) {
+ **aOutput = (unsigned char)** aInput;
+ (*aInput)++;
+ (*aInputLeft)--;
+ (*aOutput)++;
+ (*aOutputLeft)--;
+ }
+}
+
+static void
+utf16_to_isolatin1(const char16_t** aInput, uint32_t* aInputLeft,
+ char** aOutput, uint32_t* aOutputLeft)
+{
+ while (*aInputLeft && *aOutputLeft) {
+ **aOutput = (unsigned char)**aInput;
+ (*aInput)++;
+ (*aInputLeft)--;
+ (*aOutput)++;
+ (*aOutputLeft)--;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// conversion using iconv
+//-----------------------------------------------------------------------------
+#if defined(USE_ICONV)
+#include <nl_types.h> // CODESET
+#include <langinfo.h> // nl_langinfo
+#include <iconv.h> // iconv_open, iconv, iconv_close
+#include <errno.h>
+#include "plstr.h"
+
+#if defined(HAVE_ICONV_WITH_CONST_INPUT)
+#define ICONV_INPUT(x) (x)
+#else
+#define ICONV_INPUT(x) ((char **)x)
+#endif
+
+// solaris definitely needs this, but we'll enable it by default
+// just in case... but we know for sure that iconv(3) in glibc
+// doesn't need this.
+#if !defined(__GLIBC__)
+#define ENABLE_UTF8_FALLBACK_SUPPORT
+#endif
+
+#define INVALID_ICONV_T ((iconv_t)-1)
+
+static inline size_t
+xp_iconv(iconv_t converter,
+ const char** aInput, size_t* aInputLeft,
+ char** aOutput, size_t* aOutputLeft)
+{
+ size_t res, outputAvail = *aOutputLeft;
+ res = iconv(converter, ICONV_INPUT(aInput), aInputLeft, aOutput, aOutputLeft);
+ if (res == (size_t)-1) {
+ // on some platforms (e.g., linux) iconv will fail with
+ // E2BIG if it cannot convert _all_ of its input. it'll
+ // still adjust all of the in/out params correctly, so we
+ // can ignore this error. the assumption is that we will
+ // be called again to complete the conversion.
+ if ((errno == E2BIG) && (*aOutputLeft < outputAvail)) {
+ res = 0;
+ }
+ }
+ return res;
+}
+
+static inline void
+xp_iconv_reset(iconv_t converter)
+{
+ // NOTE: the man pages on Solaris claim that you can pass nullptr
+ // for all parameter to reset the converter, but beware the
+ // evil Solaris crash if you go down this route >:-)
+
+ const char* zero_char_in_ptr = nullptr;
+ char* zero_char_out_ptr = nullptr;
+ size_t zero_size_in = 0;
+ size_t zero_size_out = 0;
+
+ xp_iconv(converter,
+ &zero_char_in_ptr,
+ &zero_size_in,
+ &zero_char_out_ptr,
+ &zero_size_out);
+}
+
+static inline iconv_t
+xp_iconv_open(const char** to_list, const char** from_list)
+{
+ iconv_t res;
+ const char** from_name;
+ const char** to_name;
+
+ // try all possible combinations to locate a converter.
+ to_name = to_list;
+ while (*to_name) {
+ if (**to_name) {
+ from_name = from_list;
+ while (*from_name) {
+ if (**from_name) {
+ res = iconv_open(*to_name, *from_name);
+ if (res != INVALID_ICONV_T) {
+ return res;
+ }
+ }
+ from_name++;
+ }
+ }
+ to_name++;
+ }
+
+ return INVALID_ICONV_T;
+}
+
+/*
+ * char16_t[] is NOT a UCS-2 array BUT a UTF-16 string. Therefore, we
+ * have to use UTF-16 with iconv(3) on platforms where it's supported.
+ * However, the way UTF-16 and UCS-2 are interpreted varies across platforms
+ * and implementations of iconv(3). On Tru64, it also depends on the environment
+ * variable. To avoid the trouble arising from byte-swapping
+ * (bug 208809), we have to try UTF-16LE/BE and UCS-2LE/BE before falling
+ * back to UTF-16 and UCS-2 and variants. We assume that UTF-16 and UCS-2
+ * on systems without UTF-16LE/BE and UCS-2LE/BE have the native endianness,
+ * which isn't the case of glibc 2.1.x, for which we use 'UNICODELITTLE'
+ * and 'UNICODEBIG'. It's also not true of Tru64 V4 when the environment
+ * variable ICONV_BYTEORDER is set to 'big-endian', about which not much
+ * can be done other than adding a note in the release notes. (bug 206811)
+ */
+static const char* UTF_16_NAMES[] = {
+#if defined(IS_LITTLE_ENDIAN)
+ "UTF-16LE",
+#if defined(__GLIBC__)
+ "UNICODELITTLE",
+#endif
+ "UCS-2LE",
+#else
+ "UTF-16BE",
+#if defined(__GLIBC__)
+ "UNICODEBIG",
+#endif
+ "UCS-2BE",
+#endif
+ "UTF-16",
+ "UCS-2",
+ "UCS2",
+ "UCS_2",
+ "ucs-2",
+ "ucs2",
+ "ucs_2",
+ nullptr
+};
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+static const char* UTF_8_NAMES[] = {
+ "UTF-8",
+ "UTF8",
+ "UTF_8",
+ "utf-8",
+ "utf8",
+ "utf_8",
+ nullptr
+};
+#endif
+
+static const char* ISO_8859_1_NAMES[] = {
+ "ISO-8859-1",
+#if !defined(__GLIBC__)
+ "ISO8859-1",
+ "ISO88591",
+ "ISO_8859_1",
+ "ISO8859_1",
+ "iso-8859-1",
+ "iso8859-1",
+ "iso88591",
+ "iso_8859_1",
+ "iso8859_1",
+#endif
+ nullptr
+};
+
+class nsNativeCharsetConverter
+{
+public:
+ nsNativeCharsetConverter();
+ ~nsNativeCharsetConverter();
+
+ nsresult NativeToUnicode(const char** aInput, uint32_t* aInputLeft,
+ char16_t** aOutput, uint32_t* aOutputLeft);
+ nsresult UnicodeToNative(const char16_t** aInput, uint32_t* aInputLeft,
+ char** aOutput, uint32_t* aOutputLeft);
+
+ static void GlobalInit();
+ static void GlobalShutdown();
+ static bool IsNativeUTF8();
+
+private:
+ static iconv_t gNativeToUnicode;
+ static iconv_t gUnicodeToNative;
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ static iconv_t gNativeToUTF8;
+ static iconv_t gUTF8ToNative;
+ static iconv_t gUnicodeToUTF8;
+ static iconv_t gUTF8ToUnicode;
+#endif
+ static Mutex* gLock;
+ static bool gInitialized;
+ static bool gIsNativeUTF8;
+
+ static void LazyInit();
+
+ static void Lock()
+ {
+ if (gLock) {
+ gLock->Lock();
+ }
+ }
+ static void Unlock()
+ {
+ if (gLock) {
+ gLock->Unlock();
+ }
+ }
+};
+
+iconv_t nsNativeCharsetConverter::gNativeToUnicode = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUnicodeToNative = INVALID_ICONV_T;
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+iconv_t nsNativeCharsetConverter::gNativeToUTF8 = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUTF8ToNative = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUnicodeToUTF8 = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUTF8ToUnicode = INVALID_ICONV_T;
+#endif
+Mutex* nsNativeCharsetConverter::gLock = nullptr;
+bool nsNativeCharsetConverter::gInitialized = false;
+bool nsNativeCharsetConverter::gIsNativeUTF8 = false;
+
+void
+nsNativeCharsetConverter::LazyInit()
+{
+ // LazyInit may be called before NS_StartupNativeCharsetUtils, but
+ // the setlocale it does has to be called before nl_langinfo. Like in
+ // NS_StartupNativeCharsetUtils, assume we are called early enough that
+ // we are the first to care about the locale's charset.
+ if (!gLock) {
+ setlocale(LC_CTYPE, "");
+ }
+ const char* blank_list[] = { "", nullptr };
+ const char** native_charset_list = blank_list;
+ const char* native_charset = nl_langinfo(CODESET);
+ if (!native_charset) {
+ NS_ERROR("native charset is unknown");
+ // fallback to ISO-8859-1
+ native_charset_list = ISO_8859_1_NAMES;
+ } else {
+ native_charset_list[0] = native_charset;
+ }
+
+ // Most, if not all, Unixen supporting UTF-8 and nl_langinfo(CODESET)
+ // return 'UTF-8' (or 'utf-8')
+ if (!PL_strcasecmp(native_charset, "UTF-8")) {
+ gIsNativeUTF8 = true;
+ }
+
+ gNativeToUnicode = xp_iconv_open(UTF_16_NAMES, native_charset_list);
+ gUnicodeToNative = xp_iconv_open(native_charset_list, UTF_16_NAMES);
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUnicode == INVALID_ICONV_T) {
+ gNativeToUTF8 = xp_iconv_open(UTF_8_NAMES, native_charset_list);
+ gUTF8ToUnicode = xp_iconv_open(UTF_16_NAMES, UTF_8_NAMES);
+ NS_ASSERTION(gNativeToUTF8 != INVALID_ICONV_T, "no native to utf-8 converter");
+ NS_ASSERTION(gUTF8ToUnicode != INVALID_ICONV_T, "no utf-8 to utf-16 converter");
+ }
+ if (gUnicodeToNative == INVALID_ICONV_T) {
+ gUnicodeToUTF8 = xp_iconv_open(UTF_8_NAMES, UTF_16_NAMES);
+ gUTF8ToNative = xp_iconv_open(native_charset_list, UTF_8_NAMES);
+ NS_ASSERTION(gUnicodeToUTF8 != INVALID_ICONV_T, "no utf-16 to utf-8 converter");
+ NS_ASSERTION(gUTF8ToNative != INVALID_ICONV_T, "no utf-8 to native converter");
+ }
+#else
+ NS_ASSERTION(gNativeToUnicode != INVALID_ICONV_T, "no native to utf-16 converter");
+ NS_ASSERTION(gUnicodeToNative != INVALID_ICONV_T, "no utf-16 to native converter");
+#endif
+
+ /*
+ * On Solaris 8 (and newer?), the iconv modules converting to UCS-2
+ * prepend a byte order mark unicode character (BOM, u+FEFF) during
+ * the first use of the iconv converter. The same is the case of
+ * glibc 2.2.9x and Tru64 V5 (see bug 208809) when 'UTF-16' is used.
+ * However, we use 'UTF-16LE/BE' in both cases, instead so that we
+ * should be safe. But just in case...
+ *
+ * This dummy conversion gets rid of the BOMs and fixes bug 153562.
+ */
+ char dummy_input[1] = { ' ' };
+ char dummy_output[4];
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ const char* input = dummy_input;
+ size_t input_left = sizeof(dummy_input);
+ char* output = dummy_output;
+ size_t output_left = sizeof(dummy_output);
+
+ xp_iconv(gNativeToUnicode, &input, &input_left, &output, &output_left);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ const char* input = dummy_input;
+ size_t input_left = sizeof(dummy_input);
+ char* output = dummy_output;
+ size_t output_left = sizeof(dummy_output);
+
+ xp_iconv(gUTF8ToUnicode, &input, &input_left, &output, &output_left);
+ }
+#endif
+
+ gInitialized = true;
+}
+
+void
+nsNativeCharsetConverter::GlobalInit()
+{
+ gLock = new Mutex("nsNativeCharsetConverter.gLock");
+}
+
+void
+nsNativeCharsetConverter::GlobalShutdown()
+{
+ delete gLock;
+ gLock = nullptr;
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ iconv_close(gNativeToUnicode);
+ gNativeToUnicode = INVALID_ICONV_T;
+ }
+
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ iconv_close(gUnicodeToNative);
+ gUnicodeToNative = INVALID_ICONV_T;
+ }
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUTF8 != INVALID_ICONV_T) {
+ iconv_close(gNativeToUTF8);
+ gNativeToUTF8 = INVALID_ICONV_T;
+ }
+ if (gUTF8ToNative != INVALID_ICONV_T) {
+ iconv_close(gUTF8ToNative);
+ gUTF8ToNative = INVALID_ICONV_T;
+ }
+ if (gUnicodeToUTF8 != INVALID_ICONV_T) {
+ iconv_close(gUnicodeToUTF8);
+ gUnicodeToUTF8 = INVALID_ICONV_T;
+ }
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ iconv_close(gUTF8ToUnicode);
+ gUTF8ToUnicode = INVALID_ICONV_T;
+ }
+#endif
+
+ gInitialized = false;
+}
+
+nsNativeCharsetConverter::nsNativeCharsetConverter()
+{
+ Lock();
+ if (!gInitialized) {
+ LazyInit();
+ }
+}
+
+nsNativeCharsetConverter::~nsNativeCharsetConverter()
+{
+ // reset converters for next time
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ xp_iconv_reset(gNativeToUnicode);
+ }
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ xp_iconv_reset(gUnicodeToNative);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUTF8 != INVALID_ICONV_T) {
+ xp_iconv_reset(gNativeToUTF8);
+ }
+ if (gUTF8ToNative != INVALID_ICONV_T) {
+ xp_iconv_reset(gUTF8ToNative);
+ }
+ if (gUnicodeToUTF8 != INVALID_ICONV_T) {
+ xp_iconv_reset(gUnicodeToUTF8);
+ }
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ xp_iconv_reset(gUTF8ToUnicode);
+ }
+#endif
+ Unlock();
+}
+
+nsresult
+nsNativeCharsetConverter::NativeToUnicode(const char** aInput,
+ uint32_t* aInputLeft,
+ char16_t** aOutput,
+ uint32_t* aOutputLeft)
+{
+ size_t res = 0;
+ size_t inLeft = (size_t)*aInputLeft;
+ size_t outLeft = (size_t)*aOutputLeft * 2;
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+
+ res = xp_iconv(gNativeToUnicode, aInput, &inLeft, (char**)aOutput, &outLeft);
+
+ *aInputLeft = inLeft;
+ *aOutputLeft = outLeft / 2;
+ if (res != (size_t)-1) {
+ return NS_OK;
+ }
+
+ NS_WARNING("conversion from native to utf-16 failed");
+
+ // reset converter
+ xp_iconv_reset(gNativeToUnicode);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ else if ((gNativeToUTF8 != INVALID_ICONV_T) &&
+ (gUTF8ToUnicode != INVALID_ICONV_T)) {
+ // convert first to UTF8, then from UTF8 to UCS2
+ const char* in = *aInput;
+
+ char ubuf[1024];
+
+ // we assume we're always called with enough space in |aOutput|,
+ // so convert many chars at a time...
+ while (inLeft) {
+ char* p = ubuf;
+ size_t n = sizeof(ubuf);
+ res = xp_iconv(gNativeToUTF8, &in, &inLeft, &p, &n);
+ if (res == (size_t)-1) {
+ NS_ERROR("conversion from native to utf-8 failed");
+ break;
+ }
+ NS_ASSERTION(outLeft > 0, "bad assumption");
+ p = ubuf;
+ n = sizeof(ubuf) - n;
+ res = xp_iconv(gUTF8ToUnicode, (const char**)&p, &n,
+ (char**)aOutput, &outLeft);
+ if (res == (size_t)-1) {
+ NS_ERROR("conversion from utf-8 to utf-16 failed");
+ break;
+ }
+ }
+
+ (*aInput) += (*aInputLeft - inLeft);
+ *aInputLeft = inLeft;
+ *aOutputLeft = outLeft / 2;
+
+ if (res != (size_t)-1) {
+ return NS_OK;
+ }
+
+ // reset converters
+ xp_iconv_reset(gNativeToUTF8);
+ xp_iconv_reset(gUTF8ToUnicode);
+ }
+#endif
+
+ // fallback: zero-pad and hope for the best
+ // XXX This is lame and we have to do better.
+ isolatin1_to_utf16(aInput, aInputLeft, aOutput, aOutputLeft);
+
+ return NS_OK;
+}
+
+nsresult
+nsNativeCharsetConverter::UnicodeToNative(const char16_t** aInput,
+ uint32_t* aInputLeft,
+ char** aOutput,
+ uint32_t* aOutputLeft)
+{
+ size_t res = 0;
+ size_t inLeft = (size_t)*aInputLeft * 2;
+ size_t outLeft = (size_t)*aOutputLeft;
+
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ res = xp_iconv(gUnicodeToNative, (const char**)aInput, &inLeft,
+ aOutput, &outLeft);
+
+ *aInputLeft = inLeft / 2;
+ *aOutputLeft = outLeft;
+ if (res != (size_t)-1) {
+ return NS_OK;
+ }
+
+ NS_ERROR("iconv failed");
+
+ // reset converter
+ xp_iconv_reset(gUnicodeToNative);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ else if ((gUnicodeToUTF8 != INVALID_ICONV_T) &&
+ (gUTF8ToNative != INVALID_ICONV_T)) {
+ const char* in = (const char*)*aInput;
+
+ char ubuf[6]; // max utf-8 char length (really only needs to be 4 bytes)
+
+ // convert one uchar at a time...
+ while (inLeft && outLeft) {
+ char* p = ubuf;
+ size_t n = sizeof(ubuf), one_uchar = sizeof(char16_t);
+ res = xp_iconv(gUnicodeToUTF8, &in, &one_uchar, &p, &n);
+ if (res == (size_t)-1) {
+ NS_ERROR("conversion from utf-16 to utf-8 failed");
+ break;
+ }
+ p = ubuf;
+ n = sizeof(ubuf) - n;
+ res = xp_iconv(gUTF8ToNative, (const char**)&p, &n, aOutput, &outLeft);
+ if (res == (size_t)-1) {
+ if (errno == E2BIG) {
+ // not enough room for last uchar... back up and return.
+ in -= sizeof(char16_t);
+ res = 0;
+ } else {
+ NS_ERROR("conversion from utf-8 to native failed");
+ }
+ break;
+ }
+ inLeft -= sizeof(char16_t);
+ }
+
+ (*aInput) += (*aInputLeft - inLeft / 2);
+ *aInputLeft = inLeft / 2;
+ *aOutputLeft = outLeft;
+ if (res != (size_t)-1) {
+ return NS_OK;
+ }
+
+ // reset converters
+ xp_iconv_reset(gUnicodeToUTF8);
+ xp_iconv_reset(gUTF8ToNative);
+ }
+#endif
+
+ // fallback: truncate and hope for the best
+ // XXX This is lame and we have to do better.
+ utf16_to_isolatin1(aInput, aInputLeft, aOutput, aOutputLeft);
+
+ return NS_OK;
+}
+
+bool
+nsNativeCharsetConverter::IsNativeUTF8()
+{
+ if (!gInitialized) {
+ Lock();
+ if (!gInitialized) {
+ LazyInit();
+ }
+ Unlock();
+ }
+ return gIsNativeUTF8;
+}
+
+#endif // USE_ICONV
+
+//-----------------------------------------------------------------------------
+// conversion using mb[r]towc/wc[r]tomb
+//-----------------------------------------------------------------------------
+#if defined(USE_STDCONV)
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+#include <wchar.h> // mbrtowc, wcrtomb
+#endif
+
+class nsNativeCharsetConverter
+{
+public:
+ nsNativeCharsetConverter();
+
+ nsresult NativeToUnicode(const char** aInput, uint32_t* aInputLeft,
+ char16_t** aOutput, uint32_t* aOutputLeft);
+ nsresult UnicodeToNative(const char16_t** aInput, uint32_t* aInputLeft,
+ char** aOutput, uint32_t* aOutputLeft);
+
+ static void GlobalInit();
+ static void GlobalShutdown() { }
+ static bool IsNativeUTF8();
+
+private:
+ static bool gWCharIsUnicode;
+
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+ mbstate_t ps;
+#endif
+};
+
+bool nsNativeCharsetConverter::gWCharIsUnicode = false;
+
+nsNativeCharsetConverter::nsNativeCharsetConverter()
+{
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+ memset(&ps, 0, sizeof(ps));
+#endif
+}
+
+void
+nsNativeCharsetConverter::GlobalInit()
+{
+ // verify that wchar_t for the current locale is actually unicode.
+ // if it is not, then we should avoid calling mbtowc/wctomb and
+ // just fallback on zero-pad/truncation conversion.
+ //
+ // this test cannot be done at build time because the encoding of
+ // wchar_t may depend on the runtime locale. sad, but true!!
+ //
+ // so, if wchar_t is unicode then converting an ASCII character
+ // to wchar_t should not change its numeric value. we'll just
+ // check what happens with the ASCII 'a' character.
+ //
+ // this test is not perfect... obviously, it could yield false
+ // positives, but then at least ASCII text would be converted
+ // properly (or maybe just the 'a' character) -- oh well :(
+
+ char a = 'a';
+ unsigned int w = 0;
+
+ int res = mbtowc((wchar_t*)&w, &a, 1);
+
+ gWCharIsUnicode = (res != -1 && w == 'a');
+
+#ifdef DEBUG
+ if (!gWCharIsUnicode) {
+ NS_WARNING("wchar_t is not unicode (unicode conversion will be lossy)");
+ }
+#endif
+}
+
+nsresult
+nsNativeCharsetConverter::NativeToUnicode(const char** aInput,
+ uint32_t* aInputLeft,
+ char16_t** aOutput,
+ uint32_t* aOutputLeft)
+{
+ if (gWCharIsUnicode) {
+ int incr;
+
+ // cannot use wchar_t here since it may have been redefined (e.g.,
+ // via -fshort-wchar). hopefully, sizeof(tmp) is sufficient XP.
+ unsigned int tmp = 0;
+ while (*aInputLeft && *aOutputLeft) {
+#ifdef HAVE_MBRTOWC
+ incr = (int)mbrtowc((wchar_t*)&tmp, *aInput, *aInputLeft, &ps);
+#else
+ // XXX is this thread-safe?
+ incr = (int)mbtowc((wchar_t*)&tmp, *aInput, *aInputLeft);
+#endif
+ if (incr < 0) {
+ NS_WARNING("mbtowc failed: possible charset mismatch");
+ // zero-pad and hope for the best
+ tmp = (unsigned char)**aInput;
+ incr = 1;
+ }
+ ** aOutput = (char16_t)tmp;
+ (*aInput) += incr;
+ (*aInputLeft) -= incr;
+ (*aOutput)++;
+ (*aOutputLeft)--;
+ }
+ } else {
+ // wchar_t isn't unicode, so the best we can do is treat the
+ // input as if it is isolatin1 :(
+ isolatin1_to_utf16(aInput, aInputLeft, aOutput, aOutputLeft);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsNativeCharsetConverter::UnicodeToNative(const char16_t** aInput,
+ uint32_t* aInputLeft,
+ char** aOutput,
+ uint32_t* aOutputLeft)
+{
+ if (gWCharIsUnicode) {
+ int incr;
+
+ while (*aInputLeft && *aOutputLeft >= MB_CUR_MAX) {
+#ifdef HAVE_WCRTOMB
+ incr = (int)wcrtomb(*aOutput, (wchar_t)**aInput, &ps);
+#else
+ // XXX is this thread-safe?
+ incr = (int)wctomb(*aOutput, (wchar_t)**aInput);
+#endif
+ if (incr < 0) {
+ NS_WARNING("mbtowc failed: possible charset mismatch");
+ ** aOutput = (unsigned char)**aInput; // truncate
+ incr = 1;
+ }
+ // most likely we're dead anyways if this assertion should fire
+ NS_ASSERTION(uint32_t(incr) <= *aOutputLeft, "wrote beyond end of string");
+ (*aOutput) += incr;
+ (*aOutputLeft) -= incr;
+ (*aInput)++;
+ (*aInputLeft)--;
+ }
+ } else {
+ // wchar_t isn't unicode, so the best we can do is treat the
+ // input as if it is isolatin1 :(
+ utf16_to_isolatin1(aInput, aInputLeft, aOutput, aOutputLeft);
+ }
+
+ return NS_OK;
+}
+
+// XXX : for now, return false
+bool
+nsNativeCharsetConverter::IsNativeUTF8()
+{
+ return false;
+}
+
+#endif // USE_STDCONV
+
+//-----------------------------------------------------------------------------
+// API implementation
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString& aInput, nsAString& aOutput)
+{
+ aOutput.Truncate();
+
+ uint32_t inputLen = aInput.Length();
+
+ nsACString::const_iterator iter;
+ aInput.BeginReading(iter);
+
+ //
+ // OPTIMIZATION: preallocate space for largest possible result; convert
+ // directly into the result buffer to avoid intermediate buffer copy.
+ //
+ // this will generally result in a larger allocation, but that seems
+ // better than an extra buffer copy.
+ //
+ if (!aOutput.SetLength(inputLen, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ nsAString::iterator out_iter;
+ aOutput.BeginWriting(out_iter);
+
+ char16_t* result = out_iter.get();
+ uint32_t resultLeft = inputLen;
+
+ const char* buf = iter.get();
+ uint32_t bufLeft = inputLen;
+
+ nsNativeCharsetConverter conv;
+ nsresult rv = conv.NativeToUnicode(&buf, &bufLeft, &result, &resultLeft);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(bufLeft == 0, "did not consume entire input buffer");
+ aOutput.SetLength(inputLen - resultLeft);
+ }
+ return rv;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString& aInput, nsACString& aOutput)
+{
+ aOutput.Truncate();
+
+ nsAString::const_iterator iter, end;
+ aInput.BeginReading(iter);
+ aInput.EndReading(end);
+
+ // cannot easily avoid intermediate buffer copy.
+ char temp[4096];
+
+ nsNativeCharsetConverter conv;
+
+ const char16_t* buf = iter.get();
+ uint32_t bufLeft = Distance(iter, end);
+ while (bufLeft) {
+ char* p = temp;
+ uint32_t tempLeft = sizeof(temp);
+
+ nsresult rv = conv.UnicodeToNative(&buf, &bufLeft, &p, &tempLeft);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (tempLeft < sizeof(temp)) {
+ aOutput.Append(temp, sizeof(temp) - tempLeft);
+ }
+ }
+ return NS_OK;
+}
+
+bool
+NS_IsNativeUTF8()
+{
+ return nsNativeCharsetConverter::IsNativeUTF8();
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+ //
+ // need to initialize the locale or else charset conversion will fail.
+ // better not delay this in case some other component alters the locale
+ // settings.
+ //
+ // XXX we assume that we are called early enough that we should
+ // always be the first to care about the locale's charset.
+ //
+ setlocale(LC_CTYPE, "");
+
+ nsNativeCharsetConverter::GlobalInit();
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+ nsNativeCharsetConverter::GlobalShutdown();
+}
+
+//-----------------------------------------------------------------------------
+// XP_WIN
+//-----------------------------------------------------------------------------
+#elif defined(XP_WIN)
+
+#include <windows.h>
+#include "nsString.h"
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+
+using namespace mozilla;
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString& aInput, nsAString& aOutput)
+{
+ uint32_t inputLen = aInput.Length();
+
+ nsACString::const_iterator iter;
+ aInput.BeginReading(iter);
+
+ const char* buf = iter.get();
+
+ // determine length of result
+ uint32_t resultLen = 0;
+ int n = ::MultiByteToWideChar(CP_ACP, 0, buf, inputLen, nullptr, 0);
+ if (n > 0) {
+ resultLen += n;
+ }
+
+ // allocate sufficient space
+ if (!aOutput.SetLength(resultLen, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (resultLen > 0) {
+ nsAString::iterator out_iter;
+ aOutput.BeginWriting(out_iter);
+
+ char16_t* result = out_iter.get();
+
+ ::MultiByteToWideChar(CP_ACP, 0, buf, inputLen, wwc(result), resultLen);
+ }
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString& aInput, nsACString& aOutput)
+{
+ uint32_t inputLen = aInput.Length();
+
+ nsAString::const_iterator iter;
+ aInput.BeginReading(iter);
+
+ char16ptr_t buf = iter.get();
+
+ // determine length of result
+ uint32_t resultLen = 0;
+
+ int n = ::WideCharToMultiByte(CP_ACP, 0, buf, inputLen, nullptr, 0,
+ nullptr, nullptr);
+ if (n > 0) {
+ resultLen += n;
+ }
+
+ // allocate sufficient space
+ if (!aOutput.SetLength(resultLen, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (resultLen > 0) {
+ nsACString::iterator out_iter;
+ aOutput.BeginWriting(out_iter);
+
+ // default "defaultChar" is '?', which is an illegal character on windows
+ // file system. That will cause file uncreatable. Change it to '_'
+ const char defaultChar = '_';
+
+ char* result = out_iter.get();
+
+ ::WideCharToMultiByte(CP_ACP, 0, buf, inputLen, result, resultLen,
+ &defaultChar, nullptr);
+ }
+ return NS_OK;
+}
+
+// moved from widget/windows/nsToolkit.cpp
+int32_t
+NS_ConvertAtoW(const char* aStrInA, int aBufferSize, char16_t* aStrOutW)
+{
+ return MultiByteToWideChar(CP_ACP, 0, aStrInA, -1, wwc(aStrOutW), aBufferSize);
+}
+
+int32_t
+NS_ConvertWtoA(const char16_t* aStrInW, int aBufferSizeOut,
+ char* aStrOutA, const char* aDefault)
+{
+ if ((!aStrInW) || (!aStrOutA) || (aBufferSizeOut <= 0)) {
+ return 0;
+ }
+
+ int numCharsConverted = WideCharToMultiByte(CP_ACP, 0, char16ptr_t(aStrInW), -1,
+ aStrOutA, aBufferSizeOut,
+ aDefault, nullptr);
+
+ if (!numCharsConverted) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ // Overflow, add missing null termination but return 0
+ aStrOutA[aBufferSizeOut - 1] = '\0';
+ } else {
+ // Other error, clear string and return 0
+ aStrOutA[0] = '\0';
+ }
+ } else if (numCharsConverted < aBufferSizeOut) {
+ // Add 2nd null (really necessary?)
+ aStrOutA[numCharsConverted] = '\0';
+ }
+
+ return numCharsConverted;
+}
+
+#else
+
+#include "nsReadableUtils.h"
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString& aInput, nsAString& aOutput)
+{
+ CopyASCIItoUTF16(aInput, aOutput);
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString& aInput, nsACString& aOutput)
+{
+ LossyCopyUTF16toASCII(aInput, aOutput);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+#endif
diff --git a/xpcom/io/nsNativeCharsetUtils.h b/xpcom/io/nsNativeCharsetUtils.h
new file mode 100644
index 000000000..5c1e670d5
--- /dev/null
+++ b/xpcom/io/nsNativeCharsetUtils.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNativeCharsetUtils_h__
+#define nsNativeCharsetUtils_h__
+
+
+/*****************************************************************************\
+ * *
+ * **** NOTICE **** *
+ * *
+ * *** THESE ARE NOT GENERAL PURPOSE CONVERTERS *** *
+ * *
+ * NS_CopyNativeToUnicode / NS_CopyUnicodeToNative should only be used *
+ * for converting *FILENAMES* between native and unicode. They are not *
+ * designed or tested for general encoding converter use. *
+ * *
+\*****************************************************************************/
+
+/**
+ * thread-safe conversion routines that do not depend on uconv libraries.
+ */
+nsresult NS_CopyNativeToUnicode(const nsACString& aInput, nsAString& aOutput);
+nsresult NS_CopyUnicodeToNative(const nsAString& aInput, nsACString& aOutput);
+
+/*
+ * This function indicates whether the character encoding used in the file
+ * system (more exactly what's used for |GetNativeFoo| and |SetNativeFoo|
+ * of |nsIFile|) is UTF-8 or not. Knowing that helps us avoid an
+ * unncessary encoding conversion in some cases. For instance, to get the leaf
+ * name in UTF-8 out of nsIFile, we can just use |GetNativeLeafName| rather
+ * than using |GetLeafName| and converting the result to UTF-8 if the file
+ * system encoding is UTF-8.
+ * On Unix (but not on Mac OS X), it depends on the locale and is not known
+ * in advance (at the compilation time) so that this function needs to be
+ * a real function. On Mac OS X it's always UTF-8 while on Windows
+ * and other platforms (e.g. OS2), it's never UTF-8.
+ */
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
+bool NS_IsNativeUTF8();
+#else
+inline bool
+NS_IsNativeUTF8()
+{
+#if defined(XP_MACOSX) || defined(ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+#endif
+
+
+/**
+ * internal
+ */
+void NS_StartupNativeCharsetUtils();
+void NS_ShutdownNativeCharsetUtils();
+
+#endif // nsNativeCharsetUtils_h__
diff --git a/xpcom/io/nsPipe.h b/xpcom/io/nsPipe.h
new file mode 100644
index 000000000..29ce0ce96
--- /dev/null
+++ b/xpcom/io/nsPipe.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPipe_h__
+#define nsPipe_h__
+
+#define NS_PIPE_CONTRACTID \
+ "@mozilla.org/pipe;1"
+#define NS_PIPE_CID \
+{ /* e4a0ee4e-0775-457b-9118-b3ae97a7c758 */ \
+ 0xe4a0ee4e, \
+ 0x0775, \
+ 0x457b, \
+ {0x91,0x18,0xb3,0xae,0x97,0xa7,0xc7,0x58} \
+}
+
+// Generic factory constructor for the nsPipe class
+nsresult
+nsPipeConstructor(nsISupports* outer, REFNSIID iid, void** result);
+
+#endif // !defined(nsPipe_h__)
diff --git a/xpcom/io/nsPipe3.cpp b/xpcom/io/nsPipe3.cpp
new file mode 100644
index 000000000..56932adfc
--- /dev/null
+++ b/xpcom/io/nsPipe3.cpp
@@ -0,0 +1,2007 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+#include "mozilla/Attributes.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsIBufferedStreams.h"
+#include "nsICloneableInputStream.h"
+#include "nsIPipe.h"
+#include "nsIEventTarget.h"
+#include "nsISeekableStream.h"
+#include "mozilla/RefPtr.h"
+#include "nsSegmentedBuffer.h"
+#include "nsStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "mozilla/Logging.h"
+#include "nsIClassInfoImpl.h"
+#include "nsAlgorithm.h"
+#include "nsMemory.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+
+using namespace mozilla;
+
+#ifdef LOG
+#undef LOG
+#endif
+//
+// set MOZ_LOG=nsPipe:5
+//
+static LazyLogModule sPipeLog("nsPipe");
+#define LOG(args) MOZ_LOG(sPipeLog, mozilla::LogLevel::Debug, args)
+
+#define DEFAULT_SEGMENT_SIZE 4096
+#define DEFAULT_SEGMENT_COUNT 16
+
+class nsPipe;
+class nsPipeEvents;
+class nsPipeInputStream;
+class nsPipeOutputStream;
+class AutoReadSegment;
+
+namespace {
+
+enum MonitorAction
+{
+ DoNotNotifyMonitor,
+ NotifyMonitor
+};
+
+enum SegmentChangeResult
+{
+ SegmentNotChanged,
+ SegmentAdvanceBufferRead
+};
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+// this class is used to delay notifications until the end of a particular
+// scope. it helps avoid the complexity of issuing callbacks while inside
+// a critical section.
+class nsPipeEvents
+{
+public:
+ nsPipeEvents() { }
+ ~nsPipeEvents();
+
+ inline void NotifyInputReady(nsIAsyncInputStream* aStream,
+ nsIInputStreamCallback* aCallback)
+ {
+ mInputList.AppendElement(InputEntry(aStream, aCallback));
+ }
+
+ inline void NotifyOutputReady(nsIAsyncOutputStream* aStream,
+ nsIOutputStreamCallback* aCallback)
+ {
+ NS_ASSERTION(!mOutputCallback, "already have an output event");
+ mOutputStream = aStream;
+ mOutputCallback = aCallback;
+ }
+
+private:
+ struct InputEntry
+ {
+ InputEntry(nsIAsyncInputStream* aStream, nsIInputStreamCallback* aCallback)
+ : mStream(aStream)
+ , mCallback(aCallback)
+ {
+ MOZ_ASSERT(mStream);
+ MOZ_ASSERT(mCallback);
+ }
+
+ nsCOMPtr<nsIAsyncInputStream> mStream;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ };
+
+ nsTArray<InputEntry> mInputList;
+
+ nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
+ nsCOMPtr<nsIOutputStreamCallback> mOutputCallback;
+};
+
+//-----------------------------------------------------------------------------
+
+// This class is used to maintain input stream state. Its broken out from the
+// nsPipeInputStream class because generally the nsPipe should be modifying
+// this state and not the input stream itself.
+struct nsPipeReadState
+{
+ nsPipeReadState()
+ : mReadCursor(nullptr)
+ , mReadLimit(nullptr)
+ , mSegment(0)
+ , mAvailable(0)
+ , mActiveRead(false)
+ , mNeedDrain(false)
+ { }
+
+ char* mReadCursor;
+ char* mReadLimit;
+ int32_t mSegment;
+ uint32_t mAvailable;
+
+ // This flag is managed using the AutoReadSegment RAII stack class.
+ bool mActiveRead;
+
+ // Set to indicate that the input stream has closed and should be drained,
+ // but that drain has been delayed due to an active read. When the read
+ // completes, this flag indicate the drain should then be performed.
+ bool mNeedDrain;
+};
+
+//-----------------------------------------------------------------------------
+
+// an input end of a pipe (maintained as a list of refs within the pipe)
+class nsPipeInputStream final
+ : public nsIAsyncInputStream
+ , public nsISeekableStream
+ , public nsISearchableInputStream
+ , public nsICloneableInputStream
+ , public nsIClassInfo
+ , public nsIBufferedInputStream
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIASYNCINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSISEARCHABLEINPUTSTREAM
+ NS_DECL_NSICLONEABLEINPUTSTREAM
+ NS_DECL_NSICLASSINFO
+ NS_DECL_NSIBUFFEREDINPUTSTREAM
+
+ explicit nsPipeInputStream(nsPipe* aPipe)
+ : mPipe(aPipe)
+ , mLogicalOffset(0)
+ , mInputStatus(NS_OK)
+ , mBlocking(true)
+ , mBlocked(false)
+ , mCallbackFlags(0)
+ { }
+
+ explicit nsPipeInputStream(const nsPipeInputStream& aOther)
+ : mPipe(aOther.mPipe)
+ , mLogicalOffset(aOther.mLogicalOffset)
+ , mInputStatus(aOther.mInputStatus)
+ , mBlocking(aOther.mBlocking)
+ , mBlocked(false)
+ , mCallbackFlags(0)
+ , mReadState(aOther.mReadState)
+ { }
+
+ nsresult Fill();
+ void SetNonBlocking(bool aNonBlocking)
+ {
+ mBlocking = !aNonBlocking;
+ }
+
+ uint32_t Available();
+
+ // synchronously wait for the pipe to become readable.
+ nsresult Wait();
+
+ // These two don't acquire the monitor themselves. Instead they
+ // expect their caller to have done so and to pass the monitor as
+ // evidence.
+ MonitorAction OnInputReadable(uint32_t aBytesWritten, nsPipeEvents&,
+ const ReentrantMonitorAutoEnter& ev);
+ MonitorAction OnInputException(nsresult, nsPipeEvents&,
+ const ReentrantMonitorAutoEnter& ev);
+
+ nsPipeReadState& ReadState()
+ {
+ return mReadState;
+ }
+
+ const nsPipeReadState& ReadState() const
+ {
+ return mReadState;
+ }
+
+ nsresult Status() const;
+
+ // A version of Status() that doesn't acquire the monitor.
+ nsresult Status(const ReentrantMonitorAutoEnter& ev) const;
+
+private:
+ virtual ~nsPipeInputStream();
+
+ RefPtr<nsPipe> mPipe;
+
+ int64_t mLogicalOffset;
+ // Individual input streams can be closed without effecting the rest of the
+ // pipe. So track individual input stream status separately. |mInputStatus|
+ // is protected by |mPipe->mReentrantMonitor|.
+ nsresult mInputStatus;
+ bool mBlocking;
+
+ // these variables can only be accessed while inside the pipe's monitor
+ bool mBlocked;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ uint32_t mCallbackFlags;
+
+ // requires pipe's monitor; usually treat as an opaque token to pass to nsPipe
+ nsPipeReadState mReadState;
+};
+
+//-----------------------------------------------------------------------------
+
+// the output end of a pipe (allocated as a member of the pipe).
+class nsPipeOutputStream
+ : public nsIAsyncOutputStream
+ , public nsIClassInfo
+{
+public:
+ // since this class will be allocated as a member of the pipe, we do not
+ // need our own ref count. instead, we share the lifetime (the ref count)
+ // of the entire pipe. this macro is just convenience since it does not
+ // declare a mRefCount variable; however, don't let the name fool you...
+ // we are not inheriting from nsPipe ;-)
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIOUTPUTSTREAM
+ NS_DECL_NSIASYNCOUTPUTSTREAM
+ NS_DECL_NSICLASSINFO
+
+ explicit nsPipeOutputStream(nsPipe* aPipe)
+ : mPipe(aPipe)
+ , mWriterRefCnt(0)
+ , mLogicalOffset(0)
+ , mBlocking(true)
+ , mBlocked(false)
+ , mWritable(true)
+ , mCallbackFlags(0)
+ { }
+
+ void SetNonBlocking(bool aNonBlocking)
+ {
+ mBlocking = !aNonBlocking;
+ }
+ void SetWritable(bool aWritable)
+ {
+ mWritable = aWritable;
+ }
+
+ // synchronously wait for the pipe to become writable.
+ nsresult Wait();
+
+ MonitorAction OnOutputWritable(nsPipeEvents&);
+ MonitorAction OnOutputException(nsresult, nsPipeEvents&);
+
+private:
+ nsPipe* mPipe;
+
+ // separate refcnt so that we know when to close the producer
+ mozilla::ThreadSafeAutoRefCnt mWriterRefCnt;
+ int64_t mLogicalOffset;
+ bool mBlocking;
+
+ // these variables can only be accessed while inside the pipe's monitor
+ bool mBlocked;
+ bool mWritable;
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ uint32_t mCallbackFlags;
+};
+
+//-----------------------------------------------------------------------------
+
+class nsPipe final : public nsIPipe
+{
+public:
+ friend class nsPipeInputStream;
+ friend class nsPipeOutputStream;
+ friend class AutoReadSegment;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPIPE
+
+ // nsPipe methods:
+ nsPipe();
+
+private:
+ ~nsPipe();
+
+ //
+ // Methods below may only be called while inside the pipe's monitor. Some
+ // of these methods require passing a ReentrantMonitorAutoEnter to prove the
+ // monitor is held.
+ //
+
+ void PeekSegment(const nsPipeReadState& aReadState, uint32_t aIndex,
+ char*& aCursor, char*& aLimit);
+ SegmentChangeResult AdvanceReadSegment(nsPipeReadState& aReadState,
+ const ReentrantMonitorAutoEnter &ev);
+ bool ReadSegmentBeingWritten(nsPipeReadState& aReadState);
+ uint32_t CountSegmentReferences(int32_t aSegment);
+ void SetAllNullReadCursors();
+ bool AllReadCursorsMatchWriteCursor();
+ void RollBackAllReadCursors(char* aWriteCursor);
+ void UpdateAllReadCursors(char* aWriteCursor);
+ void ValidateAllReadCursors();
+ uint32_t GetBufferSegmentCount(const nsPipeReadState& aReadState,
+ const ReentrantMonitorAutoEnter& ev) const;
+ bool IsAdvanceBufferFull(const ReentrantMonitorAutoEnter& ev) const;
+
+ //
+ // methods below may be called while outside the pipe's monitor
+ //
+
+ void DrainInputStream(nsPipeReadState& aReadState, nsPipeEvents& aEvents);
+ nsresult GetWriteSegment(char*& aSegment, uint32_t& aSegmentLen);
+ void AdvanceWriteCursor(uint32_t aCount);
+
+ void OnInputStreamException(nsPipeInputStream* aStream, nsresult aReason);
+ void OnPipeException(nsresult aReason, bool aOutputOnly = false);
+
+ nsresult CloneInputStream(nsPipeInputStream* aOriginal,
+ nsIInputStream** aCloneOut);
+
+ // methods below should only be called by AutoReadSegment
+ nsresult GetReadSegment(nsPipeReadState& aReadState, const char*& aSegment,
+ uint32_t& aLength);
+ void ReleaseReadSegment(nsPipeReadState& aReadState,
+ nsPipeEvents& aEvents);
+ void AdvanceReadCursor(nsPipeReadState& aReadState, uint32_t aCount);
+
+ // We can't inherit from both nsIInputStream and nsIOutputStream
+ // because they collide on their Close method. Consequently we nest their
+ // implementations to avoid the extra object allocation.
+ nsPipeOutputStream mOutput;
+
+ // Since the input stream can be cloned, we may have more than one. Use
+ // a weak reference as the streams will clear their entry here in their
+ // destructor. Using a strong reference would create a reference cycle.
+ // Only usable while mReentrantMonitor is locked.
+ nsTArray<nsPipeInputStream*> mInputList;
+
+ // But hold a strong ref to our original input stream. For backward
+ // compatibility we need to be able to consistently return this same
+ // object from GetInputStream(). Note, mOriginalInput is also stored
+ // in mInputList as a weak ref.
+ RefPtr<nsPipeInputStream> mOriginalInput;
+
+ ReentrantMonitor mReentrantMonitor;
+ nsSegmentedBuffer mBuffer;
+
+ // The maximum number of segments to allow to be buffered in advance
+ // of the fastest reader. This is collection of segments is called
+ // the "advance buffer".
+ uint32_t mMaxAdvanceBufferSegmentCount;
+
+ int32_t mWriteSegment;
+ char* mWriteCursor;
+ char* mWriteLimit;
+
+ // |mStatus| is protected by |mReentrantMonitor|.
+ nsresult mStatus;
+ bool mInited;
+};
+
+//-----------------------------------------------------------------------------
+
+// RAII class representing an active read segment. When it goes out of scope
+// it automatically updates the read cursor and releases the read segment.
+class MOZ_STACK_CLASS AutoReadSegment final
+{
+public:
+ AutoReadSegment(nsPipe* aPipe, nsPipeReadState& aReadState,
+ uint32_t aMaxLength)
+ : mPipe(aPipe)
+ , mReadState(aReadState)
+ , mStatus(NS_ERROR_FAILURE)
+ , mSegment(nullptr)
+ , mLength(0)
+ , mOffset(0)
+ {
+ MOZ_ASSERT(mPipe);
+ MOZ_ASSERT(!mReadState.mActiveRead);
+ mStatus = mPipe->GetReadSegment(mReadState, mSegment, mLength);
+ if (NS_SUCCEEDED(mStatus)) {
+ MOZ_ASSERT(mReadState.mActiveRead);
+ MOZ_ASSERT(mSegment);
+ mLength = std::min(mLength, aMaxLength);
+ MOZ_ASSERT(mLength);
+ }
+ }
+
+ ~AutoReadSegment()
+ {
+ if (NS_SUCCEEDED(mStatus)) {
+ if (mOffset) {
+ mPipe->AdvanceReadCursor(mReadState, mOffset);
+ } else {
+ nsPipeEvents events;
+ mPipe->ReleaseReadSegment(mReadState, events);
+ }
+ }
+ MOZ_ASSERT(!mReadState.mActiveRead);
+ }
+
+ nsresult Status() const
+ {
+ return mStatus;
+ }
+
+ const char* Data() const
+ {
+ MOZ_ASSERT(NS_SUCCEEDED(mStatus));
+ MOZ_ASSERT(mSegment);
+ return mSegment + mOffset;
+ }
+
+ uint32_t Length() const
+ {
+ MOZ_ASSERT(NS_SUCCEEDED(mStatus));
+ MOZ_ASSERT(mLength >= mOffset);
+ return mLength - mOffset;
+ }
+
+ void
+ Advance(uint32_t aCount)
+ {
+ MOZ_ASSERT(NS_SUCCEEDED(mStatus));
+ MOZ_ASSERT(aCount <= (mLength - mOffset));
+ mOffset += aCount;
+ }
+
+ nsPipeReadState&
+ ReadState() const
+ {
+ return mReadState;
+ }
+
+private:
+ // guaranteed to remain alive due to limited stack lifetime of AutoReadSegment
+ nsPipe* mPipe;
+ nsPipeReadState& mReadState;
+ nsresult mStatus;
+ const char* mSegment;
+ uint32_t mLength;
+ uint32_t mOffset;
+};
+
+//
+// NOTES on buffer architecture:
+//
+// +-----------------+ - - mBuffer.GetSegment(0)
+// | |
+// + - - - - - - - - + - - nsPipeReadState.mReadCursor
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// +-----------------+ - - nsPipeReadState.mReadLimit
+// |
+// +-----------------+
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// +-----------------+
+// |
+// +-----------------+ - - mBuffer.GetSegment(mWriteSegment)
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// + - - - - - - - - + - - mWriteCursor
+// | |
+// | |
+// +-----------------+ - - mWriteLimit
+//
+// (shaded region contains data)
+//
+// NOTE: Each input stream produced by the nsPipe contains its own, separate
+// nsPipeReadState. This means there are multiple mReadCursor and
+// mReadLimit values in play. The pipe cannot discard old data until
+// all mReadCursors have moved beyond that point in the stream.
+//
+// Likewise, each input stream reader will have it's own amount of
+// buffered data. The pipe size threshold, however, is only applied
+// to the input stream that is being read fastest. We call this
+// the "advance buffer" in that its in advance of all readers. We
+// allow slower input streams to buffer more data so that we don't
+// stall processing of the faster input stream.
+//
+// NOTE: on some systems (notably OS/2), the heap allocator uses an arena for
+// small allocations (e.g., 64 byte allocations). this means that buffers may
+// be allocated back-to-back. in the diagram above, for example, mReadLimit
+// would actually be pointing at the beginning of the next segment. when
+// making changes to this file, please keep this fact in mind.
+//
+
+//-----------------------------------------------------------------------------
+// nsPipe methods:
+//-----------------------------------------------------------------------------
+
+nsPipe::nsPipe()
+ : mOutput(this)
+ , mOriginalInput(new nsPipeInputStream(this))
+ , mReentrantMonitor("nsPipe.mReentrantMonitor")
+ , mMaxAdvanceBufferSegmentCount(0)
+ , mWriteSegment(-1)
+ , mWriteCursor(nullptr)
+ , mWriteLimit(nullptr)
+ , mStatus(NS_OK)
+ , mInited(false)
+{
+ mInputList.AppendElement(mOriginalInput);
+}
+
+nsPipe::~nsPipe()
+{
+}
+
+NS_IMPL_ADDREF(nsPipe)
+NS_IMPL_QUERY_INTERFACE(nsPipe, nsIPipe)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsPipe::Release()
+{
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+ nsrefcnt count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "nsPipe");
+ if (count == 0) {
+ delete (this);
+ return 0;
+ }
+ // Avoid racing on |mOriginalInput| by only looking at it when
+ // the refcount is 1, that is, we are the only pointer (hence only
+ // thread) to access it.
+ if (count == 1 && mOriginalInput) {
+ mOriginalInput = nullptr;
+ return 1;
+ }
+ return count;
+}
+
+NS_IMETHODIMP
+nsPipe::Init(bool aNonBlockingIn,
+ bool aNonBlockingOut,
+ uint32_t aSegmentSize,
+ uint32_t aSegmentCount)
+{
+ mInited = true;
+
+ if (aSegmentSize == 0) {
+ aSegmentSize = DEFAULT_SEGMENT_SIZE;
+ }
+ if (aSegmentCount == 0) {
+ aSegmentCount = DEFAULT_SEGMENT_COUNT;
+ }
+
+ // protect against overflow
+ uint32_t maxCount = uint32_t(-1) / aSegmentSize;
+ if (aSegmentCount > maxCount) {
+ aSegmentCount = maxCount;
+ }
+
+ // The internal buffer is always "infinite" so that we can allow
+ // the size to expand when cloned streams are read at different
+ // rates. We enforce a limit on how much data can be buffered
+ // ahead of the fastest reader in GetWriteSegment().
+ nsresult rv = mBuffer.Init(aSegmentSize, UINT32_MAX);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mMaxAdvanceBufferSegmentCount = aSegmentCount;
+
+ mOutput.SetNonBlocking(aNonBlockingOut);
+ mOriginalInput->SetNonBlocking(aNonBlockingIn);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipe::GetInputStream(nsIAsyncInputStream** aInputStream)
+{
+ if (NS_WARN_IF(!mInited)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ RefPtr<nsPipeInputStream> ref = mOriginalInput;
+ ref.forget(aInputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipe::GetOutputStream(nsIAsyncOutputStream** aOutputStream)
+{
+ if (NS_WARN_IF(!mInited)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ NS_ADDREF(*aOutputStream = &mOutput);
+ return NS_OK;
+}
+
+void
+nsPipe::PeekSegment(const nsPipeReadState& aReadState, uint32_t aIndex,
+ char*& aCursor, char*& aLimit)
+{
+ if (aIndex == 0) {
+ NS_ASSERTION(!aReadState.mReadCursor || mBuffer.GetSegmentCount(),
+ "unexpected state");
+ aCursor = aReadState.mReadCursor;
+ aLimit = aReadState.mReadLimit;
+ } else {
+ uint32_t absoluteIndex = aReadState.mSegment + aIndex;
+ uint32_t numSegments = mBuffer.GetSegmentCount();
+ if (absoluteIndex >= numSegments) {
+ aCursor = aLimit = nullptr;
+ } else {
+ aCursor = mBuffer.GetSegment(absoluteIndex);
+ if (mWriteSegment == (int32_t)absoluteIndex) {
+ aLimit = mWriteCursor;
+ } else {
+ aLimit = aCursor + mBuffer.GetSegmentSize();
+ }
+ }
+ }
+}
+
+nsresult
+nsPipe::GetReadSegment(nsPipeReadState& aReadState, const char*& aSegment,
+ uint32_t& aLength)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (aReadState.mReadCursor == aReadState.mReadLimit) {
+ return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ // The input stream locks the pipe while getting the buffer to read from,
+ // but then unlocks while actual data copying is taking place. In
+ // order to avoid deleting the buffer out from under this lockless read
+ // set a flag to indicate a read is active. This flag is only modified
+ // while the lock is held.
+ MOZ_ASSERT(!aReadState.mActiveRead);
+ aReadState.mActiveRead = true;
+
+ aSegment = aReadState.mReadCursor;
+ aLength = aReadState.mReadLimit - aReadState.mReadCursor;
+
+ return NS_OK;
+}
+
+void
+nsPipe::ReleaseReadSegment(nsPipeReadState& aReadState, nsPipeEvents& aEvents)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ MOZ_ASSERT(aReadState.mActiveRead);
+ aReadState.mActiveRead = false;
+
+ // When a read completes and releases the mActiveRead flag, we may have blocked
+ // a drain from completing. This occurs when the input stream is closed during
+ // the read. In these cases, we need to complete the drain as soon as the
+ // active read completes.
+ if (aReadState.mNeedDrain) {
+ aReadState.mNeedDrain = false;
+ DrainInputStream(aReadState, aEvents);
+ }
+}
+
+void
+nsPipe::AdvanceReadCursor(nsPipeReadState& aReadState, uint32_t aBytesRead)
+{
+ NS_ASSERTION(aBytesRead, "don't call if no bytes read");
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ LOG(("III advancing read cursor by %u\n", aBytesRead));
+ NS_ASSERTION(aBytesRead <= mBuffer.GetSegmentSize(), "read too much");
+
+ aReadState.mReadCursor += aBytesRead;
+ NS_ASSERTION(aReadState.mReadCursor <= aReadState.mReadLimit,
+ "read cursor exceeds limit");
+
+ MOZ_ASSERT(aReadState.mAvailable >= aBytesRead);
+ aReadState.mAvailable -= aBytesRead;
+
+ // Check to see if we're at the end of the available read data. If we
+ // are, and this segment is not still being written, then we can possibly
+ // free up the segment.
+ if (aReadState.mReadCursor == aReadState.mReadLimit &&
+ !ReadSegmentBeingWritten(aReadState)) {
+
+ // Advance the segment position. If we have read any segments from the
+ // advance buffer then we can potentially notify blocked writers.
+ if (AdvanceReadSegment(aReadState, mon) == SegmentAdvanceBufferRead &&
+ mOutput.OnOutputWritable(events) == NotifyMonitor) {
+ mon.NotifyAll();
+ }
+ }
+
+ ReleaseReadSegment(aReadState, events);
+ }
+}
+
+SegmentChangeResult
+nsPipe::AdvanceReadSegment(nsPipeReadState& aReadState,
+ const ReentrantMonitorAutoEnter &ev)
+{
+ // Calculate how many segments are buffered for this stream to start.
+ uint32_t startBufferSegments = GetBufferSegmentCount(aReadState, ev);
+
+ int32_t currentSegment = aReadState.mSegment;
+
+ // Move to the next segment to read
+ aReadState.mSegment += 1;
+
+ // If this was the last reference to the first segment, then remove it.
+ if (currentSegment == 0 && CountSegmentReferences(currentSegment) == 0) {
+
+ // shift write and read segment index (-1 indicates an empty buffer).
+ mWriteSegment -= 1;
+
+ // Directly modify the current read state. If the associated input
+ // stream is closed simultaneous with reading, then it may not be
+ // in the mInputList any more.
+ aReadState.mSegment -= 1;
+
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ // Skip the current read state structure since we modify it manually
+ // before entering this loop.
+ if (&mInputList[i]->ReadState() == &aReadState) {
+ continue;
+ }
+ mInputList[i]->ReadState().mSegment -= 1;
+ }
+
+ // done with this segment
+ mBuffer.DeleteFirstSegment();
+ LOG(("III deleting first segment\n"));
+ }
+
+ if (mWriteSegment < aReadState.mSegment) {
+ // read cursor has hit the end of written data, so reset it
+ MOZ_ASSERT(mWriteSegment == (aReadState.mSegment - 1));
+ aReadState.mReadCursor = nullptr;
+ aReadState.mReadLimit = nullptr;
+ // also, the buffer is completely empty, so reset the write cursor
+ if (mWriteSegment == -1) {
+ mWriteCursor = nullptr;
+ mWriteLimit = nullptr;
+ }
+ } else {
+ // advance read cursor and limit to next buffer segment
+ aReadState.mReadCursor = mBuffer.GetSegment(aReadState.mSegment);
+ if (mWriteSegment == aReadState.mSegment) {
+ aReadState.mReadLimit = mWriteCursor;
+ } else {
+ aReadState.mReadLimit = aReadState.mReadCursor + mBuffer.GetSegmentSize();
+ }
+ }
+
+ // Calculate how many segments are buffered for the stream after
+ // reading.
+ uint32_t endBufferSegments = GetBufferSegmentCount(aReadState, ev);
+
+ // If the stream has read a segment out of the set of advanced buffer
+ // segments, then the writer may advance.
+ if (startBufferSegments >= mMaxAdvanceBufferSegmentCount &&
+ endBufferSegments < mMaxAdvanceBufferSegmentCount) {
+ return SegmentAdvanceBufferRead;
+ }
+
+ // Otherwise there are no significant changes to the segment structure.
+ return SegmentNotChanged;
+}
+
+void
+nsPipe::DrainInputStream(nsPipeReadState& aReadState, nsPipeEvents& aEvents)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // If a segment is actively being read in ReadSegments() for this input
+ // stream, then we cannot drain the stream. This can happen because
+ // ReadSegments() does not hold the lock while copying from the buffer.
+ // If we detect this condition, simply note that we need a drain once
+ // the read completes and return immediately.
+ if (aReadState.mActiveRead) {
+ MOZ_ASSERT(!aReadState.mNeedDrain);
+ aReadState.mNeedDrain = true;
+ return;
+ }
+
+ aReadState.mAvailable = 0;
+
+ while(mWriteSegment >= aReadState.mSegment) {
+
+ // If the last segment to free is still being written to, we're done
+ // draining. We can't free any more.
+ if (ReadSegmentBeingWritten(aReadState)) {
+ break;
+ }
+
+ // Don't bother checking if this results in an advance buffer segment
+ // read. Since we are draining the entire stream we will read an
+ // advance buffer segment no matter what.
+ AdvanceReadSegment(aReadState, mon);
+ }
+
+ // If we have read any segments from the advance buffer then we can
+ // potentially notify blocked writers.
+ if (!IsAdvanceBufferFull(mon) &&
+ mOutput.OnOutputWritable(aEvents) == NotifyMonitor) {
+ mon.NotifyAll();
+ }
+}
+
+bool
+nsPipe::ReadSegmentBeingWritten(nsPipeReadState& aReadState)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ bool beingWritten = mWriteSegment == aReadState.mSegment &&
+ mWriteLimit > mWriteCursor;
+ NS_ASSERTION(!beingWritten || aReadState.mReadLimit == mWriteCursor,
+ "unexpected state");
+ return beingWritten;
+}
+
+nsresult
+nsPipe::GetWriteSegment(char*& aSegment, uint32_t& aSegmentLen)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ // write cursor and limit may both be null indicating an empty buffer.
+ if (mWriteCursor == mWriteLimit) {
+ // The pipe is full if we have hit our limit on advance data buffering.
+ // This means the fastest reader is still reading slower than data is
+ // being written into the pipe.
+ if (IsAdvanceBufferFull(mon)) {
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ // The nsSegmentedBuffer is configured to be "infinite", so this
+ // should never return nullptr here.
+ char* seg = mBuffer.AppendNewSegment();
+ if (!seg) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ LOG(("OOO appended new segment\n"));
+ mWriteCursor = seg;
+ mWriteLimit = mWriteCursor + mBuffer.GetSegmentSize();
+ ++mWriteSegment;
+ }
+
+ // make sure read cursor is initialized
+ SetAllNullReadCursors();
+
+ // check to see if we can roll-back our read and write cursors to the
+ // beginning of the current/first segment. this is purely an optimization.
+ if (mWriteSegment == 0 && AllReadCursorsMatchWriteCursor()) {
+ char* head = mBuffer.GetSegment(0);
+ LOG(("OOO rolling back write cursor %u bytes\n", mWriteCursor - head));
+ RollBackAllReadCursors(head);
+ mWriteCursor = head;
+ }
+
+ aSegment = mWriteCursor;
+ aSegmentLen = mWriteLimit - mWriteCursor;
+ return NS_OK;
+}
+
+void
+nsPipe::AdvanceWriteCursor(uint32_t aBytesWritten)
+{
+ NS_ASSERTION(aBytesWritten, "don't call if no bytes written");
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ LOG(("OOO advancing write cursor by %u\n", aBytesWritten));
+
+ char* newWriteCursor = mWriteCursor + aBytesWritten;
+ NS_ASSERTION(newWriteCursor <= mWriteLimit, "write cursor exceeds limit");
+
+ // update read limit if reading in the same segment
+ UpdateAllReadCursors(newWriteCursor);
+
+ mWriteCursor = newWriteCursor;
+
+ ValidateAllReadCursors();
+
+ // update the writable flag on the output stream
+ if (mWriteCursor == mWriteLimit) {
+ mOutput.SetWritable(!IsAdvanceBufferFull(mon));
+ }
+
+ // notify input stream that pipe now contains additional data
+ bool needNotify = false;
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ if (mInputList[i]->OnInputReadable(aBytesWritten, events, mon)
+ == NotifyMonitor) {
+ needNotify = true;
+ }
+ }
+
+ if (needNotify) {
+ mon.NotifyAll();
+ }
+ }
+}
+
+void
+nsPipe::OnInputStreamException(nsPipeInputStream* aStream, nsresult aReason)
+{
+ MOZ_ASSERT(NS_FAILED(aReason));
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // Its possible to re-enter this method when we call OnPipeException() or
+ // OnInputExection() below. If there is a caller stuck in our synchronous
+ // Wait() method, then they will get woken up with a failure code which
+ // re-enters this method. Therefore, gracefully handle unknown streams
+ // here.
+
+ // If we only have one stream open and it is the given stream, then shut
+ // down the entire pipe.
+ if (mInputList.Length() == 1) {
+ if (mInputList[0] == aStream) {
+ OnPipeException(aReason);
+ }
+ return;
+ }
+
+ // Otherwise just close the particular stream that hit an exception.
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ if (mInputList[i] != aStream) {
+ continue;
+ }
+
+ MonitorAction action = mInputList[i]->OnInputException(aReason, events,
+ mon);
+ mInputList.RemoveElementAt(i);
+
+ // Notify after element is removed in case we re-enter as a result.
+ if (action == NotifyMonitor) {
+ mon.NotifyAll();
+ }
+
+ return;
+ }
+ }
+}
+
+void
+nsPipe::OnPipeException(nsresult aReason, bool aOutputOnly)
+{
+ LOG(("PPP nsPipe::OnPipeException [reason=%x output-only=%d]\n",
+ aReason, aOutputOnly));
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // if we've already hit an exception, then ignore this one.
+ if (NS_FAILED(mStatus)) {
+ return;
+ }
+
+ mStatus = aReason;
+
+ bool needNotify = false;
+
+ nsTArray<nsPipeInputStream*> tmpInputList;
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ // an output-only exception applies to the input end if the pipe has
+ // zero bytes available.
+ if (aOutputOnly && mInputList[i]->Available()) {
+ tmpInputList.AppendElement(mInputList[i]);
+ continue;
+ }
+
+ if (mInputList[i]->OnInputException(aReason, events, mon)
+ == NotifyMonitor) {
+ needNotify = true;
+ }
+ }
+ mInputList = tmpInputList;
+
+ if (mOutput.OnOutputException(aReason, events) == NotifyMonitor) {
+ needNotify = true;
+ }
+
+ // Notify after we have removed any input streams from mInputList
+ if (needNotify) {
+ mon.NotifyAll();
+ }
+ }
+}
+
+nsresult
+nsPipe::CloneInputStream(nsPipeInputStream* aOriginal,
+ nsIInputStream** aCloneOut)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ RefPtr<nsPipeInputStream> ref = new nsPipeInputStream(*aOriginal);
+ mInputList.AppendElement(ref);
+ nsCOMPtr<nsIAsyncInputStream> downcast = ref.forget();
+ downcast.forget(aCloneOut);
+ return NS_OK;
+}
+
+uint32_t
+nsPipe::CountSegmentReferences(int32_t aSegment)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ uint32_t count = 0;
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ if (aSegment >= mInputList[i]->ReadState().mSegment) {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+void
+nsPipe::SetAllNullReadCursors()
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ nsPipeReadState& readState = mInputList[i]->ReadState();
+ if (!readState.mReadCursor) {
+ NS_ASSERTION(mWriteSegment == readState.mSegment,
+ "unexpected null read cursor");
+ readState.mReadCursor = readState.mReadLimit = mWriteCursor;
+ }
+ }
+}
+
+bool
+nsPipe::AllReadCursorsMatchWriteCursor()
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ const nsPipeReadState& readState = mInputList[i]->ReadState();
+ if (readState.mSegment != mWriteSegment ||
+ readState.mReadCursor != mWriteCursor) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+nsPipe::RollBackAllReadCursors(char* aWriteCursor)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ nsPipeReadState& readState = mInputList[i]->ReadState();
+ MOZ_ASSERT(mWriteSegment == readState.mSegment);
+ MOZ_ASSERT(mWriteCursor == readState.mReadCursor);
+ MOZ_ASSERT(mWriteCursor == readState.mReadLimit);
+ readState.mReadCursor = aWriteCursor;
+ readState.mReadLimit = aWriteCursor;
+ }
+}
+
+void
+nsPipe::UpdateAllReadCursors(char* aWriteCursor)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ nsPipeReadState& readState = mInputList[i]->ReadState();
+ if (mWriteSegment == readState.mSegment &&
+ readState.mReadLimit == mWriteCursor) {
+ readState.mReadLimit = aWriteCursor;
+ }
+ }
+}
+
+void
+nsPipe::ValidateAllReadCursors()
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+ // The only way mReadCursor == mWriteCursor is if:
+ //
+ // - mReadCursor is at the start of a segment (which, based on how
+ // nsSegmentedBuffer works, means that this segment is the "first"
+ // segment)
+ // - mWriteCursor points at the location past the end of the current
+ // write segment (so the current write filled the current write
+ // segment, so we've incremented mWriteCursor to point past the end
+ // of it)
+ // - the segment to which data has just been written is located
+ // exactly one segment's worth of bytes before the first segment
+ // where mReadCursor is located
+ //
+ // Consequently, the byte immediately after the end of the current
+ // write segment is the first byte of the first segment, so
+ // mReadCursor == mWriteCursor. (Another way to think about this is
+ // to consider the buffer architecture diagram above, but consider it
+ // with an arena allocator which allocates from the *end* of the
+ // arena to the *beginning* of the arena.)
+#ifdef DEBUG
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ const nsPipeReadState& state = mInputList[i]->ReadState();
+ NS_ASSERTION(state.mReadCursor != mWriteCursor ||
+ (mBuffer.GetSegment(state.mSegment) == state.mReadCursor &&
+ mWriteCursor == mWriteLimit),
+ "read cursor is bad");
+ }
+#endif
+}
+
+uint32_t
+nsPipe::GetBufferSegmentCount(const nsPipeReadState& aReadState,
+ const ReentrantMonitorAutoEnter& ev) const
+{
+ // The write segment can be smaller than the current reader position
+ // in some cases. For example, when the first write segment has not
+ // been allocated yet mWriteSegment is negative. In these cases
+ // the stream is effectively using zero segments.
+ if (mWriteSegment < aReadState.mSegment) {
+ return 0;
+ }
+
+ MOZ_ASSERT(mWriteSegment >= 0);
+ MOZ_ASSERT(aReadState.mSegment >= 0);
+
+ // Otherwise at least one segment is being used. We add one here
+ // since a single segment is being used when the write and read
+ // segment indices are the same.
+ return 1 + mWriteSegment - aReadState.mSegment;
+}
+
+bool
+nsPipe::IsAdvanceBufferFull(const ReentrantMonitorAutoEnter& ev) const
+{
+ // If we have fewer total segments than the limit we can immediately
+ // determine we are not full. Note, we must add one to mWriteSegment
+ // to convert from a index to a count.
+ MOZ_DIAGNOSTIC_ASSERT(mWriteSegment >= -1);
+ MOZ_DIAGNOSTIC_ASSERT(mWriteSegment < INT32_MAX);
+ uint32_t totalWriteSegments = mWriteSegment + 1;
+ if (totalWriteSegments < mMaxAdvanceBufferSegmentCount) {
+ return false;
+ }
+
+ // Otherwise we must inspect all of our reader streams. We need
+ // to determine the buffer depth of the fastest reader.
+ uint32_t minBufferSegments = UINT32_MAX;
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ // Only count buffer segments from input streams that are open.
+ if (NS_FAILED(mInputList[i]->Status(ev))) {
+ continue;
+ }
+ const nsPipeReadState& state = mInputList[i]->ReadState();
+ uint32_t bufferSegments = GetBufferSegmentCount(state, ev);
+ minBufferSegments = std::min(minBufferSegments, bufferSegments);
+ // We only care if any reader has fewer segments buffered than
+ // our threshold. We can stop once we hit that threshold.
+ if (minBufferSegments < mMaxAdvanceBufferSegmentCount) {
+ return false;
+ }
+ }
+
+ // Note, its possible for minBufferSegments to exceed our
+ // mMaxAdvanceBufferSegmentCount here. This happens when a cloned
+ // reader gets far behind, but then the fastest reader stream is
+ // closed. This leaves us with a single stream that is buffered
+ // beyond our max. Naturally we continue to indicate the pipe
+ // is full at this point.
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeEvents methods:
+//-----------------------------------------------------------------------------
+
+nsPipeEvents::~nsPipeEvents()
+{
+ // dispatch any pending events
+
+ for (uint32_t i = 0; i < mInputList.Length(); ++i) {
+ mInputList[i].mCallback->OnInputStreamReady(mInputList[i].mStream);
+ }
+ mInputList.Clear();
+
+ if (mOutputCallback) {
+ mOutputCallback->OnOutputStreamReady(mOutputStream);
+ mOutputCallback = nullptr;
+ mOutputStream = nullptr;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeInputStream methods:
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ADDREF(nsPipeInputStream);
+NS_IMPL_RELEASE(nsPipeInputStream);
+
+NS_INTERFACE_TABLE_HEAD(nsPipeInputStream)
+ NS_INTERFACE_TABLE_BEGIN
+ NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsIAsyncInputStream)
+ NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsISeekableStream)
+ NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsISearchableInputStream)
+ NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsICloneableInputStream)
+ NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsIBufferedInputStream)
+ NS_INTERFACE_TABLE_ENTRY(nsPipeInputStream, nsIClassInfo)
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsPipeInputStream, nsIInputStream,
+ nsIAsyncInputStream)
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsPipeInputStream, nsISupports,
+ nsIAsyncInputStream)
+ NS_INTERFACE_TABLE_END
+NS_INTERFACE_TABLE_TAIL
+
+NS_IMPL_CI_INTERFACE_GETTER(nsPipeInputStream,
+ nsIInputStream,
+ nsIAsyncInputStream,
+ nsISeekableStream,
+ nsISearchableInputStream,
+ nsICloneableInputStream,
+ nsIBufferedInputStream)
+
+NS_IMPL_THREADSAFE_CI(nsPipeInputStream)
+
+NS_IMETHODIMP
+nsPipeInputStream::Init(nsIInputStream*, uint32_t)
+{
+ MOZ_CRASH("nsPipeInputStream should never be initialized with "
+ "nsIBufferedInputStream::Init!\n");
+}
+
+uint32_t
+nsPipeInputStream::Available()
+{
+ mPipe->mReentrantMonitor.AssertCurrentThreadIn();
+ return mReadState.mAvailable;
+}
+
+nsresult
+nsPipeInputStream::Wait()
+{
+ NS_ASSERTION(mBlocking, "wait on non-blocking pipe input stream");
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ while (NS_SUCCEEDED(Status(mon)) && (mReadState.mAvailable == 0)) {
+ LOG(("III pipe input: waiting for data\n"));
+
+ mBlocked = true;
+ mon.Wait();
+ mBlocked = false;
+
+ LOG(("III pipe input: woke up [status=%x available=%u]\n",
+ Status(mon), mReadState.mAvailable));
+ }
+
+ return Status(mon) == NS_BASE_STREAM_CLOSED ? NS_OK : Status(mon);
+}
+
+MonitorAction
+nsPipeInputStream::OnInputReadable(uint32_t aBytesWritten,
+ nsPipeEvents& aEvents,
+ const ReentrantMonitorAutoEnter& ev)
+{
+ MonitorAction result = DoNotNotifyMonitor;
+
+ mPipe->mReentrantMonitor.AssertCurrentThreadIn();
+ mReadState.mAvailable += aBytesWritten;
+
+ if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
+ aEvents.NotifyInputReady(this, mCallback);
+ mCallback = nullptr;
+ mCallbackFlags = 0;
+ } else if (mBlocked) {
+ result = NotifyMonitor;
+ }
+
+ return result;
+}
+
+MonitorAction
+nsPipeInputStream::OnInputException(nsresult aReason, nsPipeEvents& aEvents,
+ const ReentrantMonitorAutoEnter& ev)
+{
+ LOG(("nsPipeInputStream::OnInputException [this=%x reason=%x]\n",
+ this, aReason));
+
+ MonitorAction result = DoNotNotifyMonitor;
+
+ NS_ASSERTION(NS_FAILED(aReason), "huh? successful exception");
+
+ if (NS_SUCCEEDED(mInputStatus)) {
+ mInputStatus = aReason;
+ }
+
+ // force count of available bytes to zero.
+ mPipe->DrainInputStream(mReadState, aEvents);
+
+ if (mCallback) {
+ aEvents.NotifyInputReady(this, mCallback);
+ mCallback = nullptr;
+ mCallbackFlags = 0;
+ } else if (mBlocked) {
+ result = NotifyMonitor;
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::CloseWithStatus(nsresult aReason)
+{
+ LOG(("III CloseWithStatus [this=%x reason=%x]\n", this, aReason));
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ if (NS_FAILED(mInputStatus)) {
+ return NS_OK;
+ }
+
+ if (NS_SUCCEEDED(aReason)) {
+ aReason = NS_BASE_STREAM_CLOSED;
+ }
+
+ mPipe->OnInputStreamException(this, aReason);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Close()
+{
+ return CloseWithStatus(NS_BASE_STREAM_CLOSED);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Available(uint64_t* aResult)
+{
+ // nsPipeInputStream supports under 4GB stream only
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // return error if closed
+ if (!mReadState.mAvailable && NS_FAILED(Status(mon))) {
+ return Status(mon);
+ }
+
+ *aResult = (uint64_t)mReadState.mAvailable;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aReadCount)
+{
+ LOG(("III ReadSegments [this=%x count=%u]\n", this, aCount));
+
+ nsresult rv = NS_OK;
+
+ *aReadCount = 0;
+ while (aCount) {
+ AutoReadSegment segment(mPipe, mReadState, aCount);
+ rv = segment.Status();
+ if (NS_FAILED(rv)) {
+ // ignore this error if we've already read something.
+ if (*aReadCount > 0) {
+ rv = NS_OK;
+ break;
+ }
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is empty
+ if (!mBlocking) {
+ break;
+ }
+ // wait for some data to be written to the pipe
+ rv = Wait();
+ if (NS_SUCCEEDED(rv)) {
+ continue;
+ }
+ }
+ // ignore this error, just return.
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ break;
+ }
+ mPipe->OnInputStreamException(this, rv);
+ break;
+ }
+
+ uint32_t writeCount;
+ while (segment.Length()) {
+ writeCount = 0;
+
+ rv = aWriter(static_cast<nsIAsyncInputStream*>(this), aClosure,
+ segment.Data(), *aReadCount, segment.Length(), &writeCount);
+
+ if (NS_FAILED(rv) || writeCount == 0) {
+ aCount = 0;
+ // any errors returned from the writer end here: do not
+ // propagate to the caller of ReadSegments.
+ rv = NS_OK;
+ break;
+ }
+
+ NS_ASSERTION(writeCount <= segment.Length(), "wrote more than expected");
+ segment.Advance(writeCount);
+ aCount -= writeCount;
+ *aReadCount += writeCount;
+ mLogicalOffset += writeCount;
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Read(char* aToBuf, uint32_t aBufLen, uint32_t* aReadCount)
+{
+ return ReadSegments(NS_CopySegmentToBuffer, aToBuf, aBufLen, aReadCount);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ *aNonBlocking = !mBlocking;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
+ uint32_t aFlags,
+ uint32_t aRequestedCount,
+ nsIEventTarget* aTarget)
+{
+ LOG(("III AsyncWait [this=%x]\n", this));
+
+ nsPipeEvents pipeEvents;
+ {
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // replace a pending callback
+ mCallback = nullptr;
+ mCallbackFlags = 0;
+
+ if (!aCallback) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIInputStreamCallback> proxy;
+ if (aTarget) {
+ proxy = NS_NewInputStreamReadyEvent(aCallback, aTarget);
+ aCallback = proxy;
+ }
+
+ if (NS_FAILED(Status(mon)) ||
+ (mReadState.mAvailable && !(aFlags & WAIT_CLOSURE_ONLY))) {
+ // stream is already closed or readable; post event.
+ pipeEvents.NotifyInputReady(this, aCallback);
+ } else {
+ // queue up callback object to be notified when data becomes available
+ mCallback = aCallback;
+ mCallbackFlags = aFlags;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ NS_NOTREACHED("nsPipeInputStream::Seek");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Tell(int64_t* aOffset)
+{
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // return error if closed
+ if (!mReadState.mAvailable && NS_FAILED(Status(mon))) {
+ return Status(mon);
+ }
+
+ *aOffset = mLogicalOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::SetEOF()
+{
+ NS_NOTREACHED("nsPipeInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+static bool strings_equal(bool aIgnoreCase,
+ const char* aS1, const char* aS2, uint32_t aLen)
+{
+ return aIgnoreCase
+ ? !nsCRT::strncasecmp(aS1, aS2, aLen) : !nsCRT::strncmp(aS1, aS2, aLen);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Search(const char* aForString,
+ bool aIgnoreCase,
+ bool* aFound,
+ uint32_t* aOffsetSearchedTo)
+{
+ LOG(("III Search [for=%s ic=%u]\n", aForString, aIgnoreCase));
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ char* cursor1;
+ char* limit1;
+ uint32_t index = 0, offset = 0;
+ uint32_t strLen = strlen(aForString);
+
+ mPipe->PeekSegment(mReadState, 0, cursor1, limit1);
+ if (cursor1 == limit1) {
+ *aFound = false;
+ *aOffsetSearchedTo = 0;
+ LOG((" result [aFound=%u offset=%u]\n", *aFound, *aOffsetSearchedTo));
+ return NS_OK;
+ }
+
+ while (true) {
+ uint32_t i, len1 = limit1 - cursor1;
+
+ // check if the string is in the buffer segment
+ for (i = 0; i < len1 - strLen + 1; i++) {
+ if (strings_equal(aIgnoreCase, &cursor1[i], aForString, strLen)) {
+ *aFound = true;
+ *aOffsetSearchedTo = offset + i;
+ LOG((" result [aFound=%u offset=%u]\n", *aFound, *aOffsetSearchedTo));
+ return NS_OK;
+ }
+ }
+
+ // get the next segment
+ char* cursor2;
+ char* limit2;
+ uint32_t len2;
+
+ index++;
+ offset += len1;
+
+ mPipe->PeekSegment(mReadState, index, cursor2, limit2);
+ if (cursor2 == limit2) {
+ *aFound = false;
+ *aOffsetSearchedTo = offset - strLen + 1;
+ LOG((" result [aFound=%u offset=%u]\n", *aFound, *aOffsetSearchedTo));
+ return NS_OK;
+ }
+ len2 = limit2 - cursor2;
+
+ // check if the string is straddling the next buffer segment
+ uint32_t lim = XPCOM_MIN(strLen, len2 + 1);
+ for (i = 0; i < lim; ++i) {
+ uint32_t strPart1Len = strLen - i - 1;
+ uint32_t strPart2Len = strLen - strPart1Len;
+ const char* strPart2 = &aForString[strLen - strPart2Len];
+ uint32_t bufSeg1Offset = len1 - strPart1Len;
+ if (strings_equal(aIgnoreCase, &cursor1[bufSeg1Offset], aForString, strPart1Len) &&
+ strings_equal(aIgnoreCase, cursor2, strPart2, strPart2Len)) {
+ *aFound = true;
+ *aOffsetSearchedTo = offset - strPart1Len;
+ LOG((" result [aFound=%u offset=%u]\n", *aFound, *aOffsetSearchedTo));
+ return NS_OK;
+ }
+ }
+
+ // finally continue with the next buffer
+ cursor1 = cursor2;
+ limit1 = limit2;
+ }
+
+ NS_NOTREACHED("can't get here");
+ return NS_ERROR_UNEXPECTED; // keep compiler happy
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::GetCloneable(bool* aCloneableOut)
+{
+ *aCloneableOut = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Clone(nsIInputStream** aCloneOut)
+{
+ return mPipe->CloneInputStream(this, aCloneOut);
+}
+
+nsresult
+nsPipeInputStream::Status(const ReentrantMonitorAutoEnter& ev) const
+{
+ if (NS_FAILED(mInputStatus)) {
+ return mInputStatus;
+ }
+
+ if (mReadState.mAvailable) {
+ // Still something to read and this input stream state is OK.
+ return NS_OK;
+ }
+
+ // Nothing to read, just fall through to the pipe's state that
+ // may reflect state of its output stream side (already closed).
+ return mPipe->mStatus;
+}
+
+nsresult
+nsPipeInputStream::Status() const
+{
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+ return Status(mon);
+}
+
+nsPipeInputStream::~nsPipeInputStream()
+{
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeOutputStream methods:
+//-----------------------------------------------------------------------------
+
+NS_IMPL_QUERY_INTERFACE(nsPipeOutputStream,
+ nsIOutputStream,
+ nsIAsyncOutputStream,
+ nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER(nsPipeOutputStream,
+ nsIOutputStream,
+ nsIAsyncOutputStream)
+
+NS_IMPL_THREADSAFE_CI(nsPipeOutputStream)
+
+nsresult
+nsPipeOutputStream::Wait()
+{
+ NS_ASSERTION(mBlocking, "wait on non-blocking pipe output stream");
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ if (NS_SUCCEEDED(mPipe->mStatus) && !mWritable) {
+ LOG(("OOO pipe output: waiting for space\n"));
+ mBlocked = true;
+ mon.Wait();
+ mBlocked = false;
+ LOG(("OOO pipe output: woke up [pipe-status=%x writable=%u]\n",
+ mPipe->mStatus, mWritable));
+ }
+
+ return mPipe->mStatus == NS_BASE_STREAM_CLOSED ? NS_OK : mPipe->mStatus;
+}
+
+MonitorAction
+nsPipeOutputStream::OnOutputWritable(nsPipeEvents& aEvents)
+{
+ MonitorAction result = DoNotNotifyMonitor;
+
+ mWritable = true;
+
+ if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
+ aEvents.NotifyOutputReady(this, mCallback);
+ mCallback = nullptr;
+ mCallbackFlags = 0;
+ } else if (mBlocked) {
+ result = NotifyMonitor;
+ }
+
+ return result;
+}
+
+MonitorAction
+nsPipeOutputStream::OnOutputException(nsresult aReason, nsPipeEvents& aEvents)
+{
+ LOG(("nsPipeOutputStream::OnOutputException [this=%x reason=%x]\n",
+ this, aReason));
+
+ MonitorAction result = DoNotNotifyMonitor;
+
+ NS_ASSERTION(NS_FAILED(aReason), "huh? successful exception");
+ mWritable = false;
+
+ if (mCallback) {
+ aEvents.NotifyOutputReady(this, mCallback);
+ mCallback = nullptr;
+ mCallbackFlags = 0;
+ } else if (mBlocked) {
+ result = NotifyMonitor;
+ }
+
+ return result;
+}
+
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsPipeOutputStream::AddRef()
+{
+ ++mWriterRefCnt;
+ return mPipe->AddRef();
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsPipeOutputStream::Release()
+{
+ if (--mWriterRefCnt == 0) {
+ Close();
+ }
+ return mPipe->Release();
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::CloseWithStatus(nsresult aReason)
+{
+ LOG(("OOO CloseWithStatus [this=%x reason=%x]\n", this, aReason));
+
+ if (NS_SUCCEEDED(aReason)) {
+ aReason = NS_BASE_STREAM_CLOSED;
+ }
+
+ // input stream may remain open
+ mPipe->OnPipeException(aReason, true);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Close()
+{
+ return CloseWithStatus(NS_BASE_STREAM_CLOSED);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::WriteSegments(nsReadSegmentFun aReader,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ LOG(("OOO WriteSegments [this=%x count=%u]\n", this, aCount));
+
+ nsresult rv = NS_OK;
+
+ char* segment;
+ uint32_t segmentLen;
+
+ *aWriteCount = 0;
+ while (aCount) {
+ rv = mPipe->GetWriteSegment(segment, segmentLen);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is full
+ if (!mBlocking) {
+ // ignore this error if we've already written something
+ if (*aWriteCount > 0) {
+ rv = NS_OK;
+ }
+ break;
+ }
+ // wait for the pipe to have an empty segment.
+ rv = Wait();
+ if (NS_SUCCEEDED(rv)) {
+ continue;
+ }
+ }
+ mPipe->OnPipeException(rv);
+ break;
+ }
+
+ // write no more than aCount
+ if (segmentLen > aCount) {
+ segmentLen = aCount;
+ }
+
+ uint32_t readCount, originalLen = segmentLen;
+ while (segmentLen) {
+ readCount = 0;
+
+ rv = aReader(this, aClosure, segment, *aWriteCount, segmentLen, &readCount);
+
+ if (NS_FAILED(rv) || readCount == 0) {
+ aCount = 0;
+ // any errors returned from the aReader end here: do not
+ // propagate to the caller of WriteSegments.
+ rv = NS_OK;
+ break;
+ }
+
+ NS_ASSERTION(readCount <= segmentLen, "read more than expected");
+ segment += readCount;
+ segmentLen -= readCount;
+ aCount -= readCount;
+ *aWriteCount += readCount;
+ mLogicalOffset += readCount;
+ }
+
+ if (segmentLen < originalLen) {
+ mPipe->AdvanceWriteCursor(originalLen - segmentLen);
+ }
+ }
+
+ return rv;
+}
+
+static nsresult
+nsReadFromRawBuffer(nsIOutputStream* aOutStr,
+ void* aClosure,
+ char* aToRawSegment,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aReadCount)
+{
+ const char* fromBuf = (const char*)aClosure;
+ memcpy(aToRawSegment, &fromBuf[aOffset], aCount);
+ *aReadCount = aCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Write(const char* aFromBuf,
+ uint32_t aBufLen,
+ uint32_t* aWriteCount)
+{
+ return WriteSegments(nsReadFromRawBuffer, (void*)aFromBuf, aBufLen, aWriteCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Flush(void)
+{
+ // nothing to do
+ return NS_OK;
+}
+
+static nsresult
+nsReadFromInputStream(nsIOutputStream* aOutStr,
+ void* aClosure,
+ char* aToRawSegment,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aReadCount)
+{
+ nsIInputStream* fromStream = (nsIInputStream*)aClosure;
+ return fromStream->Read(aToRawSegment, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::WriteFrom(nsIInputStream* aFromStream,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ return WriteSegments(nsReadFromInputStream, aFromStream, aCount, aWriteCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ *aNonBlocking = !mBlocking;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::AsyncWait(nsIOutputStreamCallback* aCallback,
+ uint32_t aFlags,
+ uint32_t aRequestedCount,
+ nsIEventTarget* aTarget)
+{
+ LOG(("OOO AsyncWait [this=%x]\n", this));
+
+ nsPipeEvents pipeEvents;
+ {
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // replace a pending callback
+ mCallback = nullptr;
+ mCallbackFlags = 0;
+
+ if (!aCallback) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIOutputStreamCallback> proxy;
+ if (aTarget) {
+ proxy = NS_NewOutputStreamReadyEvent(aCallback, aTarget);
+ aCallback = proxy;
+ }
+
+ if (NS_FAILED(mPipe->mStatus) ||
+ (mWritable && !(aFlags & WAIT_CLOSURE_ONLY))) {
+ // stream is already closed or writable; post event.
+ pipeEvents.NotifyOutputReady(this, aCallback);
+ } else {
+ // queue up callback object to be notified when data becomes available
+ mCallback = aCallback;
+ mCallbackFlags = aFlags;
+ }
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewPipe(nsIInputStream** aPipeIn,
+ nsIOutputStream** aPipeOut,
+ uint32_t aSegmentSize,
+ uint32_t aMaxSize,
+ bool aNonBlockingInput,
+ bool aNonBlockingOutput)
+{
+ if (aSegmentSize == 0) {
+ aSegmentSize = DEFAULT_SEGMENT_SIZE;
+ }
+
+ // Handle aMaxSize of UINT32_MAX as a special case
+ uint32_t segmentCount;
+ if (aMaxSize == UINT32_MAX) {
+ segmentCount = UINT32_MAX;
+ } else {
+ segmentCount = aMaxSize / aSegmentSize;
+ }
+
+ nsIAsyncInputStream* in;
+ nsIAsyncOutputStream* out;
+ nsresult rv = NS_NewPipe2(&in, &out, aNonBlockingInput, aNonBlockingOutput,
+ aSegmentSize, segmentCount);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aPipeIn = in;
+ *aPipeOut = out;
+ return NS_OK;
+}
+
+nsresult
+NS_NewPipe2(nsIAsyncInputStream** aPipeIn,
+ nsIAsyncOutputStream** aPipeOut,
+ bool aNonBlockingInput,
+ bool aNonBlockingOutput,
+ uint32_t aSegmentSize,
+ uint32_t aSegmentCount)
+{
+ nsPipe* pipe = new nsPipe();
+ nsresult rv = pipe->Init(aNonBlockingInput,
+ aNonBlockingOutput,
+ aSegmentSize,
+ aSegmentCount);
+ if (NS_FAILED(rv)) {
+ NS_ADDREF(pipe);
+ NS_RELEASE(pipe);
+ return rv;
+ }
+
+ // These always succeed because the pipe is initialized above.
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetInputStream(aPipeIn));
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetOutputStream(aPipeOut));
+ return NS_OK;
+}
+
+nsresult
+nsPipeConstructor(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ nsPipe* pipe = new nsPipe();
+ NS_ADDREF(pipe);
+ nsresult rv = pipe->QueryInterface(aIID, aResult);
+ NS_RELEASE(pipe);
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/io/nsScriptableBase64Encoder.cpp b/xpcom/io/nsScriptableBase64Encoder.cpp
new file mode 100644
index 000000000..a8ffce8cd
--- /dev/null
+++ b/xpcom/io/nsScriptableBase64Encoder.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsScriptableBase64Encoder.h"
+#include "mozilla/Base64.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsScriptableBase64Encoder, nsIScriptableBase64Encoder)
+
+NS_IMETHODIMP
+nsScriptableBase64Encoder::EncodeToCString(nsIInputStream* aStream,
+ uint32_t aLength,
+ nsACString& aResult)
+{
+ return Base64EncodeInputStream(aStream, aResult, aLength);
+}
+
+NS_IMETHODIMP
+nsScriptableBase64Encoder::EncodeToString(nsIInputStream* aStream,
+ uint32_t aLength,
+ nsAString& aResult)
+{
+ return Base64EncodeInputStream(aStream, aResult, aLength);
+}
diff --git a/xpcom/io/nsScriptableBase64Encoder.h b/xpcom/io/nsScriptableBase64Encoder.h
new file mode 100644
index 000000000..d9121e5de
--- /dev/null
+++ b/xpcom/io/nsScriptableBase64Encoder.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsScriptableBase64Encoder_h__
+#define nsScriptableBase64Encoder_h__
+
+#include "nsIScriptableBase64Encoder.h"
+#include "mozilla/Attributes.h"
+
+#define NS_SCRIPTABLEBASE64ENCODER_CID \
+ {0xaaf68860, 0xf849, 0x40ee, \
+ {0xbb, 0x7a, 0xb2, 0x29, 0xbc, 0xe0, 0x36, 0xa3} }
+#define NS_SCRIPTABLEBASE64ENCODER_CONTRACTID \
+ "@mozilla.org/scriptablebase64encoder;1"
+
+class nsScriptableBase64Encoder final : public nsIScriptableBase64Encoder
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISCRIPTABLEBASE64ENCODER
+private:
+ ~nsScriptableBase64Encoder()
+ {
+ }
+};
+
+#endif
diff --git a/xpcom/io/nsScriptableInputStream.cpp b/xpcom/io/nsScriptableInputStream.cpp
new file mode 100644
index 000000000..6f8022238
--- /dev/null
+++ b/xpcom/io/nsScriptableInputStream.cpp
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsScriptableInputStream.h"
+#include "nsMemory.h"
+#include "nsString.h"
+
+NS_IMPL_ISUPPORTS(nsScriptableInputStream, nsIScriptableInputStream)
+
+// nsIScriptableInputStream methods
+NS_IMETHODIMP
+nsScriptableInputStream::Close()
+{
+ if (!mInputStream) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return mInputStream->Close();
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Init(nsIInputStream* aInputStream)
+{
+ if (!aInputStream) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ mInputStream = aInputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Available(uint64_t* aResult)
+{
+ if (!mInputStream) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return mInputStream->Available(aResult);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Read(uint32_t aCount, char** aResult)
+{
+ nsresult rv = NS_OK;
+ uint64_t count64 = 0;
+ char* buffer = nullptr;
+
+ if (!mInputStream) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ rv = mInputStream->Available(&count64);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // bug716556 - Ensure count+1 doesn't overflow
+ uint32_t count =
+ XPCOM_MIN((uint32_t)XPCOM_MIN<uint64_t>(count64, aCount), UINT32_MAX - 1);
+ buffer = (char*)malloc(count + 1); // make room for '\0'
+ if (!buffer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = ReadHelper(buffer, count);
+ if (NS_FAILED(rv)) {
+ free(buffer);
+ return rv;
+ }
+
+ buffer[count] = '\0';
+ *aResult = buffer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadBytes(uint32_t aCount, nsACString& aResult)
+{
+ if (!mInputStream) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (!aResult.SetLength(aCount, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ MOZ_ASSERT(aResult.Length() == aCount);
+ char* ptr = aResult.BeginWriting();
+ nsresult rv = ReadHelper(ptr, aCount);
+ if (NS_FAILED(rv)) {
+ aResult.Truncate();
+ }
+ return rv;
+}
+
+nsresult
+nsScriptableInputStream::ReadHelper(char* aBuffer, uint32_t aCount)
+{
+ uint32_t totalBytesRead = 0;
+ while (1) {
+ uint32_t bytesRead;
+ nsresult rv = mInputStream->Read(aBuffer + totalBytesRead,
+ aCount - totalBytesRead,
+ &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ totalBytesRead += bytesRead;
+ if (totalBytesRead == aCount) {
+ break;
+ }
+
+ // If we have read zero bytes, we have hit EOF.
+ if (bytesRead == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ }
+ return NS_OK;
+}
+
+nsresult
+nsScriptableInputStream::Create(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ RefPtr<nsScriptableInputStream> sis = new nsScriptableInputStream();
+ return sis->QueryInterface(aIID, aResult);
+}
diff --git a/xpcom/io/nsScriptableInputStream.h b/xpcom/io/nsScriptableInputStream.h
new file mode 100644
index 000000000..a84facd94
--- /dev/null
+++ b/xpcom/io/nsScriptableInputStream.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ___nsscriptableinputstream___h_
+#define ___nsscriptableinputstream___h_
+
+#include "nsIScriptableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+#define NS_SCRIPTABLEINPUTSTREAM_CID \
+{ 0x7225c040, 0xa9bf, 0x11d3, { 0xa1, 0x97, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44 } }
+
+#define NS_SCRIPTABLEINPUTSTREAM_CONTRACTID "@mozilla.org/scriptableinputstream;1"
+
+class nsScriptableInputStream final : public nsIScriptableInputStream
+{
+public:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIScriptableInputStream methods
+ NS_DECL_NSISCRIPTABLEINPUTSTREAM
+
+ // nsScriptableInputStream methods
+ nsScriptableInputStream()
+ {
+ }
+
+ static nsresult
+ Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+private:
+ ~nsScriptableInputStream()
+ {
+ }
+
+ nsresult ReadHelper(char* aBuffer, uint32_t aCount);
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+};
+
+#endif // ___nsscriptableinputstream___h_
diff --git a/xpcom/io/nsSegmentedBuffer.cpp b/xpcom/io/nsSegmentedBuffer.cpp
new file mode 100644
index 000000000..ab42a73c7
--- /dev/null
+++ b/xpcom/io/nsSegmentedBuffer.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSegmentedBuffer.h"
+#include "nsMemory.h"
+
+nsresult
+nsSegmentedBuffer::Init(uint32_t aSegmentSize, uint32_t aMaxSize)
+{
+ if (mSegmentArrayCount != 0) {
+ return NS_ERROR_FAILURE; // initialized more than once
+ }
+ mSegmentSize = aSegmentSize;
+ mMaxSize = aMaxSize;
+#if 0 // testing...
+ mSegmentArrayCount = 2;
+#else
+ mSegmentArrayCount = NS_SEGMENTARRAY_INITIAL_COUNT;
+#endif
+ return NS_OK;
+}
+
+char*
+nsSegmentedBuffer::AppendNewSegment()
+{
+ if (GetSize() >= mMaxSize) {
+ return nullptr;
+ }
+
+ if (!mSegmentArray) {
+ uint32_t bytes = mSegmentArrayCount * sizeof(char*);
+ mSegmentArray = (char**)moz_xmalloc(bytes);
+ if (!mSegmentArray) {
+ return nullptr;
+ }
+ memset(mSegmentArray, 0, bytes);
+ }
+
+ if (IsFull()) {
+ uint32_t newArraySize = mSegmentArrayCount * 2;
+ uint32_t bytes = newArraySize * sizeof(char*);
+ char** newSegArray = (char**)moz_xrealloc(mSegmentArray, bytes);
+ if (!newSegArray) {
+ return nullptr;
+ }
+ mSegmentArray = newSegArray;
+ // copy wrapped content to new extension
+ if (mFirstSegmentIndex > mLastSegmentIndex) {
+ // deal with wrap around case
+ memcpy(&mSegmentArray[mSegmentArrayCount],
+ mSegmentArray,
+ mLastSegmentIndex * sizeof(char*));
+ memset(mSegmentArray, 0, mLastSegmentIndex * sizeof(char*));
+ mLastSegmentIndex += mSegmentArrayCount;
+ memset(&mSegmentArray[mLastSegmentIndex], 0,
+ (newArraySize - mLastSegmentIndex) * sizeof(char*));
+ } else {
+ memset(&mSegmentArray[mLastSegmentIndex], 0,
+ (newArraySize - mLastSegmentIndex) * sizeof(char*));
+ }
+ mSegmentArrayCount = newArraySize;
+ }
+
+ char* seg = (char*)malloc(mSegmentSize);
+ if (!seg) {
+ return nullptr;
+ }
+ mSegmentArray[mLastSegmentIndex] = seg;
+ mLastSegmentIndex = ModSegArraySize(mLastSegmentIndex + 1);
+ return seg;
+}
+
+bool
+nsSegmentedBuffer::DeleteFirstSegment()
+{
+ NS_ASSERTION(mSegmentArray[mFirstSegmentIndex] != nullptr, "deleting bad segment");
+ free(mSegmentArray[mFirstSegmentIndex]);
+ mSegmentArray[mFirstSegmentIndex] = nullptr;
+ int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
+ if (mFirstSegmentIndex == last) {
+ mLastSegmentIndex = last;
+ return true;
+ } else {
+ mFirstSegmentIndex = ModSegArraySize(mFirstSegmentIndex + 1);
+ return false;
+ }
+}
+
+bool
+nsSegmentedBuffer::DeleteLastSegment()
+{
+ int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
+ NS_ASSERTION(mSegmentArray[last] != nullptr, "deleting bad segment");
+ free(mSegmentArray[last]);
+ mSegmentArray[last] = nullptr;
+ mLastSegmentIndex = last;
+ return (bool)(mLastSegmentIndex == mFirstSegmentIndex);
+}
+
+bool
+nsSegmentedBuffer::ReallocLastSegment(size_t aNewSize)
+{
+ int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
+ NS_ASSERTION(mSegmentArray[last] != nullptr, "realloc'ing bad segment");
+ char* newSegment = (char*)realloc(mSegmentArray[last], aNewSize);
+ if (newSegment) {
+ mSegmentArray[last] = newSegment;
+ return true;
+ }
+ return false;
+}
+
+void
+nsSegmentedBuffer::Empty()
+{
+ if (mSegmentArray) {
+ for (uint32_t i = 0; i < mSegmentArrayCount; i++) {
+ if (mSegmentArray[i]) {
+ free(mSegmentArray[i]);
+ }
+ }
+ free(mSegmentArray);
+ mSegmentArray = nullptr;
+ }
+ mSegmentArrayCount = NS_SEGMENTARRAY_INITIAL_COUNT;
+ mFirstSegmentIndex = mLastSegmentIndex = 0;
+}
+
+#if 0
+void
+TestSegmentedBuffer()
+{
+ nsSegmentedBuffer* buf = new nsSegmentedBuffer();
+ NS_ASSERTION(buf, "out of memory");
+ buf->Init(4, 16);
+ char* seg;
+ bool empty;
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(empty, "DeleteFirstSegment failed");
+ delete buf;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/io/nsSegmentedBuffer.h b/xpcom/io/nsSegmentedBuffer.h
new file mode 100644
index 000000000..37b3a8e4f
--- /dev/null
+++ b/xpcom/io/nsSegmentedBuffer.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSegmentedBuffer_h__
+#define nsSegmentedBuffer_h__
+
+#include "nsIMemory.h"
+
+class nsSegmentedBuffer
+{
+public:
+ nsSegmentedBuffer()
+ : mSegmentSize(0)
+ , mMaxSize(0)
+ , mSegmentArray(nullptr)
+ , mSegmentArrayCount(0)
+ , mFirstSegmentIndex(0)
+ , mLastSegmentIndex(0)
+ {
+ }
+
+ ~nsSegmentedBuffer()
+ {
+ Empty();
+ }
+
+
+ nsresult Init(uint32_t aSegmentSize, uint32_t aMaxSize);
+
+ char* AppendNewSegment(); // pushes at end
+
+ // returns true if no more segments remain:
+ bool DeleteFirstSegment(); // pops from beginning
+
+ // returns true if no more segments remain:
+ bool DeleteLastSegment(); // pops from beginning
+
+ // Call Realloc() on last segment. This is used to reduce memory
+ // consumption when data is not an exact multiple of segment size.
+ bool ReallocLastSegment(size_t aNewSize);
+
+ void Empty(); // frees all segments
+
+ inline uint32_t GetSegmentCount()
+ {
+ if (mFirstSegmentIndex <= mLastSegmentIndex) {
+ return mLastSegmentIndex - mFirstSegmentIndex;
+ } else {
+ return mSegmentArrayCount + mLastSegmentIndex - mFirstSegmentIndex;
+ }
+ }
+
+ inline uint32_t GetSegmentSize()
+ {
+ return mSegmentSize;
+ }
+ inline uint32_t GetMaxSize()
+ {
+ return mMaxSize;
+ }
+ inline uint32_t GetSize()
+ {
+ return GetSegmentCount() * mSegmentSize;
+ }
+
+ inline char* GetSegment(uint32_t aIndex)
+ {
+ NS_ASSERTION(aIndex < GetSegmentCount(), "index out of bounds");
+ int32_t i = ModSegArraySize(mFirstSegmentIndex + (int32_t)aIndex);
+ return mSegmentArray[i];
+ }
+
+protected:
+ inline int32_t ModSegArraySize(int32_t aIndex)
+ {
+ uint32_t result = aIndex & (mSegmentArrayCount - 1);
+ NS_ASSERTION(result == aIndex % mSegmentArrayCount,
+ "non-power-of-2 mSegmentArrayCount");
+ return result;
+ }
+
+ inline bool IsFull()
+ {
+ return ModSegArraySize(mLastSegmentIndex + 1) == mFirstSegmentIndex;
+ }
+
+protected:
+ uint32_t mSegmentSize;
+ uint32_t mMaxSize;
+ char** mSegmentArray;
+ uint32_t mSegmentArrayCount;
+ int32_t mFirstSegmentIndex;
+ int32_t mLastSegmentIndex;
+};
+
+// NS_SEGMENTARRAY_INITIAL_SIZE: This number needs to start out as a
+// power of 2 given how it gets used. We double the segment array
+// when we overflow it, and use that fact that it's a power of 2
+// to compute a fast modulus operation in IsFull.
+//
+// 32 segment array entries can accommodate 128k of data if segments
+// are 4k in size. That seems like a reasonable amount that will avoid
+// needing to grow the segment array.
+#define NS_SEGMENTARRAY_INITIAL_COUNT 32
+
+#endif // nsSegmentedBuffer_h__
diff --git a/xpcom/io/nsStorageStream.cpp b/xpcom/io/nsStorageStream.cpp
new file mode 100644
index 000000000..3af7bb853
--- /dev/null
+++ b/xpcom/io/nsStorageStream.cpp
@@ -0,0 +1,648 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * The storage stream provides an internal buffer that can be filled by a
+ * client using a single output stream. One or more independent input streams
+ * can be created to read the data out non-destructively. The implementation
+ * uses a segmented buffer internally to avoid realloc'ing of large buffers,
+ * with the attendant performance loss and heap fragmentation.
+ */
+
+#include "nsAlgorithm.h"
+#include "nsStorageStream.h"
+#include "nsSegmentedBuffer.h"
+#include "nsStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "nsICloneableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsISeekableStream.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+
+using mozilla::ipc::InputStreamParams;
+using mozilla::ipc::StringInputStreamParams;
+using mozilla::Maybe;
+using mozilla::Some;
+
+//
+// Log module for StorageStream logging...
+//
+// To enable logging (see prlog.h for full details):
+//
+// set MOZ_LOG=StorageStreamLog:5
+// set MOZ_LOG_FILE=storage.log
+//
+// This enables LogLevel::Debug level information and places all output in
+// the file storage.log.
+//
+static LazyLogModule sStorageStreamLog("nsStorageStream");
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(args) MOZ_LOG(sStorageStreamLog, mozilla::LogLevel::Debug, args)
+
+nsStorageStream::nsStorageStream()
+ : mSegmentedBuffer(0), mSegmentSize(0), mWriteInProgress(false),
+ mLastSegmentNum(-1), mWriteCursor(0), mSegmentEnd(0), mLogicalLength(0)
+{
+ LOG(("Creating nsStorageStream [%p].\n", this));
+}
+
+nsStorageStream::~nsStorageStream()
+{
+ delete mSegmentedBuffer;
+}
+
+NS_IMPL_ISUPPORTS(nsStorageStream,
+ nsIStorageStream,
+ nsIOutputStream)
+
+NS_IMETHODIMP
+nsStorageStream::Init(uint32_t aSegmentSize, uint32_t aMaxSize)
+{
+ mSegmentedBuffer = new nsSegmentedBuffer();
+ mSegmentSize = aSegmentSize;
+ mSegmentSizeLog2 = mozilla::FloorLog2(aSegmentSize);
+
+ // Segment size must be a power of two
+ if (mSegmentSize != ((uint32_t)1 << mSegmentSizeLog2)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return mSegmentedBuffer->Init(aSegmentSize, aMaxSize);
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetOutputStream(int32_t aStartingOffset,
+ nsIOutputStream** aOutputStream)
+{
+ if (NS_WARN_IF(!aOutputStream)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!mSegmentedBuffer)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (mWriteInProgress) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv = Seek(aStartingOffset);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Enlarge the last segment in the buffer so that it is the same size as
+ // all the other segments in the buffer. (It may have been realloc'ed
+ // smaller in the Close() method.)
+ if (mLastSegmentNum >= 0)
+ if (mSegmentedBuffer->ReallocLastSegment(mSegmentSize)) {
+ // Need to re-Seek, since realloc changed segment base pointer
+ rv = Seek(aStartingOffset);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ NS_ADDREF(this);
+ *aOutputStream = static_cast<nsIOutputStream*>(this);
+ mWriteInProgress = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Close()
+{
+ if (NS_WARN_IF(!mSegmentedBuffer)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ mWriteInProgress = false;
+
+ int32_t segmentOffset = SegOffset(mLogicalLength);
+
+ // Shrink the final segment in the segmented buffer to the minimum size
+ // needed to contain the data, so as to conserve memory.
+ if (segmentOffset) {
+ mSegmentedBuffer->ReallocLastSegment(segmentOffset);
+ }
+
+ mWriteCursor = 0;
+ mSegmentEnd = 0;
+
+ LOG(("nsStorageStream [%p] Close mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Flush()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Write(const char* aBuffer, uint32_t aCount,
+ uint32_t* aNumWritten)
+{
+ if (NS_WARN_IF(!aNumWritten) || NS_WARN_IF(!aBuffer)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!mSegmentedBuffer)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ const char* readCursor;
+ uint32_t count, availableInSegment, remaining;
+ nsresult rv = NS_OK;
+
+ LOG(("nsStorageStream [%p] Write mWriteCursor=%x mSegmentEnd=%x aCount=%d\n",
+ this, mWriteCursor, mSegmentEnd, aCount));
+
+ remaining = aCount;
+ readCursor = aBuffer;
+ // If no segments have been created yet, create one even if we don't have
+ // to write any data; this enables creating an input stream which reads from
+ // the very end of the data for any amount of data in the stream (i.e.
+ // this stream contains N bytes of data and newInputStream(N) is called),
+ // even for N=0 (with the caveat that we require .write("", 0) be called to
+ // initialize internal buffers).
+ bool firstTime = mSegmentedBuffer->GetSegmentCount() == 0;
+ while (remaining || MOZ_UNLIKELY(firstTime)) {
+ firstTime = false;
+ availableInSegment = mSegmentEnd - mWriteCursor;
+ if (!availableInSegment) {
+ mWriteCursor = mSegmentedBuffer->AppendNewSegment();
+ if (!mWriteCursor) {
+ mSegmentEnd = 0;
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+ mLastSegmentNum++;
+ mSegmentEnd = mWriteCursor + mSegmentSize;
+ availableInSegment = mSegmentEnd - mWriteCursor;
+ LOG(("nsStorageStream [%p] Write (new seg) mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ }
+
+ count = XPCOM_MIN(availableInSegment, remaining);
+ memcpy(mWriteCursor, readCursor, count);
+ remaining -= count;
+ readCursor += count;
+ mWriteCursor += count;
+ LOG(("nsStorageStream [%p] Writing mWriteCursor=%x mSegmentEnd=%x count=%d\n",
+ this, mWriteCursor, mSegmentEnd, count));
+ }
+
+out:
+ *aNumWritten = aCount - remaining;
+ mLogicalLength += *aNumWritten;
+
+ LOG(("nsStorageStream [%p] Wrote mWriteCursor=%x mSegmentEnd=%x numWritten=%d\n",
+ this, mWriteCursor, mSegmentEnd, *aNumWritten));
+ return rv;
+}
+
+NS_IMETHODIMP
+nsStorageStream::WriteFrom(nsIInputStream* aInStr, uint32_t aCount,
+ uint32_t* aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsStorageStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
+ uint32_t aCount, uint32_t* aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsStorageStream::IsNonBlocking(bool* aNonBlocking)
+{
+ *aNonBlocking = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetLength(uint32_t* aLength)
+{
+ *aLength = mLogicalLength;
+ return NS_OK;
+}
+
+// Truncate the buffer by deleting the end segments
+NS_IMETHODIMP
+nsStorageStream::SetLength(uint32_t aLength)
+{
+ if (NS_WARN_IF(!mSegmentedBuffer)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (mWriteInProgress) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (aLength > mLogicalLength) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ int32_t newLastSegmentNum = SegNum(aLength);
+ int32_t segmentOffset = SegOffset(aLength);
+ if (segmentOffset == 0) {
+ newLastSegmentNum--;
+ }
+
+ while (newLastSegmentNum < mLastSegmentNum) {
+ mSegmentedBuffer->DeleteLastSegment();
+ mLastSegmentNum--;
+ }
+
+ mLogicalLength = aLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetWriteInProgress(bool* aWriteInProgress)
+{
+ *aWriteInProgress = mWriteInProgress;
+ return NS_OK;
+}
+
+nsresult
+nsStorageStream::Seek(int32_t aPosition)
+{
+ if (NS_WARN_IF(!mSegmentedBuffer)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // An argument of -1 means "seek to end of stream"
+ if (aPosition == -1) {
+ aPosition = mLogicalLength;
+ }
+
+ // Seeking beyond the buffer end is illegal
+ if ((uint32_t)aPosition > mLogicalLength) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Seeking backwards in the write stream results in truncation
+ SetLength(aPosition);
+
+ // Special handling for seek to start-of-buffer
+ if (aPosition == 0) {
+ mWriteCursor = 0;
+ mSegmentEnd = 0;
+ LOG(("nsStorageStream [%p] Seek mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ return NS_OK;
+ }
+
+ // Segment may have changed, so reset pointers
+ mWriteCursor = mSegmentedBuffer->GetSegment(mLastSegmentNum);
+ NS_ASSERTION(mWriteCursor, "null mWriteCursor");
+ mSegmentEnd = mWriteCursor + mSegmentSize;
+
+ // Adjust write cursor for current segment offset. This test is necessary
+ // because SegNum may reference the next-to-be-allocated segment, in which
+ // case we need to be pointing at the end of the last segment.
+ int32_t segmentOffset = SegOffset(aPosition);
+ if (segmentOffset == 0 && (SegNum(aPosition) > (uint32_t) mLastSegmentNum)) {
+ mWriteCursor = mSegmentEnd;
+ } else {
+ mWriteCursor += segmentOffset;
+ }
+
+ LOG(("nsStorageStream [%p] Seek mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// There can be many nsStorageInputStreams for a single nsStorageStream
+class nsStorageInputStream final
+ : public nsIInputStream
+ , public nsISeekableStream
+ , public nsIIPCSerializableInputStream
+ , public nsICloneableInputStream
+{
+public:
+ nsStorageInputStream(nsStorageStream* aStorageStream, uint32_t aSegmentSize)
+ : mStorageStream(aStorageStream), mReadCursor(0),
+ mSegmentEnd(0), mSegmentNum(0),
+ mSegmentSize(aSegmentSize), mLogicalCursor(0),
+ mStatus(NS_OK)
+ {
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+ NS_DECL_NSICLONEABLEINPUTSTREAM
+
+private:
+ ~nsStorageInputStream()
+ {
+ }
+
+protected:
+ nsresult Seek(uint32_t aPosition);
+
+ friend class nsStorageStream;
+
+private:
+ RefPtr<nsStorageStream> mStorageStream;
+ uint32_t mReadCursor; // Next memory location to read byte, or 0
+ uint32_t mSegmentEnd; // One byte past end of current buffer segment
+ uint32_t mSegmentNum; // Segment number containing read cursor
+ uint32_t mSegmentSize; // All segments, except the last, are of this size
+ uint32_t mLogicalCursor; // Logical offset into stream
+ nsresult mStatus;
+
+ uint32_t SegNum(uint32_t aPosition)
+ {
+ return aPosition >> mStorageStream->mSegmentSizeLog2;
+ }
+ uint32_t SegOffset(uint32_t aPosition)
+ {
+ return aPosition & (mSegmentSize - 1);
+ }
+};
+
+NS_IMPL_ISUPPORTS(nsStorageInputStream,
+ nsIInputStream,
+ nsISeekableStream,
+ nsIIPCSerializableInputStream,
+ nsICloneableInputStream)
+
+NS_IMETHODIMP
+nsStorageStream::NewInputStream(int32_t aStartingOffset,
+ nsIInputStream** aInputStream)
+{
+ if (NS_WARN_IF(!mSegmentedBuffer)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ RefPtr<nsStorageInputStream> inputStream =
+ new nsStorageInputStream(this, mSegmentSize);
+
+ nsresult rv = inputStream->Seek(aStartingOffset);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ inputStream.forget(aInputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Close()
+{
+ mStatus = NS_BASE_STREAM_CLOSED;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Available(uint64_t* aAvailable)
+{
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ *aAvailable = mStorageStream->mLogicalLength - mLogicalCursor;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aNumRead)
+{
+ return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aNumRead);
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t* aNumRead)
+{
+ *aNumRead = 0;
+ if (mStatus == NS_BASE_STREAM_CLOSED) {
+ return NS_OK;
+ }
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ uint32_t count, availableInSegment, remainingCapacity, bytesConsumed;
+ nsresult rv;
+
+ remainingCapacity = aCount;
+ while (remainingCapacity) {
+ availableInSegment = mSegmentEnd - mReadCursor;
+ if (!availableInSegment) {
+ uint32_t available = mStorageStream->mLogicalLength - mLogicalCursor;
+ if (!available) {
+ goto out;
+ }
+
+ // We have data in the stream, but if mSegmentEnd is zero, then we
+ // were likely constructed prior to any data being written into
+ // the stream. Therefore, if mSegmentEnd is non-zero, we should
+ // move into the next segment; otherwise, we should stay in this
+ // segment so our input state can be updated and we can properly
+ // perform the initial read.
+ if (mSegmentEnd > 0) {
+ mSegmentNum++;
+ }
+ mReadCursor = 0;
+ mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
+ availableInSegment = mSegmentEnd;
+ }
+ const char* cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum);
+
+ count = XPCOM_MIN(availableInSegment, remainingCapacity);
+ rv = aWriter(this, aClosure, cur + mReadCursor, aCount - remainingCapacity,
+ count, &bytesConsumed);
+ if (NS_FAILED(rv) || (bytesConsumed == 0)) {
+ break;
+ }
+ remainingCapacity -= bytesConsumed;
+ mReadCursor += bytesConsumed;
+ mLogicalCursor += bytesConsumed;
+ }
+
+out:
+ *aNumRead = aCount - remainingCapacity;
+
+ bool isWriteInProgress = false;
+ if (NS_FAILED(mStorageStream->GetWriteInProgress(&isWriteInProgress))) {
+ isWriteInProgress = false;
+ }
+
+ if (*aNumRead == 0 && isWriteInProgress) {
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ // TODO: This class should implement nsIAsyncInputStream so that callers
+ // have some way of dealing with NS_BASE_STREAM_WOULD_BLOCK errors.
+
+ *aNonBlocking = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ int64_t pos = aOffset;
+
+ switch (aWhence) {
+ case NS_SEEK_SET:
+ break;
+ case NS_SEEK_CUR:
+ pos += mLogicalCursor;
+ break;
+ case NS_SEEK_END:
+ pos += mStorageStream->mLogicalLength;
+ break;
+ default:
+ NS_NOTREACHED("unexpected whence value");
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (pos == int64_t(mLogicalCursor)) {
+ return NS_OK;
+ }
+
+ return Seek(pos);
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Tell(int64_t* aResult)
+{
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ *aResult = mLogicalCursor;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::SetEOF()
+{
+ NS_NOTREACHED("nsStorageInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsStorageInputStream::Seek(uint32_t aPosition)
+{
+ uint32_t length = mStorageStream->mLogicalLength;
+ if (aPosition > length) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (length == 0) {
+ return NS_OK;
+ }
+
+ mSegmentNum = SegNum(aPosition);
+ mReadCursor = SegOffset(aPosition);
+ uint32_t available = length - aPosition;
+ mSegmentEnd = mReadCursor + XPCOM_MIN(mSegmentSize - mReadCursor, available);
+ mLogicalCursor = aPosition;
+ return NS_OK;
+}
+
+void
+nsStorageInputStream::Serialize(InputStreamParams& aParams, FileDescriptorArray&)
+{
+ nsCString combined;
+ int64_t offset;
+ mozilla::DebugOnly<nsresult> rv = Tell(&offset);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ uint64_t remaining;
+ rv = Available(&remaining);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ combined.SetCapacity(remaining);
+ uint32_t numRead = 0;
+
+ rv = Read(combined.BeginWriting(), remaining, &numRead);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ MOZ_ASSERT(numRead == remaining);
+ combined.SetLength(numRead);
+
+ rv = Seek(NS_SEEK_SET, offset);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ StringInputStreamParams params;
+ params.data() = combined;
+ aParams = params;
+}
+
+Maybe<uint64_t>
+nsStorageInputStream::ExpectedSerializedLength()
+{
+ uint64_t remaining = 0;
+ DebugOnly<nsresult> rv = Available(&remaining);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return Some(remaining);
+}
+
+bool
+nsStorageInputStream::Deserialize(const InputStreamParams& aParams,
+ const FileDescriptorArray&)
+{
+ NS_NOTREACHED("We should never attempt to deserialize a storage input stream.");
+ return false;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::GetCloneable(bool* aCloneableOut)
+{
+ *aCloneableOut = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Clone(nsIInputStream** aCloneOut)
+{
+ return mStorageStream->NewInputStream(mLogicalCursor, aCloneOut);
+}
+
+nsresult
+NS_NewStorageStream(uint32_t aSegmentSize, uint32_t aMaxSize,
+ nsIStorageStream** aResult)
+{
+ RefPtr<nsStorageStream> storageStream = new nsStorageStream();
+ nsresult rv = storageStream->Init(aSegmentSize, aMaxSize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ storageStream.forget(aResult);
+ return NS_OK;
+}
+
+// Undefine LOG, so that other .cpp files (or their includes) won't complain
+// about it already being defined, when we build in unified mode.
+#undef LOG
diff --git a/xpcom/io/nsStorageStream.h b/xpcom/io/nsStorageStream.h
new file mode 100644
index 000000000..7fddd683e
--- /dev/null
+++ b/xpcom/io/nsStorageStream.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * The storage stream provides an internal buffer that can be filled by a
+ * client using a single output stream. One or more independent input streams
+ * can be created to read the data out non-destructively. The implementation
+ * uses a segmented buffer internally to avoid realloc'ing of large buffers,
+ * with the attendant performance loss and heap fragmentation.
+ */
+
+#ifndef _nsStorageStream_h_
+#define _nsStorageStream_h_
+
+#include "nsIStorageStream.h"
+#include "nsIOutputStream.h"
+#include "nsMemory.h"
+#include "mozilla/Attributes.h"
+
+#define NS_STORAGESTREAM_CID \
+{ /* 669a9795-6ff7-4ed4-9150-c34ce2971b63 */ \
+ 0x669a9795, \
+ 0x6ff7, \
+ 0x4ed4, \
+ {0x91, 0x50, 0xc3, 0x4c, 0xe2, 0x97, 0x1b, 0x63} \
+}
+
+#define NS_STORAGESTREAM_CONTRACTID "@mozilla.org/storagestream;1"
+
+class nsSegmentedBuffer;
+
+class nsStorageStream final
+ : public nsIStorageStream
+ , public nsIOutputStream
+{
+public:
+ nsStorageStream();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISTORAGESTREAM
+ NS_DECL_NSIOUTPUTSTREAM
+
+ friend class nsStorageInputStream;
+
+private:
+ ~nsStorageStream();
+
+ nsSegmentedBuffer* mSegmentedBuffer;
+ uint32_t mSegmentSize; // All segments, except possibly the last, are of this size
+ // Must be power-of-2
+ uint32_t mSegmentSizeLog2; // log2(mSegmentSize)
+ bool mWriteInProgress; // true, if an un-Close'ed output stream exists
+ int32_t mLastSegmentNum; // Last segment # in use, -1 initially
+ char* mWriteCursor; // Pointer to next byte to be written
+ char* mSegmentEnd; // Pointer to one byte after end of segment
+ // containing the write cursor
+ uint32_t mLogicalLength; // Number of bytes written to stream
+
+ nsresult Seek(int32_t aPosition);
+ uint32_t SegNum(uint32_t aPosition)
+ {
+ return aPosition >> mSegmentSizeLog2;
+ }
+ uint32_t SegOffset(uint32_t aPosition)
+ {
+ return aPosition & (mSegmentSize - 1);
+ }
+};
+
+#endif // _nsStorageStream_h_
diff --git a/xpcom/io/nsStreamUtils.cpp b/xpcom/io/nsStreamUtils.cpp
new file mode 100644
index 000000000..891dad59f
--- /dev/null
+++ b/xpcom/io/nsStreamUtils.cpp
@@ -0,0 +1,957 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+#include "nsStreamUtils.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIPipe.h"
+#include "nsICloneableInputStream.h"
+#include "nsIEventTarget.h"
+#include "nsICancelableRunnable.h"
+#include "nsISafeOutputStream.h"
+#include "nsString.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIBufferedStreams.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+//-----------------------------------------------------------------------------
+
+// This is a nsICancelableRunnable because we can dispatch it to Workers and
+// those can be shut down at any time, and in these cases, Cancel() is called
+// instead of Run().
+class nsInputStreamReadyEvent final
+ : public CancelableRunnable
+ , public nsIInputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsInputStreamReadyEvent(nsIInputStreamCallback* aCallback,
+ nsIEventTarget* aTarget)
+ : mCallback(aCallback)
+ , mTarget(aTarget)
+ {
+ }
+
+private:
+ ~nsInputStreamReadyEvent()
+ {
+ if (!mCallback) {
+ return;
+ }
+ //
+ // whoa!! looks like we never posted this event. take care to
+ // release mCallback on the correct thread. if mTarget lives on the
+ // calling thread, then we are ok. otherwise, we have to try to
+ // proxy the Release over the right thread. if that thread is dead,
+ // then there's nothing we can do... better to leak than crash.
+ //
+ bool val;
+ nsresult rv = mTarget->IsOnCurrentThread(&val);
+ if (NS_FAILED(rv) || !val) {
+ nsCOMPtr<nsIInputStreamCallback> event =
+ NS_NewInputStreamReadyEvent(mCallback, mTarget);
+ mCallback = nullptr;
+ if (event) {
+ rv = event->OnInputStreamReady(nullptr);
+ if (NS_FAILED(rv)) {
+ NS_NOTREACHED("leaking stream event");
+ nsISupports* sup = event;
+ NS_ADDREF(sup);
+ }
+ }
+ }
+ }
+
+public:
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream* aStream) override
+ {
+ mStream = aStream;
+
+ nsresult rv =
+ mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Dispatch failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ if (mCallback) {
+ if (mStream) {
+ mCallback->OnInputStreamReady(mStream);
+ }
+ mCallback = nullptr;
+ }
+ return NS_OK;
+ }
+
+ nsresult Cancel() override
+ {
+ mCallback = nullptr;
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncInputStream> mStream;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(nsInputStreamReadyEvent, CancelableRunnable,
+ nsIInputStreamCallback)
+
+//-----------------------------------------------------------------------------
+
+// This is a nsICancelableRunnable because we can dispatch it to Workers and
+// those can be shut down at any time, and in these cases, Cancel() is called
+// instead of Run().
+class nsOutputStreamReadyEvent final
+ : public CancelableRunnable
+ , public nsIOutputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsOutputStreamReadyEvent(nsIOutputStreamCallback* aCallback,
+ nsIEventTarget* aTarget)
+ : mCallback(aCallback)
+ , mTarget(aTarget)
+ {
+ }
+
+private:
+ ~nsOutputStreamReadyEvent()
+ {
+ if (!mCallback) {
+ return;
+ }
+ //
+ // whoa!! looks like we never posted this event. take care to
+ // release mCallback on the correct thread. if mTarget lives on the
+ // calling thread, then we are ok. otherwise, we have to try to
+ // proxy the Release over the right thread. if that thread is dead,
+ // then there's nothing we can do... better to leak than crash.
+ //
+ bool val;
+ nsresult rv = mTarget->IsOnCurrentThread(&val);
+ if (NS_FAILED(rv) || !val) {
+ nsCOMPtr<nsIOutputStreamCallback> event =
+ NS_NewOutputStreamReadyEvent(mCallback, mTarget);
+ mCallback = nullptr;
+ if (event) {
+ rv = event->OnOutputStreamReady(nullptr);
+ if (NS_FAILED(rv)) {
+ NS_NOTREACHED("leaking stream event");
+ nsISupports* sup = event;
+ NS_ADDREF(sup);
+ }
+ }
+ }
+ }
+
+public:
+ NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream* aStream) override
+ {
+ mStream = aStream;
+
+ nsresult rv =
+ mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("PostEvent failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ if (mCallback) {
+ if (mStream) {
+ mCallback->OnOutputStreamReady(mStream);
+ }
+ mCallback = nullptr;
+ }
+ return NS_OK;
+ }
+
+ nsresult Cancel() override
+ {
+ mCallback = nullptr;
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncOutputStream> mStream;
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(nsOutputStreamReadyEvent, CancelableRunnable,
+ nsIOutputStreamCallback)
+
+//-----------------------------------------------------------------------------
+
+already_AddRefed<nsIInputStreamCallback>
+NS_NewInputStreamReadyEvent(nsIInputStreamCallback* aCallback,
+ nsIEventTarget* aTarget)
+{
+ NS_ASSERTION(aCallback, "null callback");
+ NS_ASSERTION(aTarget, "null target");
+ RefPtr<nsInputStreamReadyEvent> ev =
+ new nsInputStreamReadyEvent(aCallback, aTarget);
+ return ev.forget();
+}
+
+already_AddRefed<nsIOutputStreamCallback>
+NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback* aCallback,
+ nsIEventTarget* aTarget)
+{
+ NS_ASSERTION(aCallback, "null callback");
+ NS_ASSERTION(aTarget, "null target");
+ RefPtr<nsOutputStreamReadyEvent> ev =
+ new nsOutputStreamReadyEvent(aCallback, aTarget);
+ return ev.forget();
+}
+
+//-----------------------------------------------------------------------------
+// NS_AsyncCopy implementation
+
+// abstract stream copier...
+class nsAStreamCopier
+ : public nsIInputStreamCallback
+ , public nsIOutputStreamCallback
+ , public CancelableRunnable
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsAStreamCopier()
+ : mLock("nsAStreamCopier.mLock")
+ , mCallback(nullptr)
+ , mProgressCallback(nullptr)
+ , mClosure(nullptr)
+ , mChunkSize(0)
+ , mEventInProcess(false)
+ , mEventIsPending(false)
+ , mCloseSource(true)
+ , mCloseSink(true)
+ , mCanceled(false)
+ , mCancelStatus(NS_OK)
+ {
+ }
+
+ // kick off the async copy...
+ nsresult Start(nsIInputStream* aSource,
+ nsIOutputStream* aSink,
+ nsIEventTarget* aTarget,
+ nsAsyncCopyCallbackFun aCallback,
+ void* aClosure,
+ uint32_t aChunksize,
+ bool aCloseSource,
+ bool aCloseSink,
+ nsAsyncCopyProgressFun aProgressCallback)
+ {
+ mSource = aSource;
+ mSink = aSink;
+ mTarget = aTarget;
+ mCallback = aCallback;
+ mClosure = aClosure;
+ mChunkSize = aChunksize;
+ mCloseSource = aCloseSource;
+ mCloseSink = aCloseSink;
+ mProgressCallback = aProgressCallback;
+
+ mAsyncSource = do_QueryInterface(mSource);
+ mAsyncSink = do_QueryInterface(mSink);
+
+ return PostContinuationEvent();
+ }
+
+ // implemented by subclasses, returns number of bytes copied and
+ // sets source and sink condition before returning.
+ virtual uint32_t DoCopy(nsresult* aSourceCondition,
+ nsresult* aSinkCondition) = 0;
+
+ void Process()
+ {
+ if (!mSource || !mSink) {
+ return;
+ }
+
+ nsresult cancelStatus;
+ bool canceled;
+ {
+ MutexAutoLock lock(mLock);
+ canceled = mCanceled;
+ cancelStatus = mCancelStatus;
+ }
+
+ // If the copy was canceled before Process() was even called, then
+ // sourceCondition and sinkCondition should be set to error results to
+ // ensure we don't call Finish() on a canceled nsISafeOutputStream.
+ MOZ_ASSERT(NS_FAILED(cancelStatus) == canceled, "cancel needs an error");
+ nsresult sourceCondition = cancelStatus;
+ nsresult sinkCondition = cancelStatus;
+
+ // Copy data from the source to the sink until we hit failure or have
+ // copied all the data.
+ for (;;) {
+ // Note: copyFailed will be true if the source or the sink have
+ // reported an error, or if we failed to write any bytes
+ // because we have consumed all of our data.
+ bool copyFailed = false;
+ if (!canceled) {
+ uint32_t n = DoCopy(&sourceCondition, &sinkCondition);
+ if (n > 0 && mProgressCallback) {
+ mProgressCallback(mClosure, n);
+ }
+ copyFailed = NS_FAILED(sourceCondition) ||
+ NS_FAILED(sinkCondition) || n == 0;
+
+ MutexAutoLock lock(mLock);
+ canceled = mCanceled;
+ cancelStatus = mCancelStatus;
+ }
+ if (copyFailed && !canceled) {
+ if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) {
+ // need to wait for more data from source. while waiting for
+ // more source data, be sure to observe failures on output end.
+ mAsyncSource->AsyncWait(this, 0, 0, nullptr);
+
+ if (mAsyncSink)
+ mAsyncSink->AsyncWait(this,
+ nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
+ 0, nullptr);
+ break;
+ } else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) {
+ // need to wait for more room in the sink. while waiting for
+ // more room in the sink, be sure to observer failures on the
+ // input end.
+ mAsyncSink->AsyncWait(this, 0, 0, nullptr);
+
+ if (mAsyncSource)
+ mAsyncSource->AsyncWait(this,
+ nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
+ 0, nullptr);
+ break;
+ }
+ }
+ if (copyFailed || canceled) {
+ if (mCloseSource) {
+ // close source
+ if (mAsyncSource)
+ mAsyncSource->CloseWithStatus(
+ canceled ? cancelStatus : sinkCondition);
+ else {
+ mSource->Close();
+ }
+ }
+ mAsyncSource = nullptr;
+ mSource = nullptr;
+
+ if (mCloseSink) {
+ // close sink
+ if (mAsyncSink)
+ mAsyncSink->CloseWithStatus(
+ canceled ? cancelStatus : sourceCondition);
+ else {
+ // If we have an nsISafeOutputStream, and our
+ // sourceCondition and sinkCondition are not set to a
+ // failure state, finish writing.
+ nsCOMPtr<nsISafeOutputStream> sostream =
+ do_QueryInterface(mSink);
+ if (sostream && NS_SUCCEEDED(sourceCondition) &&
+ NS_SUCCEEDED(sinkCondition)) {
+ sostream->Finish();
+ } else {
+ mSink->Close();
+ }
+ }
+ }
+ mAsyncSink = nullptr;
+ mSink = nullptr;
+
+ // notify state complete...
+ if (mCallback) {
+ nsresult status;
+ if (!canceled) {
+ status = sourceCondition;
+ if (NS_SUCCEEDED(status)) {
+ status = sinkCondition;
+ }
+ if (status == NS_BASE_STREAM_CLOSED) {
+ status = NS_OK;
+ }
+ } else {
+ status = cancelStatus;
+ }
+ mCallback(mClosure, status);
+ }
+ break;
+ }
+ }
+ }
+
+ nsresult Cancel(nsresult aReason)
+ {
+ MutexAutoLock lock(mLock);
+ if (mCanceled) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_SUCCEEDED(aReason)) {
+ NS_WARNING("cancel with non-failure status code");
+ aReason = NS_BASE_STREAM_CLOSED;
+ }
+
+ mCanceled = true;
+ mCancelStatus = aReason;
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream* aSource) override
+ {
+ PostContinuationEvent();
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream* aSink) override
+ {
+ PostContinuationEvent();
+ return NS_OK;
+ }
+
+ // continuation event handler
+ NS_IMETHOD Run() override
+ {
+ Process();
+
+ // clear "in process" flag and post any pending continuation event
+ MutexAutoLock lock(mLock);
+ mEventInProcess = false;
+ if (mEventIsPending) {
+ mEventIsPending = false;
+ PostContinuationEvent_Locked();
+ }
+
+ return NS_OK;
+ }
+
+ nsresult Cancel() MOZ_MUST_OVERRIDE override = 0;
+
+ nsresult PostContinuationEvent()
+ {
+ // we cannot post a continuation event if there is currently
+ // an event in process. doing so could result in Process being
+ // run simultaneously on multiple threads, so we mark the event
+ // as pending, and if an event is already in process then we
+ // just let that existing event take care of posting the real
+ // continuation event.
+
+ MutexAutoLock lock(mLock);
+ return PostContinuationEvent_Locked();
+ }
+
+ nsresult PostContinuationEvent_Locked()
+ {
+ nsresult rv = NS_OK;
+ if (mEventInProcess) {
+ mEventIsPending = true;
+ } else {
+ rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_SUCCEEDED(rv)) {
+ mEventInProcess = true;
+ } else {
+ NS_WARNING("unable to post continuation event");
+ }
+ }
+ return rv;
+ }
+
+protected:
+ nsCOMPtr<nsIInputStream> mSource;
+ nsCOMPtr<nsIOutputStream> mSink;
+ nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
+ nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ Mutex mLock;
+ nsAsyncCopyCallbackFun mCallback;
+ nsAsyncCopyProgressFun mProgressCallback;
+ void* mClosure;
+ uint32_t mChunkSize;
+ bool mEventInProcess;
+ bool mEventIsPending;
+ bool mCloseSource;
+ bool mCloseSink;
+ bool mCanceled;
+ nsresult mCancelStatus;
+
+ // virtual since subclasses call superclass Release()
+ virtual ~nsAStreamCopier()
+ {
+ }
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(nsAStreamCopier,
+ CancelableRunnable,
+ nsIInputStreamCallback,
+ nsIOutputStreamCallback)
+
+class nsStreamCopierIB final : public nsAStreamCopier
+{
+public:
+ nsStreamCopierIB() : nsAStreamCopier()
+ {
+ }
+ virtual ~nsStreamCopierIB()
+ {
+ }
+
+ struct MOZ_STACK_CLASS ReadSegmentsState
+ {
+ // the nsIOutputStream will outlive the ReadSegmentsState on the stack
+ nsIOutputStream* MOZ_NON_OWNING_REF mSink;
+ nsresult mSinkCondition;
+ };
+
+ static nsresult ConsumeInputBuffer(nsIInputStream* aInStr,
+ void* aClosure,
+ const char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountWritten)
+ {
+ ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
+
+ nsresult rv = state->mSink->Write(aBuffer, aCount, aCountWritten);
+ if (NS_FAILED(rv)) {
+ state->mSinkCondition = rv;
+ } else if (*aCountWritten == 0) {
+ state->mSinkCondition = NS_BASE_STREAM_CLOSED;
+ }
+
+ return state->mSinkCondition;
+ }
+
+ uint32_t DoCopy(nsresult* aSourceCondition,
+ nsresult* aSinkCondition) override
+ {
+ ReadSegmentsState state;
+ state.mSink = mSink;
+ state.mSinkCondition = NS_OK;
+
+ uint32_t n;
+ *aSourceCondition =
+ mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
+ *aSinkCondition = state.mSinkCondition;
+ return n;
+ }
+
+ nsresult Cancel() override
+ {
+ return NS_OK;
+ }
+};
+
+class nsStreamCopierOB final : public nsAStreamCopier
+{
+public:
+ nsStreamCopierOB() : nsAStreamCopier()
+ {
+ }
+ virtual ~nsStreamCopierOB()
+ {
+ }
+
+ struct MOZ_STACK_CLASS WriteSegmentsState
+ {
+ // the nsIInputStream will outlive the WriteSegmentsState on the stack
+ nsIInputStream* MOZ_NON_OWNING_REF mSource;
+ nsresult mSourceCondition;
+ };
+
+ static nsresult FillOutputBuffer(nsIOutputStream* aOutStr,
+ void* aClosure,
+ char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountRead)
+ {
+ WriteSegmentsState* state = (WriteSegmentsState*)aClosure;
+
+ nsresult rv = state->mSource->Read(aBuffer, aCount, aCountRead);
+ if (NS_FAILED(rv)) {
+ state->mSourceCondition = rv;
+ } else if (*aCountRead == 0) {
+ state->mSourceCondition = NS_BASE_STREAM_CLOSED;
+ }
+
+ return state->mSourceCondition;
+ }
+
+ uint32_t DoCopy(nsresult* aSourceCondition,
+ nsresult* aSinkCondition) override
+ {
+ WriteSegmentsState state;
+ state.mSource = mSource;
+ state.mSourceCondition = NS_OK;
+
+ uint32_t n;
+ *aSinkCondition =
+ mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
+ *aSourceCondition = state.mSourceCondition;
+ return n;
+ }
+
+ nsresult Cancel() override
+ {
+ return NS_OK;
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_AsyncCopy(nsIInputStream* aSource,
+ nsIOutputStream* aSink,
+ nsIEventTarget* aTarget,
+ nsAsyncCopyMode aMode,
+ uint32_t aChunkSize,
+ nsAsyncCopyCallbackFun aCallback,
+ void* aClosure,
+ bool aCloseSource,
+ bool aCloseSink,
+ nsISupports** aCopierCtx,
+ nsAsyncCopyProgressFun aProgressCallback)
+{
+ NS_ASSERTION(aTarget, "non-null target required");
+
+ nsresult rv;
+ nsAStreamCopier* copier;
+
+ if (aMode == NS_ASYNCCOPY_VIA_READSEGMENTS) {
+ copier = new nsStreamCopierIB();
+ } else {
+ copier = new nsStreamCopierOB();
+ }
+
+ // Start() takes an owning ref to the copier...
+ NS_ADDREF(copier);
+ rv = copier->Start(aSource, aSink, aTarget, aCallback, aClosure, aChunkSize,
+ aCloseSource, aCloseSink, aProgressCallback);
+
+ if (aCopierCtx) {
+ *aCopierCtx = static_cast<nsISupports*>(static_cast<nsIRunnable*>(copier));
+ NS_ADDREF(*aCopierCtx);
+ }
+ NS_RELEASE(copier);
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_CancelAsyncCopy(nsISupports* aCopierCtx, nsresult aReason)
+{
+ nsAStreamCopier* copier =
+ static_cast<nsAStreamCopier*>(static_cast<nsIRunnable *>(aCopierCtx));
+ return copier->Cancel(aReason);
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_ConsumeStream(nsIInputStream* aStream, uint32_t aMaxCount,
+ nsACString& aResult)
+{
+ nsresult rv = NS_OK;
+ aResult.Truncate();
+
+ while (aMaxCount) {
+ uint64_t avail64;
+ rv = aStream->Available(&avail64);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ }
+ break;
+ }
+ if (avail64 == 0) {
+ break;
+ }
+
+ uint32_t avail = (uint32_t)XPCOM_MIN<uint64_t>(avail64, aMaxCount);
+
+ // resize aResult buffer
+ uint32_t length = aResult.Length();
+ if (avail > UINT32_MAX - length) {
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ aResult.SetLength(length + avail);
+ if (aResult.Length() != (length + avail)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ char* buf = aResult.BeginWriting() + length;
+
+ uint32_t n;
+ rv = aStream->Read(buf, avail, &n);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ if (n != avail) {
+ aResult.SetLength(length + n);
+ }
+ if (n == 0) {
+ break;
+ }
+ aMaxCount -= n;
+ }
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+static nsresult
+TestInputStream(nsIInputStream* aInStr,
+ void* aClosure,
+ const char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountWritten)
+{
+ bool* result = static_cast<bool*>(aClosure);
+ *result = true;
+ return NS_ERROR_ABORT; // don't call me anymore
+}
+
+bool
+NS_InputStreamIsBuffered(nsIInputStream* aStream)
+{
+ nsCOMPtr<nsIBufferedInputStream> bufferedIn = do_QueryInterface(aStream);
+ if (bufferedIn) {
+ return true;
+ }
+
+ bool result = false;
+ uint32_t n;
+ nsresult rv = aStream->ReadSegments(TestInputStream, &result, 1, &n);
+ return result || NS_SUCCEEDED(rv);
+}
+
+static nsresult
+TestOutputStream(nsIOutputStream* aOutStr,
+ void* aClosure,
+ char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountRead)
+{
+ bool* result = static_cast<bool*>(aClosure);
+ *result = true;
+ return NS_ERROR_ABORT; // don't call me anymore
+}
+
+bool
+NS_OutputStreamIsBuffered(nsIOutputStream* aStream)
+{
+ nsCOMPtr<nsIBufferedOutputStream> bufferedOut = do_QueryInterface(aStream);
+ if (bufferedOut) {
+ return true;
+ }
+
+ bool result = false;
+ uint32_t n;
+ aStream->WriteSegments(TestOutputStream, &result, 1, &n);
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_CopySegmentToStream(nsIInputStream* aInStr,
+ void* aClosure,
+ const char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountWritten)
+{
+ nsIOutputStream* outStr = static_cast<nsIOutputStream*>(aClosure);
+ *aCountWritten = 0;
+ while (aCount) {
+ uint32_t n;
+ nsresult rv = outStr->Write(aBuffer, aCount, &n);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aBuffer += n;
+ aCount -= n;
+ *aCountWritten += n;
+ }
+ return NS_OK;
+}
+
+nsresult
+NS_CopySegmentToBuffer(nsIInputStream* aInStr,
+ void* aClosure,
+ const char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountWritten)
+{
+ char* toBuf = static_cast<char*>(aClosure);
+ memcpy(&toBuf[aOffset], aBuffer, aCount);
+ *aCountWritten = aCount;
+ return NS_OK;
+}
+
+nsresult
+NS_CopySegmentToBuffer(nsIOutputStream* aOutStr,
+ void* aClosure,
+ char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountRead)
+{
+ const char* fromBuf = static_cast<const char*>(aClosure);
+ memcpy(aBuffer, &fromBuf[aOffset], aCount);
+ *aCountRead = aCount;
+ return NS_OK;
+}
+
+nsresult
+NS_DiscardSegment(nsIInputStream* aInStr,
+ void* aClosure,
+ const char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountWritten)
+{
+ *aCountWritten = aCount;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_WriteSegmentThunk(nsIInputStream* aInStr,
+ void* aClosure,
+ const char* aBuffer,
+ uint32_t aOffset,
+ uint32_t aCount,
+ uint32_t* aCountWritten)
+{
+ nsWriteSegmentThunk* thunk = static_cast<nsWriteSegmentThunk*>(aClosure);
+ return thunk->mFun(thunk->mStream, thunk->mClosure, aBuffer, aOffset, aCount,
+ aCountWritten);
+}
+
+nsresult
+NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
+ uint32_t aKeep, uint32_t* aNewBytes)
+{
+ MOZ_ASSERT(aInput, "null stream");
+ MOZ_ASSERT(aKeep <= aDest.Length(), "illegal keep count");
+
+ char* aBuffer = aDest.Elements();
+ int64_t keepOffset = int64_t(aDest.Length()) - aKeep;
+ if (aKeep != 0 && keepOffset > 0) {
+ memmove(aBuffer, aBuffer + keepOffset, aKeep);
+ }
+
+ nsresult rv =
+ aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes);
+ if (NS_FAILED(rv)) {
+ *aNewBytes = 0;
+ }
+ // NOTE: we rely on the fact that the new slots are NOT initialized by
+ // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct()
+ // in nsTArray.h:
+ aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes);
+
+ MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow");
+ return rv;
+}
+
+bool
+NS_InputStreamIsCloneable(nsIInputStream* aSource)
+{
+ if (!aSource) {
+ return false;
+ }
+
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
+ return cloneable && cloneable->GetCloneable();
+}
+
+nsresult
+NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
+ nsIInputStream** aReplacementOut)
+{
+ if (NS_WARN_IF(!aSource)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Attempt to perform the clone directly on the source stream
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
+ if (cloneable && cloneable->GetCloneable()) {
+ if (aReplacementOut) {
+ *aReplacementOut = nullptr;
+ }
+ return cloneable->Clone(aCloneOut);
+ }
+
+ // If we failed the clone and the caller does not want to replace their
+ // original stream, then we are done. Return error.
+ if (!aReplacementOut) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // The caller has opted-in to the fallback clone support that replaces
+ // the original stream. Copy the data to a pipe and return two cloned
+ // input streams.
+
+ nsCOMPtr<nsIInputStream> reader;
+ nsCOMPtr<nsIInputStream> readerClone;
+ nsCOMPtr<nsIOutputStream> writer;
+
+ nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer),
+ 0, 0, // default segment size and max size
+ true, true); // non-blocking
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ cloneable = do_QueryInterface(reader);
+ MOZ_ASSERT(cloneable && cloneable->GetCloneable());
+
+ rv = cloneable->Clone(getter_AddRefs(readerClone));
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ nsCOMPtr<nsIEventTarget> target =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ rv = NS_AsyncCopy(aSource, writer, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS);
+ if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+ readerClone.forget(aCloneOut);
+ reader.forget(aReplacementOut);
+
+ return NS_OK;
+}
diff --git a/xpcom/io/nsStreamUtils.h b/xpcom/io/nsStreamUtils.h
new file mode 100644
index 000000000..957eb2713
--- /dev/null
+++ b/xpcom/io/nsStreamUtils.h
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsStreamUtils_h__
+#define nsStreamUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsStringFwd.h"
+#include "nsIInputStream.h"
+#include "nsTArray.h"
+
+class nsIOutputStream;
+class nsIInputStreamCallback;
+class nsIOutputStreamCallback;
+class nsIEventTarget;
+
+/**
+ * A "one-shot" proxy of the OnInputStreamReady callback. The resulting
+ * proxy object's OnInputStreamReady function may only be called once! The
+ * proxy object ensures that the real notify object will be free'd on the
+ * thread corresponding to the given event target regardless of what thread
+ * the proxy object is destroyed on.
+ *
+ * This function is designed to be used to implement AsyncWait when the
+ * aTarget parameter is non-null.
+ */
+extern already_AddRefed<nsIInputStreamCallback>
+NS_NewInputStreamReadyEvent(nsIInputStreamCallback* aNotify,
+ nsIEventTarget* aTarget);
+
+/**
+ * A "one-shot" proxy of the OnOutputStreamReady callback. The resulting
+ * proxy object's OnOutputStreamReady function may only be called once! The
+ * proxy object ensures that the real notify object will be free'd on the
+ * thread corresponding to the given event target regardless of what thread
+ * the proxy object is destroyed on.
+ *
+ * This function is designed to be used to implement AsyncWait when the
+ * aTarget parameter is non-null.
+ */
+extern already_AddRefed<nsIOutputStreamCallback>
+NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback* aNotify,
+ nsIEventTarget* aTarget);
+
+/* ------------------------------------------------------------------------- */
+
+enum nsAsyncCopyMode {
+ NS_ASYNCCOPY_VIA_READSEGMENTS,
+ NS_ASYNCCOPY_VIA_WRITESEGMENTS
+};
+
+/**
+ * This function is called when a new chunk of data has been copied. The
+ * reported count is the size of the current chunk.
+ */
+typedef void (* nsAsyncCopyProgressFun)(void* closure, uint32_t count);
+
+/**
+ * This function is called when the async copy process completes. The reported
+ * status is NS_OK on success and some error code on failure.
+ */
+typedef void (* nsAsyncCopyCallbackFun)(void* closure, nsresult status);
+
+/**
+ * This function asynchronously copies data from the source to the sink. All
+ * data transfer occurs on the thread corresponding to the given event target.
+ * A null event target is not permitted.
+ *
+ * The copier handles blocking or non-blocking streams transparently. If a
+ * stream operation returns NS_BASE_STREAM_WOULD_BLOCK, then the stream will
+ * be QI'd to nsIAsync{In,Out}putStream and its AsyncWait method will be used
+ * to determine when to resume copying.
+ *
+ * Source and sink are closed by default when copying finishes or when error
+ * occurs. Caller can prevent closing source or sink by setting aCloseSource
+ * or aCloseSink to false.
+ *
+ * Caller can obtain aCopierCtx to be able to cancel copying.
+ */
+extern nsresult
+NS_AsyncCopy(nsIInputStream* aSource,
+ nsIOutputStream* aSink,
+ nsIEventTarget* aTarget,
+ nsAsyncCopyMode aMode = NS_ASYNCCOPY_VIA_READSEGMENTS,
+ uint32_t aChunkSize = 4096,
+ nsAsyncCopyCallbackFun aCallbackFun = nullptr,
+ void* aCallbackClosure = nullptr,
+ bool aCloseSource = true,
+ bool aCloseSink = true,
+ nsISupports** aCopierCtx = nullptr,
+ nsAsyncCopyProgressFun aProgressCallbackFun = nullptr);
+
+/**
+ * This function cancels copying started by function NS_AsyncCopy.
+ *
+ * @param aCopierCtx
+ * Copier context returned by NS_AsyncCopy.
+ * @param aReason
+ * A failure code indicating why the operation is being canceled.
+ * It is an error to pass a success code.
+ */
+extern nsresult
+NS_CancelAsyncCopy(nsISupports* aCopierCtx, nsresult aReason);
+
+/**
+ * This function copies all of the available data from the stream (up to at
+ * most aMaxCount bytes) into the given buffer. The buffer is truncated at
+ * the start of the function.
+ *
+ * If an error occurs while reading from the stream or while attempting to
+ * resize the buffer, then the corresponding error code is returned from this
+ * function, and any data that has already been read will be returned in the
+ * output buffer. This allows one to use this function with a non-blocking
+ * input stream that may return NS_BASE_STREAM_WOULD_BLOCK if it only has
+ * partial data available.
+ *
+ * @param aSource
+ * The input stream to read.
+ * @param aMaxCount
+ * The maximum number of bytes to consume from the stream. Pass the
+ * value UINT32_MAX to consume the entire stream. The number of
+ * bytes actually read is given by the length of aBuffer upon return.
+ * @param aBuffer
+ * The string object that will contain the stream data upon return.
+ * Note: The data copied to the string may contain null bytes and may
+ * contain non-ASCII values.
+ */
+extern nsresult
+NS_ConsumeStream(nsIInputStream* aSource, uint32_t aMaxCount,
+ nsACString& aBuffer);
+
+/**
+ * This function tests whether or not the input stream is buffered. A buffered
+ * input stream is one that implements readSegments. The test for this is to
+ * 1/ check whether the input stream implements nsIBufferedInputStream;
+ * 2/ if not, call readSegments, without actually consuming any data from the
+ * stream, to verify that it functions.
+ *
+ * NOTE: If the stream is non-blocking and has no data available yet, then this
+ * test will fail. In that case, we return false even though the test is not
+ * really conclusive.
+ *
+ * PERFORMANCE NOTE: If the stream does not implement nsIBufferedInputStream,
+ * calling readSegments may cause I/O. Therefore, you should avoid calling
+ * this function from the main thread.
+ *
+ * @param aInputStream
+ * The input stream to test.
+ */
+extern bool
+NS_InputStreamIsBuffered(nsIInputStream* aInputStream);
+
+/**
+ * This function tests whether or not the output stream is buffered. A
+ * buffered output stream is one that implements writeSegments. The test for
+ * this is to:
+ * 1/ check whether the output stream implements nsIBufferedOutputStream;
+ * 2/ if not, call writeSegments, without actually writing any data into
+ * the stream, to verify that it functions.
+ *
+ * NOTE: If the stream is non-blocking and has no available space yet, then
+ * this test will fail. In that case, we return false even though the test is
+ * not really conclusive.
+ *
+ * PERFORMANCE NOTE: If the stream does not implement nsIBufferedOutputStream,
+ * calling writeSegments may cause I/O. Therefore, you should avoid calling
+ * this function from the main thread.
+ *
+ * @param aOutputStream
+ * The output stream to test.
+ */
+extern bool
+NS_OutputStreamIsBuffered(nsIOutputStream* aOutputStream);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * copy data from the nsIInputStream into a nsIOutputStream passed as the
+ * aClosure parameter to the ReadSegments function.
+ *
+ * @see nsIInputStream.idl for a description of this function's parameters.
+ */
+extern nsresult
+NS_CopySegmentToStream(nsIInputStream* aInputStream, void* aClosure,
+ const char* aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t* aWriteCount);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * copy data from the nsIInputStream into a character buffer passed as the
+ * aClosure parameter to the ReadSegments function. The character buffer
+ * must be at least as large as the aCount parameter passed to ReadSegments.
+ *
+ * @see nsIInputStream.idl for a description of this function's parameters.
+ */
+extern nsresult
+NS_CopySegmentToBuffer(nsIInputStream* aInputStream, void* aClosure,
+ const char* aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t* aWriteCount);
+
+/**
+ * This function is intended to be passed to nsIOutputStream::WriteSegments to
+ * copy data into the nsIOutputStream from a character buffer passed as the
+ * aClosure parameter to the WriteSegments function.
+ *
+ * @see nsIOutputStream.idl for a description of this function's parameters.
+ */
+extern nsresult
+NS_CopySegmentToBuffer(nsIOutputStream* aOutputStream, void* aClosure,
+ char* aToSegment, uint32_t aFromOffset,
+ uint32_t aCount, uint32_t* aReadCount);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * discard data from the nsIInputStream. This can be used to efficiently read
+ * data from the stream without actually copying any bytes.
+ *
+ * @see nsIInputStream.idl for a description of this function's parameters.
+ */
+extern nsresult
+NS_DiscardSegment(nsIInputStream* aInputStream, void* aClosure,
+ const char* aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t* aWriteCount);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * adjust the aInputStream parameter passed to a consumer's WriteSegmentFun.
+ * The aClosure parameter must be a pointer to a nsWriteSegmentThunk object.
+ * The mStream and mClosure members of that object will be passed to the mFun
+ * function, with the remainder of the parameters being what are passed to
+ * NS_WriteSegmentThunk.
+ *
+ * This function comes in handy when implementing ReadSegments in terms of an
+ * inner stream's ReadSegments.
+ */
+extern nsresult
+NS_WriteSegmentThunk(nsIInputStream* aInputStream, void* aClosure,
+ const char* aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t* aWriteCount);
+
+struct MOZ_STACK_CLASS nsWriteSegmentThunk
+{
+ nsCOMPtr<nsIInputStream> mStream;
+ nsWriteSegmentFun mFun;
+ void* mClosure;
+};
+
+/**
+ * Read data from aInput and store in aDest. A non-zero aKeep will keep that
+ * many bytes from aDest (from the end). New data is appended after the kept
+ * bytes (if any). aDest's new length on returning from this function is
+ * aKeep + aNewBytes and is guaranteed to be less than or equal to aDest's
+ * current capacity.
+ * @param aDest the array to fill
+ * @param aInput the stream to read from
+ * @param aKeep number of bytes to keep (0 <= aKeep <= aDest.Length())
+ * @param aNewBytes (out) number of bytes read from aInput or zero if Read()
+ * failed
+ * @return the result from aInput->Read(...)
+ */
+extern nsresult
+NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
+ uint32_t aKeep, uint32_t* aNewBytes);
+
+/**
+ * Return true if the given stream can be directly cloned.
+ */
+extern bool
+NS_InputStreamIsCloneable(nsIInputStream* aSource);
+
+/**
+ * Clone the provided source stream in the most efficient way possible. This
+ * first attempts to QI to nsICloneableInputStream to use Clone(). If that is
+ * not supported or its cloneable attribute is false, then a fallback clone is
+ * provided by copying the source to a pipe. In this case the caller must
+ * replace the source stream with the resulting replacement stream. The clone
+ * and the replacement stream are then cloneable using nsICloneableInputStream
+ * without duplicating memory. This fallback clone using the pipe is only
+ * performed if a replacement stream parameter is also passed in.
+ * @param aSource The input stream to clone.
+ * @param aCloneOut Required out parameter to hold resulting clone.
+ * @param aReplacementOut Optional out parameter to hold stream to replace
+ * original source stream after clone. If not
+ * provided then the fallback clone process is not
+ * supported and a non-cloneable source will result
+ * in failure. Replacement streams are non-blocking.
+ * @return NS_OK on successful clone. Error otherwise.
+ */
+extern nsresult
+NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
+ nsIInputStream** aReplacementOut = nullptr);
+
+#endif // !nsStreamUtils_h__
diff --git a/xpcom/io/nsStringStream.cpp b/xpcom/io/nsStringStream.cpp
new file mode 100644
index 000000000..b65242c14
--- /dev/null
+++ b/xpcom/io/nsStringStream.cpp
@@ -0,0 +1,452 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Based on original code from nsIStringStream.cpp
+ */
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "nsStringStream.h"
+#include "nsStreamUtils.h"
+#include "nsReadableUtils.h"
+#include "nsICloneableInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsISupportsPrimitives.h"
+#include "nsCRT.h"
+#include "prerror.h"
+#include "plstr.h"
+#include "nsIClassInfoImpl.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsIIPCSerializableInputStream.h"
+
+using namespace mozilla::ipc;
+using mozilla::Maybe;
+using mozilla::Some;
+
+//-----------------------------------------------------------------------------
+// nsIStringInputStream implementation
+//-----------------------------------------------------------------------------
+
+class nsStringInputStream final
+ : public nsIStringInputStream
+ , public nsISeekableStream
+ , public nsISupportsCString
+ , public nsIIPCSerializableInputStream
+ , public nsICloneableInputStream
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISTRINGINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSCSTRING
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+ NS_DECL_NSICLONEABLEINPUTSTREAM
+
+ nsStringInputStream()
+ {
+ Clear();
+ }
+
+ explicit nsStringInputStream(const nsStringInputStream& aOther)
+ : mOffset(aOther.mOffset)
+ {
+ // Use Assign() here because we don't want the life of the clone to be
+ // dependent on the life of the original stream.
+ mData.Assign(aOther.mData);
+ }
+
+private:
+ ~nsStringInputStream()
+ {
+ }
+
+ uint32_t Length() const
+ {
+ return mData.Length();
+ }
+
+ uint32_t LengthRemaining() const
+ {
+ return Length() - mOffset;
+ }
+
+ void Clear()
+ {
+ mData.SetIsVoid(true);
+ }
+
+ bool Closed()
+ {
+ return mData.IsVoid();
+ }
+
+ nsDependentCSubstring mData;
+ uint32_t mOffset;
+};
+
+// This class needs to support threadsafe refcounting since people often
+// allocate a string stream, and then read it from a background thread.
+NS_IMPL_ADDREF(nsStringInputStream)
+NS_IMPL_RELEASE(nsStringInputStream)
+
+NS_IMPL_CLASSINFO(nsStringInputStream, nullptr, nsIClassInfo::THREADSAFE,
+ NS_STRINGINPUTSTREAM_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsStringInputStream,
+ nsIStringInputStream,
+ nsIInputStream,
+ nsISupportsCString,
+ nsISeekableStream,
+ nsIIPCSerializableInputStream,
+ nsICloneableInputStream)
+NS_IMPL_CI_INTERFACE_GETTER(nsStringInputStream,
+ nsIStringInputStream,
+ nsIInputStream,
+ nsISupportsCString,
+ nsISeekableStream,
+ nsICloneableInputStream)
+
+/////////
+// nsISupportsCString implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::GetType(uint16_t* aType)
+{
+ *aType = TYPE_CSTRING;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::GetData(nsACString& data)
+{
+ // The stream doesn't have any data when it is closed. We could fake it
+ // and return an empty string here, but it seems better to keep this return
+ // value consistent with the behavior of the other 'getter' methods.
+ if (NS_WARN_IF(Closed())) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ data.Assign(mData);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::SetData(const nsACString& aData)
+{
+ mData.Assign(aData);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ToString(char** aResult)
+{
+ // NOTE: This method may result in data loss, so we do not implement it.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/////////
+// nsIStringInputStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::SetData(const char* aData, int32_t aDataLen)
+{
+ if (NS_WARN_IF(!aData)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ mData.Assign(aData, aDataLen);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::AdoptData(char* aData, int32_t aDataLen)
+{
+ if (NS_WARN_IF(!aData)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ mData.Adopt(aData, aDataLen);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ShareData(const char* aData, int32_t aDataLen)
+{
+ if (NS_WARN_IF(!aData)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (aDataLen < 0) {
+ aDataLen = strlen(aData);
+ }
+
+ mData.Rebind(aData, aDataLen);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(size_t)
+nsStringInputStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ n += mData.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ return n;
+}
+
+/////////
+// nsIInputStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::Close()
+{
+ Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Available(uint64_t* aLength)
+{
+ NS_ASSERTION(aLength, "null ptr");
+
+ if (Closed()) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ *aLength = LengthRemaining();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aReadCount)
+{
+ NS_ASSERTION(aBuf, "null ptr");
+ return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t* aResult)
+{
+ NS_ASSERTION(aResult, "null ptr");
+ NS_ASSERTION(Length() >= mOffset, "bad stream state");
+
+ if (Closed()) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ // We may be at end-of-file
+ uint32_t maxCount = LengthRemaining();
+ if (maxCount == 0) {
+ *aResult = 0;
+ return NS_OK;
+ }
+
+ if (aCount > maxCount) {
+ aCount = maxCount;
+ }
+ nsresult rv = aWriter(this, aClosure, mData.BeginReading() + mOffset, 0,
+ aCount, aResult);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(*aResult <= aCount,
+ "writer should not write more than we asked it to write");
+ mOffset += *aResult;
+ }
+
+ // errors returned from the writer end here!
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ *aNonBlocking = true;
+ return NS_OK;
+}
+
+/////////
+// nsISeekableStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ if (Closed()) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ // Compute new stream position. The given offset may be a negative value.
+
+ int64_t newPos = aOffset;
+ switch (aWhence) {
+ case NS_SEEK_SET:
+ break;
+ case NS_SEEK_CUR:
+ newPos += mOffset;
+ break;
+ case NS_SEEK_END:
+ newPos += Length();
+ break;
+ default:
+ NS_ERROR("invalid aWhence");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > Length())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mOffset = (uint32_t)newPos;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Tell(int64_t* aOutWhere)
+{
+ if (Closed()) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ *aOutWhere = mOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::SetEOF()
+{
+ if (Closed()) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ mOffset = Length();
+ return NS_OK;
+}
+
+/////////
+// nsIIPCSerializableInputStream implementation
+/////////
+
+void
+nsStringInputStream::Serialize(InputStreamParams& aParams,
+ FileDescriptorArray& /* aFDs */)
+{
+ StringInputStreamParams params;
+ params.data() = PromiseFlatCString(mData);
+ aParams = params;
+}
+
+bool
+nsStringInputStream::Deserialize(const InputStreamParams& aParams,
+ const FileDescriptorArray& /* aFDs */)
+{
+ if (aParams.type() != InputStreamParams::TStringInputStreamParams) {
+ NS_ERROR("Received unknown parameters from the other process!");
+ return false;
+ }
+
+ const StringInputStreamParams& params =
+ aParams.get_StringInputStreamParams();
+
+ if (NS_FAILED(SetData(params.data()))) {
+ NS_WARNING("SetData failed!");
+ return false;
+ }
+
+ return true;
+}
+
+Maybe<uint64_t>
+nsStringInputStream::ExpectedSerializedLength()
+{
+ return Some(static_cast<uint64_t>(Length()));
+}
+
+/////////
+// nsICloneableInputStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::GetCloneable(bool* aCloneableOut)
+{
+ *aCloneableOut = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Clone(nsIInputStream** aCloneOut)
+{
+ RefPtr<nsIInputStream> ref = new nsStringInputStream(*this);
+ ref.forget(aCloneOut);
+ return NS_OK;
+}
+
+nsresult
+NS_NewByteInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead, int32_t aLength,
+ nsAssignmentType aAssignment)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ RefPtr<nsStringInputStream> stream = new nsStringInputStream();
+
+ nsresult rv;
+ switch (aAssignment) {
+ case NS_ASSIGNMENT_COPY:
+ rv = stream->SetData(aStringToRead, aLength);
+ break;
+ case NS_ASSIGNMENT_DEPEND:
+ rv = stream->ShareData(aStringToRead, aLength);
+ break;
+ case NS_ASSIGNMENT_ADOPT:
+ rv = stream->AdoptData(const_cast<char*>(aStringToRead), aLength);
+ break;
+ default:
+ NS_ERROR("invalid assignment type");
+ rv = NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ stream.forget(aStreamResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewCStringInputStream(nsIInputStream** aStreamResult,
+ const nsACString& aStringToRead)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ RefPtr<nsStringInputStream> stream = new nsStringInputStream();
+
+ stream->SetData(aStringToRead);
+
+ stream.forget(aStreamResult);
+ return NS_OK;
+}
+
+// factory method for constructing a nsStringInputStream object
+nsresult
+nsStringInputStreamConstructor(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ *aResult = nullptr;
+
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ RefPtr<nsStringInputStream> inst = new nsStringInputStream();
+ return inst->QueryInterface(aIID, aResult);
+}
diff --git a/xpcom/io/nsStringStream.h b/xpcom/io/nsStringStream.h
new file mode 100644
index 000000000..8c09530eb
--- /dev/null
+++ b/xpcom/io/nsStringStream.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsStringStream_h__
+#define nsStringStream_h__
+
+#include "nsIStringStream.h"
+#include "nsStringGlue.h"
+#include "nsMemory.h"
+
+/**
+ * Implements:
+ * nsIStringInputStream
+ * nsIInputStream
+ * nsISeekableStream
+ * nsISupportsCString
+ */
+#define NS_STRINGINPUTSTREAM_CONTRACTID "@mozilla.org/io/string-input-stream;1"
+#define NS_STRINGINPUTSTREAM_CID \
+{ /* 0abb0835-5000-4790-af28-61b3ba17c295 */ \
+ 0x0abb0835, \
+ 0x5000, \
+ 0x4790, \
+ {0xaf, 0x28, 0x61, 0xb3, 0xba, 0x17, 0xc2, 0x95} \
+}
+
+/**
+ * Factory method to get an nsInputStream from a byte buffer. Result will
+ * implement nsIStringInputStream and nsISeekableStream.
+ *
+ * If aAssignment is NS_ASSIGNMENT_COPY, then the resulting stream holds a copy
+ * of the given buffer (aStringToRead), and the caller is free to discard
+ * aStringToRead after this function returns.
+ *
+ * If aAssignment is NS_ASSIGNMENT_DEPEND, then the resulting stream refers
+ * directly to the given buffer (aStringToRead), so the caller must ensure that
+ * the buffer remains valid for the lifetime of the stream object. Use with
+ * care!!
+ *
+ * If aAssignment is NS_ASSIGNMENT_ADOPT, then the resulting stream refers
+ * directly to the given buffer (aStringToRead) and will free aStringToRead
+ * once the stream is closed.
+ *
+ * If aLength is less than zero, then the length of aStringToRead will be
+ * determined by scanning the buffer for the first null byte.
+ */
+extern nsresult
+NS_NewByteInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead, int32_t aLength = -1,
+ nsAssignmentType aAssignment = NS_ASSIGNMENT_DEPEND);
+
+/**
+ * Factory method to get an nsInputStream from an nsACString. Result will
+ * implement nsIStringInputStream and nsISeekableStream.
+ */
+extern nsresult
+NS_NewCStringInputStream(nsIInputStream** aStreamResult,
+ const nsACString& aStringToRead);
+
+#endif // nsStringStream_h__
diff --git a/xpcom/io/nsUnicharInputStream.cpp b/xpcom/io/nsUnicharInputStream.cpp
new file mode 100644
index 000000000..27c074c09
--- /dev/null
+++ b/xpcom/io/nsUnicharInputStream.cpp
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsUnicharInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIServiceManager.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "nsCRT.h"
+#include "nsStreamUtils.h"
+#include "nsUTF8Utils.h"
+#include "mozilla/Attributes.h"
+#include <fcntl.h>
+#if defined(XP_WIN)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#define STRING_BUFFER_SIZE 8192
+
+class StringUnicharInputStream final : public nsIUnicharInputStream
+{
+public:
+ explicit StringUnicharInputStream(const nsAString& aString) :
+ mString(aString), mPos(0), mLen(aString.Length()) { }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUNICHARINPUTSTREAM
+
+ nsString mString;
+ uint32_t mPos;
+ uint32_t mLen;
+
+private:
+ ~StringUnicharInputStream() { }
+};
+
+NS_IMETHODIMP
+StringUnicharInputStream::Read(char16_t* aBuf,
+ uint32_t aCount,
+ uint32_t* aReadCount)
+{
+ if (mPos >= mLen) {
+ *aReadCount = 0;
+ return NS_OK;
+ }
+ nsAString::const_iterator iter;
+ mString.BeginReading(iter);
+ const char16_t* us = iter.get();
+ uint32_t amount = mLen - mPos;
+ if (amount > aCount) {
+ amount = aCount;
+ }
+ memcpy(aBuf, us + mPos, sizeof(char16_t) * amount);
+ mPos += amount;
+ *aReadCount = amount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StringUnicharInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount, uint32_t* aReadCount)
+{
+ uint32_t bytesWritten;
+ uint32_t totalBytesWritten = 0;
+
+ nsresult rv;
+ aCount = XPCOM_MIN(mString.Length() - mPos, aCount);
+
+ nsAString::const_iterator iter;
+ mString.BeginReading(iter);
+
+ while (aCount) {
+ rv = aWriter(this, aClosure, iter.get() + mPos,
+ totalBytesWritten, aCount, &bytesWritten);
+
+ if (NS_FAILED(rv)) {
+ // don't propagate errors to the caller
+ break;
+ }
+
+ aCount -= bytesWritten;
+ totalBytesWritten += bytesWritten;
+ mPos += bytesWritten;
+ }
+
+ *aReadCount = totalBytesWritten;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StringUnicharInputStream::ReadString(uint32_t aCount, nsAString& aString,
+ uint32_t* aReadCount)
+{
+ if (mPos >= mLen) {
+ *aReadCount = 0;
+ return NS_OK;
+ }
+ uint32_t amount = mLen - mPos;
+ if (amount > aCount) {
+ amount = aCount;
+ }
+ aString = Substring(mString, mPos, amount);
+ mPos += amount;
+ *aReadCount = amount;
+ return NS_OK;
+}
+
+nsresult
+StringUnicharInputStream::Close()
+{
+ mPos = mLen;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(StringUnicharInputStream, nsIUnicharInputStream)
+
+//----------------------------------------------------------------------
+
+class UTF8InputStream final : public nsIUnicharInputStream
+{
+public:
+ UTF8InputStream();
+ nsresult Init(nsIInputStream* aStream);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUNICHARINPUTSTREAM
+
+private:
+ ~UTF8InputStream();
+
+protected:
+ int32_t Fill(nsresult* aErrorCode);
+
+ static void CountValidUTF8Bytes(const char* aBuf, uint32_t aMaxBytes,
+ uint32_t& aValidUTF8bytes,
+ uint32_t& aValidUTF16CodeUnits);
+
+ nsCOMPtr<nsIInputStream> mInput;
+ FallibleTArray<char> mByteData;
+ FallibleTArray<char16_t> mUnicharData;
+
+ uint32_t mByteDataOffset;
+ uint32_t mUnicharDataOffset;
+ uint32_t mUnicharDataLength;
+};
+
+UTF8InputStream::UTF8InputStream() :
+ mByteDataOffset(0),
+ mUnicharDataOffset(0),
+ mUnicharDataLength(0)
+{
+}
+
+nsresult
+UTF8InputStream::Init(nsIInputStream* aStream)
+{
+ if (!mByteData.SetCapacity(STRING_BUFFER_SIZE, mozilla::fallible) ||
+ !mUnicharData.SetCapacity(STRING_BUFFER_SIZE, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mInput = aStream;
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(UTF8InputStream, nsIUnicharInputStream)
+
+UTF8InputStream::~UTF8InputStream()
+{
+ Close();
+}
+
+nsresult
+UTF8InputStream::Close()
+{
+ mInput = nullptr;
+ mByteData.Clear();
+ mUnicharData.Clear();
+ return NS_OK;
+}
+
+nsresult
+UTF8InputStream::Read(char16_t* aBuf, uint32_t aCount, uint32_t* aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
+ nsresult errorCode;
+ if (0 == readCount) {
+ // Fill the unichar buffer
+ int32_t bytesRead = Fill(&errorCode);
+ if (bytesRead <= 0) {
+ *aReadCount = 0;
+ return errorCode;
+ }
+ readCount = bytesRead;
+ }
+ if (readCount > aCount) {
+ readCount = aCount;
+ }
+ memcpy(aBuf, mUnicharData.Elements() + mUnicharDataOffset,
+ readCount * sizeof(char16_t));
+ mUnicharDataOffset += readCount;
+ *aReadCount = readCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UTF8InputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount, uint32_t* aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
+ nsresult rv = NS_OK;
+ if (0 == bytesToWrite) {
+ // Fill the unichar buffer
+ int32_t bytesRead = Fill(&rv);
+ if (bytesRead <= 0) {
+ *aReadCount = 0;
+ return rv;
+ }
+ bytesToWrite = bytesRead;
+ }
+
+ if (bytesToWrite > aCount) {
+ bytesToWrite = aCount;
+ }
+
+ uint32_t bytesWritten;
+ uint32_t totalBytesWritten = 0;
+
+ while (bytesToWrite) {
+ rv = aWriter(this, aClosure,
+ mUnicharData.Elements() + mUnicharDataOffset,
+ totalBytesWritten, bytesToWrite, &bytesWritten);
+
+ if (NS_FAILED(rv)) {
+ // don't propagate errors to the caller
+ break;
+ }
+
+ bytesToWrite -= bytesWritten;
+ totalBytesWritten += bytesWritten;
+ mUnicharDataOffset += bytesWritten;
+ }
+
+ *aReadCount = totalBytesWritten;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UTF8InputStream::ReadString(uint32_t aCount, nsAString& aString,
+ uint32_t* aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
+ nsresult errorCode;
+ if (0 == readCount) {
+ // Fill the unichar buffer
+ int32_t bytesRead = Fill(&errorCode);
+ if (bytesRead <= 0) {
+ *aReadCount = 0;
+ return errorCode;
+ }
+ readCount = bytesRead;
+ }
+ if (readCount > aCount) {
+ readCount = aCount;
+ }
+ const char16_t* buf = mUnicharData.Elements() + mUnicharDataOffset;
+ aString.Assign(buf, readCount);
+
+ mUnicharDataOffset += readCount;
+ *aReadCount = readCount;
+ return NS_OK;
+}
+
+int32_t
+UTF8InputStream::Fill(nsresult* aErrorCode)
+{
+ if (!mInput) {
+ // We already closed the stream!
+ *aErrorCode = NS_BASE_STREAM_CLOSED;
+ return -1;
+ }
+
+ NS_ASSERTION(mByteData.Length() >= mByteDataOffset, "unsigned madness");
+ uint32_t remainder = mByteData.Length() - mByteDataOffset;
+ mByteDataOffset = remainder;
+ uint32_t nb;
+ *aErrorCode = NS_FillArray(mByteData, mInput, remainder, &nb);
+ if (nb == 0) {
+ // Because we assume a many to one conversion, the lingering data
+ // in the byte buffer must be a partial conversion
+ // fragment. Because we know that we have received no more new
+ // data to add to it, we can't convert it. Therefore, we discard
+ // it.
+ return nb;
+ }
+ NS_ASSERTION(remainder + nb == mByteData.Length(), "bad nb");
+
+ // Now convert as much of the byte buffer to unicode as possible
+ uint32_t srcLen, dstLen;
+ CountValidUTF8Bytes(mByteData.Elements(), remainder + nb, srcLen, dstLen);
+
+ // the number of UCS2 characters should always be <= the number of
+ // UTF8 chars
+ NS_ASSERTION(remainder + nb >= srcLen, "cannot be longer than out buffer");
+ NS_ASSERTION(dstLen <= mUnicharData.Capacity(),
+ "Ouch. I would overflow my buffer if I wasn't so careful.");
+ if (dstLen > mUnicharData.Capacity()) {
+ return 0;
+ }
+
+ ConvertUTF8toUTF16 converter(mUnicharData.Elements());
+
+ nsASingleFragmentCString::const_char_iterator start = mByteData.Elements();
+ nsASingleFragmentCString::const_char_iterator end = mByteData.Elements() + srcLen;
+
+ copy_string(start, end, converter);
+ if (converter.Length() != dstLen) {
+ *aErrorCode = NS_BASE_STREAM_BAD_CONVERSION;
+ return -1;
+ }
+
+ mUnicharDataOffset = 0;
+ mUnicharDataLength = dstLen;
+ mByteDataOffset = srcLen;
+
+ return dstLen;
+}
+
+void
+UTF8InputStream::CountValidUTF8Bytes(const char* aBuffer, uint32_t aMaxBytes,
+ uint32_t& aValidUTF8bytes,
+ uint32_t& aValidUTF16CodeUnits)
+{
+ const char* c = aBuffer;
+ const char* end = aBuffer + aMaxBytes;
+ const char* lastchar = c; // pre-initialize in case of 0-length buffer
+ uint32_t utf16length = 0;
+ while (c < end && *c) {
+ lastchar = c;
+ utf16length++;
+
+ if (UTF8traits::isASCII(*c)) {
+ c++;
+ } else if (UTF8traits::is2byte(*c)) {
+ c += 2;
+ } else if (UTF8traits::is3byte(*c)) {
+ c += 3;
+ } else if (UTF8traits::is4byte(*c)) {
+ c += 4;
+ utf16length++; // add 1 more because this will be converted to a
+ // surrogate pair.
+ } else if (UTF8traits::is5byte(*c)) {
+ c += 5;
+ } else if (UTF8traits::is6byte(*c)) {
+ c += 6;
+ } else {
+ NS_WARNING("Unrecognized UTF8 string in UTF8InputStream::CountValidUTF8Bytes()");
+ break; // Otherwise we go into an infinite loop. But what happens now?
+ }
+ }
+ if (c > end) {
+ c = lastchar;
+ utf16length--;
+ }
+
+ aValidUTF8bytes = c - aBuffer;
+ aValidUTF16CodeUnits = utf16length;
+}
+
+nsresult
+NS_NewUnicharInputStream(nsIInputStream* aStreamToWrap,
+ nsIUnicharInputStream** aResult)
+{
+ *aResult = nullptr;
+
+ // Create converter input stream
+ RefPtr<UTF8InputStream> it = new UTF8InputStream();
+ nsresult rv = it->Init(aStreamToWrap);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ it.forget(aResult);
+ return NS_OK;
+}
diff --git a/xpcom/io/nsUnicharInputStream.h b/xpcom/io/nsUnicharInputStream.h
new file mode 100644
index 000000000..d4631af7e
--- /dev/null
+++ b/xpcom/io/nsUnicharInputStream.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsUnicharInputStream_h__
+#define nsUnicharInputStream_h__
+
+#include "nsIUnicharInputStream.h"
+
+nsresult NS_NewUnicharInputStream(nsIInputStream* aStreamToWrap,
+ nsIUnicharInputStream** aResult);
+
+#endif // nsUnicharInputStream_h__
diff --git a/xpcom/io/nsWildCard.cpp b/xpcom/io/nsWildCard.cpp
new file mode 100644
index 000000000..9125cbbb8
--- /dev/null
+++ b/xpcom/io/nsWildCard.cpp
@@ -0,0 +1,481 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* *
+ *
+ *
+ * nsWildCard.cpp: shell-like wildcard match routines
+ *
+ * See nsIZipReader.findEntries documentation in nsIZipReader.idl for
+ * a description of the syntax supported by the routines in this file.
+ *
+ * Rob McCool
+ *
+ */
+
+#include "nsWildCard.h"
+#include "nsXPCOM.h"
+#include "nsCRTGlue.h"
+#include "nsCharTraits.h"
+
+/* -------------------- ASCII-specific character methods ------------------- */
+
+typedef int static_assert_character_code_arrangement['a' > 'A' ? 1 : -1];
+
+template<class T>
+static int
+alpha(T aChar)
+{
+ return ('a' <= aChar && aChar <= 'z') ||
+ ('A' <= aChar && aChar <= 'Z');
+}
+
+template<class T>
+static int
+alphanumeric(T aChar)
+{
+ return ('0' <= aChar && aChar <= '9') || ::alpha(aChar);
+}
+
+template<class T>
+static int
+lower(T aChar)
+{
+ return ('A' <= aChar && aChar <= 'Z') ? aChar + ('a' - 'A') : aChar;
+}
+
+template<class T>
+static int
+upper(T aChar)
+{
+ return ('a' <= aChar && aChar <= 'z') ? aChar - ('a' - 'A') : aChar;
+}
+
+/* ----------------------------- _valid_subexp ---------------------------- */
+
+template<class T>
+static int
+_valid_subexp(const T* aExpr, T aStop1, T aStop2)
+{
+ int x;
+ int nsc = 0; /* Number of special characters */
+ int np; /* Number of pipe characters in union */
+ int tld = 0; /* Number of tilde characters */
+
+ for (x = 0; aExpr[x] && (aExpr[x] != aStop1) && (aExpr[x] != aStop2); ++x) {
+ switch (aExpr[x]) {
+ case '~':
+ if (tld) { /* at most one exclusion */
+ return INVALID_SXP;
+ }
+ if (aStop1) { /* no exclusions within unions */
+ return INVALID_SXP;
+ }
+ if (!aExpr[x + 1]) { /* exclusion cannot be last character */
+ return INVALID_SXP;
+ }
+ if (!x) { /* exclusion cannot be first character */
+ return INVALID_SXP;
+ }
+ ++tld;
+ MOZ_FALLTHROUGH;
+ case '*':
+ case '?':
+ case '$':
+ ++nsc;
+ break;
+ case '[':
+ ++nsc;
+ if ((!aExpr[++x]) || (aExpr[x] == ']')) {
+ return INVALID_SXP;
+ }
+ for (; aExpr[x] && (aExpr[x] != ']'); ++x) {
+ if (aExpr[x] == '\\' && !aExpr[++x]) {
+ return INVALID_SXP;
+ }
+ }
+ if (!aExpr[x]) {
+ return INVALID_SXP;
+ }
+ break;
+ case '(':
+ ++nsc;
+ if (aStop1) { /* no nested unions */
+ return INVALID_SXP;
+ }
+ np = -1;
+ do {
+ int t = ::_valid_subexp(&aExpr[++x], T(')'), T('|'));
+ if (t == 0 || t == INVALID_SXP) {
+ return INVALID_SXP;
+ }
+ x += t;
+ if (!aExpr[x]) {
+ return INVALID_SXP;
+ }
+ ++np;
+ } while (aExpr[x] == '|');
+ if (np < 1) { /* must be at least one pipe */
+ return INVALID_SXP;
+ }
+ break;
+ case ')':
+ case ']':
+ case '|':
+ return INVALID_SXP;
+ case '\\':
+ ++nsc;
+ if (!aExpr[++x]) {
+ return INVALID_SXP;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (!aStop1 && !nsc) { /* must be at least one special character */
+ return NON_SXP;
+ }
+ return ((aExpr[x] == aStop1 || aExpr[x] == aStop2) ? x : INVALID_SXP);
+}
+
+
+template<class T>
+int
+NS_WildCardValid_(const T* aExpr)
+{
+ int x = ::_valid_subexp(aExpr, T('\0'), T('\0'));
+ return (x < 0 ? x : VALID_SXP);
+}
+
+int
+NS_WildCardValid(const char* aExpr)
+{
+ return NS_WildCardValid_(aExpr);
+}
+
+int
+NS_WildCardValid(const char16_t* aExpr)
+{
+ return NS_WildCardValid_(aExpr);
+}
+
+/* ----------------------------- _shexp_match ----------------------------- */
+
+
+#define MATCH 0
+#define NOMATCH 1
+#define ABORTED -1
+
+template<class T>
+static int
+_shexp_match(const T* aStr, const T* aExpr, bool aCaseInsensitive,
+ unsigned int aLevel);
+
+/**
+ * Count characters until we reach a NUL character or either of the
+ * two delimiter characters, stop1 or stop2. If we encounter a bracketed
+ * expression, look only for NUL or ']' inside it. Do not look for stop1
+ * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
+ * Handle all escaping.
+ * Return index in input string of first stop found, or ABORTED if not found.
+ * If "dest" is non-nullptr, copy counted characters to it and null terminate.
+ */
+template<class T>
+static int
+_scan_and_copy(const T* aExpr, T aStop1, T aStop2, T* aDest)
+{
+ int sx; /* source index */
+ T cc;
+
+ for (sx = 0; (cc = aExpr[sx]) && cc != aStop1 && cc != aStop2; ++sx) {
+ if (cc == '\\') {
+ if (!aExpr[++sx]) {
+ return ABORTED; /* should be impossible */
+ }
+ } else if (cc == '[') {
+ while ((cc = aExpr[++sx]) && cc != ']') {
+ if (cc == '\\' && !aExpr[++sx]) {
+ return ABORTED;
+ }
+ }
+ if (!cc) {
+ return ABORTED; /* should be impossible */
+ }
+ }
+ }
+ if (aDest && sx) {
+ /* Copy all but the closing delimiter. */
+ memcpy(aDest, aExpr, sx * sizeof(T));
+ aDest[sx] = 0;
+ }
+ return cc ? sx : ABORTED; /* index of closing delimiter */
+}
+
+/* On input, expr[0] is the opening parenthesis of a union.
+ * See if any of the alternatives in the union matches as a pattern.
+ * The strategy is to take each of the alternatives, in turn, and append
+ * the rest of the expression (after the closing ')' that marks the end of
+ * this union) to that alternative, and then see if the resultant expression
+ * matches the input string. Repeat this until some alternative matches,
+ * or we have an abort.
+ */
+template<class T>
+static int
+_handle_union(const T* aStr, const T* aExpr, bool aCaseInsensitive,
+ unsigned int aLevel)
+{
+ int sx; /* source index */
+ int cp; /* source index of closing parenthesis */
+ int count;
+ int ret = NOMATCH;
+ T* e2;
+
+ /* Find the closing parenthesis that ends this union in the expression */
+ cp = ::_scan_and_copy(aExpr, T(')'), T('\0'), static_cast<T*>(nullptr));
+ if (cp == ABORTED || cp < 4) { /* must be at least "(a|b" before ')' */
+ return ABORTED;
+ }
+ ++cp; /* now index of char after closing parenthesis */
+ e2 = (T*)moz_xmalloc((1 + nsCharTraits<T>::length(aExpr)) * sizeof(T));
+ if (!e2) {
+ return ABORTED;
+ }
+ for (sx = 1; ; ++sx) {
+ /* Here, aExpr[sx] is one character past the preceding '(' or '|'. */
+ /* Copy everything up to the next delimiter to e2 */
+ count = ::_scan_and_copy(aExpr + sx, T(')'), T('|'), e2);
+ if (count == ABORTED || !count) {
+ ret = ABORTED;
+ break;
+ }
+ sx += count;
+ /* Append everything after closing parenthesis to e2. This is safe. */
+ nsCharTraits<T>::copy(e2 + count, aExpr + cp,
+ nsCharTraits<T>::length(aExpr + cp) + 1);
+ ret = ::_shexp_match(aStr, e2, aCaseInsensitive, aLevel + 1);
+ if (ret != NOMATCH || !aExpr[sx] || aExpr[sx] == ')') {
+ break;
+ }
+ }
+ free(e2);
+ if (sx < 2) {
+ ret = ABORTED;
+ }
+ return ret;
+}
+
+/* returns 1 if val is in range from start..end, case insensitive. */
+static int
+_is_char_in_range(unsigned char aStart, unsigned char aEnd, unsigned char aVal)
+{
+ char map[256];
+ memset(map, 0, sizeof(map));
+ while (aStart <= aEnd) {
+ map[lower(aStart++)] = 1;
+ }
+ return map[lower(aVal)];
+}
+
+template<class T>
+static int
+_shexp_match(const T* aStr, const T* aExpr, bool aCaseInsensitive,
+ unsigned int aLevel)
+{
+ int x; /* input string index */
+ int y; /* expression index */
+ int ret, neg;
+
+ if (aLevel > 20) { /* Don't let the stack get too deep. */
+ return ABORTED;
+ }
+ for (x = 0, y = 0; aExpr[y]; ++y, ++x) {
+ if (!aStr[x] && aExpr[y] != '$' && aExpr[y] != '*') {
+ return NOMATCH;
+ }
+ switch (aExpr[y]) {
+ case '$':
+ if (aStr[x]) {
+ return NOMATCH;
+ }
+ --x; /* we don't want loop to increment x */
+ break;
+ case '*':
+ while (aExpr[++y] == '*') {
+ }
+ if (!aExpr[y]) {
+ return MATCH;
+ }
+ while (aStr[x]) {
+ ret = ::_shexp_match(&aStr[x++], &aExpr[y], aCaseInsensitive,
+ aLevel + 1);
+ switch (ret) {
+ case NOMATCH:
+ continue;
+ case ABORTED:
+ return ABORTED;
+ default:
+ return MATCH;
+ }
+ }
+ if (aExpr[y] == '$' && aExpr[y + 1] == '\0' && !aStr[x]) {
+ return MATCH;
+ } else {
+ return NOMATCH;
+ }
+ case '[': {
+ T start, end = 0;
+ int i;
+ ++y;
+ neg = (aExpr[y] == '^' && aExpr[y + 1] != ']');
+ if (neg) {
+ ++y;
+ }
+ i = y;
+ start = aExpr[i++];
+ if (start == '\\') {
+ start = aExpr[i++];
+ }
+ if (::alphanumeric(start) && aExpr[i++] == '-') {
+ end = aExpr[i++];
+ if (end == '\\') {
+ end = aExpr[i++];
+ }
+ }
+ if (::alphanumeric(end) && aExpr[i] == ']') {
+ /* This is a range form: a-b */
+ T val = aStr[x];
+ if (end < start) { /* swap them */
+ T tmp = end;
+ end = start;
+ start = tmp;
+ }
+ if (aCaseInsensitive && ::alpha(val)) {
+ val = ::_is_char_in_range((unsigned char)start,
+ (unsigned char)end,
+ (unsigned char)val);
+ if (neg == val) {
+ return NOMATCH;
+ }
+ } else if (neg != (val < start || val > end)) {
+ return NOMATCH;
+ }
+ y = i;
+ } else {
+ /* Not range form */
+ int matched = 0;
+ for (; aExpr[y] != ']'; ++y) {
+ if (aExpr[y] == '\\') {
+ ++y;
+ }
+ if (aCaseInsensitive) {
+ matched |= (::upper(aStr[x]) == ::upper(aExpr[y]));
+ } else {
+ matched |= (aStr[x] == aExpr[y]);
+ }
+ }
+ if (neg == matched) {
+ return NOMATCH;
+ }
+ }
+ }
+ break;
+ case '(':
+ if (!aExpr[y + 1]) {
+ return ABORTED;
+ }
+ return ::_handle_union(&aStr[x], &aExpr[y], aCaseInsensitive,
+ aLevel + 1);
+ case '?':
+ break;
+ case ')':
+ case ']':
+ case '|':
+ return ABORTED;
+ case '\\':
+ ++y;
+ MOZ_FALLTHROUGH;
+ default:
+ if (aCaseInsensitive) {
+ if (::upper(aStr[x]) != ::upper(aExpr[y])) {
+ return NOMATCH;
+ }
+ } else {
+ if (aStr[x] != aExpr[y]) {
+ return NOMATCH;
+ }
+ }
+ break;
+ }
+ }
+ return (aStr[x] ? NOMATCH : MATCH);
+}
+
+template<class T>
+static int
+ns_WildCardMatch(const T* aStr, const T* aXp, bool aCaseInsensitive)
+{
+ T* expr = nullptr;
+ int ret = MATCH;
+
+ if (!nsCharTraits<T>::find(aXp, nsCharTraits<T>::length(aXp), T('~'))) {
+ return ::_shexp_match(aStr, aXp, aCaseInsensitive, 0);
+ }
+
+ expr = (T*)moz_xmalloc((nsCharTraits<T>::length(aXp) + 1) * sizeof(T));
+ if (!expr) {
+ return NOMATCH;
+ }
+ memcpy(expr, aXp, (nsCharTraits<T>::length(aXp) + 1) * sizeof(T));
+
+ int x = ::_scan_and_copy(expr, T('~'), T('\0'), static_cast<T*>(nullptr));
+ if (x != ABORTED && expr[x] == '~') {
+ expr[x++] = '\0';
+ ret = ::_shexp_match(aStr, &expr[x], aCaseInsensitive, 0);
+ switch (ret) {
+ case NOMATCH:
+ ret = MATCH;
+ break;
+ case MATCH:
+ ret = NOMATCH;
+ break;
+ default:
+ break;
+ }
+ }
+ if (ret == MATCH) {
+ ret = ::_shexp_match(aStr, expr, aCaseInsensitive, 0);
+ }
+
+ free(expr);
+ return ret;
+}
+
+template<class T>
+int
+NS_WildCardMatch_(const T* aStr, const T* aExpr, bool aCaseInsensitive)
+{
+ int is_valid = NS_WildCardValid(aExpr);
+ switch (is_valid) {
+ case INVALID_SXP:
+ return -1;
+ default:
+ return ::ns_WildCardMatch(aStr, aExpr, aCaseInsensitive);
+ }
+}
+
+int
+NS_WildCardMatch(const char* aStr, const char* aXp, bool aCaseInsensitive)
+{
+ return NS_WildCardMatch_(aStr, aXp, aCaseInsensitive);
+}
+
+int
+NS_WildCardMatch(const char16_t* aStr, const char16_t* aXp,
+ bool aCaseInsensitive)
+{
+ return NS_WildCardMatch_(aStr, aXp, aCaseInsensitive);
+}
diff --git a/xpcom/io/nsWildCard.h b/xpcom/io/nsWildCard.h
new file mode 100644
index 000000000..a077382bb
--- /dev/null
+++ b/xpcom/io/nsWildCard.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * nsWildCard.h: Defines and prototypes for shell exp. match routines
+ *
+ * See nsIZipReader.findEntries docs in nsIZipReader.idl for a description of
+ * the supported expression syntax.
+ *
+ * Note that the syntax documentation explicitly says the results of certain
+ * expressions are undefined. This is intentional to require less robustness
+ * in the code. Regular expression parsing is hard; the smaller the set of
+ * features and interactions this code must support, the easier it is to
+ * ensure it works.
+ *
+ */
+
+#ifndef nsWildCard_h__
+#define nsWildCard_h__
+
+#include "nscore.h"
+
+/* --------------------------- Public routines ---------------------------- */
+
+
+/*
+ * NS_WildCardValid takes a shell expression exp as input. It returns:
+ *
+ * NON_SXP if exp is a standard string
+ * INVALID_SXP if exp is a shell expression, but invalid
+ * VALID_SXP if exp is a valid shell expression
+ */
+
+#define NON_SXP -1
+#define INVALID_SXP -2
+#define VALID_SXP 1
+
+int NS_WildCardValid(const char* aExpr);
+
+int NS_WildCardValid(const char16_t* aExpr);
+
+/* return values for the search routines */
+#define MATCH 0
+#define NOMATCH 1
+#define ABORTED -1
+
+/*
+ * NS_WildCardMatch
+ *
+ * Takes a prevalidated shell expression exp, and a string str.
+ *
+ * Returns 0 on match and 1 on non-match.
+ */
+
+int NS_WildCardMatch(const char* aStr, const char* aExpr,
+ bool aCaseInsensitive);
+
+int NS_WildCardMatch(const char16_t* aStr, const char16_t* aExpr,
+ bool aCaseInsensitive);
+
+#endif /* nsWildCard_h__ */
diff --git a/xpcom/libxpt/xptcall/porting.html b/xpcom/libxpt/xptcall/porting.html
new file mode 100644
index 000000000..84465306e
--- /dev/null
+++ b/xpcom/libxpt/xptcall/porting.html
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<html>
+<head>
+<title>Document Moved!</title>
+</head>
+<body bgcolor = "white">
+<center>The xptcall porting document has been moved to:
+<P>
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/porting.html">http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/porting.html</a>
+<P>
+Please update your links.
+</center>
+</body>
+</html> \ No newline at end of file
diff --git a/xpcom/libxpt/xptcall/status.html b/xpcom/libxpt/xptcall/status.html
new file mode 100644
index 000000000..ba7fe008f
--- /dev/null
+++ b/xpcom/libxpt/xptcall/status.html
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<html>
+<head>
+<title>Document Moved!</title>
+</head>
+<body bgcolor = "white">
+<center>The xptcall status document has been moved to:
+<P>
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/status.html">http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/status.html</a>
+<P>
+Please update your links.
+</center>
+</body>
+</html>
diff --git a/xpcom/moz.build b/xpcom/moz.build
new file mode 100644
index 000000000..45a83c5c8
--- /dev/null
+++ b/xpcom/moz.build
@@ -0,0 +1,50 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('**'):
+ BUG_COMPONENT = ('Core', 'XPCOM')
+
+DIRS += [
+ 'idl-parser/xpidl',
+]
+
+DIRS += [
+ 'typelib',
+ 'string',
+ 'glue',
+ 'base',
+ 'ds',
+ 'io',
+ 'components',
+ 'threads',
+ 'reflect',
+ 'system',
+ '../chrome',
+ 'build',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT' and CONFIG['MOZ_DEBUG']:
+ DIRS += ['windbgdlg']
+
+TEST_DIRS += [
+ 'tests',
+ 'rust/nsstring/gtest',
+]
+
+# Can't build internal xptcall tests that use symbols which are not exported.
+#TEST_DIRS += [
+# 'reflect/xptinfo/tests',
+# 'reflect/xptcall/tests,
+#]
+
+CONFIGURE_DEFINE_FILES += [
+ 'xpcom-config.h',
+ 'xpcom-private.h',
+]
+
+EXPORTS += [
+ '!xpcom-config.h',
+]
diff --git a/xpcom/reflect/moz.build b/xpcom/reflect/moz.build
new file mode 100644
index 000000000..c04892d00
--- /dev/null
+++ b/xpcom/reflect/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += ['xptinfo', 'xptcall']
+
diff --git a/xpcom/reflect/xptcall/README b/xpcom/reflect/xptcall/README
new file mode 100644
index 000000000..0c401fe88
--- /dev/null
+++ b/xpcom/reflect/xptcall/README
@@ -0,0 +1,6 @@
+see:
+
+http://www.mozilla.org/scriptable/xptcall-faq.html
+and
+http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/porting.html
+
diff --git a/xpcom/reflect/xptcall/genstubs.pl b/xpcom/reflect/xptcall/genstubs.pl
new file mode 100644
index 000000000..16ae2006a
--- /dev/null
+++ b/xpcom/reflect/xptcall/genstubs.pl
@@ -0,0 +1,88 @@
+#!/usr/local/bin/perl
+# 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/.
+
+
+# This is used to generate stub entry points. We generate a file to
+# be included in the declaraion and a file to be used for expanding macros
+# to represent the implementation of the stubs.
+
+#
+# if "$entry_count" is ever changed and the .inc files regenerated then
+# the following issues need to be addressed:
+#
+# 1) The current Linux ARM code has a limitation of only having 256-3 stubs,
+# as a result of the limitations of immediate values in ARM assembly.
+#
+# This number is verified by the IDL parser in xpcom/idl-parser/xpidl.py, as
+# well as in xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp, to
+# prevent generating interfaces or loading xpt files that would cause the
+# stubs to run off the entries.
+# If you change this number, please update that location.
+
+# 3 entries are already 'used' by the 3 methods of nsISupports.
+# 3+247+5=255 This should get us in under the Linux ARM limitation
+$entry_count = 247;
+$sentinel_count = 5;
+
+$decl_name = "xptcstubsdecl.inc";
+$def_name = "xptcstubsdef.inc";
+
+##
+## Write the declarations include file
+##
+
+die "Can't open $decl_name" if !open(OUTFILE, ">$decl_name");
+
+print OUTFILE "/* generated file - DO NOT EDIT */\n\n";
+print OUTFILE "/* includes ",$entry_count," stub entries, and ",
+ $sentinel_count," sentinel entries */\n\n";
+print OUTFILE "/*\n";
+print OUTFILE "* declarations of normal stubs...\n";
+print OUTFILE "* 0 is QueryInterface\n";
+print OUTFILE "* 1 is AddRef\n";
+print OUTFILE "* 2 is Release\n";
+print OUTFILE "*/\n";
+print OUTFILE "#if !defined(__ia64) || (!defined(__hpux) && !defined(__linux__) && !defined(__FreeBSD__))\n";
+for($i = 0; $i < $entry_count; $i++) {
+ print OUTFILE "NS_IMETHOD Stub",$i+3,"();\n";
+}
+print OUTFILE "#else\n";
+for($i = 0; $i < $entry_count; $i++) {
+ print OUTFILE "NS_IMETHOD Stub",$i+3,"(uint64_t,uint64_t,\n";
+ print OUTFILE " uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);\n";
+
+}
+print OUTFILE "#endif\n";
+
+print OUTFILE "\n/* declarations of sentinel stubs */\n";
+
+for($i = 0; $i < $sentinel_count; $i++) {
+ print OUTFILE "NS_IMETHOD Sentinel",$i,"();\n";
+}
+close(OUTFILE);
+
+
+##
+## Write the definitions include file. This assumes a macro will be used to
+## expand the entries written...
+##
+
+die "Can't open $def_name" if !open(OUTFILE, ">$def_name");
+
+## Disabled for bug 275004 - followup to fix is Bug 419604
+my $warn_inc_is_generated = 0;
+if ($warn_inc_is_generated) {
+print OUTFILE "/* generated file - DO NOT EDIT */\n\n";
+print OUTFILE "/* includes ",$entry_count," stub entries, and ",
+ $sentinel_count," sentinel entries */\n\n";
+}
+
+for($i = 0; $i < $entry_count; $i++) {
+ print OUTFILE "STUB_ENTRY(",$i+3,")\n";
+}
+
+for($i = 0; $i < $sentinel_count; $i++) {
+ print OUTFILE "SENTINEL_ENTRY(",$i,")\n";
+}
diff --git a/xpcom/reflect/xptcall/md/moz.build b/xpcom/reflect/xptcall/md/moz.build
new file mode 100644
index 000000000..a4b59aaf0
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+arch = CONFIG['OS_ARCH']
+
+if arch == 'WINNT':
+ DIRS += ['win32']
+else:
+ DIRS += ['unix']
diff --git a/xpcom/reflect/xptcall/md/test/README b/xpcom/reflect/xptcall/md/test/README
new file mode 100644
index 000000000..04850b2e0
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/README
@@ -0,0 +1,6 @@
+These are just simple test programs in which stripped down versions of the
+XPConnect invoke and stubs code can be built and tested as the code is brought
+up on various platforms. These probrams do not test the param sizing and copying
+functionality of the routines. However, they do supply a place where the lowest
+level assembly language code can be developed and debugged in the simplest of
+contexts before it is moved into the real routines. \ No newline at end of file
diff --git a/xpcom/reflect/xptcall/md/test/clean.bat b/xpcom/reflect/xptcall/md/test/clean.bat
new file mode 100755
index 000000000..f320e222c
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/clean.bat
@@ -0,0 +1,5 @@
+@echo off
+echo deleting intermediate files
+if exist *.obj del *.obj > NUL
+if exist *.ilk del *.ilk > NUL
+if exist *.pdb del *.pdb > NUL \ No newline at end of file
diff --git a/xpcom/reflect/xptcall/md/test/invoke_test.cpp b/xpcom/reflect/xptcall/md/test/invoke_test.cpp
new file mode 100644
index 000000000..068db8b2f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/invoke_test.cpp
@@ -0,0 +1,207 @@
+/* -*- Mode: C; tab-width: 4; 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 <stdio.h>
+
+typedef unsigned nsresult;
+typedef unsigned uint32_t;
+typedef unsigned nsXPCVariant;
+
+
+#if defined(WIN32)
+#define NS_IMETHOD virtual nsresult __stdcall
+#define NS_IMETHODIMP nsresult __stdcall
+#else
+#define NS_IMETHOD virtual nsresult
+#define NS_IMETHODIMP nsresult
+#endif
+
+
+class base{
+public:
+ NS_IMETHOD ignored() = 0;
+};
+
+class foo : public base {
+public:
+ NS_IMETHOD callme1(int i, int j) = 0;
+ NS_IMETHOD callme2(int i, int j) = 0;
+ NS_IMETHOD callme3(int i, int j) = 0;
+};
+
+class bar : public foo{
+public:
+ NS_IMETHOD ignored();
+ NS_IMETHOD callme1(int i, int j);
+ NS_IMETHOD callme2(int i, int j);
+ NS_IMETHOD callme3(int i, int j);
+};
+
+/*
+class baz : public base {
+public:
+ NS_IMETHOD ignored();
+ NS_IMETHOD callme1();
+ NS_IMETHOD callme2();
+ NS_IMETHOD callme3();
+ void setfoo(foo* f) {other = f;}
+
+ foo* other;
+};
+NS_IMETHODIMP baz::ignored(){return 0;}
+*/
+
+NS_IMETHODIMP bar::ignored(){return 0;}
+
+NS_IMETHODIMP bar::callme1(int i, int j)
+{
+ printf("called bar::callme1 with: %d %d\n", i, j);
+ return 5;
+}
+
+NS_IMETHODIMP bar::callme2(int i, int j)
+{
+ printf("called bar::callme2 with: %d %d\n", i, j);
+ return 5;
+}
+
+NS_IMETHODIMP bar::callme3(int i, int j)
+{
+ printf("called bar::callme3 with: %d %d\n", i, j);
+ return 5;
+}
+
+void docall(foo* f, int i, int j){
+ f->callme1(i, j);
+}
+
+/***************************************************************************/
+#if defined(WIN32)
+
+static uint32_t __stdcall
+invoke_count_words(uint32_t paramCount, nsXPCVariant* s)
+{
+ return paramCount;
+}
+
+static void __stdcall
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPCVariant* s)
+{
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ *((uint32_t*)d) = *((uint32_t*)s);
+ }
+}
+
+static nsresult __stdcall
+DoInvoke(void* that, uint32_t index,
+ uint32_t paramCount, nsXPCVariant* params)
+{
+ __asm {
+ push params
+ push paramCount
+ call invoke_count_words // stdcall, result in eax
+ shl eax,2 // *= 4
+ sub esp,eax // make space for params
+ mov edx,esp
+ push params
+ push paramCount
+ push edx
+ call invoke_copy_to_stack // stdcall
+ mov ecx,that // instance in ecx
+ push ecx // push this
+ mov edx,[ecx] // vtable in edx
+ mov eax,index
+ shl eax,2 // *= 4
+ add edx,eax
+ call [edx] // stdcall, i.e. callee cleans up stack.
+ }
+}
+
+#else
+/***************************************************************************/
+// just Linux_x86 now. Add other later...
+
+static uint32_t
+invoke_count_words(uint32_t paramCount, nsXPCVariant* s)
+{
+ return paramCount;
+}
+
+static void
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPCVariant* s)
+{
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ *((uint32_t*)d) = *((uint32_t*)s);
+ }
+}
+
+static nsresult
+DoInvoke(void* that, uint32_t index,
+ uint32_t paramCount, nsXPCVariant* params)
+{
+ uint32_t result;
+ void* fn_count = invoke_count_words;
+ void* fn_copy = invoke_copy_to_stack;
+
+ __asm__ __volatile__(
+ "pushl %4\n\t"
+ "pushl %3\n\t"
+ "movl %5, %%eax\n\t"
+ "call *%%eax\n\t" /* count words */
+ "addl $0x8, %%esp\n\t"
+ "shl $2, %%eax\n\t" /* *= 4 */
+ "subl %%eax, %%esp\n\t" /* make room for params */
+ "movl %%esp, %%edx\n\t"
+ "pushl %4\n\t"
+ "pushl %3\n\t"
+ "pushl %%edx\n\t"
+ "movl %6, %%eax\n\t"
+ "call *%%eax\n\t" /* copy params */
+ "addl $0xc, %%esp\n\t"
+ "movl %1, %%ecx\n\t"
+ "pushl %%ecx\n\t"
+ "movl (%%ecx), %%edx\n\t"
+ "movl %2, %%eax\n\t" /* function index */
+ "shl $2, %%eax\n\t" /* *= 4 */
+ "addl $8, %%eax\n\t" /* += 8 */
+ "addl %%eax, %%edx\n\t"
+ "call *(%%edx)\n\t" /* safe to not cleanup esp */
+ "movl %%eax, %0"
+ : "=g" (result) /* %0 */
+ : "g" (that), /* %1 */
+ "g" (index), /* %2 */
+ "g" (paramCount), /* %3 */
+ "g" (params), /* %4 */
+ "g" (fn_count), /* %5 */
+ "g" (fn_copy) /* %6 */
+ : "ax", "cx", "dx", "memory"
+ );
+
+ return result;
+}
+
+#endif
+/***************************************************************************/
+
+int main()
+{
+ nsXPCVariant params1[2] = {1,2};
+ nsXPCVariant params2[2] = {2,4};
+ nsXPCVariant params3[2] = {3,6};
+
+ foo* a = new bar();
+
+// printf("calling via C++...\n");
+// docall(a, 12, 24);
+
+ printf("calling via ASM...\n");
+ DoInvoke(a, 1, 2, params1);
+ DoInvoke(a, 2, 2, params2);
+ DoInvoke(a, 3, 2, params3);
+
+ return 0;
+}
diff --git a/xpcom/reflect/xptcall/md/test/mk_invoke.bat b/xpcom/reflect/xptcall/md/test/mk_invoke.bat
new file mode 100755
index 000000000..10a9be51d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/mk_invoke.bat
@@ -0,0 +1,9 @@
+@echo off
+@echo deleing old output
+if exist invoke_test.obj del invoke_test.obj > NUL
+if exist invoke_test.ilk del invoke_test.ilk > NUL
+if exist *.pdb del *.pdb > NUL
+if exist invoke_test.exe del invoke_test.exe > NUL
+
+@echo building...
+cl /nologo -Zi -DWIN32 invoke_test.cpp \ No newline at end of file
diff --git a/xpcom/reflect/xptcall/md/test/mk_stub.bat b/xpcom/reflect/xptcall/md/test/mk_stub.bat
new file mode 100755
index 000000000..f9af17aff
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/mk_stub.bat
@@ -0,0 +1,9 @@
+@echo off
+@echo deleing old output
+if exist stub_test.obj del stub_test.obj > NUL
+if exist stub_test.ilk del stub_test.ilk > NUL
+if exist *.pdb del *.pdb > NUL
+if exist stub_test.exe del stub_test.exe > NUL
+
+@echo building...
+cl /nologo -Zi -DWIN32 stub_test.cpp \ No newline at end of file
diff --git a/xpcom/reflect/xptcall/md/test/moz.build b/xpcom/reflect/xptcall/md/test/moz.build
new file mode 100644
index 000000000..2261c3ce6
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SimplePrograms([
+ 'stub_test',
+])
diff --git a/xpcom/reflect/xptcall/md/test/stub_test.cpp b/xpcom/reflect/xptcall/md/test/stub_test.cpp
new file mode 100644
index 000000000..6c9559c65
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/test/stub_test.cpp
@@ -0,0 +1,213 @@
+/* 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 <stdio.h>
+
+typedef unsigned nsresult;
+typedef unsigned uint32_t;
+typedef unsigned nsXPCVariant;
+
+
+#if defined(WIN32)
+#define NS_IMETHOD virtual nsresult __stdcall
+#define NS_IMETHODIMP nsresult __stdcall
+#else
+#define NS_IMETHOD virtual nsresult
+#define NS_IMETHODIMP nsresult
+#endif
+
+
+class base{
+public:
+ NS_IMETHOD ignored() = 0;
+};
+
+class foo : public base {
+public:
+ NS_IMETHOD callme1(int i, int j) = 0;
+ NS_IMETHOD callme2(int i, int j) = 0;
+ NS_IMETHOD callme3(int i, int j) = 0;
+};
+
+class bar : public foo{
+public:
+ NS_IMETHOD ignored();
+ NS_IMETHOD callme1(int i, int j);
+ NS_IMETHOD callme2(int i, int j);
+ NS_IMETHOD callme3(int i, int j);
+};
+
+class baz : public base {
+public:
+ NS_IMETHOD ignored();
+ NS_IMETHOD callme1();
+ NS_IMETHOD callme2();
+ NS_IMETHOD callme3();
+ void setfoo(foo* f) {other = f;}
+
+ foo* other;
+};
+NS_IMETHODIMP baz::ignored(){return 0;}
+
+NS_IMETHODIMP bar::ignored(){return 0;}
+
+NS_IMETHODIMP bar::callme1(int i, int j)
+{
+ printf("called bar::callme1 with: %d %d\n", i, j);
+ return 15;
+}
+
+NS_IMETHODIMP bar::callme2(int i, int j)
+{
+ printf("called bar::callme2 with: %d %d\n", i, j);
+ return 25;
+}
+
+NS_IMETHODIMP bar::callme3(int i, int j)
+{
+ printf("called bar::callme3 with: %d %d\n", i, j);
+ return 35;
+}
+
+void docall(foo* f, int i, int j){
+ f->callme1(i, j);
+}
+
+/***************************************************************************/
+#if defined(WIN32)
+
+static int __stdcall
+PrepareAndDispatch(baz* self, uint32_t methodIndex,
+ uint32_t* args, uint32_t* stackBytesToPop)
+{
+ fprintf(stdout, "PrepareAndDispatch (%p, %d, %p)\n",
+ (void*)self, methodIndex, (void*)args);
+ foo* a = self->other;
+ int p1 = (int) *args;
+ int p2 = (int) *(args+1);
+ int out = 0;
+ switch(methodIndex)
+ {
+ case 1: out = a->callme1(p1, p2); break;
+ case 2: out = a->callme2(p1, p2); break;
+ case 3: out = a->callme3(p1, p2); break;
+ }
+ *stackBytesToPop = 2*4;
+ return out;
+}
+
+#ifndef __GNUC__
+static __declspec(naked) void SharedStub(void)
+{
+ __asm {
+ push ebp // set up simple stack frame
+ mov ebp, esp // stack has: ebp/vtbl_index/retaddr/this/args
+ push ecx // make room for a ptr
+ lea eax, [ebp-4] // pointer to stackBytesToPop
+ push eax
+ lea ecx, [ebp+16] // pointer to args
+ push ecx
+ mov edx, [ebp+4] // vtbl_index
+ push edx
+ mov eax, [ebp+12] // this
+ push eax
+ call PrepareAndDispatch
+ mov edx, [ebp+8] // return address
+ mov ecx, [ebp-4] // stackBytesToPop
+ add ecx, 12 // for this, the index, and ret address
+ mov esp, ebp
+ pop ebp
+ add esp, ecx // fix up stack pointer
+ jmp edx // simulate __stdcall return
+ }
+}
+
+// these macros get expanded (many times) in the file #included below
+#define STUB_ENTRY(n) \
+__declspec(naked) nsresult __stdcall baz::callme##n() \
+{ __asm push n __asm jmp SharedStub }
+
+#else /* __GNUC__ */
+
+#define STUB_ENTRY(n) \
+nsresult __stdcall baz::callme##n() \
+{ \
+ uint32_t *args, stackBytesToPop; \
+ int result = 0; \
+ baz *obj; \
+ __asm__ __volatile__ ( \
+ "leal 0x0c(%%ebp), %0\n\t" /* args */ \
+ "movl 0x08(%%ebp), %1\n\t" /* this */ \
+ : "=r" (args), \
+ "=r" (obj)); \
+ result = PrepareAndDispatch(obj, n, args,&stackBytesToPop); \
+ fprintf(stdout, "stub returning: %d\n", result); \
+ fprintf(stdout, "bytes to pop: %d\n", stackBytesToPop); \
+ return result; \
+}
+
+#endif /* ! __GNUC__ */
+
+#else
+/***************************************************************************/
+// just Linux_x86 now. Add other later...
+
+static int
+PrepareAndDispatch(baz* self, uint32_t methodIndex, uint32_t* args)
+{
+ foo* a = self->other;
+ int p1 = (int) *args;
+ int p2 = (int) *(args+1);
+ switch(methodIndex)
+ {
+ case 1: a->callme1(p1, p2); break;
+ case 2: a->callme2(p1, p2); break;
+ case 3: a->callme3(p1, p2); break;
+ }
+ return 1;
+}
+
+#define STUB_ENTRY(n) \
+nsresult baz::callme##n() \
+{ \
+ void* method = PrepareAndDispatch; \
+ nsresult result; \
+ __asm__ __volatile__( \
+ "leal 0x0c(%%ebp), %%ecx\n\t" /* args */ \
+ "pushl %%ecx\n\t" \
+ "pushl $"#n"\n\t" /* method index */ \
+ "movl 0x08(%%ebp), %%ecx\n\t" /* this */ \
+ "pushl %%ecx\n\t" \
+ "call *%%edx" /* PrepareAndDispatch */ \
+ : "=a" (result) /* %0 */ \
+ : "d" (method) /* %1 */ \
+ : "memory" ); \
+ return result; \
+}
+
+#endif
+/***************************************************************************/
+
+STUB_ENTRY(1)
+STUB_ENTRY(2)
+STUB_ENTRY(3)
+
+int main()
+{
+ foo* a = new bar();
+ baz* b = new baz();
+
+ /* here we make the global 'check for alloc failure' checker happy */
+ if(!a || !b)
+ return 1;
+
+ foo* c = (foo*)b;
+
+ b->setfoo(a);
+ c->callme1(1,2);
+ c->callme2(2,4);
+ c->callme3(3,6);
+
+ return 0;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/Makefile.in b/xpcom/reflect/xptcall/md/unix/Makefile.in
new file mode 100644
index 000000000..e4cdc389b
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/Makefile.in
@@ -0,0 +1,79 @@
+#
+# 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/.
+
+######################################################################
+# HPPA
+######################################################################
+#
+# HP-UX/PA32
+#
+# for gas and gcc, check comment in xptcinvoke_asm_pa32.s
+ifeq ($(OS_ARCH),HP-UX)
+ifneq ($(CC),gcc)
+# #18875 Building the CPP's (CXX) optimized causes a crash
+CXXFLAGS := $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(CXXFLAGS))
+endif
+endif
+
+#
+# Linux/HPPA/gcc
+#
+ifeq ($(OS_ARCH),Linux)
+ifneq (,$(filter hppa hppa2.0 hppa1.1,$(OS_TEST)))
+ifndef GNU_CXX
+else
+# #434190 optimized builds crash
+CXXFLAGS := $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(CXXFLAGS))
+endif
+endif
+endif
+
+
+######################################################################
+# PowerPC
+######################################################################
+#
+# AIX/PPC
+#
+ifeq ($(OS_ARCH),AIX)
+# #24617 Building the CPP's (CXX) optimized causes a crash
+CXXFLAGS := $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(CXXFLAGS))
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+ifeq ($(OS_ARCH),Linux)
+ifneq (,$(findstring mips, $(OS_TEST)))
+xptcstubs_asm_mips.o: $(DIST)/include/xptcstubsdef.inc
+endif
+endif
+
+ifeq ($(OS_ARCH),Darwin)
+xptcstubs_asm_ppc_darwin.s: xptcstubs_asm_ppc_darwin.s.m4 $(DIST)/include/xptcstubsdef.inc Makefile
+ gm4 $(INCLUDES) $< > $@
+endif
+
+ifeq ($(OS_ARCH),AIX)
+ifdef HAVE_64BIT_BUILD
+xptcstubs_asm_ppc_aix64.s: xptcstubs_asm_ppc_aix64.s.m4 $(DIST)/include/xptcstubsdef.inc Makefile
+ m4 -DAIX_OBJMODEL=$(AIX_OBJMODEL) $(INCLUDES) -I. $< > $@
+else
+xptcstubs_asm_ppc_aix.s: xptcstubs_asm_ppc_aix.s.m4 $(DIST)/include/xptcstubsdef.inc Makefile
+ m4 -DAIX_OBJMODEL=$(AIX_OBJMODEL) $(INCLUDES) -I. $< > $@
+endif
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ifeq (86,$(findstring 86,$(OS_TEST)))
+ifndef GNU_CC
+xptcstubsdef_asm.solx86: $(DIST)/include/xptcstubsdef.inc
+ sed \
+ -e 's/^\(STUB_ENTRY\)(\([0-9]\))/\11\(\2\)/' \
+ -e 's/^\(STUB_ENTRY\)(\([0-9][0-9]\))/\12\(\2\)/' \
+ -e 's/^\(STUB_ENTRY\)(\([0-9][0-9][0-9]\))/\13\(\2\)/' \
+ $(DIST)/include/xptcstubsdef.inc > $@
+endif
+endif
+endif
diff --git a/xpcom/reflect/xptcall/md/unix/moz.build b/xpcom/reflect/xptcall/md/unix/moz.build
new file mode 100644
index 000000000..1d182bbd6
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/moz.build
@@ -0,0 +1,327 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['OS_ARCH'] == 'Darwin':
+ SOURCES += [
+ 'xptcinvoke_darwin.cpp',
+ 'xptcstubs_darwin.cpp',
+ ]
+ if CONFIG['OS_TEST'] == 'powerpc':
+ SOURCES += [
+ '!xptcstubs_asm_ppc_darwin.s',
+ 'xptcinvoke_asm_ppc_rhapsody.s',
+ ]
+ if '86' in CONFIG['OS_TEST'] and CONFIG['OS_TEST'] != 'x86_64':
+ DEFINES['MOZ_NEED_LEADING_UNDERSCORE'] = True
+
+if CONFIG['OS_ARCH'] == 'GNU':
+ if CONFIG['CPU_ARCH'] == 'x86':
+ SOURCES += [
+ 'xptcinvoke_gcc_x86_unix.cpp',
+ 'xptcstubs_gcc_x86_unix.cpp'
+ ]
+
+if CONFIG['OS_ARCH'] in ('Linux', 'Bitrig', 'DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD') or \
+ CONFIG['OS_ARCH'].startswith('GNU_'):
+ if CONFIG['OS_TEST'] == 'x86_64':
+ SOURCES += [
+ 'xptcinvoke_x86_64_unix.cpp',
+ 'xptcstubs_x86_64_linux.cpp',
+ ]
+ elif '86' in CONFIG['OS_TEST']:
+ SOURCES += [
+ 'xptcinvoke_gcc_x86_unix.cpp',
+ 'xptcstubs_gcc_x86_unix.cpp'
+ ]
+
+if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD'):
+ if 'ia64' in CONFIG['OS_TEST']:
+ SOURCES += [
+ 'xptcinvoke_asm_ipf64.s',
+ 'xptcinvoke_ipf64.cpp',
+ 'xptcstubs_asm_ipf64.s',
+ 'xptcstubs_ipf64.cpp'
+ ]
+
+if CONFIG['OS_ARCH'] == 'SunOS' and '86' in CONFIG['OS_TEST']:
+ GENERATED_FILES = [
+ 'xptcstubsdef_asm.solx86',
+ ]
+ if CONFIG['OS_TEST'] == 'x86_64':
+ if CONFIG['GNU_CC']:
+ SOURCES += [
+ 'xptcinvoke_x86_64_unix.cpp',
+ 'xptcstubs_x86_64_linux.cpp'
+ ]
+ else:
+ ASFLAGS += ['-xarch=amd64']
+ SOURCES += [
+ 'xptcinvoke_x86_64_solaris.cpp',
+ 'xptcstubs_asm_x86_64_solaris_SUNW.s',
+ 'xptcstubs_x86_64_solaris.cpp',
+ ]
+ else:
+ if CONFIG['GNU_CC']:
+ SOURCES += [
+ 'xptcinvoke_gcc_x86_unix.cpp',
+ 'xptcstubs_gcc_x86_unix.cpp'
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_asm_x86_solaris_SUNW.s',
+ 'xptcinvoke_x86_solaris.cpp',
+ 'xptcstubs_asm_x86_solaris_SUNW.s',
+ 'xptcstubs_x86_solaris.cpp'
+ ]
+
+if CONFIG['OS_TEST'] == 'alpha':
+ if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD', 'NetBSD'):
+ SOURCES += [
+ 'xptcinvoke_linux_alpha.cpp',
+ 'xptcstubs_linux_alpha.cpp',
+ ]
+ elif CONFIG['OS_ARCH'] == 'OpenBSD':
+ SOURCES += [
+ 'xptcinvoke_alpha_openbsd.cpp',
+ 'xptcstubs_alpha_openbsd.cpp',
+ ]
+
+if CONFIG['CPU_ARCH'] == 'arm' or CONFIG['OS_TEST'] == 'sa110':
+ if CONFIG['OS_ARCH'] == 'Linux':
+ SOURCES += [
+ 'xptcinvoke_arm.cpp',
+ 'xptcstubs_arm.cpp'
+ ]
+ CXXFLAGS += ['-O2']
+ elif CONFIG['OS_ARCH'] == 'NetBSD':
+ SOURCES += [
+ 'xptcinvoke_arm_netbsd.cpp',
+ 'xptcstubs_arm_netbsd.cpp',
+ ]
+
+if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['OS_ARCH'] in ('Bitrig', 'OpenBSD'):
+ SOURCES += [
+ 'xptcinvoke_arm_openbsd.cpp',
+ 'xptcstubs_arm_openbsd.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'HP-UX':
+ if CONFIG['CC'] != 'gcc':
+ if CONFIG['OS_TEST'] == 'ia64':
+ SOURCES += [
+ 'xptcinvoke_asm_ipf32.s',
+ 'xptcinvoke_ipf32.cpp',
+ 'xptcstubs_asm_ipf32.s',
+ 'xptcstubs_ipf32.cpp',
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_asm_pa32.s',
+ 'xptcinvoke_pa32.cpp',
+ 'xptcstubs_asm_pa32.s',
+ 'xptcstubs_pa32.cpp'
+ ]
+
+if CONFIG['OS_ARCH'] == 'Linux':
+ if CONFIG['OS_TEST'] in ('hppa', 'hppa2.0', 'hppa1.1'):
+ if CONFIG['GNU_CXX']:
+ SOURCES += [
+ 'xptcinvoke_asm_parisc_linux.s',
+ 'xptcinvoke_pa32.cpp',
+ 'xptcstubs_asm_parisc_linux.s',
+ 'xptcstubs_pa32.cpp',
+ ]
+ elif CONFIG['COMPILE_ENVIRONMENT']:
+ error('Unknown C++ compiler, xptcall assembly will probably be incorrect.')
+
+if CONFIG['OS_ARCH'] == 'NetBSD':
+ if CONFIG['OS_TEST'] in ('amiga', 'atari', 'hp300', 'mac68k', 'mvme68k',
+ 'next68k', 'sun3', 'sun3x', 'x68k'):
+ SOURCES += [
+ 'xptcinvoke_netbsd_m68k.cpp',
+ 'xptcstubs_netbsd_m68k.cpp'
+ ]
+
+if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD', 'NetBSD', 'OpenBSD'):
+ if CONFIG['OS_TEST'] == 'aarch64':
+ SOURCES += [
+ 'xptcinvoke_aarch64.cpp',
+ 'xptcinvoke_asm_aarch64.s',
+ 'xptcstubs_aarch64.cpp',
+ 'xptcstubs_asm_aarch64.s',
+ ]
+ if CONFIG['OS_TEST'] == 'm68k':
+ SOURCES += [
+ 'xptcinvoke_linux_m68k.cpp',
+ 'xptcstubs_linux_m68k.cpp',
+ ]
+ if 'mips' in CONFIG['OS_TEST']:
+ if 'mips64' in CONFIG['OS_TEST']:
+ SOURCES += [
+ 'xptcinvoke_asm_mips64.S',
+ 'xptcinvoke_mips64.cpp',
+ 'xptcstubs_asm_mips64.S',
+ 'xptcstubs_mips64.cpp',
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_asm_mips.S',
+ 'xptcinvoke_mips.cpp',
+ 'xptcstubs_asm_mips.S',
+ 'xptcstubs_mips.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'AIX':
+ if CONFIG['HAVE_64BIT_BUILD']:
+ SOURCES += [
+ '!xptcstubs_asm_ppc_aix64.s',
+ 'xptcinvoke_asm_ppc_aix64.s',
+ 'xptcinvoke_ppc_aix64.cpp',
+ 'xptcstubs_ppc_aix64.cpp',
+ ]
+ else:
+ SOURCES += [
+ '!xptcstubs_asm_ppc_aix.s',
+ 'xptcinvoke_ppc_aix.cpp',
+ 'xptcstubs_ppc_aix.cpp',
+ ]
+ if CONFIG['AIX_OBJMODEL'] == 'ibm':
+ SOURCES += [
+ 'xptcinvoke_asm_ppc_ibmobj_aix.s',
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_asm_ppc_aix.s',
+ ]
+
+if CONFIG['OS_TEST'] == 'powerpc':
+ if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD'):
+ SOURCES += [
+ 'xptcinvoke_asm_ppc_linux.S',
+ 'xptcinvoke_ppc_linux.cpp',
+ 'xptcstubs_asm_ppc_linux.S',
+ 'xptcstubs_ppc_linux.cpp',
+ ]
+
+if CONFIG['OS_TEST'] in ('powerpc64', 'powerpc64le'):
+ if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD'):
+ SOURCES += [
+ 'xptcinvoke_asm_ppc64_linux.S',
+ 'xptcinvoke_ppc64_linux.cpp',
+ 'xptcstubs_asm_ppc64_linux.S',
+ 'xptcstubs_ppc64_linux.cpp',
+ ]
+
+if CONFIG['OS_TEST'] in ('macppc', 'bebox', 'ofppc', 'prep', 'amigappc'):
+ if CONFIG['OS_ARCH'] == 'NetBSD':
+ SOURCES += [
+ 'xptcinvoke_asm_ppc_netbsd.s',
+ 'xptcinvoke_ppc_netbsd.cpp',
+ 'xptcstubs_asm_ppc_netbsd.s',
+ 'xptcstubs_ppc_netbsd.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'OpenBSD' and CONFIG['OS_TEST'] == 'powerpc':
+ SOURCES += [
+ 'xptcinvoke_asm_ppc_openbsd.S',
+ 'xptcinvoke_ppc_openbsd.cpp',
+ 'xptcstubs_asm_ppc_openbsd.S',
+ 'xptcstubs_ppc_openbsd.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'Linux' and 'sparc' in CONFIG['OS_TEST']:
+ SOURCES += [
+ 'xptcinvoke_asm_sparc_linux_GCC3.s',
+ 'xptcinvoke_sparc_solaris.cpp',
+ 'xptcstubs_asm_sparc_solaris.s',
+ 'xptcstubs_sparc_solaris.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'NetBSD' and CONFIG['OS_TEST'] == 'sparc':
+ SOURCES += [
+ 'xptcinvoke_asm_sparc_netbsd.s',
+ 'xptcinvoke_sparc_netbsd.cpp',
+ 'xptcstubs_asm_sparc_netbsd.s',
+ 'xptcstubs_sparc_netbsd.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'OpenBSD' and CONFIG['OS_TEST'] == 'sparc':
+ SOURCES += [
+ 'xptcinvoke_asm_sparc_openbsd.s',
+ 'xptcinvoke_sparc_openbsd.cpp',
+ 'xptcstubs_asm_sparc_openbsd.s',
+ 'xptcstubs_sparc_openbsd.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] in ('OpenBSD', 'FreeBSD') and CONFIG['OS_TEST'] == 'sparc64':
+ SOURCES += [
+ 'xptcinvoke_asm_sparc64_openbsd.s',
+ 'xptcinvoke_sparc64_openbsd.cpp',
+ 'xptcstubs_asm_sparc64_openbsd.s',
+ 'xptcstubs_sparc64_openbsd.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'SunOS' and '86' not in CONFIG['OS_TEST']:
+ if CONFIG['HAVE_64BIT_BUILD']:
+ ASFLAGS += ['-xarch=v9']
+ SOURCES += [
+ 'xptcinvoke_sparcv9_solaris.cpp',
+ 'xptcstubs_sparcv9_solaris.cpp',
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_sparc_solaris.cpp',
+ 'xptcstubs_sparc_solaris.cpp',
+ ]
+ if CONFIG['GNU_CC']:
+ SOURCES += [
+ 'xptcinvoke_asm_sparc_solaris_GCC3.s',
+ 'xptcstubs_asm_sparc_solaris.s',
+ ]
+ else:
+ if CONFIG['HAVE_64BIT_BUILD']:
+ SOURCES += [
+ 'xptcinvoke_asm_sparcv9_solaris_SUNW.s',
+ 'xptcstubs_asm_sparcv9_solaris.s',
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_asm_sparc_solaris_SUNW.s',
+ 'xptcstubs_asm_sparc_solaris.s',
+ ]
+
+if CONFIG['OS_ARCH'] == 'Linux':
+ if CONFIG['OS_TEST'] == 's390':
+ SOURCES += [
+ 'xptcinvoke_linux_s390.cpp',
+ 'xptcstubs_linux_s390.cpp',
+ ]
+ CXXFLAGS += [
+ '-fno-strict-aliasing',
+ '-fno-inline',
+ '-fomit-frame-pointer',
+ '-mbackchain',
+ ]
+ elif CONFIG['OS_TEST'] == 's390x':
+ SOURCES += [
+ 'xptcinvoke_linux_s390x.cpp',
+ 'xptcstubs_linux_s390x.cpp',
+ ]
+ CXXFLAGS += [
+ '-fno-strict-aliasing',
+ '-fno-inline',
+ '-fomit-frame-pointer',
+ '-mbackchain',
+ ]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../..',
+ '/xpcom/reflect/xptinfo',
+]
+
+NO_PGO = True
diff --git a/xpcom/reflect/xptcall/md/unix/vtable_layout_x86.cpp b/xpcom/reflect/xptcall/md/unix/vtable_layout_x86.cpp
new file mode 100644
index 000000000..0cb327a3d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/vtable_layout_x86.cpp
@@ -0,0 +1,66 @@
+/* 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/. */
+
+/* this code contributed by Bert Driehuis <bert_driehuis@nl.compuware.com> */
+
+#include <stdio.h>
+
+// Try to determine the vtable layout generated by G++
+// Produces the offset at which the first vtable entry can be
+// found, and the factor to apply for subsequent entries on stdout.
+// Example output:
+// #define GCC_VTABLE_START 0xc
+// #define GCC_VTABLE_FACTOR 0x8
+
+class test {
+public:
+ virtual int t1(void);
+ virtual int t2(void);
+ int x;
+};
+
+test::test() { this->x = 0x12121212; };
+
+int test::t1(void) { return 1; }
+int test::t2(void) { return 2; }
+
+void die(char *x) {
+ fprintf(stderr, "%s\n", x);
+ exit(1);
+}
+
+int
+main()
+{
+ int i;
+ test *t = new test();
+ int *tp = (int *) t;
+ int off1 = -1;
+ int off2 = -1;
+ int factor;
+ int factorshift;
+
+ if (*tp++ != 0x12121212)
+ die("Integer element test::x not found!");
+ tp = (int *) *tp;
+ for (i = 0; i < 10; i++) {
+ if (tp[i] == (int) t->t1)
+ off1 = i;
+ if (tp[i] == (int) t->t2)
+ off2 = i;
+ }
+ if (off1 == -1 || off2 == -1)
+ die("Could not determine offset into vtable!");
+ factor = (off2 - off1) * 4;
+ factorshift = -1;
+ while (factor) {
+ factorshift++;
+ factor >>= 1;
+ }
+ printf("/* Automatically generated by vtable_layout_x86.cpp */\n");
+ printf("#define GCC_VTABLE_START\t0x%x\n", off1 * 4);
+ printf("#define GCC_VTABLE_FACTOR\t0x%x\n", (off2 - off1) * 4);
+ printf("#define GCC_VTABLE_SHIFT\t0x%x\n", factorshift);
+ exit(0);
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptc_gcc_x86_unix.h b/xpcom/reflect/xptcall/md/unix/xptc_gcc_x86_unix.h
new file mode 100644
index 000000000..733646c49
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptc_gcc_x86_unix.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Special include file for xptc*_gcc_x86_unix.cpp */
+
+//
+// this may improve the static function calls, but may not.
+//
+
+#ifdef MOZ_NEED_LEADING_UNDERSCORE
+#define SYMBOL_UNDERSCORE "_"
+#else
+#define SYMBOL_UNDERSCORE
+#endif
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp
new file mode 100644
index 000000000..e5807dbcd
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#if !defined(__aarch64__)
+#error "This code is for Linux AArch64 only."
+#endif
+
+
+/* "Procedure Call Standard for the ARM 64-bit Architecture" document, sections
+ * "5.4 Parameter Passing" and "6.1.2 Procedure Calling" contain all the
+ * needed information.
+ *
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf
+ */
+
+#ifndef __AARCH64EL__
+#error "Only little endian compatibility was tested"
+#endif
+
+/*
+ * Allocation of integer function arguments initially to registers r1-r7
+ * and then to stack. Handling of 'that' argument which goes to register r0
+ * is handled separately and does not belong here.
+ *
+ * 'ireg_args' - pointer to the current position in the buffer,
+ * corresponding to the register arguments
+ * 'stack_args' - pointer to the current position in the buffer,
+ * corresponding to the arguments on stack
+ * 'end' - pointer to the end of the registers argument
+ * buffer.
+ */
+static inline void alloc_word(uint64_t* &ireg_args,
+ uint64_t* &stack_args,
+ uint64_t* end,
+ uint64_t data)
+{
+ if (ireg_args < end) {
+ *ireg_args = data;
+ ireg_args++;
+ } else {
+ *stack_args = data;
+ stack_args++;
+ }
+}
+
+static inline void alloc_double(double* &freg_args,
+ uint64_t* &stack_args,
+ double* end,
+ double data)
+{
+ if (freg_args < end) {
+ *freg_args = data;
+ freg_args++;
+ } else {
+ memcpy(stack_args, &data, sizeof(data));
+ stack_args++;
+ }
+}
+
+static inline void alloc_float(double* &freg_args,
+ uint64_t* &stack_args,
+ double* end,
+ float data)
+{
+ if (freg_args < end) {
+ memcpy(freg_args, &data, sizeof(data));
+ freg_args++;
+ } else {
+ memcpy(stack_args, &data, sizeof(data));
+ stack_args++;
+ }
+}
+
+
+extern "C" void
+invoke_copy_to_stack(uint64_t* stk, uint64_t *end,
+ uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint64_t *ireg_args = stk;
+ uint64_t *ireg_end = ireg_args + 8;
+ double *freg_args = (double *)ireg_end;
+ double *freg_end = freg_args + 8;
+ uint64_t *stack_args = (uint64_t *)freg_end;
+
+ // leave room for 'that' argument in x0
+ ++ireg_args;
+
+ for (uint32_t i = 0; i < paramCount; i++, s++) {
+ uint64_t word;
+
+ if (s->IsPtrData()) {
+ word = (uint64_t)s->ptr;
+ } else {
+ // According to the ABI, integral types that are smaller than 8
+ // bytes are to be passed in 8-byte registers or 8-byte stack
+ // slots.
+ switch (s->type) {
+ case nsXPTType::T_FLOAT:
+ alloc_float(freg_args, stack_args, freg_end, s->val.f);
+ continue;
+ case nsXPTType::T_DOUBLE:
+ alloc_double(freg_args, stack_args, freg_end, s->val.d);
+ continue;
+ case nsXPTType::T_I8: word = s->val.i8; break;
+ case nsXPTType::T_I16: word = s->val.i16; break;
+ case nsXPTType::T_I32: word = s->val.i32; break;
+ case nsXPTType::T_I64: word = s->val.i64; break;
+ case nsXPTType::T_U8: word = s->val.u8; break;
+ case nsXPTType::T_U16: word = s->val.u16; break;
+ case nsXPTType::T_U32: word = s->val.u32; break;
+ case nsXPTType::T_U64: word = s->val.u64; break;
+ case nsXPTType::T_BOOL: word = s->val.b; break;
+ case nsXPTType::T_CHAR: word = s->val.c; break;
+ case nsXPTType::T_WCHAR: word = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ word = reinterpret_cast<uint64_t>(s->val.p);
+ break;
+ }
+ }
+
+ alloc_word(ireg_args, stack_args, ireg_end, word);
+ }
+}
+
+extern "C" nsresult _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ return _NS_InvokeByIndex(that, methodIndex, paramCount, params);
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_alpha_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_alpha_openbsd.cpp
new file mode 100644
index 000000000..dc111e435
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_alpha_openbsd.cpp
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+/* Prototype specifies unmangled function name and disables unused warning */
+static void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+__asm__("invoke_copy_to_stack") __attribute__((used));
+
+static void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ const uint8_t NUM_ARG_REGS = 6-1; // -1 for "this" pointer
+
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *d = (uint64_t)s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *d = (uint64_t)s->val.i8; break;
+ case nsXPTType::T_I16 : *d = (uint64_t)s->val.i16; break;
+ case nsXPTType::T_I32 : *d = (uint64_t)s->val.i32; break;
+ case nsXPTType::T_I64 : *d = (uint64_t)s->val.i64; break;
+ case nsXPTType::T_U8 : *d = (uint64_t)s->val.u8; break;
+ case nsXPTType::T_U16 : *d = (uint64_t)s->val.u16; break;
+ case nsXPTType::T_U32 : *d = (uint64_t)s->val.u32; break;
+ case nsXPTType::T_U64 : *d = (uint64_t)s->val.u64; break;
+ case nsXPTType::T_FLOAT :
+ if(i < NUM_ARG_REGS)
+ {
+ // convert floats to doubles if they are to be passed
+ // via registers so we can just deal with doubles later
+ union { uint64_t u64; double d; } t;
+ t.d = (double)s->val.f;
+ *d = t.u64;
+ }
+ else
+ // otherwise copy to stack normally
+ *d = (uint64_t)s->val.u32;
+ break;
+ case nsXPTType::T_DOUBLE : *d = (uint64_t)s->val.u64; break;
+ case nsXPTType::T_BOOL : *d = (uint64_t)s->val.b; break;
+ case nsXPTType::T_CHAR : *d = (uint64_t)s->val.c; break;
+ case nsXPTType::T_WCHAR : *d = (uint64_t)s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *d = (uint64_t)s->val.p;
+ break;
+ }
+ }
+}
+
+/*
+ * EXPORT_XPCOM_API(nsresult)
+ * NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ * uint32_t paramCount, nsXPTCVariant* params)
+ */
+__asm__(
+ "#### NS_InvokeByIndex ####\n"
+".text\n\t"
+ ".align 5\n\t"
+ ".globl NS_InvokeByIndex\n\t"
+ ".ent NS_InvokeByIndex\n"
+"NS_InvokeByIndex:\n\t"
+ ".frame $15,32,$26,0\n\t"
+ ".mask 0x4008000,-32\n\t"
+ "ldgp $29,0($27)\n"
+"$NS_InvokeByIndex..ng:\n\t"
+ "subq $30,32,$30\n\t"
+ "stq $26,0($30)\n\t"
+ "stq $15,8($30)\n\t"
+ "bis $30,$30,$15\n\t"
+ ".prologue 1\n\t"
+
+ /*
+ * Allocate enough stack space to hold the greater of 6 or "paramCount"+1
+ * parameters. (+1 for "this" pointer) Room for at least 6 parameters
+ * is required for storage of those passed via registers.
+ */
+
+ "bis $31,5,$2\n\t" /* count = MAX(5, "paramCount") */
+ "cmplt $2,$18,$1\n\t"
+ "cmovne $1,$18,$2\n\t"
+ "s8addq $2,16,$1\n\t" /* room for count+1 params (8 bytes each) */
+ "bic $1,15,$1\n\t" /* stack space is rounded up to 0 % 16 */
+ "subq $30,$1,$30\n\t"
+
+ "stq $16,0($30)\n\t" /* save "that" (as "this" pointer) */
+ "stq $17,16($15)\n\t" /* save "methodIndex" */
+
+ "addq $30,8,$16\n\t" /* pass stack pointer */
+ "bis $18,$18,$17\n\t" /* pass "paramCount" */
+ "bis $19,$19,$18\n\t" /* pass "params" */
+ "bsr $26,$invoke_copy_to_stack..ng\n\t" /* call invoke_copy_to_stack */
+
+ /*
+ * Copy the first 6 parameters to registers and remove from stack frame.
+ * Both the integer and floating point registers are set for each parameter
+ * except the first which is the "this" pointer. (integer only)
+ * The floating point registers are all set as doubles since the
+ * invoke_copy_to_stack function should have converted the floats.
+ */
+ "ldq $16,0($30)\n\t" /* integer registers */
+ "ldq $17,8($30)\n\t"
+ "ldq $18,16($30)\n\t"
+ "ldq $19,24($30)\n\t"
+ "ldq $20,32($30)\n\t"
+ "ldq $21,40($30)\n\t"
+ "ldt $f17,8($30)\n\t" /* floating point registers */
+ "ldt $f18,16($30)\n\t"
+ "ldt $f19,24($30)\n\t"
+ "ldt $f20,32($30)\n\t"
+ "ldt $f21,40($30)\n\t"
+
+ "addq $30,48,$30\n\t" /* remove params from stack */
+
+ /*
+ * Call the virtual function with the constructed stack frame.
+ */
+ "bis $16,$16,$1\n\t" /* load "this" */
+ "ldq $2,16($15)\n\t" /* load "methodIndex" */
+ "ldq $1,0($1)\n\t" /* load vtable */
+ "s8addq $2,$31,$2\n\t" /* vtable index = "methodIndex" * 8 */
+ "addq $1,$2,$1\n\t"
+ "ldq $27,0($1)\n\t" /* load address of function */
+ "jsr $26,($27),0\n\t" /* call virtual function */
+ "ldgp $29,0($26)\n\t"
+
+ "bis $15,$15,$30\n\t"
+ "ldq $26,0($30)\n\t"
+ "ldq $15,8($30)\n\t"
+ "addq $30,32,$30\n\t"
+ "ret $31,($26),1\n\t"
+ ".end NS_InvokeByIndex"
+ );
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp
new file mode 100644
index 000000000..4cd5eb47d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#include "mozilla/Compiler.h"
+
+#if !defined(__arm__) && !(defined(LINUX) || defined(ANDROID) || defined(XP_IOS))
+#error "This code is for Linux/iOS ARM only. Check that it works on your system, too.\nBeware that this code is highly compiler dependent."
+#endif
+
+#if MOZ_IS_GCC
+#if defined(__ARM_EABI__) && !defined(__ARM_PCS_VFP) && !defined(__ARM_PCS)
+#error "Can't identify floating point calling conventions.\nPlease ensure that your toolchain defines __ARM_PCS or __ARM_PCS_VFP."
+#endif
+#endif
+
+#ifndef __ARM_PCS_VFP
+
+/* This function copies a 64-bits word from dw to the given pointer in
+ * a buffer delimited by start and end, possibly wrapping around the
+ * buffer boundaries, and/or properly aligning the data at 64-bits word
+ * boundaries (for EABI).
+ * start and end are both assumed to be 64-bits aligned.
+ * Returns a pointer to the second 32-bits word copied (to accomodate
+ * the invoke_copy_to_stack loop).
+ */
+static uint32_t *
+copy_double_word(uint32_t *start, uint32_t *current, uint32_t *end, uint64_t *dw)
+{
+#ifdef __ARM_EABI__
+ /* Aligning the pointer for EABI */
+ current = (uint32_t *)(((uint32_t)current + 7) & ~7);
+ /* Wrap when reaching the end of the buffer */
+ if (current == end) current = start;
+#else
+ /* On non-EABI, 64-bits values are not aligned and when we reach the end
+ * of the buffer, we need to write half of the data at the end, and the
+ * other half at the beginning. */
+ if (current == end - 1) {
+ *current = ((uint32_t*)dw)[0];
+ *start = ((uint32_t*)dw)[1];
+ return start;
+ }
+#endif
+
+ *((uint64_t*) current) = *dw;
+ return current + 1;
+}
+
+/* See stack_space comment in NS_InvokeByIndex to see why this needs not to
+ * be static on DEBUG builds. */
+#ifndef DEBUG
+static
+#endif
+void
+invoke_copy_to_stack(uint32_t* stk, uint32_t *end,
+ uint32_t paramCount, nsXPTCVariant* s)
+{
+ /* The stack buffer is 64-bits aligned. The end argument points to its end.
+ * The caller is assumed to create a stack buffer of at least four 32-bits
+ * words.
+ * We use the last three 32-bit words to store the values for r1, r2 and r3
+ * for the method call, i.e. the first words for arguments passing.
+ */
+ uint32_t *d = end - 3;
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ /* Wrap when reaching the end of the stack buffer */
+ if (d == end) d = stk;
+ NS_ASSERTION(d >= stk && d < end,
+ "invoke_copy_to_stack is copying outside its given buffer");
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ // According to the ARM EABI, integral types that are smaller than a word
+ // are to be sign/zero-extended to a full word and treated as 4-byte values.
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 :
+ d = copy_double_word(stk, d, end, (uint64_t *)&s->val.i64);
+ break;
+ case nsXPTType::T_U8 : *((uint32_t*)d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 :
+ d = copy_double_word(stk, d, end, (uint64_t *)&s->val.u64);
+ break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE :
+ d = copy_double_word(stk, d, end, (uint64_t *)&s->val.d);
+ break;
+ case nsXPTType::T_BOOL : *((int32_t*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((int32_t*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int32_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+
+typedef nsresult (*vtable_func)(nsISupports *, uint32_t, uint32_t, uint32_t);
+
+// Avoid AddressSanitizer instrumentation for the next function because it
+// depends on __builtin_alloca behavior and alignment that cannot be relied on
+// once the function is compiled with a version of ASan that has dynamic-alloca
+// instrumentation enabled.
+
+MOZ_ASAN_BLACKLIST
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+
+/* This is to call a given method of class that.
+ * The parameters are in params, the number is in paramCount.
+ * The routine will issue calls to count the number of words
+ * required for argument passing and to copy the arguments to
+ * the stack.
+ * ACPS passes the first 3 params in r1-r3 (with exceptions for 64-bits
+ * arguments), and the remaining goes onto the stack.
+ * We allocate a buffer on the stack for a "worst case" estimate of how much
+ * stack might be needed for EABI, i.e. twice the number of parameters.
+ * The end of this buffer will be used to store r1 to r3, so that the start
+ * of the stack is the remaining parameters.
+ * The magic here is to call the method with "that" and three 32-bits
+ * arguments corresponding to r1-r3, so that the compiler generates the
+ * proper function call. The stack will also contain the remaining arguments.
+ *
+ * !!! IMPORTANT !!!
+ * This routine makes assumptions about the vtable layout of the c++ compiler. It's implemented
+ * for arm-linux GNU g++ >= 2.8.1 (including egcs and gcc-2.95.[1-3])!
+ *
+ */
+
+ vtable_func *vtable, func;
+ int base_size = (paramCount > 1) ? paramCount : 2;
+
+/* !!! IMPORTANT !!!
+ * On DEBUG builds, the NS_ASSERTION used in invoke_copy_to_stack needs to use
+ * the stack to pass the 5th argument to NS_DebugBreak. When invoke_copy_to_stack
+ * is inlined, this can result, depending on the compiler and flags, in the
+ * stack pointer not pointing at stack_space when the method is called at the
+ * end of this function. More generally, any function call requiring stack
+ * allocation of arguments is unsafe to be inlined in this function.
+ */
+ uint32_t *stack_space = (uint32_t *) __builtin_alloca(base_size * 8);
+
+ invoke_copy_to_stack(stack_space, &stack_space[base_size * 2],
+ paramCount, params);
+
+ vtable = *reinterpret_cast<vtable_func **>(that);
+ func = vtable[methodIndex];
+
+ return func(that, stack_space[base_size * 2 - 3],
+ stack_space[base_size * 2 - 2],
+ stack_space[base_size * 2 - 1]);
+}
+
+#else /* __ARM_PCS_VFP */
+
+/* "Procedure Call Standard for the ARM Architecture" document, sections
+ * "5.5 Parameter Passing" and "6.1.2 Procedure Calling" contain all the
+ * needed information.
+ *
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf
+ */
+
+#if defined(__thumb__) && !defined(__thumb2__)
+#error "Thumb1 is not supported"
+#endif
+
+#ifndef __ARMEL__
+#error "Only little endian compatibility was tested"
+#endif
+
+/*
+ * Allocation of integer function arguments initially to registers r1-r3
+ * and then to stack. Handling of 'this' argument which goes to r0 registers
+ * is handled separately and does not belong to these two inline functions.
+ *
+ * The doubleword arguments are allocated to even:odd
+ * register pairs or get aligned at 8-byte boundary on stack. The "holes"
+ * which may appear as a result of this realignment remain unused.
+ *
+ * 'ireg_args' - pointer to the current position in the buffer,
+ * corresponding to the register arguments
+ * 'stack_args' - pointer to the current position in the buffer,
+ * corresponding to the arguments on stack
+ * 'end' - pointer to the end of the registers argument
+ * buffer (it is guaranteed to be 8-bytes aligned)
+ */
+
+static inline void copy_word(uint32_t* &ireg_args,
+ uint32_t* &stack_args,
+ uint32_t* end,
+ uint32_t data)
+{
+ if (ireg_args < end) {
+ *ireg_args = data;
+ ireg_args++;
+ } else {
+ *stack_args = data;
+ stack_args++;
+ }
+}
+
+static inline void copy_dword(uint32_t* &ireg_args,
+ uint32_t* &stack_args,
+ uint32_t* end,
+ uint64_t data)
+{
+ if (ireg_args + 1 < end) {
+ if ((uint32_t)ireg_args & 4) {
+ ireg_args++;
+ }
+ *(uint64_t *)ireg_args = data;
+ ireg_args += 2;
+ } else {
+ ireg_args = end;
+ if ((uint32_t)stack_args & 4) {
+ stack_args++;
+ }
+ *(uint64_t *)stack_args = data;
+ stack_args += 2;
+ }
+}
+
+/*
+ * Allocation of floating point arguments to VFP registers (s0-s15, d0-d7).
+ *
+ * Unlike integer registers allocation, "back-filling" needs to be
+ * supported. For example, the third floating point argument in the
+ * following function is going to be allocated to s1 register, back-filling
+ * the "hole":
+ * void f(float s0, double d1, float s1)
+ *
+ * Refer to the "Procedure Call Standard for the ARM Architecture" document
+ * for more details.
+ *
+ * 'vfp_s_args' - pointer to the current position in the buffer with
+ * the next unallocated single precision register
+ * 'vfp_d_args' - pointer to the current position in the buffer with
+ * the next unallocated double precision register,
+ * it has the same value as 'vfp_s_args' when back-filling
+ * is not used
+ * 'end' - pointer to the end of the vfp registers argument
+ * buffer (it is guaranteed to be 8-bytes aligned)
+ *
+ * Mozilla bugtracker has a test program attached which be used for
+ * experimenting with VFP registers allocation code and testing its
+ * correctness:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=601914#c19
+ */
+
+static inline bool copy_vfp_single(float* &vfp_s_args, double* &vfp_d_args,
+ float* end, float data)
+{
+ if (vfp_s_args >= end)
+ return false;
+
+ *vfp_s_args = data;
+ vfp_s_args++;
+ if (vfp_s_args < (float *)vfp_d_args) {
+ // It was the case of back-filling, now the next free single precision
+ // register should overlap with the next free double precision register
+ vfp_s_args = (float *)vfp_d_args;
+ } else if (vfp_s_args > (float *)vfp_d_args) {
+ // also update the pointer to the next free double precision register
+ vfp_d_args++;
+ }
+ return true;
+}
+
+static inline bool copy_vfp_double(float* &vfp_s_args, double* &vfp_d_args,
+ float* end, double data)
+{
+ if (vfp_d_args >= (double *)end) {
+ // The back-filling continues only so long as no VFP CPRC has been
+ // allocated to a slot on the stack. Basically no VFP registers can
+ // be allocated after this point.
+ vfp_s_args = end;
+ return false;
+ }
+
+ if (vfp_s_args == (float *)vfp_d_args) {
+ // also update the pointer to the next free single precision register
+ vfp_s_args += 2;
+ }
+ *vfp_d_args = data;
+ vfp_d_args++;
+ return true;
+}
+
+static void
+invoke_copy_to_stack(uint32_t* stk, uint32_t *end,
+ uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t *ireg_args = end - 3;
+ float *vfp_s_args = (float *)end;
+ double *vfp_d_args = (double *)end;
+ float *vfp_end = vfp_s_args + 16;
+
+ for (uint32_t i = 0; i < paramCount; i++, s++) {
+ if (s->IsPtrData()) {
+ copy_word(ireg_args, stk, end, (uint32_t)s->ptr);
+ continue;
+ }
+ // According to the ARM EABI, integral types that are smaller than a word
+ // are to be sign/zero-extended to a full word and treated as 4-byte values
+ switch (s->type)
+ {
+ case nsXPTType::T_FLOAT:
+ if (!copy_vfp_single(vfp_s_args, vfp_d_args, vfp_end, s->val.f)) {
+ copy_word(end, stk, end, reinterpret_cast<uint32_t&>(s->val.f));
+ }
+ break;
+ case nsXPTType::T_DOUBLE:
+ if (!copy_vfp_double(vfp_s_args, vfp_d_args, vfp_end, s->val.d)) {
+ copy_dword(end, stk, end, reinterpret_cast<uint64_t&>(s->val.d));
+ }
+ break;
+ case nsXPTType::T_I8: copy_word(ireg_args, stk, end, s->val.i8); break;
+ case nsXPTType::T_I16: copy_word(ireg_args, stk, end, s->val.i16); break;
+ case nsXPTType::T_I32: copy_word(ireg_args, stk, end, s->val.i32); break;
+ case nsXPTType::T_I64: copy_dword(ireg_args, stk, end, s->val.i64); break;
+ case nsXPTType::T_U8: copy_word(ireg_args, stk, end, s->val.u8); break;
+ case nsXPTType::T_U16: copy_word(ireg_args, stk, end, s->val.u16); break;
+ case nsXPTType::T_U32: copy_word(ireg_args, stk, end, s->val.u32); break;
+ case nsXPTType::T_U64: copy_dword(ireg_args, stk, end, s->val.u64); break;
+ case nsXPTType::T_BOOL: copy_word(ireg_args, stk, end, s->val.b); break;
+ case nsXPTType::T_CHAR: copy_word(ireg_args, stk, end, s->val.c); break;
+ case nsXPTType::T_WCHAR: copy_word(ireg_args, stk, end, s->val.wc); break;
+ default:
+ // all the others are plain pointer types
+ copy_word(ireg_args, stk, end, reinterpret_cast<uint32_t>(s->val.p));
+ break;
+ }
+ }
+}
+
+typedef uint32_t (*vtable_func)(nsISupports *, uint32_t, uint32_t, uint32_t);
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ vtable_func *vtable = *reinterpret_cast<vtable_func **>(that);
+ vtable_func func = vtable[methodIndex];
+ // 'register uint32_t result asm("r0")' could be used here, but it does not
+ // seem to be reliable in all cases: http://gcc.gnu.org/PR46164
+ nsresult result;
+ asm (
+ "mov r3, sp\n"
+ "mov %[stack_space_size], %[param_count_plus_2], lsl #3\n"
+ "tst r3, #4\n" /* check stack alignment */
+
+ "add %[stack_space_size], #(4 * 16)\n" /* space for VFP registers */
+ "mov r3, %[params]\n"
+
+ "it ne\n"
+ "addne %[stack_space_size], %[stack_space_size], #4\n"
+ "sub r0, sp, %[stack_space_size]\n" /* allocate space on stack */
+
+ "sub r2, %[param_count_plus_2], #2\n"
+ "mov sp, r0\n"
+
+ "add r1, r0, %[param_count_plus_2], lsl #3\n"
+ "blx %[invoke_copy_to_stack]\n"
+
+ "add ip, sp, %[param_count_plus_2], lsl #3\n"
+ "mov r0, %[that]\n"
+ "ldmdb ip, {r1, r2, r3}\n"
+ "vldm ip, {d0, d1, d2, d3, d4, d5, d6, d7}\n"
+ "blx %[func]\n"
+
+ "add sp, sp, %[stack_space_size]\n" /* cleanup stack */
+ "mov %[stack_space_size], r0\n" /* it's actually 'result' variable */
+ : [stack_space_size] "=&r" (result)
+ : [func] "r" (func),
+ [that] "r" (that),
+ [params] "r" (params),
+ [param_count_plus_2] "r" (paramCount + 2),
+ [invoke_copy_to_stack] "r" (invoke_copy_to_stack)
+ : "cc", "memory",
+ // Mark all the scratch registers as clobbered because they may be
+ // modified by the functions, called from this inline assembly block
+ "r0", "r1", "r2", "r3", "ip", "lr",
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ // Also unconditionally mark d16-d31 registers as clobbered even though
+ // they actually don't exist in vfpv2 and vfpv3-d16 variants. There is
+ // no way to identify VFP variant using preprocessor at the momemnt
+ // (see http://gcc.gnu.org/PR46128 for more details), but fortunately
+ // current versions of gcc do not seem to complain about these registers
+ // even when this code is compiled with '-mfpu=vfpv3-d16' option.
+ // If gcc becomes more strict in the future and/or provides a way to
+ // identify VFP variant, the following d16-d31 registers list needs
+ // to be wrapped into some #ifdef
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
+ "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
+ );
+ return result;
+}
+
+#endif
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_netbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_netbsd.cpp
new file mode 100644
index 000000000..1b0c214ea
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_netbsd.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+// Remember that these 'words' are 32bit DWORDS
+
+static uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ return result;
+}
+
+static void
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int8_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int16_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint8_t*) d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint16_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ case nsXPTType::T_BOOL : *((bool*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((char*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+
+extern "C"
+struct my_params_struct {
+ nsISupports* that;
+ uint32_t Index;
+ uint32_t Count;
+ nsXPTCVariant* params;
+ uint32_t fn_count;
+ uint32_t fn_copy;
+};
+
+XPTC_PUBLIC_API(nsresult)
+XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ uint32_t result;
+ struct my_params_struct my_params;
+ my_params.that = that;
+ my_params.Index = methodIndex;
+ my_params.Count = paramCount;
+ my_params.params = params;
+ my_params.fn_copy = (uint32_t) &invoke_copy_to_stack;
+ my_params.fn_count = (uint32_t) &invoke_count_words;
+
+/* This is to call a given method of class that.
+ * The parameters are in params, the number is in paramCount.
+ * The routine will issue calls to count the number of words
+ * required for argument passing and to copy the arguments to
+ * the stack.
+ * Since APCS passes the first 3 params in r1-r3, we need to
+ * load the first three words from the stack and correct the stack
+ * pointer (sp) in the appropriate way. This means:
+ *
+ * 1.) more than 3 arguments: load r1-r3, correct sp and remember No.
+ * of bytes left on the stack in r4
+ *
+ * 2.) <= 2 args: load r1-r3 (we won't be causing a stack overflow I hope),
+ * restore sp as if nothing had happened and set the marker r4 to zero.
+ *
+ * Afterwards sp will be restored using the value in r4 (which is not a temporary register
+ * and will be preserved by the function/method called according to APCS [ARM Procedure
+ * Calling Standard]).
+ *
+ * !!! IMPORTANT !!!
+ * This routine makes assumptions about the vtable layout of the c++ compiler. It's implemented
+ * for arm-linux GNU g++ >= 2.8.1 (including egcs and gcc-2.95.[1-3])!
+ *
+ */
+
+ __asm__ __volatile__(
+ "ldr r1, [%1, #12] \n\t" /* prepare to call invoke_count_words */
+ "ldr ip, [%1, #16] \n\t" /* r0=paramCount, r1=params */
+ "ldr r0, [%1, #8] \n\t"
+ "mov lr, pc \n\t" /* call it... */
+ "mov pc, ip \n\t"
+ "mov r4, r0, lsl #2 \n\t" /* This is the amount of bytes needed. */
+ "sub sp, sp, r4 \n\t" /* use stack space for the args... */
+ "mov r0, sp \n\t" /* prepare a pointer an the stack */
+ "ldr r1, [%1, #8] \n\t" /* =paramCount */
+ "ldr r2, [%1, #12] \n\t" /* =params */
+ "ldr ip, [%1, #20] \n\t" /* =invoke_copy_to_stack */
+ "mov lr, pc \n\t" /* copy args to the stack like the */
+ "mov pc, ip \n\t" /* compiler would. */
+ "ldr r0, [%1] \n\t" /* =that */
+ "ldr r1, [r0, #0] \n\t" /* get that->vtable offset */
+ "ldr r2, [%1, #4] \n\t"
+ "add r2, r1, r2, lsl #3\n\t" /* a vtable_entry(x)=8 + (8 bytes * x) */
+ "add r2, r2, #8 \n\t" /* with this compilers */
+ "ldr r3, [r2] \n\t" /* get virtual offset from vtable */
+ "mov r3, r3, lsl #16 \n\t"
+ "add r0, r0, r3, asr #16\n\t"
+ "ldr ip, [r2, #4] \n\t" /* get method address from vtable */
+ "cmp r4, #12 \n\t" /* more than 3 arguments??? */
+ "ldmgtia sp!, {r1, r2, r3}\n\t" /* yes: load arguments for r1-r3 */
+ "subgt r4, r4, #12 \n\t" /* and correct the stack pointer */
+ "ldmleia sp, {r1, r2, r3}\n\t" /* no: load r1-r3 from stack */
+ "addle sp, sp, r4 \n\t" /* and restore stack pointer */
+ "movle r4, #0 \n\t" /* a mark for restoring sp */
+ "mov lr, pc \n\t" /* call mathod */
+ "mov pc, ip \n\t"
+ "add sp, sp, r4 \n\t" /* restore stack pointer */
+ "mov %0, r0 \n\t" /* the result... */
+ : "=r" (result)
+ : "r" (&my_params)
+ : "r0", "r1", "r2", "r3", "r4", "ip", "lr"
+ );
+
+ return result;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_openbsd.cpp
new file mode 100644
index 000000000..4ec7dd20f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm_openbsd.cpp
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+// Remember that these 'words' are 32bit DWORDS
+
+static uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ return result;
+}
+
+static void
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int8_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int16_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint8_t*) d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint16_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ case nsXPTType::T_BOOL : *((bool*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((char*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+
+extern "C" {
+ struct my_params_struct {
+ nsISupports* that;
+ uint32_t Index;
+ uint32_t Count;
+ nsXPTCVariant* params;
+ uint32_t fn_count;
+ uint32_t fn_copy;
+ };
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ uint32_t result;
+ struct my_params_struct my_params;
+ my_params.that = that;
+ my_params.Index = methodIndex;
+ my_params.Count = paramCount;
+ my_params.params = params;
+ my_params.fn_copy = (uint32_t) &invoke_copy_to_stack;
+ my_params.fn_count = (uint32_t) &invoke_count_words;
+
+/* This is to call a given method of class that.
+ * The parameters are in params, the number is in paramCount.
+ * The routine will issue calls to count the number of words
+ * required for argument passing and to copy the arguments to
+ * the stack.
+ * Since APCS passes the first 3 params in r1-r3, we need to
+ * load the first three words from the stack and correct the stack
+ * pointer (sp) in the appropriate way. This means:
+ *
+ * 1.) more than 3 arguments: load r1-r3, correct sp and remember No.
+ * of bytes left on the stack in r4
+ *
+ * 2.) <= 2 args: load r1-r3 (we won't be causing a stack overflow I hope),
+ * restore sp as if nothing had happened and set the marker r4 to zero.
+ *
+ * Afterwards sp will be restored using the value in r4 (which is not a temporary register
+ * and will be preserved by the function/method called according to APCS [ARM Procedure
+ * Calling Standard]).
+ *
+ * !!! IMPORTANT !!!
+ * This routine makes assumptions about the vtable layout of the c++ compiler. It's implemented
+ * for arm-linux GNU g++ >= 2.8.1 (including egcs and gcc-2.95.[1-3])!
+ *
+ */
+
+#ifdef __GNUC__
+ __asm__ __volatile__(
+ "ldr r1, [%1, #12] \n\t" /* prepare to call invoke_count_words */
+ "ldr ip, [%1, #16] \n\t" /* r0=paramCount, r1=params */
+ "ldr r0, [%1, #8] \n\t"
+ "mov lr, pc \n\t" /* call it... */
+ "mov pc, ip \n\t"
+ "mov r4, r0, lsl #2 \n\t" /* This is the amount of bytes needed. */
+ "sub sp, sp, r4 \n\t" /* use stack space for the args... */
+ "mov r0, sp \n\t" /* prepare a pointer an the stack */
+ "ldr r1, [%1, #8] \n\t" /* =paramCount */
+ "ldr r2, [%1, #12] \n\t" /* =params */
+ "ldr ip, [%1, #20] \n\t" /* =invoke_copy_to_stack */
+ "mov lr, pc \n\t" /* copy args to the stack like the */
+ "mov pc, ip \n\t" /* compiler would. */
+ "ldr r0, [%1] \n\t" /* =that */
+ "ldr r1, [r0, #0] \n\t" /* get that->vtable offset */
+ "ldr r2, [%1, #4] \n\t"
+ "mov r2, r2, lsl #2 \n\t" /* a vtable_entry(x)=8 + (4 bytes * x) */
+ "ldr ip, [r1, r2] \n\t" /* get method adress from vtable */
+ "cmp r4, #12 \n\t" /* more than 3 arguments??? */
+ "ldmgtia sp!, {r1, r2, r3}\n\t" /* yes: load arguments for r1-r3 */
+ "subgt r4, r4, #12 \n\t" /* and correct the stack pointer */
+ "ldmleia sp, {r1, r2, r3}\n\t" /* no: load r1-r3 from stack */
+ "addle sp, sp, r4 \n\t" /* and restore stack pointer */
+ "movle r4, #0 \n\t" /* a mark for restoring sp */
+ "ldr r0, [%1, #0] \n\t" /* get (self) */
+ "mov lr, pc \n\t" /* call mathod */
+ "mov pc, ip \n\t"
+ "add sp, sp, r4 \n\t" /* restore stack pointer */
+ "mov %0, r0 \n\t" /* the result... */
+ : "=r" (result)
+ : "r" (&my_params), "m" (my_params)
+ : "r0", "r1", "r2", "r3", "r4", "ip", "lr", "sp"
+ );
+#else
+#error "Unsupported compiler. Use g++ >= 2.8 for OpenBSD/arm."
+#endif /* G++ >= 2.8 */
+
+ return result;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_aarch64.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_aarch64.s
new file mode 100644
index 000000000..efe691a68
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_aarch64.s
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+ .section ".text"
+ .globl _NS_InvokeByIndex
+ .type _NS_InvokeByIndex,@function
+
+/*
+ * _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ * uint32_t paramCount, nsXPTCVariant* params)
+ */
+
+_NS_InvokeByIndex:
+ # set up frame
+ stp x29, x30, [sp,#-32]!
+ mov x29, sp
+ stp x19, x20, [sp,#16]
+
+ # save methodIndex across function calls
+ mov w20, w1
+
+ # end of stack area passed to invoke_copy_to_stack
+ mov x1, sp
+
+ # assume 8 bytes of stack for each argument with 16-byte alignment
+ add w19, w2, #1
+ and w19, w19, #0xfffffffe
+ sub sp, sp, w19, uxth #3
+
+ # temporary place to store args passed in r0-r7,v0-v7
+ sub sp, sp, #128
+
+ # save 'that' on stack
+ str x0, [sp]
+
+ # start of stack area passed to invoke_copy_to_stack
+ mov x0, sp
+ bl invoke_copy_to_stack
+
+ # load arguments passed in r0-r7
+ ldp x6, x7, [sp, #48]
+ ldp x4, x5, [sp, #32]
+ ldp x2, x3, [sp, #16]
+ ldp x0, x1, [sp],#64
+
+ # load arguments passed in v0-v7
+ ldp d6, d7, [sp, #48]
+ ldp d4, d5, [sp, #32]
+ ldp d2, d3, [sp, #16]
+ ldp d0, d1, [sp],#64
+
+ # call the method
+ ldr x16, [x0]
+ add x16, x16, w20, uxth #3
+ ldr x16, [x16]
+ blr x16
+
+ add sp, sp, w19, uxth #3
+ ldp x19, x20, [sp,#16]
+ ldp x29, x30, [sp],#32
+ ret
+
+ .size _NS_InvokeByIndex, . - _NS_InvokeByIndex
+
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf32.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf32.s
new file mode 100644
index 000000000..794c4f5c1
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf32.s
@@ -0,0 +1,145 @@
+
+// Select C numeric constant
+ .radix C
+// for 64 bit mode, use .psr abi64
+ .psr abi32
+// big endian
+ .psr msb
+// Section has executable code
+ .section .text, "ax","progbits"
+// procedure named 'NS_InvokeByIndex'
+ .proc NS_InvokeByIndex
+// manual bundling
+ .explicit
+
+// extern "C" uint32_t
+// invoke_copy_to_stack(uint64_t* d,
+// const uint32_t paramCount, nsXPTCVariant* s)
+ .global invoke_copy_to_stack
+// .exclass invoke_copy_to_stack, @fullyvisible
+ .type invoke_copy_to_stack,@function
+
+// .exclass NS_InvokeByIndex, @fullyvisible
+ .type NS_InvokeByIndex,@function
+
+// NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+// uint32_t paramCount, nsXPTCVariant* params);
+NS_InvokeByIndex::
+ .prologue
+ .save ar.pfs, r37
+// allocate 4 input args, 6 local args, and 8 output args
+ alloc r37 = ar.pfs, 4, 6, 8, 0 // M
+ nop.i 0 ;; // I
+
+// unwind table already knows gp, no need to specify anything
+ add r39 = 0, gp // A
+ .save rp, r36
+ mov r36 = rp // I
+ .vframe r38
+ add r38 = 0, sp ;; // A
+
+// We first calculate the amount of extra memory stack space required
+// for the arguments, and register storage.
+// We then call invoke_copy_to_stack() to write the argument contents
+// to the specified memory regions.
+// We then copy the integer arguments to integer registers, and floating
+// arguments to float registers.
+// Lastly we load the virtual table jump pointer, and call the target
+// subroutine.
+
+// in0 : that
+// in1 : methodIndex
+// in2 : paramCount
+// in3 : params
+
+// stack frame size is 16 + (8 * even(paramCount)) + 64 + 64
+// 16 byte frame header
+// 8 * even(paramCount) memory argument area
+// 64 bytes integer register area
+// 64 bytes float register area
+// This scheme makes sure stack fram size is a multiple of 16
+
+ .body
+ add r10 = 8, r0 // A
+// r41 points to float register area
+ add r41 = -64, sp // A
+// r40 points to int register area
+ add r40 = -128, sp ;; // A
+
+ add out1 = 0, r40 // A
+ add out2 = 0, r41 // A
+ tbit.z p14,p15 = in2,0 ;; // I
+
+// compute 8 * even(paramCount)
+(p14) shladd r11 = in2, 3, r0 ;; // A
+(p15) shladd r11 = in2, 3, r10 ;; // A
+ sub out0 = r40, r11 ;; // A
+
+// advance the stack frame
+ add sp = -16, out0 // A
+ add out3 = 0, in2 // A
+ add out4 = 0, in3 // A
+
+// branch to invoke_copy_to_stack
+ br.call.sptk.few rp = invoke_copy_to_stack ;; // B
+
+// restore gp
+ add gp = 0, r39 // A
+ add out0 = 0, in0 // A
+
+// load the integer and float registers
+ ld8 out1 = [r40], 8 // M
+ ldfd f8 = [r41], 8 ;; // M
+
+ ld8 out2 = [r40], 8 // M
+ ldfd f9 = [r41], 8 ;; // M
+
+ ld8 out3 = [r40], 8 // M
+ ldfd f10 = [r41], 8 ;; // M
+
+ ld8 out4 = [r40], 8 // M
+ ldfd f11 = [r41], 8 ;; // M
+
+ ld8 out5 = [r40], 8 // M
+ ldfd f12 = [r41], 8 ;; // M
+// 16 * methodIndex
+ shladd r11 = in1, 4, r0 // A
+
+ ld8 out6 = [r40], 8 // M
+ ldfd f13 = [r41], 8 ;; // M
+
+ ld8 out7 = [r40], 8 // M
+ ldfd f14 = [r41], 8 // M
+ addp4 r8 = 0, in0 ;; // A
+
+// look up virtual base table and dispatch to target subroutine
+// This section assumes 32 bit pointer mode, and virtual base table
+// layout from the ABI http://www.codesourcery.com/cxx-abi/abi.html
+
+// load base table
+ ld4 r8 = [r8] ;; // M
+ addp4 r8 = r11, r8 ;; // A
+
+ // first entry is jump pointer, second entry is gp
+ addp4 r9 = 8, r8 ;; // A
+// load jump pointer
+ ld8 r8 = [r8]
+
+// load gp
+ ld8 gp = [r9] ;; // M
+ mov b6 = r8 ;; // I
+
+// branch to target virtual function
+ br.call.sptk.few rp = b6 ;; // B
+
+// epilog
+ mov ar.pfs = r37 // I
+ mov rp = r36 ;; // I
+
+ add sp = 0, r38 // A
+ add gp = 0, r39 // A
+ br.ret.sptk.few rp ;; // B
+
+ .endp
+
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf64.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf64.s
new file mode 100644
index 000000000..140c44117
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ipf64.s
@@ -0,0 +1,146 @@
+
+// Select C numeric constant
+ .radix C
+// for 64 bit mode, use .psr abi64
+ .psr abi64
+// little endian
+ .psr lsb
+// Section has executable code
+ .section .text, "ax","progbits"
+// procedure named 'NS_InvokeByIndex'
+ .proc NS_InvokeByIndex
+// manual bundling
+ .explicit
+
+// extern "C" uint32_t
+// invoke_copy_to_stack(uint64_t* d,
+// const uint32_t paramCount, nsXPTCVariant* s)
+ .global invoke_copy_to_stack
+// .exclass invoke_copy_to_stack, @fullyvisible
+ .type invoke_copy_to_stack,@function
+
+// .exclass NS_InvokeByIndex, @fullyvisible
+ .type NS_InvokeByIndex,@function
+
+// XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+// uint32_t paramCount, nsXPTCVariant* params);
+NS_InvokeByIndex::
+ .prologue
+ .save ar.pfs, r37
+// allocate 4 input args, 6 local args, and 8 output args
+ alloc r37 = ar.pfs, 4, 6, 8, 0 // M
+ nop.i 0 ;; // I
+
+// unwind table already knows gp, no need to specify anything
+ add r39 = 0, gp // A
+ .save rp, r36
+ mov r36 = rp // I
+ .vframe r38
+ add r38 = 0, sp ;; // A
+
+// We first calculate the amount of extra memory stack space required
+// for the arguments, and register storage.
+// We then call invoke_copy_to_stack() to write the argument contents
+// to the specified memory regions.
+// We then copy the integer arguments to integer registers, and floating
+// arguments to float registers.
+// Lastly we load the virtual table jump pointer, and call the target
+// subroutine.
+
+// in0 : that
+// in1 : methodIndex
+// in2 : paramCount
+// in3 : params
+
+// stack frame size is 16 + (8 * even(paramCount)) + 64 + 64
+// 16 byte frame header
+// 8 * even(paramCount) memory argument area
+// 64 bytes integer register area
+// 64 bytes float register area
+// This scheme makes sure stack fram size is a multiple of 16
+
+ .body
+ add r10 = 8, r0 // A
+// r41 points to float register area
+ add r41 = -64, sp // A
+// r40 points to int register area
+ add r40 = -128, sp ;; // A
+
+ add out1 = 0, r40 // A
+ add out2 = 0, r41 // A
+ tbit.z p14,p15 = in2,0 ;; // I
+
+// compute 8 * even(paramCount)
+(p14) shladd r11 = in2, 3, r0 ;; // A
+(p15) shladd r11 = in2, 3, r10 ;; // A
+ sub out0 = r40, r11 ;; // A
+
+// advance the stack frame
+ add sp = -16, out0 // A
+ add out3 = 0, in2 // A
+ add out4 = 0, in3 // A
+
+// branch to invoke_copy_to_stack
+ br.call.sptk.few rp = invoke_copy_to_stack ;; // B
+
+// restore gp
+ add gp = 0, r39 // A
+ add out0 = 0, in0 // A
+
+// load the integer and float registers
+ ld8 out1 = [r40], 8 // M
+ ldfd f8 = [r41], 8 ;; // M
+
+ ld8 out2 = [r40], 8 // M
+ ldfd f9 = [r41], 8 ;; // M
+
+ ld8 out3 = [r40], 8 // M
+ ldfd f10 = [r41], 8 ;; // M
+
+ ld8 out4 = [r40], 8 // M
+ ldfd f11 = [r41], 8 ;; // M
+
+ ld8 out5 = [r40], 8 // M
+ ldfd f12 = [r41], 8 ;; // M
+// 16 * methodIndex
+ shladd r11 = in1, 4, r0 // A
+
+ ld8 out6 = [r40], 8 // M
+ ldfd f13 = [r41], 8 ;; // M
+
+ ld8 out7 = [r40], 8 // M
+ ldfd f14 = [r41], 8 // M
+ add r8 = 0, in0 ;; // A
+
+// look up virtual base table and dispatch to target subroutine
+// This section assumes 64 bit pointer mode, and virtual base table
+// layout from the ABI http://www.codesourcery.com/cxx-abi/abi.html
+
+// load base table
+ ld8 r8 = [r8] ;; // M
+ add r8 = r11, r8 ;; // A
+
+ // first entry is jump pointer, second entry is gp
+ add r9 = 8, r8 ;; // A
+// load jump pointer
+ ld8 r8 = [r8]
+
+// load gp
+ ld8 gp = [r9] ;; // M
+ mov b6 = r8 ;; // I
+
+// branch to target virtual function
+ br.call.sptk.few rp = b6 ;; // B
+
+// epilog
+ mov ar.pfs = r37 // I
+ mov rp = r36 ;; // I
+
+ add sp = 0, r38 // A
+ add gp = 0, r39 // A
+ br.ret.sptk.few rp ;; // B
+
+ .endp
+
+/* Magic indicating no need for an executable stack */
+.section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips.S b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips.S
new file mode 100644
index 000000000..32ff3b356
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips.S
@@ -0,0 +1,134 @@
+/* -*- Mode: asm; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * Version: MPL 1.1
+ *
+ * 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/. */
+
+/* This code is for MIPS using the O32 ABI. */
+
+#ifdef ANDROID
+#include <asm/regdef.h>
+#include <asm/asm.h>
+#include <machine/asm.h>
+#else
+#include <sys/regdef.h>
+#include <sys/asm.h>
+#endif
+
+# NARGSAVE is the argument space in the callers frame, including extra
+# 'shadowed' space for the argument registers. The minimum of 4
+# argument slots is sometimes predefined in the header files.
+#ifndef NARGSAVE
+#define NARGSAVE 4
+#endif
+
+#define LOCALSZ 3 /* gp, fp, ra */
+#define FRAMESZ ((((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK)
+
+#define RAOFF (FRAMESZ - (1*SZREG))
+#define FPOFF (FRAMESZ - (2*SZREG))
+#define GPOFF (FRAMESZ - (3*SZREG))
+
+#define A0OFF (FRAMESZ + (0*SZREG))
+#define A1OFF (FRAMESZ + (1*SZREG))
+#define A2OFF (FRAMESZ + (2*SZREG))
+#define A3OFF (FRAMESZ + (3*SZREG))
+
+ .text
+
+#
+# _NS_InvokeByIndex(that, methodIndex, paramCount, params)
+# a0 a1 a2 a3
+
+ .globl _NS_InvokeByIndex
+ .align 2
+ .type _NS_InvokeByIndex,@function
+ .ent _NS_InvokeByIndex,0
+ .frame fp, FRAMESZ, ra
+_NS_InvokeByIndex:
+ SETUP_GP
+ subu sp, FRAMESZ
+
+ # specify the save register mask for gp, fp, ra, a3 - a0
+ .mask 0xD00000F0, RAOFF-FRAMESZ
+
+ sw ra, RAOFF(sp)
+ sw fp, FPOFF(sp)
+
+ # we can't use .cprestore in a variable stack frame
+ sw gp, GPOFF(sp)
+
+ sw a0, A0OFF(sp)
+ sw a1, A1OFF(sp)
+ sw a2, A2OFF(sp)
+ sw a3, A3OFF(sp)
+
+ # save bottom of fixed frame
+ move fp, sp
+
+ # extern "C" uint32
+ # invoke_count_words(uint32_t paramCount, nsXPTCVariant* s);
+ la t9, invoke_count_words
+ move a0, a2
+ move a1, a3
+ jalr t9
+ lw gp, GPOFF(fp)
+
+ # allocate variable stack, with a size of:
+ # wordsize (of 4 bytes) * result (already aligned to dword)
+ # but a minimum of 16 byte
+ sll v0, 2
+ slt t0, v0, 16
+ beqz t0, 1f
+ li v0, 16
+1: subu sp, v0
+
+ # let a0 point to the bottom of the variable stack, allocate
+ # another fixed stack for:
+ # extern "C" void
+ # invoke_copy_to_stack(uint32_t* d, uint32_t paramCount,
+ # nsXPTCVariant* s);
+ la t9, invoke_copy_to_stack
+ move a0, sp
+ lw a1, A2OFF(fp)
+ lw a2, A3OFF(fp)
+ subu sp, 16
+ jalr t9
+ lw gp, GPOFF(fp)
+
+ # back to the variable stack frame
+ addu sp, 16
+
+ # calculate the function we need to jump to, which must then be
+ # stored in t9
+ lw a0, A0OFF(fp) # a0 = set "that" to be "this"
+ lw t0, A1OFF(fp) # a1 = methodIndex
+ lw t9, 0(a0)
+ # t0 = methodIndex << PTRLOG
+ sll t0, t0, PTRLOG
+ addu t9, t0
+ lw t9, (t9)
+
+ # Set a1-a3 to what invoke_copy_to_stack told us. a0 is already
+ # the "this" pointer. We don't have to care about floating
+ # point arguments, the non-FP "this" pointer as first argument
+ # means they'll never be used.
+ lw a1, 1*SZREG(sp)
+ lw a2, 2*SZREG(sp)
+ lw a3, 3*SZREG(sp)
+
+ jalr t9
+ # Micro-optimization: There's no gp usage below this point, so
+ # we don't reload.
+ # lw gp, GPOFF(fp)
+
+ # leave variable stack frame
+ move sp, fp
+
+ lw ra, RAOFF(sp)
+ lw fp, FPOFF(sp)
+
+ addiu sp, FRAMESZ
+ j ra
+END(_NS_InvokeByIndex)
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips64.S b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips64.S
new file mode 100644
index 000000000..eef34de7f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_mips64.S
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 <sys/regdef.h>
+#include <sys/asm.h>
+
+.text
+.globl invoke_count_words
+.globl invoke_copy_to_stack
+
+LOCALSZ=7 # a0, a1, a2, a3, s0, ra, gp
+FRAMESZ=(((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
+
+RAOFF=FRAMESZ-(1*SZREG)
+A0OFF=FRAMESZ-(2*SZREG)
+A1OFF=FRAMESZ-(3*SZREG)
+A2OFF=FRAMESZ-(4*SZREG)
+A3OFF=FRAMESZ-(5*SZREG)
+S0OFF=FRAMESZ-(6*SZREG)
+GPOFF=FRAMESZ-(7*SZREG)
+
+#
+# _NS_InvokeByIndex(that, methodIndex, paramCount, params)
+# a0 a1 a2 a3
+
+NESTED(_NS_InvokeByIndex, FRAMESZ, ra)
+ PTR_SUBU sp, FRAMESZ
+ SETUP_GP64(GPOFF, _NS_InvokeByIndex)
+
+ REG_S ra, RAOFF(sp)
+ REG_S a0, A0OFF(sp)
+ REG_S a1, A1OFF(sp)
+ REG_S a2, A2OFF(sp)
+ REG_S a3, A3OFF(sp)
+ REG_S s0, S0OFF(sp)
+
+ # invoke_count_words(paramCount, params)
+ move a0, a2
+ move a1, a3
+ jal invoke_count_words
+
+ # invoke_copy_to_stack(uint32_t* d, uint32_t paramCount,
+ # nsXPTCVariant* s, uint32_t *reg)
+
+ REG_L a1, A2OFF(sp) # a1 - paramCount
+ REG_L a2, A3OFF(sp) # a2 - params
+
+ # save sp before we copy the params to the stack
+ move t0, sp
+
+ # assume full size of 16 bytes per param to be safe
+ sll v0, 4 # 16 bytes * num params
+ PTR_SUBU sp, sp, v0 # make room
+ move a0, sp # a0 - param stack address
+
+ # create temporary stack space to write int and fp regs
+ PTR_SUBU sp, 64 # 64 = 8 regs of 8 bytes
+ move a3, sp
+
+ # save the old sp and save the arg stack
+ PTR_SUBU sp, sp, 16
+ REG_S t0, 0(sp)
+ REG_S a0, 8(sp)
+
+ # copy the param into the stack areas
+ jal invoke_copy_to_stack
+
+ REG_L t3, 8(sp) # get previous a0
+ REG_L sp, 0(sp) # get orig sp back
+
+ REG_L a0, A0OFF(sp) # a0 - that
+ REG_L a1, A1OFF(sp) # a1 - methodIndex
+
+ # t1 = methodIndex * pow(2, PTRLOG)
+ # (use shift instead of mult)
+ sll t1, a1, PTRLOG
+
+ # calculate the function we need to jump to,
+ # which must then be saved in t9
+ PTR_L t9, 0(a0)
+ PTR_ADDU t9, t9, t1
+ PTR_L t9, (t9)
+
+ # get register save area from invoke_copy_to_stack
+ PTR_SUBU t1, t3, 64
+
+ # a1..a7 and f13..f19 should now be set to what
+ # invoke_copy_to_stack told us. skip a0 and f12
+ # because that's the "this" pointer
+
+ REG_L a1, 0(t1)
+ REG_L a2, 8(t1)
+ REG_L a3, 16(t1)
+ REG_L a4, 24(t1)
+ REG_L a5, 32(t1)
+ REG_L a6, 40(t1)
+ REG_L a7, 48(t1)
+
+ l.d $f13, 0(t1)
+ l.d $f14, 8(t1)
+ l.d $f15, 16(t1)
+ l.d $f16, 24(t1)
+ l.d $f17, 32(t1)
+ l.d $f18, 40(t1)
+ l.d $f19, 48(t1)
+
+ # save away our stack pointer and create
+ # the stack pointer for the function
+ move s0, sp
+ move sp, t3
+
+ jalr t9
+
+ move sp, s0
+
+ RESTORE_GP64
+ REG_L ra, RAOFF(sp)
+ REG_L s0, S0OFF(sp)
+ PTR_ADDU sp, FRAMESZ
+ j ra
+.end _NS_InvokeByIndex
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_pa32.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_pa32.s
new file mode 100644
index 000000000..6fa9a86ba
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_pa32.s
@@ -0,0 +1,131 @@
+ .LEVEL 1.1
+framesz .EQU 128
+
+; XPTC_InvokeByIndex(nsISuppots* that, uint32_t methodIndex,
+; uint32_t paramCount, nsXPTCVariant* params);
+
+; g++ need to compile everything with -fvtable-thunks !
+
+ .SPACE $TEXT$,SORT=8
+ .SUBSPA $CODE$,QUAD=0,ALIGN=4,ACCESS=0x2c,CODE_ONLY,SORT=24
+XPTC_InvokeByIndex
+ .PROC
+ .CALLINFO CALLER,FRAME=72,ENTRY_GR=%r3,SAVE_RP,SAVE_SP,ARGS_SAVED,ALLOCA_FRAME
+
+ ; frame marker takes 48 bytes,
+ ; register spill area takes 8 bytes,
+ ; local stack area takes 72 bytes result in 128 bytes total
+
+ .ENTRY
+ STW %rp,-20(%sp)
+ STW,MA %r3,128(%sp)
+
+ LDO -framesz(%r30),%r28
+ STW %r28,-4(%r30) ; save previous sp
+ STW %r19,-32(%r30)
+
+ STW %r26,-36-framesz(%r30) ; save argument registers in
+ STW %r25,-40-framesz(%r30) ; in PREVIOUS frame
+ STW %r24,-44-framesz(%r30) ;
+ STW %r23,-48-framesz(%r30) ;
+
+ B,L .+8,%r2
+ ADDIL L'invoke_count_bytes-$PIC_pcrel$1+4,%r2,%r1
+ LDO R'invoke_count_bytes-$PIC_pcrel$2+8(%r1),%r1
+$PIC_pcrel$1
+ LDSID (%r1),%r31
+$PIC_pcrel$2
+ MTSP %r31,%sr0
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR ;in=24,25,26;out=28
+ BE,L 0(%sr0,%r1),%r31
+ COPY %r31,%r2
+
+ CMPIB,>= 0,%r28, .+76
+ COPY %r30,%r3 ; copy stack ptr to saved stack ptr
+ ADD %r30,%r28,%r30 ; extend stack frame
+ LDW -4(%r3),%r28 ; move frame
+ STW %r28,-4(%r30)
+ LDW -8(%r3),%r28
+ STW %r28,-8(%r30)
+ LDW -12(%r3),%r28
+ STW %r28,-12(%r30)
+ LDW -16(%r3),%r28
+ STW %r28,-16(%r30)
+ LDW -20(%r3),%r28
+ STW %r28,-20(%r30)
+ LDW -24(%r3),%r28
+ STW %r28,-24(%r30)
+ LDW -28(%r3),%r28
+ STW %r28,-28(%r30)
+ LDW -32(%r3),%r28
+ STW %r28,-32(%r30)
+
+ LDO -40(%r30),%r26 ; load copy address
+ LDW -44-framesz(%r3),%r25 ; load rest of 2 arguments
+ LDW -48-framesz(%r3),%r24 ;
+
+ LDW -32(%r30),%r19 ; shared lib call destroys r19; reload
+ B,L .+8,%r2
+ ADDIL L'invoke_copy_to_stack-$PIC_pcrel$3+4,%r2,%r1
+ LDO R'invoke_copy_to_stack-$PIC_pcrel$4+8(%r1),%r1
+$PIC_pcrel$3
+ LDSID (%r1),%r31
+$PIC_pcrel$4
+ MTSP %r31,%sr0
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR ;in=24,25,26
+ BE,L 0(%sr0,%r1),%r31
+ COPY %r31,%r2
+
+ LDO -48(%r30),%r20
+ EXTRW,U,= %r28,31,1,%r22
+ FLDD 0(%r20),%fr7 ; load double arg 1
+ EXTRW,U,= %r28,30,1,%r22
+ FLDW 8(%r20),%fr5L ; load float arg 1
+ EXTRW,U,= %r28,29,1,%r22
+ FLDW 4(%r20),%fr6L ; load float arg 2
+ EXTRW,U,= %r28,28,1,%r22
+ FLDW 0(%r20),%fr7L ; load float arg 3
+
+ LDW -36-framesz(%r3),%r26 ; load ptr to 'that'
+ LDW -40(%r30),%r25 ; load the rest of dispatch argument registers
+ LDW -44(%r30),%r24
+ LDW -48(%r30),%r23
+
+ LDW -36-framesz(%r3),%r20 ; load vtable addr
+ LDW -40-framesz(%r3),%r28 ; load index
+ LDW 0(%r20),%r20 ; follow vtable
+ LDO 16(%r20),%r20 ; offset vtable by 16 bytes (g++: 8, aCC: 16)
+ SH2ADDL %r28,%r20,%r28 ; add 4*index to vtable entry
+ LDW 0(%r28),%r22 ; load vtable entry
+
+ B,L .+8,%r2
+ ADDIL L'$$dyncall_external-$PIC_pcrel$5+4,%r2,%r1
+ LDO R'$$dyncall_external-$PIC_pcrel$6+8(%r1),%r1
+$PIC_pcrel$5
+ LDSID (%r1),%r31
+$PIC_pcrel$6
+ MTSP %r31,%sr0
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR,RTNVAL=GR
+;in=22-26;out=28;
+ BE,L 0(%sr0,%r1),%r31
+ COPY %r31,%r2
+
+ LDW -32(%r30),%r19
+ COPY %r3,%r30 ; restore saved stack ptr
+
+ LDW -148(%sp),%rp
+ BVE (%rp)
+ .EXIT
+ LDW,MB -128(%sp),%r3
+
+ .PROCEND ;in=23,24,25,26;
+
+ .ALIGN 8
+ .SPACE $TEXT$
+ .SUBSPA $CODE$
+ .IMPORT $$dyncall_external,MILLICODE
+ .IMPORT invoke_count_bytes,CODE
+ .IMPORT invoke_copy_to_stack,CODE
+ .EXPORT XPTC_InvokeByIndex,ENTRY,PRIV_LEV=3,ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR,RTNVAL=GR,LONG_RETURN
+ .END
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_parisc_linux.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_parisc_linux.s
new file mode 100644
index 000000000..7a207addf
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_parisc_linux.s
@@ -0,0 +1,108 @@
+/* -*- Mode: asm; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * Version: MPL 1.1
+ *
+ * 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/. */
+
+ .LEVEL 1.1
+ .text
+ .align 4
+
+framesz:
+ .equ 128
+
+.globl NS_InvokeByIndex
+ .type NS_InvokeByIndex, @function
+
+
+NS_InvokeByIndex:
+ .PROC
+ .CALLINFO FRAME=72, CALLER,SAVE_RP, SAVE_SP, ENTRY_GR=3
+ .ENTRY
+
+; frame marker takes 48 bytes,
+; register spill area takes 8 bytes,
+; local stack area takes 72 bytes result in 128 bytes total
+
+ STW %rp,-20(%sp)
+ STW,MA %r3,128(%sp)
+
+ LDO -framesz(%r30),%r28
+ STW %r28,-4(%r30) ; save previous sp
+ STW %r19,-32(%r30)
+
+ STW %r26,-36-framesz(%r30) ; save argument registers in
+ STW %r25,-40-framesz(%r30) ; in PREVIOUS frame
+ STW %r24,-44-framesz(%r30) ;
+ STW %r23,-48-framesz(%r30) ;
+
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR ;in=24,25,26;out=28
+ BL invoke_count_bytes,%r31
+ COPY %r31,%r2
+
+ CMPIB,>= 0,%r28, .+76
+ COPY %r30,%r3 ; copy stack ptr to saved stack ptr
+ ADD %r30,%r28,%r30 ; extend stack frame
+ LDW -4(%r3),%r28 ; move frame
+ STW %r28,-4(%r30)
+ LDW -8(%r3),%r28
+ STW %r28,-8(%r30)
+ LDW -12(%r3),%r28
+ STW %r28,-12(%r30)
+ LDW -16(%r3),%r28
+ STW %r28,-16(%r30)
+ LDW -20(%r3),%r28
+ STW %r28,-20(%r30)
+ LDW -24(%r3),%r28
+ STW %r28,-24(%r30)
+ LDW -28(%r3),%r28
+ STW %r28,-28(%r30)
+ LDW -32(%r3),%r28
+ STW %r28,-32(%r30)
+
+ LDO -40(%r30),%r26 ; load copy address
+ LDW -44-framesz(%r3),%r25 ; load rest of 2 arguments
+ LDW -48-framesz(%r3),%r24 ;
+
+ LDW -32(%r30),%r19 ; shared lib call destroys r19; reload
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR ;in=24,25,26
+ BL invoke_copy_to_stack,%r31
+ COPY %r31,%r2
+
+ LDO -48(%r30),%r20
+ EXTRW,U,= %r28,31,1,%r22
+ FLDD 0(%r20),%fr7 ; load double arg 1
+ EXTRW,U,= %r28,30,1,%r22
+ FLDW 8(%r20),%fr5L ; load float arg 1
+ EXTRW,U,= %r28,29,1,%r22
+ FLDW 4(%r20),%fr6L ; load float arg 2
+ EXTRW,U,= %r28,28,1,%r22
+ FLDW 0(%r20),%fr7L ; load float arg 3
+
+ LDW -36-framesz(%r3),%r26 ; load ptr to 'that'
+ LDW -40(%r30),%r25 ; load the rest of dispatch argument registers
+ LDW -44(%r30),%r24
+ LDW -48(%r30),%r23
+
+ LDW -36-framesz(%r3),%r20 ; load vtable addr
+ LDW -40-framesz(%r3),%r28 ; load index
+ LDW 0(%r20),%r20 ; follow vtable
+ SH2ADDL %r28,%r20,%r28 ; add 4*index to vtable entry
+ LDW 0(%r28),%r22 ; load vtable entry
+
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR,RTNVAL=GR ;in=22-26;out=28;
+ BL $$dyncall,%r31
+ COPY %r31,%r2
+
+ LDW -32(%r30),%r19
+ COPY %r3,%r30 ; restore saved stack ptr
+
+ LDW -148(%sp),%rp
+ LDWM -128(%sp),%r3
+ BV,N (%rp)
+ NOP
+ .EXIT
+ .PROCEND ;in=23,24,25,26;
+ .SIZE NS_InvokeByIndex, .-NS_InvokeByIndex
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S
new file mode 100644
index 000000000..37211c885
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S
@@ -0,0 +1,167 @@
+# 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/.
+
+.set r0,0; .set r1,1; .set r2,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+# The ABI defines a fixed stack frame area of 4 doublewords (ELFv2)
+# or 6 doublewords (ELFv1); the last of these doublewords is used
+# as TOC pointer save area. The fixed area is followed by a parameter
+# save area of 8 doublewords (used for vararg routines), followed
+# by space for parameters passed on the stack.
+#
+# We set STACK_TOC to the offset of the TOC pointer save area, and
+# STACK_PARAMS to the offset of the first on-stack parameter.
+
+#if _CALL_ELF == 2
+#define STACK_TOC 24
+#define STACK_PARAMS 96
+#else
+#define STACK_TOC 40
+#define STACK_PARAMS 112
+#endif
+
+#
+# NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+#if _CALL_ELF == 2
+ .section ".text"
+ .type NS_InvokeByIndex,@function
+ .globl NS_InvokeByIndex
+ .align 2
+NS_InvokeByIndex:
+0: addis 2,12,(.TOC.-0b)@ha
+ addi 2,2,(.TOC.-0b)@l
+ .localentry NS_InvokeByIndex,.-NS_InvokeByIndex
+#else
+ .section ".toc","aw"
+ .section ".text"
+ .align 2
+ .globl NS_InvokeByIndex
+ .section ".opd","aw"
+ .align 3
+NS_InvokeByIndex:
+ .quad .NS_InvokeByIndex,.TOC.@tocbase
+ .previous
+ .type NS_InvokeByIndex,@function
+.NS_InvokeByIndex:
+#endif
+ mflr 0
+ std 0,16(r1)
+
+ std r29,-24(r1)
+ std r30,-16(r1)
+ std r31,-8(r1)
+
+ mr r29,r3 # Save 'that' in r29
+ mr r30,r4 # Save 'methodIndex' in r30
+ mr r31,r1 # Save old frame
+
+ # Allocate stack frame with space for params. Since at least the
+ # first 7 parameters (not including 'that') will be in registers,
+ # we don't actually need stack space for those. We must ensure
+ # that the stack remains 16-byte aligned.
+ #
+ # | (fixed area + | | 7 GP | 13 FP | 3 NV |
+ # | param. save) |(params)........| regs | regs | regs |
+ # (r1)......(+STACK_PARAMS)... (-23*8).(-16*8).(-3*8)..(r31)
+
+ # +stack frame, -unused stack params, +regs storage, +1 for alignment
+ addi r7,r5,((STACK_PARAMS/8)-7+7+13+3+1)
+ rldicr r7,r7,3,59 # multiply by 8 and mask with ~15
+ neg r7,r7
+ stdux r1,r1,r7
+
+
+ # Call invoke_copy_to_stack(uint64_t* gpregs, double* fpregs,
+ # uint32_t paramCount, nsXPTCVariant* s,
+ # uint64_t* d))
+
+ # r5, r6 are passed through intact (paramCount, params)
+ # r7 (d) has to be r1+STACK_PARAMS
+ # -- where parameters are passed on the stack.
+ # r3, r4 are above that, easier to address from r31 than from r1
+
+ subi r3,r31,(23*8) # r3 --> GPRS
+ subi r4,r31,(16*8) # r4 --> FPRS
+ addi r7,r1,STACK_PARAMS # r7 --> params
+ bl invoke_copy_to_stack
+ nop
+
+ # Set up to invoke function
+
+ ld r9,0(r29) # vtable (r29 is 'that')
+ mr r3,r29 # self is first arg, obviously
+
+ sldi r30,r30,3 # Find function descriptor
+ add r9,r9,r30
+ ld r12,0(r9)
+
+ std r2,STACK_TOC(r1) # Save r2 (TOC pointer)
+
+#if _CALL_ELF == 2
+ mtctr r12
+#else
+ ld r0,0(r12) # Actual address from fd.
+ mtctr 0
+ ld r11,16(r12) # Environment pointer from fd.
+ ld r2,8(r12) # TOC pointer from fd.
+#endif
+
+ # Load FP and GP registers as required
+ ld r4, -(23*8)(r31)
+ ld r5, -(22*8)(r31)
+ ld r6, -(21*8)(r31)
+ ld r7, -(20*8)(r31)
+ ld r8, -(19*8)(r31)
+ ld r9, -(18*8)(r31)
+ ld r10, -(17*8)(r31)
+
+ lfd f1, -(16*8)(r31)
+ lfd f2, -(15*8)(r31)
+ lfd f3, -(14*8)(r31)
+ lfd f4, -(13*8)(r31)
+ lfd f5, -(12*8)(r31)
+ lfd f6, -(11*8)(r31)
+ lfd f7, -(10*8)(r31)
+ lfd f8, -(9*8)(r31)
+ lfd f9, -(8*8)(r31)
+ lfd f10, -(7*8)(r31)
+ lfd f11, -(6*8)(r31)
+ lfd f12, -(5*8)(r31)
+ lfd f13, -(4*8)(r31)
+
+ bctrl # Do it
+
+ ld r2,STACK_TOC(r1) # Load our own TOC pointer
+ ld r1,0(r1) # Revert stack frame
+ ld 0,16(r1) # Reload lr
+ ld 29,-24(r1) # Restore NVGPRS
+ ld 30,-16(r1)
+ ld 31,-8(r1)
+ mtlr 0
+ blr
+
+#if _CALL_ELF == 2
+ .size NS_InvokeByIndex,.-NS_InvokeByIndex
+#else
+ .size NS_InvokeByIndex,.-.NS_InvokeByIndex
+#endif
+
+ # Magic indicating no need for an executable stack
+ .section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix.s
new file mode 100644
index 000000000..eb6b6661d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix.s
@@ -0,0 +1,129 @@
+#
+# -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+.set BO_IF,12
+.set CR0_EQ,2
+
+
+
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.NS_InvokeByIndex{TC},"NS_InvokeByIndex"
+
+
+# .text section
+
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .NS_InvokeByIndex
+ .globl NS_InvokeByIndex{DS}
+ .extern .invoke_copy_to_stack
+ .extern ._ptrgl{PR}
+
+
+#
+# NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+.NS_InvokeByIndex:
+ mflr r0
+ stw r31,-4(sp)
+#
+# save off the incoming values in the caller's parameter area
+#
+ stw r3,24(sp) # that
+ stw r4,28(sp) # methodIndex
+ stw r5,32(sp) # paramCount
+ stw r6,36(sp) # params
+ stw r0,8(sp)
+ stwu sp,-136(sp) # = 24 for linkage area, 8 * 13 for fprData area, 8 for saved registers
+
+# prepare args for 'invoke_copy_to_stack' call
+#
+ lwz r4,168(sp) # paramCount
+ lwz r5,172(sp) # params
+ mr r6,sp # fprData
+ slwi r3,r4,3 # number of bytes of stack required
+ # at most 8*paramCount
+ addi r3,r3,28 # linkage area
+ mr r31,sp # save original stack top
+ subfc sp,r3,sp # bump the stack
+ addi r3,sp,28 # parameter pointer excludes linkage area size + 'this'
+
+ bl .invoke_copy_to_stack
+ nop
+
+ lfd f1,0(r31) # Restore floating point registers
+ lfd f2,8(r31)
+ lfd f3,16(r31)
+ lfd f4,24(r31)
+ lfd f5,32(r31)
+ lfd f6,40(r31)
+ lfd f7,48(r31)
+ lfd f8,56(r31)
+ lfd f9,64(r31)
+ lfd f10,72(r31)
+ lfd f11,80(r31)
+ lfd f12,88(r31)
+ lfd f13,96(r31)
+
+ lwz r3,160(r31) # that
+ lwz r4,0(r3) # get vTable from 'that'
+ lwz r5,164(r31) # methodIndex
+ slwi r5,r5,3 # methodIndex * 8
+ addi r5,r5,8 # step over junk at start of vTable !
+ lwzx r11,r5,r4 # get function pointer
+
+ addi r5,r5,4 # We need to manually adjust the 'that' pointer, this is CFRONT based
+ lwzx r5,r4,r5 # offset = r4(vtable) + r5(methodIndex offset) - 4
+ add r3,r5,r3 # adjust 'that' r3 = r3 + r5
+
+ lwz r4,28(sp)
+ lwz r5,32(sp)
+ lwz r6,36(sp)
+ lwz r7,40(sp)
+ lwz r8,44(sp)
+ lwz r9,48(sp)
+ lwz r10,52(sp)
+
+ bl ._ptrgl{PR}
+ nop
+
+ mr sp,r31
+ lwz r0,144(sp)
+ addi sp,sp,136
+ mtlr r0
+ lwz r31,-4(sp)
+ blr
+
+
+# .data section
+
+ .toc # 0x00000038
+T.18.NS_InvokeByIndex:
+ .tc H.18.NS_InvokeByIndex{TC},NS_InvokeByIndex{DS}
+
+ .csect NS_InvokeByIndex{DS}
+ .long .NS_InvokeByIndex # "\0\0\0\0"
+ .long TOC{TC0} # "\0\0\0008"
+ .long 0x00000000 # "\0\0\0\0"
+# End csect NS_InvokeByIndex{DS}
+
+# .bss section
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix64.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix64.s
new file mode 100644
index 000000000..722583de5
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_aix64.s
@@ -0,0 +1,128 @@
+# 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+.set BO_IF,12
+.set CR0_EQ,2
+
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.NS_InvokeByIndex{TC},"NS_InvokeByIndex"
+
+
+# .text section
+
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .NS_InvokeByIndex
+ .globl NS_InvokeByIndex{DS}
+ .extern .invoke_copy_to_stack
+ .extern ._ptrgl{PR}
+
+#
+# NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+.NS_InvokeByIndex:
+ mflr r0
+ std r31,-8(sp)
+#
+# save off the incoming values in the caller's parameter area
+#
+ std r3,48(sp) # that
+ std r4,56(sp) # methodIndex
+ std r5,64(sp) # paramCount
+ std r6,72(sp) # params
+ std r0,16(sp)
+ stdu sp,-168(sp) # 2*24=48 for linkage area,
+ # 8*13=104 for fprData area
+ # 16 for saved registers
+
+# prepare args for 'invoke_copy_to_stack' call
+#
+ ld r4,232(sp) # paramCount (168+8+56)
+ ld r5,240(sp) # params
+ mr r6,sp # fprData
+ sldi r3,r4,3 # number of bytes of stack required
+ # is at most numParams*8
+ addi r3,r3,56 # linkage area (48) + this (8)
+ mr r31,sp # save original stack top
+ subfc sp,r3,sp # bump the stack
+ addi r3,sp,56 # parameter pointer excludes linkage area
+ # size + 'this'
+
+ bl .invoke_copy_to_stack
+ nop
+
+ lfd f1,0(r31) # Restore floating point registers
+ lfd f2,8(r31)
+ lfd f3,16(r31)
+ lfd f4,24(r31)
+ lfd f5,32(r31)
+ lfd f6,40(r31)
+ lfd f7,48(r31)
+ lfd f8,56(r31)
+ lfd f9,64(r31)
+ lfd f10,72(r31)
+ lfd f11,80(r31)
+ lfd f12,88(r31)
+ lfd f13,96(r31)
+
+ ld r3,216(r31) # that (168+48)
+ ld r4,0(r3) # get vTable from 'that'
+ ld r5,224(r31) # methodIndex (168+56)
+ sldi r5,r5,3 # methodIndex * 8
+ # No junk at the start of 64bit vtable !!!
+ ldx r11,r5,r4 # get function pointer (this jumps
+ # either to the function if no adjustment
+ # is needed (displacement = 0), or it
+ # jumps to the thunk code, which will jump
+ # to the function at the end)
+
+ # No adjustment of the that pointer in 64bit mode, this is done
+ # by the thunk code
+
+ ld r4,56(sp)
+ ld r5,64(sp)
+ ld r6,72(sp)
+ ld r7,80(sp)
+ ld r8,88(sp)
+ ld r9,96(sp)
+ ld r10,104(sp)
+
+ bl ._ptrgl{PR}
+ nop
+
+ mr sp,r31
+ ld r0,184(sp) # 168+16
+ addi sp,sp,168
+ mtlr r0
+ ld r31,-8(sp)
+ blr
+
+# .data section
+
+ .toc # 0x00000038
+T.18.NS_InvokeByIndex:
+ .tc H.18.NS_InvokeByIndex{TC},NS_InvokeByIndex{DS}
+
+ .csect NS_InvokeByIndex{DS}
+ .llong .NS_InvokeByIndex # "\0\0\0\0"
+ .llong TOC{TC0} # "\0\0\0008"
+ .llong 0x00000000 # "\0\0\0\0"
+# End csect NS_InvokeByIndex{DS}
+
+# .bss section
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_ibmobj_aix.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_ibmobj_aix.s
new file mode 100644
index 000000000..414a6536f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_ibmobj_aix.s
@@ -0,0 +1,124 @@
+#
+# -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+.set BO_IF,12
+.set CR0_EQ,2
+
+
+
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.NS_InvokeByIndex{TC},"NS_InvokeByIndex"
+
+
+# .text section
+
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .NS_InvokeByIndex
+ .globl NS_InvokeByIndex{DS}
+ .extern .invoke_copy_to_stack
+ .extern ._ptrgl{PR}
+
+
+#
+# NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+.NS_InvokeByIndex:
+ mflr r0
+ stw r31,-4(sp)
+#
+# save off the incoming values in the caller's parameter area
+#
+ stw r3,24(sp) # that
+ stw r4,28(sp) # methodIndex
+ stw r5,32(sp) # paramCount
+ stw r6,36(sp) # params
+ stw r0,8(sp)
+ stwu sp,-136(sp) # = 24 for linkage area, 8 * 13 for fprData area, 8 for saved registers
+
+# prepare args for 'invoke_copy_to_stack' call
+#
+ lwz r4,168(sp) # paramCount
+ lwz r5,172(sp) # params
+ mr r6,sp # fprData
+ slwi r3,r4,3 # number of bytes of stack required
+ # at most 8*paramCount
+ addi r3,r3,28 # linkage area
+ mr r31,sp # save original stack top
+ subfc sp,r3,sp # bump the stack
+ addi r3,sp,28 # parameter pointer excludes linkage area size + 'this'
+
+ bl .invoke_copy_to_stack
+ nop
+
+ lfd f1,0(r31) # Restore floating point registers
+ lfd f2,8(r31)
+ lfd f3,16(r31)
+ lfd f4,24(r31)
+ lfd f5,32(r31)
+ lfd f6,40(r31)
+ lfd f7,48(r31)
+ lfd f8,56(r31)
+ lfd f9,64(r31)
+ lfd f10,72(r31)
+ lfd f11,80(r31)
+ lfd f12,88(r31)
+ lfd f13,96(r31)
+
+ lwz r3,160(r31) # that
+ lwz r4,0(r3) # get vTable from 'that'
+ lwz r5,164(r31) # methodIndex
+ slwi r5,r5,2 # methodIndex * 4
+ lwzx r11,r5,r4 # get function pointer
+
+ lwz r4,28(sp)
+ lwz r5,32(sp)
+ lwz r6,36(sp)
+ lwz r7,40(sp)
+ lwz r8,44(sp)
+ lwz r9,48(sp)
+ lwz r10,52(sp)
+
+ bl ._ptrgl{PR}
+ nop
+
+ mr sp,r31
+ lwz r0,144(sp)
+ addi sp,sp,136
+ mtlr r0
+ lwz r31,-4(sp)
+ blr
+
+
+# .data section
+
+ .toc # 0x00000038
+T.18.NS_InvokeByIndex:
+ .tc H.18.NS_InvokeByIndex{TC},NS_InvokeByIndex{DS}
+
+ .csect NS_InvokeByIndex{DS}
+ .long .NS_InvokeByIndex # "\0\0\0\0"
+ .long TOC{TC0} # "\0\0\0008"
+ .long 0x00000000 # "\0\0\0\0"
+# End csect NS_InvokeByIndex{DS}
+
+# .bss section
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_linux.S b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_linux.S
new file mode 100644
index 000000000..e87382286
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_linux.S
@@ -0,0 +1,98 @@
+// -*- Mode: Asm -*-
+//
+// 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+ .section ".text"
+ .align 2
+ .globl NS_InvokeByIndex
+ .type NS_InvokeByIndex,@function
+
+//
+// NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+// uint32_t paramCount, nsXPTCVariant* params)
+//
+
+NS_InvokeByIndex:
+ stwu sp,-32(sp) // setup standard stack frame
+ mflr r0 // save LR
+ stw r3,8(sp) // r3 <= that
+ stw r4,12(sp) // r4 <= methodIndex
+ stw r30,16(sp)
+ stw r31,20(sp)
+
+ stw r0,36(sp) // store LR backchain
+ mr r31,sp
+
+ rlwinm r10,r5,3,0,27 // r10 = (ParamCount * 2 * 4) & ~0x0f
+ addi r0,r10,96 // reserve stack for GPR and FPR register save area r0 = r10 + 96
+ lwz r9,0(sp) // r9 = backchain
+ neg r0,r0
+ stwux r9,sp,r0 // reserve stack space and save SP backchain
+
+ addi r3,sp,8 // r3 <= args
+ mr r4,r5 // r4 <= paramCount
+ mr r5,r6 // r5 <= params
+ add r6,r3,r10 // r6 <= gpregs ( == args + r10 )
+ mr r30,r6 // store in r30 for use later...
+#ifndef __NO_FPRS__
+ addi r7,r6,32 // r7 <= fpregs ( == gpregs + 32 )
+#else
+ li r7, 0
+#endif
+
+ bl invoke_copy_to_stack@local // (args, paramCount, params, gpregs, fpregs)
+#ifndef __NO_FPRS__
+ lfd f1,32(r30) // load FP registers with method parameters
+ lfd f2,40(r30)
+ lfd f3,48(r30)
+ lfd f4,56(r30)
+ lfd f5,64(r30)
+ lfd f6,72(r30)
+ lfd f7,80(r30)
+ lfd f8,88(r30)
+#endif
+ lwz r3,8(r31) // r3 <= that
+ lwz r4,12(r31) // r4 <= methodIndex
+ lwz r5,0(r3) // r5 <= vtable ( == *that )
+ slwi r4,r4,2 // convert to offset ( *= 4 )
+ lwzx r0,r5,r4 // r0 <= methodpointer ( == vtable + offset )
+
+ lwz r4,4(r30) // load GP regs with method parameters
+ lwz r5,8(r30)
+ lwz r6,12(r30)
+ lwz r7,16(r30)
+ lwz r8,20(r30)
+ lwz r9,24(r30)
+ lwz r10,28(r30)
+
+ mtlr r0 // copy methodpointer to LR
+ blrl // call method
+
+ lwz r30,16(r31) // restore r30 & r31
+ lwz r31,20(r31)
+
+ lwz r11,0(sp) // clean up the stack
+ lwz r0,4(r11)
+ mtlr r0
+ mr sp,r11
+ blr
+
+/* Magic indicating no need for an executable stack */
+.section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_netbsd.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_netbsd.s
new file mode 100644
index 000000000..33650f399
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_netbsd.s
@@ -0,0 +1,95 @@
+# -*- Mode: Asm -*-
+#
+# 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/.
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+ .section ".text"
+ .align 2
+ .globl XPTC_InvokeByIndex
+ .type XPTC_InvokeByIndex,@function
+
+#
+# XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+XPTC_InvokeByIndex:
+ stwu sp,-32(sp) # setup standard stack frame
+ mflr r0 # save LR
+ stw r3,8(sp) # r3 <= that
+ stw r4,12(sp) # r4 <= methodIndex
+ stw r30,16(sp)
+ stw r31,20(sp)
+
+ stw r0,36(sp) # store LR backchain
+ mr r31,sp
+
+ rlwinm r10,r5,3,0,27 # r10 = (ParamCount * 2 * 4) & ~0x0f
+ addi r0,r10,96 # reserve stack for GPR and FPR register save area r0 = r10 + 96
+ lwz r9,0(sp) # r9 = backchain
+ neg r0,r0
+ stwux r9,sp,r0 # reserve stack sapce and save SP backchain
+
+ addi r3,sp,8 # r3 <= args
+ mr r4,r5 # r4 <= paramCount
+ mr r5,r6 # r5 <= params
+ add r6,r3,r10 # r6 <= gpregs ( == args + r10 )
+ mr r30,r6 # store in r30 for use later...
+ addi r7,r6,32 # r7 <= fpregs ( == gpregs + 32 )
+
+ bl invoke_copy_to_stack@local # (args, paramCount, params, gpregs, fpregs)
+
+ lfd f1,32(r30) # load FP registers with method parameters
+ lfd f2,40(r30)
+ lfd f3,48(r30)
+ lfd f4,56(r30)
+ lfd f5,64(r30)
+ lfd f6,72(r30)
+ lfd f7,80(r30)
+ lfd f8,88(r30)
+
+ lwz r3,8(r31) # r3 <= that
+ lwz r4,12(r31) # r4 <= methodIndex
+ lwz r5,0(r3) # r5 <= vtable ( == *that )
+ slwi r4,r4,3 # convert to offset ( *= 8 )
+ addi r4,r4,8 # skip first two vtable entries
+ add r4,r4,r5
+ lhz r0,0(r4) # virtual base offset
+ extsh r0,r0
+ add r3,r3,r0
+ lwz r0,4(r4) # r0 <= methodpointer ( == vtable + offset )
+
+ lwz r4,4(r30) # load GP regs with method parameters
+ lwz r5,8(r30)
+ lwz r6,12(r30)
+ lwz r7,16(r30)
+ lwz r8,20(r30)
+ lwz r9,24(r30)
+ lwz r10,28(r30)
+
+ mtlr r0 # copy methodpointer to LR
+ blrl # call method
+
+ lwz r30,16(r31) # restore r30 & r31
+ lwz r31,20(r31)
+
+ lwz r11,0(sp) # clean up the stack
+ lwz r0,4(r11)
+ mtlr r0
+ mr sp,r11
+ blr
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_openbsd.S b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_openbsd.S
new file mode 100644
index 000000000..f02c0b220
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_openbsd.S
@@ -0,0 +1,94 @@
+// -*- Mode: Asm -*-
+//
+// 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+ .section ".text"
+ .align 2
+ .globl NS_InvokeByIndex
+ .type NS_InvokeByIndex,@function
+
+//
+// NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+// uint32_t paramCount, nsXPTCVariant* params)
+//
+
+NS_InvokeByIndex:
+ stwu sp,-32(sp) // setup standard stack frame
+ mflr r0 // save LR
+ stw r3,8(sp) // r3 <= that
+ stw r4,12(sp) // r4 <= methodIndex
+ stw r30,16(sp)
+ stw r31,20(sp)
+
+ stw r0,36(sp) // store LR backchain
+ mr r31,sp
+
+ rlwinm r10,r5,3,0,27 // r10 = (ParamCount * 2 * 4) & ~0x0f
+ addi r0,r10,96 // reserve stack for GPR and FPR register save area r0 = r10 + 96
+ lwz r9,0(sp) // r9 = backchain
+ neg r0,r0
+ stwux r9,sp,r0 // reserve stack space and save SP backchain
+
+ addi r3,sp,8 // r3 <= args
+ mr r4,r5 // r4 <= paramCount
+ mr r5,r6 // r5 <= params
+ add r6,r3,r10 // r6 <= gpregs ( == args + r10 )
+ mr r30,r6 // store in r30 for use later...
+ addi r7,r6,32 // r7 <= fpregs ( == gpregs + 32 )
+
+ bl invoke_copy_to_stack@local // (args, paramCount, params, gpregs, fpregs)
+
+ lfd f1,32(r30) // load FP registers with method parameters
+ lfd f2,40(r30)
+ lfd f3,48(r30)
+ lfd f4,56(r30)
+ lfd f5,64(r30)
+ lfd f6,72(r30)
+ lfd f7,80(r30)
+ lfd f8,88(r30)
+
+ lwz r3,8(r31) // r3 <= that
+ lwz r4,12(r31) // r4 <= methodIndex
+ lwz r5,0(r3) // r5 <= vtable ( == *that )
+ slwi r4,r4,2 // convert to offset ( *= 4 )
+ lwzx r0,r5,r4 // r0 <= methodpointer ( == vtable + offset )
+
+ lwz r4,4(r30) // load GP regs with method parameters
+ lwz r5,8(r30)
+ lwz r6,12(r30)
+ lwz r7,16(r30)
+ lwz r8,20(r30)
+ lwz r9,24(r30)
+ lwz r10,28(r30)
+
+ mtlr r0 // copy methodpointer to LR
+ blrl // call method
+
+ lwz r30,16(r31) // restore r30 & r31
+ lwz r31,20(r31)
+
+ lwz r11,0(sp) // clean up the stack
+ lwz r0,4(r11)
+ mtlr r0
+ mr sp,r11
+ blr
+
+// Magic indicating no need for an executable stack
+.section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_rhapsody.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_rhapsody.s
new file mode 100644
index 000000000..1dc12b2b3
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc_rhapsody.s
@@ -0,0 +1,142 @@
+#
+# -*- Mode: Asm -*-
+#
+# 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/.
+
+#
+# ** Assumed vtable layout (obtained by disassembling with gdb):
+# ** 4 bytes per vtable entry, skip 0th and 1st entries, so the mapping
+# ** from index to entry is (4 * index) + 8.
+#
+
+.text
+ .align 2
+#
+# NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+.globl __NS_InvokeByIndex
+__NS_InvokeByIndex:
+ mflr r0
+ stw r31,-4(r1)
+#
+# save off the incoming values in the callers parameter area
+#
+ stw r3,24(r1) ; that
+ stw r4,28(r1) ; methodIndex
+ stw r5,32(r1) ; paramCount
+ stw r6,36(r1) ; params
+ stw r0,8(r1)
+ stwu r1,-144(r1) ; 24 for linkage area,
+ ; 8*13 for fprData area,
+ ; 8 for saved registers,
+ ; 8 to keep stack 16-byte aligned
+
+# set up for and call 'invoke_count_words' to get new stack size
+#
+ mr r3,r5
+ mr r4,r6
+
+ stwu r1,-24(r1)
+ bl L_invoke_count_words$stub
+ lwz r1,0(r1)
+
+# prepare args for 'invoke_copy_to_stack' call
+#
+ lwz r4,176(r1) ; paramCount
+ lwz r5,180(r1) ; params
+ mr r6,r1 ; fprData
+ slwi r3,r3,2 ; number of stack bytes required
+ addi r3,r3,28 ; linkage area
+ mr r31,r1 ; save original stack top
+ sub r1,r1,r3 ; bump the stack
+ clrrwi r1,r1,4 ; keep the stack 16-byte aligned
+ addi r3,r31,144 ; act like real alloca, so 0(sp) always
+ stw r3,0(r1) ; points back to previous stack frame
+ addi r3,r1,28 ; parameter pointer excludes linkage area size + 'this'
+
+# create "temporary" stack frame for _invoke_copy_to_stack to operate in.
+ stwu r1,-40(r1)
+ bl L_invoke_copy_to_stack$stub
+# remove temporary stack frame.
+ lwz r1,0(r1)
+
+ lfd f1,0(r31)
+ lfd f2,8(r31)
+ lfd f3,16(r31)
+ lfd f4,24(r31)
+ lfd f5,32(r31)
+ lfd f6,40(r31)
+ lfd f7,48(r31)
+ lfd f8,56(r31)
+ lfd f9,64(r31)
+ lfd f10,72(r31)
+ lfd f11,80(r31)
+ lfd f12,88(r31)
+ lfd f13,96(r31)
+
+ lwz r3,168(r31) ; that
+ lwz r4,0(r3) ; get vTable from 'that'
+ lwz r5,172(r31) ; methodIndex
+ slwi r5,r5,2 ; methodIndex * 4
+ lwzx r12,r5,r4 ; get function pointer
+
+ lwz r4,28(r1)
+ lwz r5,32(r1)
+ lwz r6,36(r1)
+ lwz r7,40(r1)
+ lwz r8,44(r1)
+ lwz r9,48(r1)
+ lwz r10,52(r1)
+
+ mtlr r12
+ blrl
+
+ mr r1,r31
+ lwz r0,152(r1)
+ addi r1,r1,144
+ mtlr r0
+ lwz r31,-4(r1)
+
+ blr
+
+.picsymbol_stub
+L_invoke_count_words$stub:
+ .indirect_symbol _invoke_count_words
+ mflr r0
+ bcl 20,31,L1$pb
+L1$pb:
+ mflr r11
+ addis r11,r11,ha16(L1$lz-L1$pb)
+ mtlr r0
+ lwz r12,lo16(L1$lz-L1$pb)(r11)
+ mtctr r12
+ addi r11,r11,lo16(L1$lz-L1$pb)
+ bctr
+.lazy_symbol_pointer
+L1$lz:
+ .indirect_symbol _invoke_count_words
+ .long dyld_stub_binding_helper
+
+
+.picsymbol_stub
+L_invoke_copy_to_stack$stub:
+ .indirect_symbol _invoke_copy_to_stack
+ mflr r0
+ bcl 20,31,L2$pb
+L2$pb:
+ mflr r11
+ addis r11,r11,ha16(L2$lz-L2$pb)
+ mtlr r0
+ lwz r12,lo16(L2$lz-L2$pb)(r11)
+ mtctr r12
+ addi r11,r11,lo16(L2$lz-L2$pb)
+ bctr
+.lazy_symbol_pointer
+L2$lz:
+ .indirect_symbol _invoke_copy_to_stack
+ .long dyld_stub_binding_helper
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc64_openbsd.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc64_openbsd.s
new file mode 100644
index 000000000..dfd6189f8
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc64_openbsd.s
@@ -0,0 +1,86 @@
+/* -*- Mode: asm; 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/. */
+
+/*
+ Platform specific code to invoke XPCOM methods on native objects
+ for sparcv9 Solaris.
+
+ See the SPARC Compliance Definition (SCD) Chapter 3
+ for more information about what is going on here, including
+ the use of BIAS (0x7ff).
+ The SCD is available from http://www.sparc.com/.
+*/
+
+ .global NS_InvokeByIndex
+ .type NS_InvokeByIndex, #function
+
+/*
+ NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+*/
+NS_InvokeByIndex:
+ save %sp,-(128 + 64),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 64
+ sll %i2,4,%l0 ! assume the worst case
+ ! paramCount * 2 * 8 bytes
+ cmp %l0, 0 ! are there any args? If not,
+ be .invoke ! no need to copy args to stack
+ nop
+
+ sub %sp,%l0,%sp ! create the additional stack space
+ add %sp,0x7ff+136,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+
+!
+! load arguments from stack into the outgoing registers
+! BIAS is 0x7ff (2047)
+!
+
+! load the %o1..5 64bit (extended word) output registers registers
+ ldx [%sp + 0x7ff + 136],%o1 ! %i1
+ ldx [%sp + 0x7ff + 144],%o2 ! %i2
+ ldx [%sp + 0x7ff + 152],%o3 ! %i3
+ ldx [%sp + 0x7ff + 160],%o4 ! %i4
+ ldx [%sp + 0x7ff + 168],%o5 ! %i5
+
+! load the even number double registers starting with %f2
+ ldd [%sp + 0x7ff + 136],%f2
+ ldd [%sp + 0x7ff + 144],%f4
+ ldd [%sp + 0x7ff + 152],%f6
+ ldd [%sp + 0x7ff + 160],%f8
+ ldd [%sp + 0x7ff + 168],%f10
+ ldd [%sp + 0x7ff + 176],%f12
+ ldd [%sp + 0x7ff + 184],%f14
+ ldd [%sp + 0x7ff + 192],%f16
+ ldd [%sp + 0x7ff + 200],%f18
+ ldd [%sp + 0x7ff + 208],%f20
+ ldd [%sp + 0x7ff + 216],%f22
+ ldd [%sp + 0x7ff + 224],%f24
+ ldd [%sp + 0x7ff + 232],%f26
+ ldd [%sp + 0x7ff + 240],%f28
+ ldd [%sp + 0x7ff + 248],%f30
+
+!
+! calculate the target address from the vtable
+!
+.invoke:
+ sll %i1,3,%l0 ! index *= 8
+! add %l0,16,%l0 ! there are 2 extra entries in the vTable (16bytes)
+ ldx [%i0],%l1 ! *that --> address of vtable
+ ldx [%l0 + %l1],%l0 ! that->vtable[index * 8 + 16] --> address
+
+ jmpl %l0,%o7 ! call the routine
+ mov %i0,%o0 ! move 'this' pointer to out register
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
+
+ .size NS_InvokeByIndex, .-NS_InvokeByIndex
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_linux_GCC3.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_linux_GCC3.s
new file mode 100644
index 000000000..36196805e
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_linux_GCC3.s
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/*
+ * Platform specific code to invoke XPCOM methods on native objects for
+ * Linux/Sparc with gcc 3 ABI.
+ */
+ .global NS_InvokeByIndex
+/*
+ * NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ * uint32_t paramCount, nsXPTCVariant* params);
+ *
+ */
+NS_InvokeByIndex:
+ save %sp,-(64 + 16),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 16
+ mov %i2,%o0 ! paramCount
+ call invoke_count_words ! returns the required stack size in %o0
+ mov %i3,%o1 ! params
+
+ sll %o0,2,%l0 ! number of bytes
+ sub %sp,%l0,%sp ! create the additional stack space
+
+ mov %sp,%o0 ! pointer for copied args
+ add %o0,72,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+!
+! calculate the target address from the vtable
+!
+ ld [%i0],%l1 ! *that --> vTable
+ sll %i1,2,%i1 ! multiply index by 4
+ add %i1,%l1,%l1 ! l1 now points to vTable entry
+ ld [%l1],%l0 ! target address
+
+.L5: ld [%sp + 88],%o5
+.L4: ld [%sp + 84],%o4
+.L3: ld [%sp + 80],%o3
+.L2: ld [%sp + 76],%o2
+.L1: ld [%sp + 72],%o1
+.L0:
+ jmpl %l0,%o7 ! call the routine
+! always have a 'this', from the incoming 'that'
+ mov %i0,%o0
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_netbsd.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_netbsd.s
new file mode 100644
index 000000000..893432a5e
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_netbsd.s
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+ .global XPTC_InvokeByIndex
+/*
+ XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+*/
+XPTC_InvokeByIndex:
+ save %sp,-(64 + 16),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 16
+ mov %i2,%o0 ! paramCount
+ call invoke_count_words ! returns the required stack size in %o0
+ mov %i3,%o1 ! params
+
+ sll %o0,2,%l0 ! number of bytes
+ sub %sp,%l0,%sp ! create the additional stack space
+
+ mov %sp,%o0 ! pointer for copied args
+ add %o0,72,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+!
+! calculate the target address from the vtable
+!
+ add %i1,1,%i1 ! vTable is zero-based, index is 1 based (?)
+ ld [%i0],%l1 ! *that --> vTable
+ sll %i1,3,%i1
+ add %i1,%l1,%l1 ! vTable[index * 8], l1 now points to vTable entry
+ lduh [%l1],%l0 ! this adjustor
+ sll %l0,16,%l0 ! sign extend to 32 bits
+ sra %l0,16,%l0
+ add %l0,%i0,%i0 ! adjust this
+ ld [%l1 + 4],%l0 ! target address
+
+.L5: ld [%sp + 88],%o5
+.L4: ld [%sp + 84],%o4
+.L3: ld [%sp + 80],%o3
+.L2: ld [%sp + 76],%o2
+.L1: ld [%sp + 72],%o1
+.L0:
+ jmpl %l0,%o7 ! call the routine
+! always have a 'this', from the incoming 'that'
+ mov %i0,%o0
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_openbsd.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_openbsd.s
new file mode 100644
index 000000000..1ef695370
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_openbsd.s
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+ .global XPTC_InvokeByIndex
+/*
+ XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+*/
+XPTC_InvokeByIndex:
+ save %sp,-(64 + 16),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 16
+ mov %i2,%o0 ! paramCount
+ call invoke_count_words ! returns the required stack size in %o0
+ mov %i3,%o1 ! params
+
+ sll %o0,2,%l0 ! number of bytes
+ sub %sp,%l0,%sp ! create the additional stack space
+
+ mov %sp,%o0 ! pointer for copied args
+ add %o0,72,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+!
+! calculate the target address from the vtable
+!
+ add %i1,1,%i1 ! vTable is zero-based, index is 1 based (?)
+ ld [%i0],%l1 ! *that --> vTable
+ sll %i1,3,%i1
+ add %i1,%l1,%l1 ! vTable[index * 8], l1 now points to vTable entry
+ lduh [%l1],%l0 ! this adjustor
+ sll %l0,16,%l0 ! sign extend to 32 bits
+ sra %l0,16,%l0
+ add %l0,%i0,%i0 ! adjust this
+ ld [%l1 + 4],%l0 ! target address
+
+.L5: ld [%sp + 88],%o5
+.L4: ld [%sp + 84],%o4
+.L3: ld [%sp + 80],%o3
+.L2: ld [%sp + 76],%o2
+.L1: ld [%sp + 72],%o1
+.L0:
+ jmpl %l0,%o7 ! call the routine
+! always have a 'this', from the incoming 'that'
+ mov %i0,%o0
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_GCC3.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_GCC3.s
new file mode 100644
index 000000000..54adcd147
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_GCC3.s
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/*
+ * Platform specific code to invoke XPCOM methods on native objects for
+ * solaris/sparc with gcc 3 ABI.
+ */
+ .global NS_InvokeByIndex
+/*
+ * NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ * uint32_t paramCount, nsXPTCVariant* params);
+ */
+NS_InvokeByIndex:
+ save %sp,-(64 + 32),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 32
+ mov %i2,%o0 ! paramCount
+ call invoke_count_words ! returns the required stack size in %o0
+ mov %i3,%o1 ! params
+
+ sll %o0,2,%l0 ! number of bytes
+ sub %sp,%l0,%sp ! create the additional stack space
+
+ mov %sp,%o0 ! pointer for copied args
+ add %o0,72,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+!
+! calculate the target address from the vtable
+!
+ ld [%i0],%l1 ! *that --> vTable
+ sll %i1,2,%i1 ! multiply index by 4
+ add %i1,%l1,%l1 ! l1 now points to vTable entry
+ ld [%l1],%l0 ! target address
+
+.L5: ld [%sp + 88],%o5
+.L4: ld [%sp + 84],%o4
+.L3: ld [%sp + 80],%o3
+.L2: ld [%sp + 76],%o2
+.L1: ld [%sp + 72],%o1
+.L0:
+ jmpl %l0,%o7 ! call the routine
+! always have a 'this', from the incoming 'that'
+ mov %i0,%o0
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_SUNW.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_SUNW.s
new file mode 100644
index 000000000..013770392
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparc_solaris_SUNW.s
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+ .global NS_InvokeByIndex
+ .type NS_InvokeByIndex, #function
+/*
+ NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+*/
+NS_InvokeByIndex:
+ save %sp,-(64 + 32),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 32
+ sll %i2,3,%l0 ! assume the worst case
+ ! paramCount * 2 * 4 bytes
+ cmp %l0, 0 ! are there any args? If not,
+ be .invoke ! no need to copy args to stack
+
+ sub %sp,%l0,%sp ! create the additional stack space
+ add %sp,72,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+
+!
+! load arguments from stack into the outgoing registers
+!
+ ld [%sp + 72],%o1
+ ld [%sp + 76],%o2
+ ld [%sp + 80],%o3
+ ld [%sp + 84],%o4
+ ld [%sp + 88],%o5
+
+!
+! calculate the target address from the vtable
+!
+.invoke:
+ sll %i1,2,%l0 ! index *= 4
+ add %l0,8,%l0 ! there are 2 extra entries in the vTable
+ ld [%i0],%l1 ! *that --> address of vtable
+ ld [%l0 + %l1],%l0 ! that->vtable[index * 4 + 8] --> address
+
+ jmpl %l0,%o7 ! call the routine
+ mov %i0,%o0 ! move 'this' pointer to out register
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
+
+ .size NS_InvokeByIndex, .-NS_InvokeByIndex
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparcv9_solaris_SUNW.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparcv9_solaris_SUNW.s
new file mode 100644
index 000000000..34861abc0
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_sparcv9_solaris_SUNW.s
@@ -0,0 +1,85 @@
+/* -*- Mode: asm; 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/. */
+
+/*
+ Platform specific code to invoke XPCOM methods on native objects
+ for sparcv9 Solaris.
+
+ See the SPARC Compliance Definition (SCD) Chapter 3
+ for more information about what is going on here, including
+ the use of BIAS (0x7ff).
+ The SCD is available from http://www.sparc.com/.
+*/
+
+ .global NS_InvokeByIndex
+ .type NS_InvokeByIndex, #function
+
+/*
+ NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+*/
+NS_InvokeByIndex:
+ save %sp,-(128 + 64),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 64
+ sll %i2,4,%l0 ! assume the worst case
+ ! paramCount * 2 * 8 bytes
+ cmp %l0, 0 ! are there any args? If not,
+ be .invoke ! no need to copy args to stack
+
+ sub %sp,%l0,%sp ! create the additional stack space
+ add %sp,0x7ff+136,%o0 ! step past the register window, the
+ ! struct result pointer and the 'this' slot
+ mov %i2,%o1 ! paramCount
+ call invoke_copy_to_stack
+ mov %i3,%o2 ! params
+
+!
+! load arguments from stack into the outgoing registers
+! BIAS is 0x7ff (2047)
+!
+
+! load the %o1..5 64bit (extended word) output registers registers
+ ldx [%sp + 0x7ff + 136],%o1 ! %i1
+ ldx [%sp + 0x7ff + 144],%o2 ! %i2
+ ldx [%sp + 0x7ff + 152],%o3 ! %i3
+ ldx [%sp + 0x7ff + 160],%o4 ! %i4
+ ldx [%sp + 0x7ff + 168],%o5 ! %i5
+
+! load the even number double registers starting with %d2
+ ldd [%sp + 0x7ff + 136],%d2
+ ldd [%sp + 0x7ff + 144],%d4
+ ldd [%sp + 0x7ff + 152],%d6
+ ldd [%sp + 0x7ff + 160],%d8
+ ldd [%sp + 0x7ff + 168],%d10
+ ldd [%sp + 0x7ff + 176],%d12
+ ldd [%sp + 0x7ff + 184],%d14
+ ldd [%sp + 0x7ff + 192],%d16
+ ldd [%sp + 0x7ff + 200],%d18
+ ldd [%sp + 0x7ff + 208],%d20
+ ldd [%sp + 0x7ff + 216],%d22
+ ldd [%sp + 0x7ff + 224],%d24
+ ldd [%sp + 0x7ff + 232],%d26
+ ldd [%sp + 0x7ff + 240],%d28
+ ldd [%sp + 0x7ff + 248],%d30
+
+!
+! calculate the target address from the vtable
+!
+.invoke:
+ sll %i1,3,%l0 ! index *= 8
+ add %l0,16,%l0 ! there are 2 extra entries in the vTable (16bytes)
+ ldx [%i0],%l1 ! *that --> address of vtable
+ ldx [%l0 + %l1],%l0 ! that->vtable[index * 8 + 16] --> address
+
+ jmpl %l0,%o7 ! call the routine
+ mov %i0,%o0 ! move 'this' pointer to out register
+
+ mov %o0,%i0 ! propagate return value
+ ret
+ restore
+
+ .size NS_InvokeByIndex, .-NS_InvokeByIndex
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_solaris_SUNW.s b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_solaris_SUNW.s
new file mode 100644
index 000000000..af665a162
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_solaris_SUNW.s
@@ -0,0 +1,55 @@
+ .globl NS_InvokeByIndex
+ .type NS_InvokeByIndex, @function
+NS_InvokeByIndex:
+ push %ebp
+ movl %esp,%ebp
+ push %ebx
+ call .CG0.66
+.CG0.66:
+ pop %ebx
+ addl $_GLOBAL_OFFSET_TABLE_+0x1,%ebx
+ push 20(%ebp)
+ push 16(%ebp)
+ push 12(%ebp)
+ push 8(%ebp)
+ / INLINE: invoke_by_index
+
+
+
+ pushl %ebx
+ pushl %esi
+ movl %esp, %ebx
+
+ pushl 0x14(%ebp)
+ pushl 0x10(%ebp)
+ call invoke_count_words
+ mov %ebx, %esp
+
+ sall $0x2 , %eax
+ subl %eax, %esp
+ movl %esp, %esi
+
+ pushl %esp
+ pushl 0x14(%ebp)
+ pushl 0x10(%ebp)
+ call invoke_copy_to_stack
+ movl %esi, %esp
+
+ movl 0x8(%ebp), %ecx
+ pushl %ecx
+ movl (%ecx), %edx
+ movl 0xc(%ebp), %eax
+ movl 0x8(%edx, %eax, 4), %edx
+
+ call *%edx
+ mov %ebx, %esp
+ popl %esi
+ popl %ebx
+ / INLINE_END
+ addl $16,%esp
+ pop %ebx
+ movl %ebp,%esp
+ pop %ebp
+ ret
+ .size NS_InvokeByIndex, . - NS_InvokeByIndex
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_darwin.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_darwin.cpp
new file mode 100644
index 000000000..04407db39
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_darwin.cpp
@@ -0,0 +1,16 @@
+/* -*- Mode: C -*- */
+/* 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/. */
+
+#if defined(__i386__)
+#include "xptcinvoke_gcc_x86_unix.cpp"
+#elif defined(__x86_64__)
+#include "xptcinvoke_x86_64_unix.cpp"
+#elif defined(__ppc__)
+#include "xptcinvoke_ppc_rhapsody.cpp"
+#elif defined(__arm__)
+#include "xptcinvoke_arm.cpp"
+#else
+#error unknown cpu architecture
+#endif
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp
new file mode 100644
index 000000000..d4a8a12fb
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+#include "xptc_gcc_x86_unix.h"
+
+extern "C" {
+static void ATTRIBUTE_USED __attribute__ ((regparm(3)))
+invoke_copy_to_stack(uint32_t paramCount, nsXPTCVariant* s, uint32_t* d)
+{
+ for(uint32_t i = paramCount; i >0; i--, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ default : *((void**)d) = s->val.p; break;
+ }
+ }
+}
+} // extern "C"
+
+/*
+ EXPORT_XPCOM_API(nsresult)
+ NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+ Each param takes at most two 4-byte words.
+ It doesn't matter if we push too many words, and calculating the exact
+ amount takes time.
+
+ that = ebp + 0x08
+ methodIndex = ebp + 0x0c
+ paramCount = ebp + 0x10
+ params = ebp + 0x14
+
+*/
+
+__asm__ (
+ ".text\n\t"
+/* alignment here seems unimportant here; this was 16, now it's 2 which
+ is what xptcstubs uses. */
+ ".align 2\n\t"
+ ".globl " SYMBOL_UNDERSCORE "NS_InvokeByIndex\n\t"
+#ifndef XP_MACOSX
+ ".type " SYMBOL_UNDERSCORE "NS_InvokeByIndex,@function\n"
+#endif
+ SYMBOL_UNDERSCORE "NS_InvokeByIndex:\n\t"
+ "pushl %ebp\n\t"
+ "movl %esp, %ebp\n\t"
+ "movl 0x10(%ebp), %eax\n\t"
+ "leal 0(,%eax,8),%edx\n\t"
+
+ /* set up call frame for method. */
+ "subl %edx, %esp\n\t" /* make room for params. */
+/* Align to maximum x86 data size: 128 bits == 16 bytes == XMM register size.
+ * This is to avoid protection faults where SSE+ alignment of stack pointer
+ * is assumed and required, e.g. by GCC4's -ftree-vectorize option.
+ */
+ "andl $0xfffffff0, %esp\n\t" /* drop(?) stack ptr to 128-bit align */
+/* $esp should be aligned to a 16-byte boundary here (note we include an
+ * additional 4 bytes in a later push instruction). This will ensure $ebp
+ * in the function called below is aligned to a 0x8 boundary. SSE instructions
+ * like movapd/movdqa expect memory operand to be aligned on a 16-byte
+ * boundary. The GCC compiler will generate the memory operand using $ebp
+ * with an 8-byte offset.
+ */
+ "subl $0xc, %esp\n\t" /* lower again; push/call below will re-align */
+ "movl %esp, %ecx\n\t" /* ecx = d */
+ "movl 8(%ebp), %edx\n\t" /* edx = this */
+ "pushl %edx\n\t" /* push this. esp % 16 == 0 */
+
+ "movl 0x14(%ebp), %edx\n\t"
+ "call " SYMBOL_UNDERSCORE "invoke_copy_to_stack\n\t"
+ "movl 0x08(%ebp), %ecx\n\t" /* 'that' */
+ "movl (%ecx), %edx\n\t"
+ "movl 0x0c(%ebp), %eax\n\t" /* function index */
+ "leal (%edx,%eax,4), %edx\n\t"
+ "call *(%edx)\n\t"
+ "movl %ebp, %esp\n\t"
+ "popl %ebp\n\t"
+ "ret\n"
+#ifndef XP_MACOSX
+ ".size " SYMBOL_UNDERSCORE "NS_InvokeByIndex, . -" SYMBOL_UNDERSCORE "NS_InvokeByIndex\n\t"
+#endif
+);
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf32.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf32.cpp
new file mode 100644
index 000000000..1500f3175
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf32.cpp
@@ -0,0 +1,132 @@
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "xptcprivate.h"
+
+#include <iostream.h>
+
+// "This code is for IA64 only"
+
+
+/* invoke_copy_to_stack() will copy from variant array 's' to
+ * the stack argument area 'mloc', the integer register area 'iloc', and
+ * the float register area 'floc'.
+ *
+ */
+extern "C" void
+invoke_copy_to_stack(uint64_t* mloc, uint64_t* iloc, uint64_t* floc,
+ const uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint64_t* dest = mloc;
+ uint32_t len = paramCount;
+ nsXPTCVariant* source = s;
+
+ uint32_t indx;
+ uint32_t endlen;
+ endlen = (len > 7) ? 7 : len;
+ /* handle the memory arguments */
+ for (indx = 7; indx < len; ++indx)
+ {
+ if (source[indx].IsPtrData())
+ {
+#ifdef __LP64__
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].ptr;
+#else
+ /* 32 bit pointer mode */
+ uint32_t* adr = (uint32_t*) dest;
+ *(adr) = 0;
+ *(adr+1) = (uint32_t) source[indx].ptr;
+#endif
+ }
+ else
+ switch (source[indx].type)
+ {
+ case nsXPTType::T_I8 : *(dest) = source[indx].val.i8; break;
+ case nsXPTType::T_I16 : *(dest) = source[indx].val.i16; break;
+ case nsXPTType::T_I32 : *(dest) = source[indx].val.i32; break;
+ case nsXPTType::T_I64 : *(dest) = source[indx].val.i64; break;
+ case nsXPTType::T_U8 : *(dest) = source[indx].val.u8; break;
+ case nsXPTType::T_U16 : *(dest) = source[indx].val.u16; break;
+ case nsXPTType::T_U32 : *(dest) = source[indx].val.u32; break;
+ case nsXPTType::T_U64 : *(dest) = source[indx].val.u64; break;
+ case nsXPTType::T_FLOAT : *(dest) = source[indx].val.u32; break;
+ case nsXPTType::T_DOUBLE: *(dest) = source[indx].val.u64; break;
+ case nsXPTType::T_BOOL : *(dest) = source[indx].val.b; break;
+ case nsXPTType::T_CHAR : *(dest) = source[indx].val.c; break;
+ case nsXPTType::T_WCHAR : *(dest) = source[indx].val.wc; break;
+ default:
+ // all the others are plain pointer types
+#ifdef __LP64__
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].val.p;
+#else
+ {
+ /* 32 bit pointer mode */
+ uint32_t* adr = (uint32_t*) dest;
+ *(adr) = 0;
+ *(adr+1) = (uint32_t) source[indx].val.p;
+ }
+#endif
+ }
+ ++dest;
+ }
+ /* process register arguments */
+ dest = iloc;
+ for (indx = 0; indx < endlen; ++indx)
+ {
+ if (source[indx].IsPtrData())
+ {
+#ifdef __LP64__
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].ptr;
+#else
+ /* 32 bit pointer mode */
+ uint32_t* adr = (uint32_t*) dest;
+ *(adr) = 0;
+ *(adr+1) = (uint32_t) source[indx].ptr;
+#endif
+ }
+ else
+ switch (source[indx].type)
+ {
+ case nsXPTType::T_I8 : *(dest) = source[indx].val.i8; break;
+ case nsXPTType::T_I16 : *(dest) = source[indx].val.i16; break;
+ case nsXPTType::T_I32 : *(dest) = source[indx].val.i32; break;
+ case nsXPTType::T_I64 : *(dest) = source[indx].val.i64; break;
+ case nsXPTType::T_U8 : *(dest) = source[indx].val.u8; break;
+ case nsXPTType::T_U16 : *(dest) = source[indx].val.u16; break;
+ case nsXPTType::T_U32 : *(dest) = source[indx].val.u32; break;
+ case nsXPTType::T_U64 : *(dest) = source[indx].val.u64; break;
+ case nsXPTType::T_FLOAT :
+ *((double*) (floc++)) = (double) source[indx].val.f;
+ break;
+ case nsXPTType::T_DOUBLE:
+ *((double*) (floc++)) = source[indx].val.d;
+ break;
+ case nsXPTType::T_BOOL : *(dest) = source[indx].val.b; break;
+ case nsXPTType::T_CHAR : *(dest) = source[indx].val.c; break;
+ case nsXPTType::T_WCHAR : *(dest) = source[indx].val.wc; break;
+ default:
+ // all the others are plain pointer types
+#ifdef __LP64__
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].val.p;
+#else
+ {
+ /* 32 bit pointer mode */
+ uint32_t* adr = (uint32_t*) dest;
+ *(adr) = 0;
+ *(adr+1) = (uint32_t) source[indx].val.p;
+ }
+#endif
+ }
+ ++dest;
+ }
+
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf64.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf64.cpp
new file mode 100644
index 000000000..c2a4dd088
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ipf64.cpp
@@ -0,0 +1,100 @@
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "xptcprivate.h"
+
+#include <stdint.h>
+
+// "This code is for IA64 only"
+
+
+/* invoke_copy_to_stack() will copy from variant array 's' to
+ * the stack argument area 'mloc', the integer register area 'iloc', and
+ * the float register area 'floc'.
+ *
+ */
+extern "C" void
+invoke_copy_to_stack(uint64_t* mloc, uint64_t* iloc, uint64_t* floc,
+ const uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint64_t* dest = mloc;
+ uint32_t len = paramCount;
+ nsXPTCVariant* source = s;
+
+ uint32_t indx;
+ uint32_t endlen;
+ endlen = (len > 7) ? 7 : len;
+ /* handle the memory arguments */
+ for (indx = 7; indx < len; ++indx)
+ {
+ if (source[indx].IsPtrData())
+ {
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].ptr;
+ }
+ else
+ switch (source[indx].type)
+ {
+ case nsXPTType::T_I8 : *(dest) = source[indx].val.i8; break;
+ case nsXPTType::T_I16 : *(dest) = source[indx].val.i16; break;
+ case nsXPTType::T_I32 : *(dest) = source[indx].val.i32; break;
+ case nsXPTType::T_I64 : *(dest) = source[indx].val.i64; break;
+ case nsXPTType::T_U8 : *(dest) = source[indx].val.u8; break;
+ case nsXPTType::T_U16 : *(dest) = source[indx].val.u16; break;
+ case nsXPTType::T_U32 : *(dest) = source[indx].val.u32; break;
+ case nsXPTType::T_U64 : *(dest) = source[indx].val.u64; break;
+ case nsXPTType::T_FLOAT : *(dest) = source[indx].val.u32; break;
+ case nsXPTType::T_DOUBLE: *(dest) = source[indx].val.u64; break;
+ case nsXPTType::T_BOOL : *(dest) = source[indx].val.b; break;
+ case nsXPTType::T_CHAR : *(dest) = source[indx].val.c; break;
+ case nsXPTType::T_WCHAR : *(dest) = source[indx].val.wc; break;
+ default:
+ // all the others are plain pointer types
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].val.p;
+ }
+ ++dest;
+ }
+ /* process register arguments */
+ dest = iloc;
+ for (indx = 0; indx < endlen; ++indx)
+ {
+ if (source[indx].IsPtrData())
+ {
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].ptr;
+ }
+ else
+ switch (source[indx].type)
+ {
+ case nsXPTType::T_I8 : *(dest) = source[indx].val.i8; break;
+ case nsXPTType::T_I16 : *(dest) = source[indx].val.i16; break;
+ case nsXPTType::T_I32 : *(dest) = source[indx].val.i32; break;
+ case nsXPTType::T_I64 : *(dest) = source[indx].val.i64; break;
+ case nsXPTType::T_U8 : *(dest) = source[indx].val.u8; break;
+ case nsXPTType::T_U16 : *(dest) = source[indx].val.u16; break;
+ case nsXPTType::T_U32 : *(dest) = source[indx].val.u32; break;
+ case nsXPTType::T_U64 : *(dest) = source[indx].val.u64; break;
+ case nsXPTType::T_FLOAT :
+ *((double*) (floc++)) = (double) source[indx].val.f;
+ break;
+ case nsXPTType::T_DOUBLE:
+ *((double*) (floc++)) = source[indx].val.d;
+ break;
+ case nsXPTType::T_BOOL : *(dest) = source[indx].val.b; break;
+ case nsXPTType::T_CHAR : *(dest) = source[indx].val.c; break;
+ case nsXPTType::T_WCHAR : *(dest) = source[indx].val.wc; break;
+ default:
+ // all the others are plain pointer types
+ /* 64 bit pointer mode */
+ *((void**) dest) = source[indx].val.p;
+ }
+ ++dest;
+ }
+
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_alpha.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_alpha.cpp
new file mode 100644
index 000000000..dc111e435
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_alpha.cpp
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+/* Prototype specifies unmangled function name and disables unused warning */
+static void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+__asm__("invoke_copy_to_stack") __attribute__((used));
+
+static void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ const uint8_t NUM_ARG_REGS = 6-1; // -1 for "this" pointer
+
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *d = (uint64_t)s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *d = (uint64_t)s->val.i8; break;
+ case nsXPTType::T_I16 : *d = (uint64_t)s->val.i16; break;
+ case nsXPTType::T_I32 : *d = (uint64_t)s->val.i32; break;
+ case nsXPTType::T_I64 : *d = (uint64_t)s->val.i64; break;
+ case nsXPTType::T_U8 : *d = (uint64_t)s->val.u8; break;
+ case nsXPTType::T_U16 : *d = (uint64_t)s->val.u16; break;
+ case nsXPTType::T_U32 : *d = (uint64_t)s->val.u32; break;
+ case nsXPTType::T_U64 : *d = (uint64_t)s->val.u64; break;
+ case nsXPTType::T_FLOAT :
+ if(i < NUM_ARG_REGS)
+ {
+ // convert floats to doubles if they are to be passed
+ // via registers so we can just deal with doubles later
+ union { uint64_t u64; double d; } t;
+ t.d = (double)s->val.f;
+ *d = t.u64;
+ }
+ else
+ // otherwise copy to stack normally
+ *d = (uint64_t)s->val.u32;
+ break;
+ case nsXPTType::T_DOUBLE : *d = (uint64_t)s->val.u64; break;
+ case nsXPTType::T_BOOL : *d = (uint64_t)s->val.b; break;
+ case nsXPTType::T_CHAR : *d = (uint64_t)s->val.c; break;
+ case nsXPTType::T_WCHAR : *d = (uint64_t)s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *d = (uint64_t)s->val.p;
+ break;
+ }
+ }
+}
+
+/*
+ * EXPORT_XPCOM_API(nsresult)
+ * NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ * uint32_t paramCount, nsXPTCVariant* params)
+ */
+__asm__(
+ "#### NS_InvokeByIndex ####\n"
+".text\n\t"
+ ".align 5\n\t"
+ ".globl NS_InvokeByIndex\n\t"
+ ".ent NS_InvokeByIndex\n"
+"NS_InvokeByIndex:\n\t"
+ ".frame $15,32,$26,0\n\t"
+ ".mask 0x4008000,-32\n\t"
+ "ldgp $29,0($27)\n"
+"$NS_InvokeByIndex..ng:\n\t"
+ "subq $30,32,$30\n\t"
+ "stq $26,0($30)\n\t"
+ "stq $15,8($30)\n\t"
+ "bis $30,$30,$15\n\t"
+ ".prologue 1\n\t"
+
+ /*
+ * Allocate enough stack space to hold the greater of 6 or "paramCount"+1
+ * parameters. (+1 for "this" pointer) Room for at least 6 parameters
+ * is required for storage of those passed via registers.
+ */
+
+ "bis $31,5,$2\n\t" /* count = MAX(5, "paramCount") */
+ "cmplt $2,$18,$1\n\t"
+ "cmovne $1,$18,$2\n\t"
+ "s8addq $2,16,$1\n\t" /* room for count+1 params (8 bytes each) */
+ "bic $1,15,$1\n\t" /* stack space is rounded up to 0 % 16 */
+ "subq $30,$1,$30\n\t"
+
+ "stq $16,0($30)\n\t" /* save "that" (as "this" pointer) */
+ "stq $17,16($15)\n\t" /* save "methodIndex" */
+
+ "addq $30,8,$16\n\t" /* pass stack pointer */
+ "bis $18,$18,$17\n\t" /* pass "paramCount" */
+ "bis $19,$19,$18\n\t" /* pass "params" */
+ "bsr $26,$invoke_copy_to_stack..ng\n\t" /* call invoke_copy_to_stack */
+
+ /*
+ * Copy the first 6 parameters to registers and remove from stack frame.
+ * Both the integer and floating point registers are set for each parameter
+ * except the first which is the "this" pointer. (integer only)
+ * The floating point registers are all set as doubles since the
+ * invoke_copy_to_stack function should have converted the floats.
+ */
+ "ldq $16,0($30)\n\t" /* integer registers */
+ "ldq $17,8($30)\n\t"
+ "ldq $18,16($30)\n\t"
+ "ldq $19,24($30)\n\t"
+ "ldq $20,32($30)\n\t"
+ "ldq $21,40($30)\n\t"
+ "ldt $f17,8($30)\n\t" /* floating point registers */
+ "ldt $f18,16($30)\n\t"
+ "ldt $f19,24($30)\n\t"
+ "ldt $f20,32($30)\n\t"
+ "ldt $f21,40($30)\n\t"
+
+ "addq $30,48,$30\n\t" /* remove params from stack */
+
+ /*
+ * Call the virtual function with the constructed stack frame.
+ */
+ "bis $16,$16,$1\n\t" /* load "this" */
+ "ldq $2,16($15)\n\t" /* load "methodIndex" */
+ "ldq $1,0($1)\n\t" /* load vtable */
+ "s8addq $2,$31,$2\n\t" /* vtable index = "methodIndex" * 8 */
+ "addq $1,$2,$1\n\t"
+ "ldq $27,0($1)\n\t" /* load address of function */
+ "jsr $26,($27),0\n\t" /* call virtual function */
+ "ldgp $29,0($26)\n\t"
+
+ "bis $15,$15,$30\n\t"
+ "ldq $26,0($30)\n\t"
+ "ldq $15,8($30)\n\t"
+ "addq $30,32,$30\n\t"
+ "ret $31,($26),1\n\t"
+ ".end NS_InvokeByIndex"
+ );
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_m68k.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_m68k.cpp
new file mode 100644
index 000000000..6f2934cbc
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_m68k.cpp
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+// Remember that these 'words' are 32bit DWORDS
+
+extern "C" {
+ static uint32_t
+ invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+ {
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ return result;
+ }
+
+ void
+ invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+ {
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ // 8 and 16 bit types should be promoted to 32 bits when copying
+ // onto the stack.
+ case nsXPTType::T_I8 : *((uint32_t*)d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((uint32_t*)d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint32_t*)d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ case nsXPTType::T_BOOL : *((uint32_t*)d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*)d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+ }
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ uint32_t result, n;
+
+ n = invoke_count_words(paramCount, params) * 4;
+
+ __asm__ __volatile__(
+ "subl %5, %%sp\n\t" /* make room for params */
+ "movel %4, %%sp@-\n\t"
+ "movel %3, %%sp@-\n\t"
+ "pea %%sp@(8)\n\t"
+ "jbsr invoke_copy_to_stack\n\t" /* copy params */
+ "addw #12, %%sp\n\t"
+ "movel %1, %%sp@-\n\t"
+ "movel %1@, %%a0\n\t"
+ "movel %%a0@(%2:l:4), %%a0\n\t"
+ "jbsr %%a0@\n\t" /* safe to not cleanup sp */
+ "lea %%sp@(4,%5:l), %%sp\n\t"
+ "movel %%d0, %0"
+ : "=d" (result) /* %0 */
+ : "a" (that), /* %1 */
+ "d" (methodIndex), /* %2 */
+ "g" (paramCount), /* %3 */
+ "g" (params), /* %4 */
+ "d" (n) /* %5 */
+ : "a0", "a1", "d0", "d1", "memory"
+ );
+
+ return result;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390.cpp
new file mode 100644
index 000000000..573dfb00c
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+
+static uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t overflow = 0, gpr = 1 /*this*/, fpr = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ if (gpr < 5) gpr++; else overflow++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ case nsXPTType::T_I64 :
+ if (gpr < 4) gpr+=2; else gpr=5, overflow+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ case nsXPTType::T_U64 :
+ if (gpr < 4) gpr+=2; else gpr=5, overflow+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ if (fpr < 2) fpr++; else overflow++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ if (fpr < 2) fpr++; else overflow+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ default:
+ // all the others are plain pointer types
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ }
+ }
+ /* Round up number of overflow words to ensure stack
+ stays aligned to 8 bytes. */
+ return (overflow + 1) & ~1;
+}
+
+static void
+invoke_copy_to_stack(uint32_t paramCount, nsXPTCVariant* s, uint32_t* d_ov, uint32_t overflow)
+{
+ uint32_t *d_gpr = d_ov + overflow;
+ uint64_t *d_fpr = (uint64_t *)(d_gpr + 4);
+ uint32_t gpr = 1 /*this*/, fpr = 0;
+
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ if (gpr < 5)
+ *((void**)d_gpr) = s->ptr, d_gpr++, gpr++;
+ else
+ *((void**)d_ov ) = s->ptr, d_ov++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ if (gpr < 5)
+ *((int32_t*) d_gpr) = s->val.i8, d_gpr++, gpr++;
+ else
+ *((int32_t*) d_ov ) = s->val.i8, d_ov++;
+ break;
+ case nsXPTType::T_I16 :
+ if (gpr < 5)
+ *((int32_t*) d_gpr) = s->val.i16, d_gpr++, gpr++;
+ else
+ *((int32_t*) d_ov ) = s->val.i16, d_ov++;
+ break;
+ case nsXPTType::T_I32 :
+ if (gpr < 5)
+ *((int32_t*) d_gpr) = s->val.i32, d_gpr++, gpr++;
+ else
+ *((int32_t*) d_ov ) = s->val.i32, d_ov++;
+ break;
+ case nsXPTType::T_I64 :
+ if (gpr < 4)
+ *((int64_t*) d_gpr) = s->val.i64, d_gpr+=2, gpr+=2;
+ else
+ *((int64_t*) d_ov ) = s->val.i64, d_ov+=2, gpr=5;
+ break;
+ case nsXPTType::T_U8 :
+ if (gpr < 5)
+ *((uint32_t*) d_gpr) = s->val.u8, d_gpr++, gpr++;
+ else
+ *((uint32_t*) d_ov ) = s->val.u8, d_ov++;
+ break;
+ case nsXPTType::T_U16 :
+ if (gpr < 5)
+ *((uint32_t*)d_gpr) = s->val.u16, d_gpr++, gpr++;
+ else
+ *((uint32_t*)d_ov ) = s->val.u16, d_ov++;
+ break;
+ case nsXPTType::T_U32 :
+ if (gpr < 5)
+ *((uint32_t*)d_gpr) = s->val.u32, d_gpr++, gpr++;
+ else
+ *((uint32_t*)d_ov ) = s->val.u32, d_ov++;
+ break;
+ case nsXPTType::T_U64 :
+ if (gpr < 4)
+ *((uint64_t*)d_gpr) = s->val.u64, d_gpr+=2, gpr+=2;
+ else
+ *((uint64_t*)d_ov ) = s->val.u64, d_ov+=2, gpr=5;
+ break;
+ case nsXPTType::T_FLOAT :
+ if (fpr < 2)
+ *((float*) d_fpr) = s->val.f, d_fpr++, fpr++;
+ else
+ *((float*) d_ov ) = s->val.f, d_ov++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ if (fpr < 2)
+ *((double*) d_fpr) = s->val.d, d_fpr++, fpr++;
+ else
+ *((double*) d_ov ) = s->val.d, d_ov+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ if (gpr < 5)
+ *((uint32_t*)d_gpr) = s->val.b, d_gpr++, gpr++;
+ else
+ *((uint32_t*)d_ov ) = s->val.b, d_ov++;
+ break;
+ case nsXPTType::T_CHAR :
+ if (gpr < 5)
+ *((uint32_t*)d_gpr) = s->val.c, d_gpr++, gpr++;
+ else
+ *((uint32_t*)d_ov ) = s->val.c, d_ov++;
+ break;
+ case nsXPTType::T_WCHAR :
+ if (gpr < 5)
+ *((uint32_t*)d_gpr) = s->val.wc, d_gpr++, gpr++;
+ else
+ *((uint32_t*)d_ov ) = s->val.wc, d_ov++;
+ break;
+ default:
+ // all the others are plain pointer types
+ if (gpr < 5)
+ *((void**) d_gpr) = s->val.p, d_gpr++, gpr++;
+ else
+ *((void**) d_ov ) = s->val.p, d_ov++;
+ break;
+ }
+ }
+}
+
+typedef nsresult (*vtable_func)(nsISupports *, uint32_t, uint32_t, uint32_t, uint32_t, double, double);
+
+// Avoid AddressSanitizer instrumentation for the next function because it
+// depends on __builtin_alloca behavior and alignment that cannot be relied on
+// once the function is compiled with a version of ASan that has dynamic-alloca
+// instrumentation enabled.
+
+MOZ_ASAN_BLACKLIST
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ vtable_func *vtable = *reinterpret_cast<vtable_func **>(that);
+ vtable_func method = vtable[methodIndex];
+ uint32_t overflow = invoke_count_words (paramCount, params);
+ uint32_t *stack_space = reinterpret_cast<uint32_t *>(__builtin_alloca((overflow + 8 /* 4 32-bits gpr + 2 64-bits fpr */) * 4));
+
+ invoke_copy_to_stack(paramCount, params, stack_space, overflow);
+
+ uint32_t *d_gpr = stack_space + overflow;
+ double *d_fpr = reinterpret_cast<double *>(d_gpr + 4);
+
+ return method(that, d_gpr[0], d_gpr[1], d_gpr[2], d_gpr[3], d_fpr[0], d_fpr[1]);
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390x.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390x.cpp
new file mode 100644
index 000000000..86478a850
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_linux_s390x.cpp
@@ -0,0 +1,190 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+
+static uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t overflow = 0, gpr = 1 /*this*/, fpr = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ if (gpr < 5) gpr++; else overflow++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ case nsXPTType::T_I64 :
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ case nsXPTType::T_U64 :
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ case nsXPTType::T_FLOAT :
+ case nsXPTType::T_DOUBLE :
+ if (fpr < 4) fpr++; else overflow++;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ default:
+ // all the others are plain pointer types
+ if (gpr < 5) gpr++; else overflow++;
+ break;
+ }
+ }
+ /* Round up number of overflow words to ensure stack
+ stays aligned to 8 bytes. */
+ return (overflow + 1) & ~1;
+}
+
+static void
+invoke_copy_to_stack(uint32_t paramCount, nsXPTCVariant* s, uint64_t* d_ov, uint32_t overflow)
+{
+ uint64_t *d_gpr = d_ov + overflow;
+ uint64_t *d_fpr = (uint64_t *)(d_gpr + 4);
+ uint32_t gpr = 1 /*this*/, fpr = 0;
+
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ if (gpr < 5)
+ *((void**)d_gpr) = s->ptr, d_gpr++, gpr++;
+ else
+ *((void**)d_ov ) = s->ptr, d_ov++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ if (gpr < 5)
+ *((int64_t*) d_gpr) = s->val.i8, d_gpr++, gpr++;
+ else
+ *((int64_t*) d_ov ) = s->val.i8, d_ov++;
+ break;
+ case nsXPTType::T_I16 :
+ if (gpr < 5)
+ *((int64_t*) d_gpr) = s->val.i16, d_gpr++, gpr++;
+ else
+ *((int64_t*) d_ov ) = s->val.i16, d_ov++;
+ break;
+ case nsXPTType::T_I32 :
+ if (gpr < 5)
+ *((int64_t*) d_gpr) = s->val.i32, d_gpr++, gpr++;
+ else
+ *((int64_t*) d_ov ) = s->val.i32, d_ov++;
+ break;
+ case nsXPTType::T_I64 :
+ if (gpr < 5)
+ *((int64_t*) d_gpr) = s->val.i64, d_gpr++, gpr++;
+ else
+ *((int64_t*) d_ov ) = s->val.i64, d_ov++;
+ break;
+ case nsXPTType::T_U8 :
+ if (gpr < 5)
+ *((uint64_t*) d_gpr) = s->val.u8, d_gpr++, gpr++;
+ else
+ *((uint64_t*) d_ov ) = s->val.u8, d_ov++;
+ break;
+ case nsXPTType::T_U16 :
+ if (gpr < 5)
+ *((uint64_t*)d_gpr) = s->val.u16, d_gpr++, gpr++;
+ else
+ *((uint64_t*)d_ov ) = s->val.u16, d_ov++;
+ break;
+ case nsXPTType::T_U32 :
+ if (gpr < 5)
+ *((uint64_t*)d_gpr) = s->val.u32, d_gpr++, gpr++;
+ else
+ *((uint64_t*)d_ov ) = s->val.u32, d_ov++;
+ break;
+ case nsXPTType::T_U64 :
+ if (gpr < 5)
+ *((uint64_t*)d_gpr) = s->val.u64, d_gpr++, gpr++;
+ else
+ *((uint64_t*)d_ov ) = s->val.u64, d_ov++;
+ break;
+ case nsXPTType::T_FLOAT :
+ if (fpr < 4)
+ *((float*) d_fpr) = s->val.f, d_fpr++, fpr++;
+ else
+ *(((float*) d_ov )+1) = s->val.f, d_ov++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ if (fpr < 4)
+ *((double*) d_fpr) = s->val.d, d_fpr++, fpr++;
+ else
+ *((double*) d_ov ) = s->val.d, d_ov++;
+ break;
+ case nsXPTType::T_BOOL :
+ if (gpr < 5)
+ *((uint64_t*)d_gpr) = s->val.b, d_gpr++, gpr++;
+ else
+ *((uint64_t*)d_ov ) = s->val.b, d_ov++;
+ break;
+ case nsXPTType::T_CHAR :
+ if (gpr < 5)
+ *((uint64_t*)d_gpr) = s->val.c, d_gpr++, gpr++;
+ else
+ *((uint64_t*)d_ov ) = s->val.c, d_ov++;
+ break;
+ case nsXPTType::T_WCHAR :
+ if (gpr < 5)
+ *((uint64_t*)d_gpr) = s->val.wc, d_gpr++, gpr++;
+ else
+ *((uint64_t*)d_ov ) = s->val.wc, d_ov++;
+ break;
+ default:
+ // all the others are plain pointer types
+ if (gpr < 5)
+ *((void**) d_gpr) = s->val.p, d_gpr++, gpr++;
+ else
+ *((void**) d_ov ) = s->val.p, d_ov++;
+ break;
+ }
+ }
+}
+
+typedef nsresult (*vtable_func)(nsISupports *, uint64_t, uint64_t, uint64_t, uint64_t, double, double, double, double);
+
+// Avoid AddressSanitizer instrumentation for the next function because it
+// depends on __builtin_alloca behavior and alignment that cannot be relied on
+// once the function is compiled with a version of ASan that has dynamic-alloca
+// instrumentation enabled.
+
+MOZ_ASAN_BLACKLIST
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ vtable_func *vtable = *reinterpret_cast<vtable_func **>(that);
+ vtable_func method = vtable[methodIndex];
+ uint64_t overflow = invoke_count_words (paramCount, params);
+ uint64_t *stack_space = reinterpret_cast<uint64_t *>(__builtin_alloca((overflow + 8 /* 4 64-bits gpr + 4 64-bits fpr */) * 8));
+ uint64_t result;
+
+ invoke_copy_to_stack(paramCount, params, stack_space, overflow);
+
+ uint64_t *d_gpr = stack_space + overflow;
+ double *d_fpr = reinterpret_cast<double *>(d_gpr + 4);
+
+ return method(that, d_gpr[0], d_gpr[1], d_gpr[2], d_gpr[3], d_fpr[0], d_fpr[1], d_fpr[2], d_fpr[3]);
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_mips.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_mips.cpp
new file mode 100644
index 000000000..f6585e7f0
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_mips.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * Version: MPL 1.1
+ *
+ * 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/. */
+
+/* This code is for MIPS using the O32 ABI. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#include <stdint.h>
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ // Count a word for a0 even though it's never stored or loaded
+ // We do this only for alignment of register pairs.
+ uint32_t result = 1;
+ for (uint32_t i = 0; i < paramCount; i++, result++, s++)
+ {
+ if (s->IsPtrData())
+ continue;
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_DOUBLE :
+ if (result & 1)
+ result++;
+ result++;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return (result + 1) & ~(uint32_t)1;
+}
+
+extern "C" void
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount,
+ nsXPTCVariant* s)
+{
+ // Skip the unused a0 slot, which we keep only for register pair alignment.
+ d++;
+
+ for (uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if (s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *d = (uint32_t) s->val.i8; break;
+ case nsXPTType::T_I16 : *d = (uint32_t) s->val.i16; break;
+ case nsXPTType::T_I32 : *d = (uint32_t) s->val.i32; break;
+ case nsXPTType::T_I64 :
+ if ((intptr_t)d & 4) d++;
+ *((int64_t*) d) = s->val.i64; d++;
+ break;
+ case nsXPTType::T_U8 : *d = (uint32_t) s->val.u8; break;
+ case nsXPTType::T_U16 : *d = (uint32_t) s->val.u16; break;
+ case nsXPTType::T_U32 : *d = (uint32_t) s->val.u32; break;
+ case nsXPTType::T_U64 :
+ if ((intptr_t)d & 4) d++;
+ *((uint64_t*) d) = s->val.u64; d++;
+ break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE :
+ if ((intptr_t)d & 4) d++;
+ *((double*) d) = s->val.d; d++;
+ break;
+ case nsXPTType::T_BOOL : *d = (bool) s->val.b; break;
+ case nsXPTType::T_CHAR : *d = (char) s->val.c; break;
+ case nsXPTType::T_WCHAR : *d = (wchar_t) s->val.wc; break;
+ default:
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+
+extern "C" nsresult _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount,
+ nsXPTCVariant* params);
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ return _NS_InvokeByIndex(that, methodIndex, paramCount, params);
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_mips64.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_mips64.cpp
new file mode 100644
index 000000000..1dd54f96f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_mips64.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#if (_MIPS_SIM != _ABIN32) && (_MIPS_SIM != _ABI64)
+#error "This code is for MIPS n32/n64 only"
+#endif
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ return paramCount;
+}
+
+extern "C" void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount,
+ nsXPTCVariant* s, uint64_t *regs)
+{
+#define N_ARG_REGS 7 /* 8 regs minus 1 for "this" ptr */
+
+ for (uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if (s->IsPtrData()) {
+ if (i < N_ARG_REGS)
+ regs[i] = (uint64_t)s->ptr;
+ else
+ *d++ = (uint64_t)s->ptr;
+ continue;
+ }
+ switch (s->type) {
+ //
+ // signed types first
+ //
+ case nsXPTType::T_I8:
+ if (i < N_ARG_REGS)
+ ((int64_t*)regs)[i] = s->val.i8;
+ else
+ *d++ = s->val.i8;
+ break;
+ case nsXPTType::T_I16:
+ if (i < N_ARG_REGS)
+ ((int64_t*)regs)[i] = s->val.i16;
+ else
+ *d++ = s->val.i16;
+ break;
+ case nsXPTType::T_I32:
+ if (i < N_ARG_REGS)
+ ((int64_t*)regs)[i] = s->val.i32;
+ else
+ *d++ = s->val.i32;
+ break;
+ case nsXPTType::T_I64:
+ if (i < N_ARG_REGS)
+ ((int64_t*)regs)[i] = s->val.i64;
+ else
+ *d++ = s->val.i64;
+ break;
+ //
+ // unsigned types next
+ //
+ case nsXPTType::T_U8:
+ if (i < N_ARG_REGS)
+ regs[i] = s->val.u8;
+ else
+ *d++ = s->val.u8;
+ break;
+ case nsXPTType::T_U16:
+ if (i < N_ARG_REGS)
+ regs[i] = s->val.u16;
+ else
+ *d++ = s->val.u16;
+ break;
+ case nsXPTType::T_U32:
+ if (i < N_ARG_REGS)
+ // 32-bit values need to be sign-extended
+ // in register, so use the signed value.
+ regs[i] = s->val.i32;
+ else
+ *d++ = s->val.u32;
+ break;
+ case nsXPTType::T_U64:
+ if (i < N_ARG_REGS)
+ regs[i] = s->val.u64;
+ else
+ *d++ = s->val.u64;
+ break;
+ case nsXPTType::T_FLOAT:
+ if (i < N_ARG_REGS)
+ *(float*)&regs[i] = s->val.f;
+ else
+ *(float*)d++ = s->val.f;
+ break;
+ case nsXPTType::T_DOUBLE:
+ if (i < N_ARG_REGS)
+ *(double*)&regs[i] = s->val.d;
+ else
+ *(double*)d++ = s->val.d;
+ break;
+ case nsXPTType::T_BOOL:
+ if (i < N_ARG_REGS)
+ regs[i] = s->val.b;
+ else
+ *d++ = s->val.b;
+ break;
+ case nsXPTType::T_CHAR:
+ if (i < N_ARG_REGS)
+ regs[i] = s->val.c;
+ else
+ *d++ = s->val.c;
+ break;
+ case nsXPTType::T_WCHAR:
+ if (i < N_ARG_REGS)
+ regs[i] = s->val.wc;
+ else
+ *d++ = s->val.wc;
+ break;
+ default:
+ // all the others are plain pointer types
+ if (i < N_ARG_REGS)
+ regs[i] = (uint64_t)s->val.p;
+ else
+ *d++ = (uint64_t)s->val.p;
+ break;
+ }
+ }
+}
+
+extern "C" nsresult _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount,
+ nsXPTCVariant* params);
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ return _NS_InvokeByIndex(that, methodIndex, paramCount, params);
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_netbsd_m68k.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_netbsd_m68k.cpp
new file mode 100644
index 000000000..47e0e7666
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_netbsd_m68k.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+// Remember that these 'words' are 32bit DWORDS
+
+#if !defined(__NetBSD__) || !defined(__m68k__)
+#error This code is for NetBSD/m68k only
+#endif
+
+extern "C" {
+ static uint32_t
+ invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+ {
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ return result;
+ }
+
+ static void
+ invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+ {
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ // 8 and 16 bit types should be promoted to 32 bits when copying
+ // onto the stack.
+ case nsXPTType::T_I8 : *((uint32_t*)d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((uint32_t*)d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint32_t*)d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ case nsXPTType::T_BOOL : *((uint32_t*)d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*)d) = s->val.c; break;
+ // wchar_t is an int (32 bits) on NetBSD
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+ }
+}
+
+XPTC_PUBLIC_API(nsresult)
+XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ uint32_t result;
+
+ __asm__ __volatile__(
+ "movl %4, sp@-\n\t"
+ "movl %3, sp@-\n\t"
+ "jbsr _invoke_count_words\n\t" /* count words */
+ "addql #8, sp\n\t"
+ "lsll #2, d0\n\t" /* *= 4 */
+ "movl sp, a2\n\t" /* save original sp */
+ "subl d0, sp\n\t" /* make room for params */
+ "movl sp, a0\n\t"
+ "movl %4, sp@-\n\t"
+ "movl %3, sp@-\n\t"
+ "movl a0, sp@-\n\t"
+ "jbsr _invoke_copy_to_stack\n\t" /* copy params */
+ "addl #12, sp\n\t"
+ "movl %1, a0\n\t"
+ "movl a0@, a1\n\t"
+ "movl %2, d0\n\t" /* function index */
+ "movl a0, d1\n\t"
+ "movw a1@(8,d0:l:8), a0\n\t"
+ "addl a0, d1\n\t"
+ "movl a1@(12,d0:l:8), a1\n\t"
+ "movl d1, sp@-\n\t"
+ "jbsr a1@\n\t"
+ "movl a2, sp\n\t" /* restore original sp */
+ "movl d0, %0\n\t"
+ : "=g" (result) /* %0 */
+ : "g" (that), /* %1 */
+ "g" (methodIndex), /* %2 */
+ "g" (paramCount), /* %3 */
+ "g" (params) /* %4 */
+ : "a0", "a1", "a2", "d0", "d1", "memory"
+ );
+
+ return result;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_pa32.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_pa32.cpp
new file mode 100644
index 000000000..0e6d28fbd
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_pa32.cpp
@@ -0,0 +1,149 @@
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "xptcprivate.h"
+
+#if _HPUX
+#error "This code is for HP-PA RISC 32 bit mode only"
+#endif
+
+#include <alloca.h>
+
+typedef unsigned nsXPCVariant;
+
+extern "C" int32_t
+invoke_count_bytes(nsISupports* that, const uint32_t methodIndex,
+ const uint32_t paramCount, const nsXPTCVariant* s)
+{
+ int32_t result = 4; /* variant records do not include self pointer */
+
+ /* counts the number of bytes required by the argument stack,
+ 64 bit integer, and double requires 8 bytes. All else requires
+ 4 bytes.
+ */
+
+ {
+ uint32_t indx;
+ for (indx = paramCount; indx > 0; --indx, ++s)
+ {
+ if (! s->IsPtrData())
+ {
+ if (s->type == nsXPTType::T_I64 || s->type == nsXPTType::T_U64 ||
+ s->type == nsXPTType::T_DOUBLE)
+ {
+ /* 64 bit integer and double aligned on 8 byte boundaries */
+ result += (result & 4) + 8;
+ continue;
+ }
+ }
+ result += 4; /* all other cases use 4 bytes */
+ }
+ }
+ result -= 72; /* existing stack buffer is 72 bytes */
+ if (result < 0)
+ return 0;
+ {
+ /* round up to 64 bytes boundary */
+ int32_t remainder = result & 63;
+ return (remainder == 0) ? result : (result + 64 - remainder);
+ }
+}
+
+extern "C" uint32_t
+invoke_copy_to_stack(uint32_t* d,
+ const uint32_t paramCount, nsXPTCVariant* s)
+{
+
+ typedef struct
+ {
+ uint32_t hi;
+ uint32_t lo;
+ } DU;
+
+ uint32_t* dest = d;
+ nsXPTCVariant* source = s;
+ /* we clobber param vars by copying stuff on stack, have to use local var */
+
+ uint32_t floatflags = 0;
+ /* flag indicating which floating point registers to load */
+
+ uint32_t regwords = 1; /* register 26 is reserved for ptr to 'that' */
+ uint32_t indx;
+
+ for (indx = paramCount; indx > 0; --indx, --dest, ++source)
+ {
+ if (source->IsPtrData())
+ {
+ *((void**) dest) = source->ptr;
+ ++regwords;
+ continue;
+ }
+ switch (source->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) dest) = source->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) dest) = source->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) dest) = source->val.i32; break;
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ if (regwords & 1)
+ {
+ /* align on double word boundary */
+ --dest;
+ ++regwords;
+ }
+ *((uint32_t*) dest) = ((DU *) source)->lo;
+ *((uint32_t*) --dest) = ((DU *) source)->hi;
+ /* big endian - hi word in low addr */
+ regwords += 2;
+ continue;
+ case nsXPTType::T_DOUBLE :
+ if (regwords & 1)
+ {
+ /* align on double word boundary */
+ --dest;
+ ++regwords;
+ }
+ switch (regwords) /* load double precision float register */
+ {
+ case 2:
+ floatflags |= 1;
+ }
+ *((uint32_t*) dest) = ((DU *) source)->lo;
+ *((uint32_t*) --dest) = ((DU *) source)->hi;
+ /* big endian - hi word in low addr */
+ regwords += 2;
+ continue;
+ case nsXPTType::T_FLOAT :
+ switch (regwords) /* load single precision float register */
+ {
+ case 1:
+ floatflags |= 2;
+ break;
+ case 2:
+ floatflags |= 4;
+ break;
+ case 3:
+ floatflags |= 8;
+ }
+ *((float*) dest) = source->val.f;
+ break;
+ case nsXPTType::T_U8 : *((uint32_t*) (dest)) = source->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*) (dest)) = source->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*) (dest)) = source->val.u32; break;
+ case nsXPTType::T_BOOL : *((uint32_t*) (dest)) = source->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*) (dest)) = source->val.c; break;
+ case nsXPTType::T_WCHAR : *((int32_t*) (dest)) = source->val.wc; break;
+
+ default:
+ // all the others are plain pointer types
+ *((void**) dest) = source->val.p;
+ }
+ ++regwords;
+ }
+ return floatflags;
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp
new file mode 100644
index 000000000..c93dc4621
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Platform specific code to invoke XPCOM methods on native objects
+
+// The purpose of NS_InvokeByIndex() is to map a platform
+// independent call to the platform ABI. To do that,
+// NS_InvokeByIndex() has to determine the method to call via vtable
+// access. The parameters for the method are read from the
+// nsXPTCVariant* and prepared for the native ABI.
+
+// The PowerPC64 platform ABI can be found here:
+// http://www.freestandards.org/spec/ELF/ppc64/
+// and in particular:
+// http://www.freestandards.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-CALL
+
+#include <stdio.h>
+#include "xptcprivate.h"
+
+// 8 integral parameters are passed in registers, not including 'that'
+#define GPR_COUNT 7
+
+// 8 floating point parameters are passed in registers, floats are
+// promoted to doubles when passed in registers
+#define FPR_COUNT 13
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ return uint32_t(((paramCount * 2) + 3) & ~3);
+}
+
+extern "C" void
+invoke_copy_to_stack(uint64_t* gpregs,
+ double* fpregs,
+ uint32_t paramCount,
+ nsXPTCVariant* s,
+ uint64_t* d)
+{
+ uint64_t tempu64;
+
+ for(uint32_t i = 0; i < paramCount; i++, s++) {
+ if(s->IsPtrData())
+ tempu64 = (uint64_t) s->ptr;
+ else {
+ switch(s->type) {
+ case nsXPTType::T_FLOAT: break;
+ case nsXPTType::T_DOUBLE: break;
+ case nsXPTType::T_I8: tempu64 = s->val.i8; break;
+ case nsXPTType::T_I16: tempu64 = s->val.i16; break;
+ case nsXPTType::T_I32: tempu64 = s->val.i32; break;
+ case nsXPTType::T_I64: tempu64 = s->val.i64; break;
+ case nsXPTType::T_U8: tempu64 = s->val.u8; break;
+ case nsXPTType::T_U16: tempu64 = s->val.u16; break;
+ case nsXPTType::T_U32: tempu64 = s->val.u32; break;
+ case nsXPTType::T_U64: tempu64 = s->val.u64; break;
+ case nsXPTType::T_BOOL: tempu64 = s->val.b; break;
+ case nsXPTType::T_CHAR: tempu64 = s->val.c; break;
+ case nsXPTType::T_WCHAR: tempu64 = s->val.wc; break;
+ default: tempu64 = (uint64_t) s->val.p; break;
+ }
+ }
+
+ if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
+ if (i < FPR_COUNT)
+ fpregs[i] = s->val.d;
+ else
+ *(double *)d = s->val.d;
+ }
+ else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
+ if (i < FPR_COUNT) {
+ fpregs[i] = s->val.f; // if passed in registers, floats are promoted to doubles
+ } else {
+ float *p = (float *)d;
+#ifndef __LITTLE_ENDIAN__
+ p++;
+#endif
+ *p = s->val.f;
+ }
+ }
+ else {
+ if (i < GPR_COUNT)
+ gpregs[i] = tempu64;
+ else
+ *d = tempu64;
+ }
+ if (i >= 7)
+ d++;
+ }
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix.cpp
new file mode 100644
index 000000000..ba2a5dab0
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#ifndef AIX
+#error "This code is for PowerPC only"
+#endif
+
+extern "C" void
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s, double *fprData)
+{
+/*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+*/
+ uint32_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint32_t l_paramCount = paramCount, fpCount = 0;
+ double *l_fprData = fprData;
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+ for(uint32_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if(l_s->IsPtrData())
+ {
+ *((void**)l_d) = l_s->ptr;
+ continue;
+ }
+ switch(l_s->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) l_d) = l_s->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) l_d) = l_s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) l_d) = l_s->val.i32; break;
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ *((uint32_t*) l_d++) = ((DU *)l_s)->hi;
+ *((uint32_t*) l_d) = ((DU *)l_s)->lo;
+ break;
+ case nsXPTType::T_DOUBLE :
+ *((uint32_t*) l_d++) = ((DU *)l_s)->hi;
+ *((uint32_t*) l_d) = ((DU *)l_s)->lo;
+ if(fpCount < 13)
+ l_fprData[fpCount++] = l_s->val.d;
+ break;
+ case nsXPTType::T_U8 : *((uint32_t*) l_d) = l_s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*) l_d) = l_s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*) l_d) = l_s->val.u32; break;
+ case nsXPTType::T_FLOAT :
+ *((float*) l_d) = l_s->val.f;
+ if(fpCount < 13)
+ l_fprData[fpCount++] = l_s->val.f;
+ break;
+ case nsXPTType::T_BOOL : *((uint32_t*) l_d) = l_s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*) l_d) = l_s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int32_t*) l_d) = l_s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)l_d) = l_s->val.p;
+ break;
+ }
+ }
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix64.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix64.cpp
new file mode 100644
index 000000000..616c2f687
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_aix64.cpp
@@ -0,0 +1,63 @@
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#ifdef _AIX
+
+extern "C" void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s, double *fprData)
+{
+/*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+*/
+ uint64_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint32_t l_paramCount = paramCount, fpCount = 0;
+ double *l_fprData = fprData;
+
+ for(uint32_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if(l_s->IsPtrData())
+ {
+ *l_d = (uint64_t)l_s->ptr;
+ continue;
+ }
+ switch(l_s->type)
+ {
+ case nsXPTType::T_I8: *l_d = (uint64_t)l_s->val.i8; break;
+ case nsXPTType::T_I16: *l_d = (uint64_t)l_s->val.i16; break;
+ case nsXPTType::T_I32: *l_d = (uint64_t)l_s->val.i32; break;
+ case nsXPTType::T_I64: *l_d = (uint64_t)l_s->val.i64; break;
+ case nsXPTType::T_U8: *l_d = (uint64_t)l_s->val.u8; break;
+ case nsXPTType::T_U16: *l_d = (uint64_t)l_s->val.u16; break;
+ case nsXPTType::T_U32: *l_d = (uint64_t)l_s->val.u32; break;
+ case nsXPTType::T_U64: *l_d = (uint64_t)l_s->val.u64; break;
+ case nsXPTType::T_BOOL: *l_d = (uint64_t)l_s->val.b; break;
+ case nsXPTType::T_CHAR: *l_d = (uint64_t)l_s->val.c; break;
+ case nsXPTType::T_WCHAR: *l_d = (uint64_t)l_s->val.wc; break;
+
+ case nsXPTType::T_DOUBLE:
+ *((double*)l_d) = l_s->val.d;
+ if(fpCount < 13)
+ l_fprData[fpCount++] = l_s->val.d;
+ break;
+ case nsXPTType::T_FLOAT:
+ *((float*)l_d) = l_s->val.f;
+ if(fpCount < 13)
+ l_fprData[fpCount++] = l_s->val.f;
+ break;
+ default:
+ // all the others are plain pointer types
+ *l_d = (uint64_t)l_s->val.p;
+ break;
+ }
+ }
+}
+#endif
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_linux.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_linux.cpp
new file mode 100644
index 000000000..deb72db8f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_linux.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Platform specific code to invoke XPCOM methods on native objects
+
+// The purpose of NS_InvokeByIndex() is to map a platform
+// indepenpent call to the platform ABI. To do that,
+// NS_InvokeByIndex() has to determine the method to call via vtable
+// access. The parameters for the method are read from the
+// nsXPTCVariant* and prepared for th native ABI. For the Linux/PPC
+// ABI this means that the first 8 integral and floating point
+// parameters are passed in registers.
+
+#include "xptcprivate.h"
+
+// 8 integral parameters are passed in registers
+#define GPR_COUNT 8
+
+// With hardfloat support 8 floating point parameters are passed in registers,
+// floats are promoted to doubles when passed in registers
+// In Softfloat mode, everything is handled via gprs
+#ifndef __NO_FPRS__
+#define FPR_COUNT 8
+#endif
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ return uint32_t(((paramCount * 2) + 3) & ~3);
+}
+
+extern "C" void
+invoke_copy_to_stack(uint32_t* d,
+ uint32_t paramCount,
+ nsXPTCVariant* s,
+ uint32_t* gpregs,
+ double* fpregs)
+{
+ uint32_t gpr = 1; // skip one GP reg for 'that'
+#ifndef __NO_FPRS__
+ uint32_t fpr = 0;
+#endif
+ uint32_t tempu32;
+ uint64_t tempu64;
+
+ for(uint32_t i = 0; i < paramCount; i++, s++) {
+ if(s->IsPtrData()) {
+ if(s->type == nsXPTType::T_JSVAL)
+ tempu32 = (uint32_t) &s->ptr;
+ else
+ tempu32 = (uint32_t) s->ptr;
+ }
+ else {
+ switch(s->type) {
+ case nsXPTType::T_FLOAT: break;
+ case nsXPTType::T_DOUBLE: break;
+ case nsXPTType::T_I8: tempu32 = s->val.i8; break;
+ case nsXPTType::T_I16: tempu32 = s->val.i16; break;
+ case nsXPTType::T_I32: tempu32 = s->val.i32; break;
+ case nsXPTType::T_I64: tempu64 = s->val.i64; break;
+ case nsXPTType::T_U8: tempu32 = s->val.u8; break;
+ case nsXPTType::T_U16: tempu32 = s->val.u16; break;
+ case nsXPTType::T_U32: tempu32 = s->val.u32; break;
+ case nsXPTType::T_U64: tempu64 = s->val.u64; break;
+ case nsXPTType::T_BOOL: tempu32 = s->val.b; break;
+ case nsXPTType::T_CHAR: tempu32 = s->val.c; break;
+ case nsXPTType::T_WCHAR: tempu32 = s->val.wc; break;
+ default: tempu32 = (uint32_t) s->val.p; break;
+ }
+ }
+
+ if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
+#ifndef __NO_FPRS__
+ if (fpr < FPR_COUNT)
+ fpregs[fpr++] = s->val.d;
+#else
+ if (gpr & 1)
+ gpr++;
+ if ((gpr + 1) < GPR_COUNT) {
+ *((double*) &gpregs[gpr]) = s->val.d;
+ gpr += 2;
+ }
+#endif
+ else {
+ if ((uint32_t) d & 4) d++; // doubles are 8-byte aligned on stack
+ *((double*) d) = s->val.d;
+ d += 2;
+ }
+ }
+ else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
+#ifndef __NO_FPRS__
+ if (fpr < FPR_COUNT)
+ fpregs[fpr++] = s->val.f; // if passed in registers, floats are promoted to doubles
+#else
+ if (gpr < GPR_COUNT)
+ *((float*) &gpregs[gpr++]) = s->val.f;
+#endif
+ else
+ *((float*) d++) = s->val.f;
+ }
+ else if (!s->IsPtrData() && (s->type == nsXPTType::T_I64
+ || s->type == nsXPTType::T_U64)) {
+ if (gpr & 1) gpr++; // longlongs are aligned in odd/even register pairs, eg. r5/r6
+ if ((gpr + 1) < GPR_COUNT) {
+ *((uint64_t*) &gpregs[gpr]) = tempu64;
+ gpr += 2;
+ }
+ else {
+ if ((uint32_t) d & 4) d++; // longlongs are 8-byte aligned on stack
+ *((uint64_t*) d) = tempu64;
+ d += 2;
+ }
+ }
+ else {
+ if (gpr < GPR_COUNT)
+ gpregs[gpr++] = tempu32;
+ else
+ *d++ = tempu32;
+ }
+
+ }
+}
+
+extern "C"
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_netbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_netbsd.cpp
new file mode 100644
index 000000000..515e8c50b
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_netbsd.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Platform specific code to invoke XPCOM methods on native objects
+
+// The purpose of XPTC_InvokeByIndex() is to map a platform
+// indepenpent call to the platform ABI. To do that,
+// XPTC_InvokeByIndex() has to determine the method to call via vtable
+// access. The parameters for the method are read from the
+// nsXPTCVariant* and prepared for the native ABI. For the Linux/PPC
+// ABI this means that the first 8 integral and floating point
+// parameters are passed in registers.
+
+#include "xptcprivate.h"
+
+// 8 integral parameters are passed in registers
+#define GPR_COUNT 8
+
+// 8 floating point parameters are passed in registers, floats are
+// promoted to doubles when passed in registers
+#define FPR_COUNT 8
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ return uint32_t(((paramCount * 2) + 3) & ~3);
+}
+
+extern "C" void
+invoke_copy_to_stack(uint32_t* d,
+ uint32_t paramCount,
+ nsXPTCVariant* s,
+ uint32_t* gpregs,
+ double* fpregs)
+{
+ uint32_t gpr = 1; // skip one GP reg for 'that'
+ uint32_t fpr = 0;
+ uint32_t tempu32;
+ uint64_t tempu64;
+
+ for(uint32_t i = 0; i < paramCount; i++, s++) {
+ if(s->IsPtrData()) {
+ if(s->type == nsXPTType::T_JSVAL)
+ tempu32 = (uint32_t) &(s->ptr);
+ else
+ tempu32 = (uint32_t) s->ptr;
+ } else {
+ switch(s->type) {
+ case nsXPTType::T_FLOAT: break;
+ case nsXPTType::T_DOUBLE: break;
+ case nsXPTType::T_I8: tempu32 = s->val.i8; break;
+ case nsXPTType::T_I16: tempu32 = s->val.i16; break;
+ case nsXPTType::T_I32: tempu32 = s->val.i32; break;
+ case nsXPTType::T_I64: tempu64 = s->val.i64; break;
+ case nsXPTType::T_U8: tempu32 = s->val.u8; break;
+ case nsXPTType::T_U16: tempu32 = s->val.u16; break;
+ case nsXPTType::T_U32: tempu32 = s->val.u32; break;
+ case nsXPTType::T_U64: tempu64 = s->val.u64; break;
+ case nsXPTType::T_BOOL: tempu32 = s->val.b; break;
+ case nsXPTType::T_CHAR: tempu32 = s->val.c; break;
+ case nsXPTType::T_WCHAR: tempu32 = s->val.wc; break;
+ default: tempu32 = (uint32_t) s->val.p; break;
+ }
+ }
+
+ if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
+ if (fpr < FPR_COUNT)
+ fpregs[fpr++] = s->val.d;
+ else {
+ if ((uint32_t) d & 4) d++; // doubles are 8-byte aligned on stack
+ *((double*) d) = s->val.d;
+ d += 2;
+ if (gpr < GPR_COUNT)
+ gpr += 2;
+ }
+ }
+ else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
+ if (fpr < FPR_COUNT)
+ fpregs[fpr++] = s->val.f; // if passed in registers, floats are promoted to doubles
+ else {
+ *((float*) d) = s->val.f;
+ d += 1;
+ if (gpr < GPR_COUNT)
+ gpr += 1;
+ }
+ }
+ else if (!s->IsPtrData() && (s->type == nsXPTType::T_I64
+ || s->type == nsXPTType::T_U64)) {
+ if ((gpr + 1) < GPR_COUNT) {
+ if (gpr & 1) gpr++; // longlongs are aligned in odd/even register pairs, eg. r5/r6
+ *((uint64_t*) &gpregs[gpr]) = tempu64;
+ gpr += 2;
+ }
+ else {
+ if ((uint32_t) d & 4) d++; // longlongs are 8-byte aligned on stack
+ *((uint64_t*) d) = tempu64;
+ d += 2;
+ }
+ }
+ else {
+ if (gpr < GPR_COUNT)
+ gpregs[gpr++] = tempu32;
+ else
+ *d++ = tempu32;
+ }
+
+ }
+}
+
+extern "C"
+XPTC_PUBLIC_API(nsresult)
+XPTC_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_openbsd.cpp
new file mode 100644
index 000000000..5353220fe
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_openbsd.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Platform specific code to invoke XPCOM methods on native objects
+
+// The purpose of NS_InvokeByIndex() is to map a platform
+// indepenpent call to the platform ABI. To do that,
+// NS_InvokeByIndex() has to determine the method to call via vtable
+// access. The parameters for the method are read from the
+// nsXPTCVariant* and prepared for th native ABI. For the Linux/PPC
+// ABI this means that the first 8 integral and floating point
+// parameters are passed in registers.
+
+#include "xptcprivate.h"
+
+// 8 integral parameters are passed in registers
+#define GPR_COUNT 8
+
+// 8 floating point parameters are passed in registers, floats are
+// promoted to doubles when passed in registers
+#define FPR_COUNT 8
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ return uint32_t(((paramCount * 2) + 3) & ~3);
+}
+
+extern "C" void
+invoke_copy_to_stack(uint32_t* d,
+ uint32_t paramCount,
+ nsXPTCVariant* s,
+ uint32_t* gpregs,
+ double* fpregs)
+{
+ uint32_t gpr = 1; // skip one GP reg for 'that'
+ uint32_t fpr = 0;
+ uint32_t tempu32;
+ uint64_t tempu64;
+
+ for(uint32_t i = 0; i < paramCount; i++, s++) {
+ if(s->IsPtrData()) {
+ if(s->type == nsXPTType::T_JSVAL)
+ tempu32 = (uint32_t) &(s->ptr);
+ else
+ tempu32 = (uint32_t) s->ptr;
+ } else {
+ switch(s->type) {
+ case nsXPTType::T_FLOAT: break;
+ case nsXPTType::T_DOUBLE: break;
+ case nsXPTType::T_I8: tempu32 = s->val.i8; break;
+ case nsXPTType::T_I16: tempu32 = s->val.i16; break;
+ case nsXPTType::T_I32: tempu32 = s->val.i32; break;
+ case nsXPTType::T_I64: tempu64 = s->val.i64; break;
+ case nsXPTType::T_U8: tempu32 = s->val.u8; break;
+ case nsXPTType::T_U16: tempu32 = s->val.u16; break;
+ case nsXPTType::T_U32: tempu32 = s->val.u32; break;
+ case nsXPTType::T_U64: tempu64 = s->val.u64; break;
+ case nsXPTType::T_BOOL: tempu32 = s->val.b; break;
+ case nsXPTType::T_CHAR: tempu32 = s->val.c; break;
+ case nsXPTType::T_WCHAR: tempu32 = s->val.wc; break;
+ default: tempu32 = (uint32_t) s->val.p; break;
+ }
+ }
+
+ if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
+ if (fpr < FPR_COUNT)
+ fpregs[fpr++] = s->val.d;
+ else {
+ if ((uint32_t) d & 4) d++; // doubles are 8-byte aligned on stack
+ *((double*) d) = s->val.d;
+ d += 2;
+ }
+ }
+ else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
+ if (fpr < FPR_COUNT)
+ fpregs[fpr++] = s->val.f; // if passed in registers, floats are promoted to doubles
+ else
+ *((float*) d++) = s->val.f;
+ }
+ else if (!s->IsPtrData() && (s->type == nsXPTType::T_I64
+ || s->type == nsXPTType::T_U64)) {
+ if ((gpr + 1) < GPR_COUNT) {
+ if (gpr & 1) gpr++; // longlongs are aligned in odd/even register pairs, eg. r5/r6
+ *((uint64_t*) &gpregs[gpr]) = tempu64;
+ gpr += 2;
+ }
+ else {
+ if ((uint32_t) d & 4) d++; // longlongs are 8-byte aligned on stack
+ *((uint64_t*) d) = tempu64;
+ d += 2;
+ }
+ }
+ else {
+ if (gpr < GPR_COUNT)
+ gpregs[gpr++] = tempu32;
+ else
+ *d++ = tempu32;
+ }
+
+ }
+}
+
+extern "C"
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_rhapsody.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_rhapsody.cpp
new file mode 100644
index 000000000..1a40cfeea
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc_rhapsody.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ /* fprintf(stderr,"invoke_count_words(%d,%p)\n",paramCount, s);*/
+
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ return result;
+}
+
+extern "C" void
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s, double *fprData)
+{
+ uint32_t fpCount = 0;
+
+ /* fprintf(stderr,"invoke_copy_to_stack(%p, %d, %p, %p)\n", d, paramCount, s, fprData);*/
+
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint32_t*) d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f;
+ if (fpCount < 13)
+ fprData[fpCount++] = s->val.f;
+ break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++;
+ if (fpCount < 13)
+ fprData[fpCount++] = s->val.d;
+ break;
+ case nsXPTType::T_BOOL : *((uint32_t*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((int32_t*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((uint32_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+
+extern "C" nsresult _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount,
+ nsXPTCVariant* params);
+
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ return _NS_InvokeByIndex(that, methodIndex, paramCount, params);
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc64_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc64_openbsd.cpp
new file mode 100644
index 000000000..050f9414d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc64_openbsd.cpp
@@ -0,0 +1,69 @@
+/* -*- 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/. */
+
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#if !defined(__sparc) && !defined(__sparc__)
+#error "This code is for Sparc only"
+#endif
+
+extern "C" uint64_t
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ /*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+ */
+ uint64_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint64_t l_paramCount = paramCount;
+ uint64_t regCount = 0; // return the number of registers to load from the stack
+
+ for(uint64_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if (regCount < 5) regCount++;
+
+ if (l_s->IsPtrData())
+ {
+ *l_d = (uint64_t)l_s->ptr;
+ continue;
+ }
+ switch (l_s->type)
+ {
+ case nsXPTType::T_I8 : *((int64_t*)l_d) = l_s->val.i8; break;
+ case nsXPTType::T_I16 : *((int64_t*)l_d) = l_s->val.i16; break;
+ case nsXPTType::T_I32 : *((int64_t*)l_d) = l_s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*)l_d) = l_s->val.i64; break;
+
+ case nsXPTType::T_U8 : *((uint64_t*)l_d) = l_s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint64_t*)l_d) = l_s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint64_t*)l_d) = l_s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)l_d) = l_s->val.u64; break;
+
+ /* in the case of floats, we want to put the bits in to the
+ 64bit space right justified... floats in the parameter array on
+ sparcv9 use odd numbered registers.. %f1, %f3, so we have to skip
+ the space that would be occupied by %f0, %f2, etc.
+ */
+ case nsXPTType::T_FLOAT : *(((float*)l_d) + 1) = l_s->val.f; break;
+ case nsXPTType::T_DOUBLE: *((double*)l_d) = l_s->val.d; break;
+ case nsXPTType::T_BOOL : *((int64_t*)l_d) = l_s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint64_t*)l_d) = l_s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int64_t*)l_d) = l_s->val.wc; break;
+
+ default:
+ // all the others are plain pointer types
+ *((void**)l_d) = l_s->val.p;
+ break;
+ }
+ }
+
+ return regCount;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_netbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_netbsd.cpp
new file mode 100644
index 000000000..126ef1dad
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_netbsd.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+/* solaris defines __sparc for workshop compilers and
+ linux defines __sparc__ */
+
+#if !defined(__sparc) && !defined(__sparc__)
+#error "This code is for Sparc only"
+#endif
+
+typedef unsigned nsXPCVariant;
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ // nuts, I know there's a cooler way of doing this, but it's late
+ // now and it'll probably come to me in the morning.
+ if (result & 0x3) result += 4 - (result & 0x3); // ensure q-word alignment
+ return result;
+}
+
+extern "C" uint32_t
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+/*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+*/
+ uint32_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint32_t l_paramCount = paramCount;
+ uint32_t regCount = 0; // return the number of registers to load from the stack
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+ for(uint32_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if (regCount < 5) regCount++;
+ if(l_s->IsPtrData())
+ {
+ if(l_s->type == nsXPTType::T_JSVAL)
+ {
+ // On SPARC, we need to pass a pointer to HandleValue
+ *((void**)l_d) = &l_s->ptr;
+ } else
+ {
+ *((void**)l_d) = l_s->ptr;
+ }
+ continue;
+ }
+ switch(l_s->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) l_d) = l_s->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) l_d) = l_s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) l_d) = l_s->val.i32; break;
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_DOUBLE : *((uint32_t*) l_d++) = ((DU *)l_s)->hi;
+ if (regCount < 5) regCount++;
+ *((uint32_t*) l_d) = ((DU *)l_s)->lo;
+ break;
+ case nsXPTType::T_U8 : *((uint32_t*) l_d) = l_s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*) l_d) = l_s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*) l_d) = l_s->val.u32; break;
+ case nsXPTType::T_FLOAT : *((float*) l_d) = l_s->val.f; break;
+ case nsXPTType::T_BOOL : *((uint32_t*) l_d) = l_s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*) l_d) = l_s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int32_t*) l_d) = l_s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)l_d) = l_s->val.p;
+ break;
+ }
+ }
+ return regCount;
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_openbsd.cpp
new file mode 100644
index 000000000..9b2e4f858
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_openbsd.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#if !defined(__sparc__)
+#error "This code is for Sparc only"
+#endif
+
+typedef unsigned nsXPCVariant;
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ // nuts, I know there's a cooler way of doing this, but it's late
+ // now and it'll probably come to me in the morning.
+ if (result & 0x3) result += 4 - (result & 0x3); // ensure q-word alignment
+ return result;
+}
+
+extern "C" uint32_t
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+/*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+*/
+ uint32_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint32_t l_paramCount = paramCount;
+ uint32_t regCount = 0; // return the number of registers to load from the stack
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+ for(uint32_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if (regCount < 5) regCount++;
+ if(l_s->IsPtrData())
+ {
+ if(l_s->type == nsXPTType::T_JSVAL)
+ {
+ // On SPARC, we need to pass a pointer to HandleValue
+ *((void**)l_d) = &l_s->ptr;
+ } else
+ {
+ *((void**)l_d) = l_s->ptr;
+ }
+ continue;
+ }
+ switch(l_s->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) l_d) = l_s->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) l_d) = l_s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) l_d) = l_s->val.i32; break;
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_DOUBLE : *((uint32_t*) l_d++) = ((DU *)l_s)->hi;
+ if (regCount < 5) regCount++;
+ *((uint32_t*) l_d) = ((DU *)l_s)->lo;
+ break;
+ case nsXPTType::T_U8 : *((uint32_t*) l_d) = l_s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*) l_d) = l_s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*) l_d) = l_s->val.u32; break;
+ case nsXPTType::T_FLOAT : *((float*) l_d) = l_s->val.f; break;
+ case nsXPTType::T_BOOL : *((uint32_t*) l_d) = l_s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*) l_d) = l_s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int32_t*) l_d) = l_s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)l_d) = l_s->val.p;
+ break;
+ }
+ }
+ return regCount;
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_solaris.cpp
new file mode 100644
index 000000000..126ef1dad
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparc_solaris.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+/* solaris defines __sparc for workshop compilers and
+ linux defines __sparc__ */
+
+#if !defined(__sparc) && !defined(__sparc__)
+#error "This code is for Sparc only"
+#endif
+
+typedef unsigned nsXPCVariant;
+
+extern "C" uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 :
+ case nsXPTType::T_I16 :
+ case nsXPTType::T_I32 :
+ result++;
+ break;
+ case nsXPTType::T_I64 :
+ result+=2;
+ break;
+ case nsXPTType::T_U8 :
+ case nsXPTType::T_U16 :
+ case nsXPTType::T_U32 :
+ result++;
+ break;
+ case nsXPTType::T_U64 :
+ result+=2;
+ break;
+ case nsXPTType::T_FLOAT :
+ result++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ result+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ case nsXPTType::T_CHAR :
+ case nsXPTType::T_WCHAR :
+ result++;
+ break;
+ default:
+ // all the others are plain pointer types
+ result++;
+ break;
+ }
+ }
+ // nuts, I know there's a cooler way of doing this, but it's late
+ // now and it'll probably come to me in the morning.
+ if (result & 0x3) result += 4 - (result & 0x3); // ensure q-word alignment
+ return result;
+}
+
+extern "C" uint32_t
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+/*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+*/
+ uint32_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint32_t l_paramCount = paramCount;
+ uint32_t regCount = 0; // return the number of registers to load from the stack
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+ for(uint32_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if (regCount < 5) regCount++;
+ if(l_s->IsPtrData())
+ {
+ if(l_s->type == nsXPTType::T_JSVAL)
+ {
+ // On SPARC, we need to pass a pointer to HandleValue
+ *((void**)l_d) = &l_s->ptr;
+ } else
+ {
+ *((void**)l_d) = l_s->ptr;
+ }
+ continue;
+ }
+ switch(l_s->type)
+ {
+ case nsXPTType::T_I8 : *((int32_t*) l_d) = l_s->val.i8; break;
+ case nsXPTType::T_I16 : *((int32_t*) l_d) = l_s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) l_d) = l_s->val.i32; break;
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_DOUBLE : *((uint32_t*) l_d++) = ((DU *)l_s)->hi;
+ if (regCount < 5) regCount++;
+ *((uint32_t*) l_d) = ((DU *)l_s)->lo;
+ break;
+ case nsXPTType::T_U8 : *((uint32_t*) l_d) = l_s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint32_t*) l_d) = l_s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*) l_d) = l_s->val.u32; break;
+ case nsXPTType::T_FLOAT : *((float*) l_d) = l_s->val.f; break;
+ case nsXPTType::T_BOOL : *((uint32_t*) l_d) = l_s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint32_t*) l_d) = l_s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int32_t*) l_d) = l_s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)l_d) = l_s->val.p;
+ break;
+ }
+ }
+ return regCount;
+}
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparcv9_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparcv9_solaris.cpp
new file mode 100644
index 000000000..0d2d6b0a8
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_sparcv9_solaris.cpp
@@ -0,0 +1,73 @@
+/* -*- 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/. */
+
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#if !defined(__sparc) && !defined(__sparc__)
+#error "This code is for Sparc only"
+#endif
+
+/* Prototype specifies unmangled function name */
+extern "C" uint64_t
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s);
+
+extern "C" uint64_t
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ /*
+ We need to copy the parameters for this function to locals and use them
+ from there since the parameters occupy the same stack space as the stack
+ we're trying to populate.
+ */
+ uint64_t *l_d = d;
+ nsXPTCVariant *l_s = s;
+ uint64_t l_paramCount = paramCount;
+ uint64_t regCount = 0; // return the number of registers to load from the stack
+
+ for(uint64_t i = 0; i < l_paramCount; i++, l_d++, l_s++)
+ {
+ if (regCount < 5) regCount++;
+
+ if (l_s->IsPtrData())
+ {
+ *l_d = (uint64_t)l_s->ptr;
+ continue;
+ }
+ switch (l_s->type)
+ {
+ case nsXPTType::T_I8 : *((int64_t*)l_d) = l_s->val.i8; break;
+ case nsXPTType::T_I16 : *((int64_t*)l_d) = l_s->val.i16; break;
+ case nsXPTType::T_I32 : *((int64_t*)l_d) = l_s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*)l_d) = l_s->val.i64; break;
+
+ case nsXPTType::T_U8 : *((uint64_t*)l_d) = l_s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint64_t*)l_d) = l_s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint64_t*)l_d) = l_s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)l_d) = l_s->val.u64; break;
+
+ /* in the case of floats, we want to put the bits in to the
+ 64bit space right justified... floats in the parameter array on
+ sparcv9 use odd numbered registers.. %f1, %f3, so we have to skip
+ the space that would be occupied by %f0, %f2, etc.
+ */
+ case nsXPTType::T_FLOAT : *(((float*)l_d) + 1) = l_s->val.f; break;
+ case nsXPTType::T_DOUBLE: *((double*)l_d) = l_s->val.d; break;
+ case nsXPTType::T_BOOL : *((uint64_t*)l_d) = l_s->val.b; break;
+ case nsXPTType::T_CHAR : *((uint64_t*)l_d) = l_s->val.c; break;
+ case nsXPTType::T_WCHAR : *((int64_t*)l_d) = l_s->val.wc; break;
+
+ default:
+ // all the others are plain pointer types
+ *((void**)l_d) = l_s->val.p;
+ break;
+ }
+ }
+
+ return regCount;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_solaris.cpp
new file mode 100644
index 000000000..0d3424366
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_solaris.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+// Platform specific code to invoke XPCOM methods on native objects
+
+#include "xptcprivate.h"
+#include "alloca.h"
+
+// 6 integral parameters are passed in registers
+const uint32_t GPR_COUNT = 6;
+
+// 8 floating point parameters are passed in SSE registers
+const uint32_t FPR_COUNT = 8;
+
+// Remember that these 'words' are 64-bit long
+static inline void
+invoke_count_words(uint32_t paramCount, nsXPTCVariant * s,
+ uint32_t & nr_gpr, uint32_t & nr_fpr, uint32_t & nr_stack)
+{
+ nr_gpr = 1; // skip one GP register for 'that'
+ nr_fpr = 0;
+ nr_stack = 0;
+
+ /* Compute number of eightbytes of class MEMORY. */
+ for (uint32_t i = 0; i < paramCount; i++, s++) {
+ if (!s->IsPtrData()
+ && (s->type == nsXPTType::T_FLOAT || s->type == nsXPTType::T_DOUBLE)) {
+ if (nr_fpr < FPR_COUNT)
+ nr_fpr++;
+ else
+ nr_stack++;
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ nr_gpr++;
+ else
+ nr_stack++;
+ }
+ }
+}
+
+static void
+invoke_copy_to_stack(uint64_t * d, uint32_t paramCount, nsXPTCVariant * s,
+ uint64_t * gpregs, double * fpregs)
+{
+ uint32_t nr_gpr = 1; // skip one GP register for 'that'
+ uint32_t nr_fpr = 0;
+ uint64_t value;
+
+ for (uint32_t i = 0; i < paramCount; i++, s++) {
+ if (s->IsPtrData())
+ value = (uint64_t) s->ptr;
+ else {
+ switch (s->type) {
+ case nsXPTType::T_FLOAT: break;
+ case nsXPTType::T_DOUBLE: break;
+ case nsXPTType::T_I8: value = s->val.i8; break;
+ case nsXPTType::T_I16: value = s->val.i16; break;
+ case nsXPTType::T_I32: value = s->val.i32; break;
+ case nsXPTType::T_I64: value = s->val.i64; break;
+ case nsXPTType::T_U8: value = s->val.u8; break;
+ case nsXPTType::T_U16: value = s->val.u16; break;
+ case nsXPTType::T_U32: value = s->val.u32; break;
+ case nsXPTType::T_U64: value = s->val.u64; break;
+ case nsXPTType::T_BOOL: value = s->val.b; break;
+ case nsXPTType::T_CHAR: value = s->val.c; break;
+ case nsXPTType::T_WCHAR: value = s->val.wc; break;
+ default: value = (uint64_t) s->val.p; break;
+ }
+ }
+
+ if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
+ if (nr_fpr < FPR_COUNT)
+ fpregs[nr_fpr++] = s->val.d;
+ else {
+ *((double *)d) = s->val.d;
+ d++;
+ }
+ }
+ else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
+ if (nr_fpr < FPR_COUNT)
+ // The value in %xmm register is already prepared to
+ // be retrieved as a float. Therefore, we pass the
+ // value verbatim, as a double without conversion.
+ fpregs[nr_fpr++] = s->val.d;
+ else {
+ *((float *)d) = s->val.f;
+ d++;
+ }
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ gpregs[nr_gpr++] = value;
+ else
+ *d++ = value;
+ }
+ }
+}
+
+// Avoid AddressSanitizer instrumentation for the next function because it
+// depends on __builtin_alloca behavior and alignment that cannot be relied on
+// once the function is compiled with a version of ASan that has dynamic-alloca
+// instrumentation enabled.
+
+MOZ_ASAN_BLACKLIST
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports * that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant * params)
+{
+ uint32_t nr_gpr, nr_fpr, nr_stack;
+ invoke_count_words(paramCount, params, nr_gpr, nr_fpr, nr_stack);
+
+ // Stack, if used, must be 16-bytes aligned
+ if (nr_stack)
+ nr_stack = (nr_stack + 1) & ~1;
+
+ // Load parameters to stack, if necessary
+ uint64_t *stack = (uint64_t *) __builtin_alloca(nr_stack * 8);
+ uint64_t gpregs[GPR_COUNT];
+ double fpregs[FPR_COUNT];
+ invoke_copy_to_stack(stack, paramCount, params, gpregs, fpregs);
+
+ switch (nr_fpr) {
+ case 8: asm("movupd %0, %xmm7" : : "xmm7" (fpregs[7]));
+ case 7: asm("movupd %0, %xmm6" : : "xmm6" (fpregs[6]));
+ case 6: asm("movupd %0, %xmm5" : : "xmm5" (fpregs[5]));
+ case 5: asm("movupd %0, %xmm4" : : "xmm4" (fpregs[4]));
+ case 4: asm("movupd %0, %xmm3" : : "xmm3" (fpregs[3]));
+ case 3: asm("movupd %0, %xmm2" : : "xmm2" (fpregs[2]));
+ case 2: asm("movupd %0, %xmm1" : : "xmm1" (fpregs[1]));
+ case 1: asm("movupd %0, %xmm0" : : "xmm0" (fpregs[0]));
+ case 0:;
+ }
+
+ // Ensure that assignments to SSE registers won't be optimized away
+ asm("" ::: "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7");
+
+ // Get pointer to method
+ uint64_t methodAddress = *((uint64_t *)that);
+ methodAddress += 16 + 8 * methodIndex;
+ methodAddress = *((uint64_t *)methodAddress);
+
+ typedef uint32_t (*Method)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
+ uint32_t result = ((Method)methodAddress)((uint64_t)that, gpregs[1], gpregs[2], gpregs[3], gpregs[4], gpregs[5]);
+ return result;
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp
new file mode 100644
index 000000000..08e519889
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp
@@ -0,0 +1,188 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+// Platform specific code to invoke XPCOM methods on native objects
+
+#include "xptcprivate.h"
+
+// 6 integral parameters are passed in registers
+const uint32_t GPR_COUNT = 6;
+
+// 8 floating point parameters are passed in SSE registers
+const uint32_t FPR_COUNT = 8;
+
+// Remember that these 'words' are 64-bit long
+static inline void
+invoke_count_words(uint32_t paramCount, nsXPTCVariant * s,
+ uint32_t & nr_stack)
+{
+ uint32_t nr_gpr;
+ uint32_t nr_fpr;
+ nr_gpr = 1; // skip one GP register for 'that'
+ nr_fpr = 0;
+ nr_stack = 0;
+
+ /* Compute number of eightbytes of class MEMORY. */
+ for (uint32_t i = 0; i < paramCount; i++, s++) {
+ if (!s->IsPtrData()
+ && (s->type == nsXPTType::T_FLOAT || s->type == nsXPTType::T_DOUBLE)) {
+ if (nr_fpr < FPR_COUNT)
+ nr_fpr++;
+ else
+ nr_stack++;
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ nr_gpr++;
+ else
+ nr_stack++;
+ }
+ }
+}
+
+static void
+invoke_copy_to_stack(uint64_t * d, uint32_t paramCount, nsXPTCVariant * s,
+ uint64_t * gpregs, double * fpregs)
+{
+ uint32_t nr_gpr = 1u; // skip one GP register for 'that'
+ uint32_t nr_fpr = 0u;
+ uint64_t value = 0u;
+
+ for (uint32_t i = 0; i < paramCount; i++, s++) {
+ if (s->IsPtrData())
+ value = (uint64_t) s->ptr;
+ else {
+ switch (s->type) {
+ case nsXPTType::T_FLOAT: break;
+ case nsXPTType::T_DOUBLE: break;
+ case nsXPTType::T_I8: value = s->val.i8; break;
+ case nsXPTType::T_I16: value = s->val.i16; break;
+ case nsXPTType::T_I32: value = s->val.i32; break;
+ case nsXPTType::T_I64: value = s->val.i64; break;
+ case nsXPTType::T_U8: value = s->val.u8; break;
+ case nsXPTType::T_U16: value = s->val.u16; break;
+ case nsXPTType::T_U32: value = s->val.u32; break;
+ case nsXPTType::T_U64: value = s->val.u64; break;
+ case nsXPTType::T_BOOL: value = s->val.b; break;
+ case nsXPTType::T_CHAR: value = s->val.c; break;
+ case nsXPTType::T_WCHAR: value = s->val.wc; break;
+ default: value = (uint64_t) s->val.p; break;
+ }
+ }
+
+ if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
+ if (nr_fpr < FPR_COUNT)
+ fpregs[nr_fpr++] = s->val.d;
+ else {
+ *((double *)d) = s->val.d;
+ d++;
+ }
+ }
+ else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
+ if (nr_fpr < FPR_COUNT)
+ // The value in %xmm register is already prepared to
+ // be retrieved as a float. Therefore, we pass the
+ // value verbatim, as a double without conversion.
+ fpregs[nr_fpr++] = s->val.d;
+ else {
+ *((float *)d) = s->val.f;
+ d++;
+ }
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ gpregs[nr_gpr++] = value;
+ else
+ *d++ = value;
+ }
+ }
+}
+
+// Disable avx for the next function to allow compilation with
+// -march=native on new machines, or similar hardcoded -march options.
+// Having avx enabled appears to change the alignment behavior of alloca
+// (apparently adding an extra 16 bytes) of padding/alignment (and using
+// 32-byte alignment instead of 16-byte). This seems to be the best
+// available workaround, given that this code, which should perhaps
+// better be written in assembly, is written in C++.
+#ifndef __clang__
+#pragma GCC push_options
+#pragma GCC target ("no-avx")
+#endif
+
+// Avoid AddressSanitizer instrumentation for the next function because it
+// depends on __builtin_alloca behavior and alignment that cannot be relied on
+// once the function is compiled with a version of ASan that has dynamic-alloca
+// instrumentation enabled.
+
+MOZ_ASAN_BLACKLIST
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports * that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant * params)
+{
+ uint32_t nr_stack;
+ invoke_count_words(paramCount, params, nr_stack);
+
+ // Stack, if used, must be 16-bytes aligned
+ if (nr_stack)
+ nr_stack = (nr_stack + 1) & ~1;
+
+ // Load parameters to stack, if necessary
+ uint64_t *stack = (uint64_t *) __builtin_alloca(nr_stack * 8);
+ uint64_t gpregs[GPR_COUNT];
+ double fpregs[FPR_COUNT];
+ invoke_copy_to_stack(stack, paramCount, params, gpregs, fpregs);
+
+ // We used to have switches to make sure we would only load the registers
+ // that are needed for this call. That produced larger code that was
+ // not faster in practice. It also caused compiler warnings about the
+ // variables being used uninitialized.
+ // We now just load every every register. There could still be a warning
+ // from a memory analysis tools that we are loading uninitialized stack
+ // positions.
+
+ // FIXME: this function depends on the above __builtin_alloca placing
+ // the array in the correct spot for the ABI.
+
+ // Load FPR registers from fpregs[]
+ double d0, d1, d2, d3, d4, d5, d6, d7;
+
+ d7 = fpregs[7];
+ d6 = fpregs[6];
+ d5 = fpregs[5];
+ d4 = fpregs[4];
+ d3 = fpregs[3];
+ d2 = fpregs[2];
+ d1 = fpregs[1];
+ d0 = fpregs[0];
+
+ // Load GPR registers from gpregs[]
+ uint64_t a0, a1, a2, a3, a4, a5;
+
+ a5 = gpregs[5];
+ a4 = gpregs[4];
+ a3 = gpregs[3];
+ a2 = gpregs[2];
+ a1 = gpregs[1];
+ a0 = (uint64_t) that;
+
+ // Get pointer to method
+ uint64_t methodAddress = *((uint64_t *)that);
+ methodAddress += 8 * methodIndex;
+ methodAddress = *((uint64_t *)methodAddress);
+
+ typedef nsresult (*Method)(uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t, uint64_t, double, double, double,
+ double, double, double, double, double);
+ nsresult result = ((Method)methodAddress)(a0, a1, a2, a3, a4, a5,
+ d0, d1, d2, d3, d4, d5,
+ d6, d7);
+ return result;
+}
+
+#ifndef __clang__
+#pragma GCC pop_options
+#endif
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_solaris.cpp
new file mode 100644
index 000000000..f545fcc99
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_solaris.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+extern "C" {
+
+// Remember that these 'words' are 32bit DWORDS
+
+uint32_t
+invoke_count_words(uint32_t paramCount, nsXPTCVariant* s)
+{
+ uint32_t result = 0;
+ for(uint32_t i = 0; i < paramCount; i++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ result++;
+ continue;
+ }
+ result++;
+ switch(s->type)
+ {
+ case nsXPTType::T_I64 :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_DOUBLE :
+ result++;
+ break;
+ }
+ }
+ return result;
+}
+
+void
+invoke_copy_to_stack(uint32_t paramCount, nsXPTCVariant* s, uint32_t* d)
+{
+ for(uint32_t i = 0; i < paramCount; i++, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+
+/* XXX: the following line is here (rather than as the default clause in
+ * the following switch statement) so that the Sun native compiler
+ * will generate the correct assembly code on the Solaris Intel
+ * platform. See the comments in bug #28817 for more details.
+ */
+
+ *((void**)d) = s->val.p;
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ }
+ }
+}
+
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_aarch64.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_aarch64.cpp
new file mode 100644
index 000000000..571fb42a2
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_aarch64.cpp
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+#ifndef __AARCH64EL__
+#error "Only little endian compatibility was tested"
+#endif
+
+/*
+ * This is for AArch64 ABI
+ *
+ * When we're called, the "gp" registers are stored in gprData and
+ * the "fp" registers are stored in fprData. Each array has 8 regs
+ * but first reg in gprData is a placeholder for 'self'.
+ */
+extern "C" nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args,
+ uint64_t *gprData, double *fprData)
+{
+#define PARAM_BUFFER_COUNT 16
+#define PARAM_GPR_COUNT 8
+#define PARAM_FPR_COUNT 8
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = NULL;
+ const nsXPTMethodInfo* info;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ uint32_t paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if (paramCount > PARAM_BUFFER_COUNT) {
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ } else {
+ dispatchParams = paramBuffer;
+ }
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint64_t* ap = args;
+ uint32_t next_gpr = 1; // skip first arg which is 'self'
+ uint32_t next_fpr = 0;
+ for (uint32_t i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (param.IsOut() || !type.IsArithmetic()) {
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.p = (void*)gprData[next_gpr++];
+ } else {
+ dp->val.p = (void*)*ap++;
+ }
+ continue;
+ }
+
+ switch (type) {
+ case nsXPTType::T_I8:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.i8 = (int8_t)gprData[next_gpr++];
+ } else {
+ dp->val.i8 = (int8_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_I16:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.i16 = (int16_t)gprData[next_gpr++];
+ } else {
+ dp->val.i16 = (int16_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_I32:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.i32 = (int32_t)gprData[next_gpr++];
+ } else {
+ dp->val.i32 = (int32_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_I64:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.i64 = (int64_t)gprData[next_gpr++];
+ } else {
+ dp->val.i64 = (int64_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_U8:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.u8 = (uint8_t)gprData[next_gpr++];
+ } else {
+ dp->val.u8 = (uint8_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_U16:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.u16 = (uint16_t)gprData[next_gpr++];
+ } else {
+ dp->val.u16 = (uint16_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_U32:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.u32 = (uint32_t)gprData[next_gpr++];
+ } else {
+ dp->val.u32 = (uint32_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_U64:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.u64 = (uint64_t)gprData[next_gpr++];
+ } else {
+ dp->val.u64 = (uint64_t)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_FLOAT:
+ if (next_fpr < PARAM_FPR_COUNT) {
+ memcpy(&dp->val.f, &fprData[next_fpr++], sizeof(dp->val.f));
+ } else {
+ memcpy(&dp->val.f, ap++, sizeof(dp->val.f));
+ }
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ if (next_fpr < PARAM_FPR_COUNT) {
+ memcpy(&dp->val.d, &fprData[next_fpr++], sizeof(dp->val.d));
+ } else {
+ memcpy(&dp->val.d, ap++, sizeof(dp->val.d));
+ }
+ break;
+
+ case nsXPTType::T_BOOL:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.b = (bool)gprData[next_gpr++];
+ } else {
+ dp->val.b = (bool)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_CHAR:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.c = (char)gprData[next_gpr++];
+ } else {
+ dp->val.c = (char)*ap++;
+ }
+ break;
+
+ case nsXPTType::T_WCHAR:
+ if (next_gpr < PARAM_GPR_COUNT) {
+ dp->val.wc = (wchar_t)gprData[next_gpr++];
+ } else {
+ dp->val.wc = (wchar_t)*ap++;
+ }
+ break;
+
+ default:
+ NS_ASSERTION(0, "bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if (dispatchParams != paramBuffer) {
+ delete [] dispatchParams;
+ }
+
+ return result;
+}
+
+// Load w17 with the constant 'n' and branch to SharedStub().
+# define STUB_ENTRY(n) \
+ __asm__ ( \
+ ".section \".text\" \n\t" \
+ ".align 2\n\t" \
+ ".if "#n" < 10 \n\t" \
+ ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ ".hidden _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n" \
+ "_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t" \
+ ".elseif "#n" < 100 \n\t" \
+ ".globl _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ ".hidden _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n" \
+ "_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t" \
+ ".elseif "#n" < 1000 \n\t" \
+ ".globl _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ ".hidden _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n" \
+ "_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t" \
+ ".else \n\t" \
+ ".err \"stub number "#n" >= 1000 not yet supported\"\n" \
+ ".endif \n\t" \
+ "mov w17,#"#n" \n\t" \
+ "b SharedStub \n" \
+);
+
+#define SENTINEL_ENTRY(n) \
+ nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ASSERTION(0,"nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_alpha_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_alpha_openbsd.cpp
new file mode 100644
index 000000000..d538767e1
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_alpha_openbsd.cpp
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+/* Prototype specifies unmangled function name and disables unused warning */
+static nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
+__asm__("PrepareAndDispatch") ATTRIBUTE_USED;
+
+static nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
+{
+ const uint8_t PARAM_BUFFER_COUNT = 16;
+ const uint8_t NUM_ARG_REGS = 6-1; // -1 for "this" pointer
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // args[0] to args[NUM_ARG_REGS] hold floating point register values
+ uint64_t* ap = args + NUM_ARG_REGS;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = (int8_t) *ap; break;
+ case nsXPTType::T_I16 : dp->val.i16 = (int16_t) *ap; break;
+ case nsXPTType::T_I32 : dp->val.i32 = (int32_t) *ap; break;
+ case nsXPTType::T_I64 : dp->val.i64 = (int64_t) *ap; break;
+ case nsXPTType::T_U8 : dp->val.u8 = (uint8_t) *ap; break;
+ case nsXPTType::T_U16 : dp->val.u16 = (uint16_t) *ap; break;
+ case nsXPTType::T_U32 : dp->val.u32 = (uint32_t) *ap; break;
+ case nsXPTType::T_U64 : dp->val.u64 = (uint64_t) *ap; break;
+ case nsXPTType::T_FLOAT :
+ if(i < NUM_ARG_REGS)
+ {
+ // floats passed via registers are stored as doubles
+ // in the first NUM_ARG_REGS entries in args
+ dp->val.u64 = (uint64_t) args[i];
+ dp->val.f = (float) dp->val.d; // convert double to float
+ }
+ else
+ dp->val.u32 = (uint32_t) *ap;
+ break;
+ case nsXPTType::T_DOUBLE :
+ // doubles passed via registers are also stored
+ // in the first NUM_ARG_REGS entries in args
+ dp->val.u64 = (i < NUM_ARG_REGS) ? args[i] : *ap;
+ break;
+ case nsXPTType::T_BOOL : dp->val.b = (bool) *ap; break;
+ case nsXPTType::T_CHAR : dp->val.c = (char) *ap; break;
+ case nsXPTType::T_WCHAR : dp->val.wc = (char16_t) *ap; break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+/*
+ * SharedStub()
+ * Collects arguments and calls PrepareAndDispatch. The "methodIndex" is
+ * passed to this function via $1 to preserve the argument registers.
+ */
+__asm__(
+ "#### SharedStub ####\n"
+".text\n\t"
+ ".align 5\n\t"
+ ".ent SharedStub\n"
+"SharedStub:\n\t"
+ ".frame $30,96,$26,0\n\t"
+ ".mask 0x4000000,-96\n\t"
+ "ldgp $29,0($27)\n"
+"$SharedStub..ng:\n\t"
+ "subq $30,96,$30\n\t"
+ "stq $26,0($30)\n\t"
+ ".prologue 1\n\t"
+
+ /*
+ * Store arguments passed via registers to the stack.
+ * Floating point registers are stored as doubles and converted
+ * to floats in PrepareAndDispatch if necessary.
+ */
+ "stt $f17,16($30)\n\t" /* floating point registers */
+ "stt $f18,24($30)\n\t"
+ "stt $f19,32($30)\n\t"
+ "stt $f20,40($30)\n\t"
+ "stt $f21,48($30)\n\t"
+ "stq $17,56($30)\n\t" /* integer registers */
+ "stq $18,64($30)\n\t"
+ "stq $19,72($30)\n\t"
+ "stq $20,80($30)\n\t"
+ "stq $21,88($30)\n\t"
+
+ /*
+ * Call PrepareAndDispatch function.
+ */
+ "bis $1,$1,$17\n\t" /* pass "methodIndex" */
+ "addq $30,16,$18\n\t" /* pass "args" */
+ "bsr $26,$PrepareAndDispatch..ng\n\t"
+
+ "ldq $26,0($30)\n\t"
+ "addq $30,96,$30\n\t"
+ "ret $31,($26),1\n\t"
+ ".end SharedStub"
+ );
+
+/*
+ * nsresult nsXPTCStubBase::Stub##n()
+ * Sets register $1 to "methodIndex" and jumps to SharedStub.
+ */
+#define STUB_MANGLED_ENTRY(n, symbol) \
+ "#### Stub"#n" ####" "\n\t" \
+ ".text" "\n\t" \
+ ".align 5" "\n\t" \
+ ".globl " symbol "\n\t" \
+ ".ent " symbol "\n" \
+symbol ":" "\n\t" \
+ ".frame $30,0,$26,0" "\n\t" \
+ "ldgp $29,0($27)" "\n" \
+"$" symbol "..ng:" "\n\t" \
+ ".prologue 1" "\n\t" \
+ "lda $1,"#n "\n\t" \
+ "br $31,$SharedStub..ng" "\n\t" \
+ ".end " symbol
+
+#define STUB_ENTRY(n) \
+__asm__( \
+ ".if "#n" < 10" "\n\t" \
+ STUB_MANGLED_ENTRY(n, "_ZN14nsXPTCStubBase5Stub"#n"Ev") "\n\t" \
+ ".elseif "#n" < 100" "\n\t" \
+ STUB_MANGLED_ENTRY(n, "_ZN14nsXPTCStubBase6Stub"#n"Ev") "\n\t" \
+ ".elseif "#n" < 1000" "\n\t" \
+ STUB_MANGLED_ENTRY(n, "_ZN14nsXPTCStubBase7Stub"#n"Ev") "\n\t" \
+ ".else" "\n\t" \
+ ".err \"Stub"#n" >= 1000 not yet supported.\"" "\n\t" \
+ ".endif" \
+ );
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp
new file mode 100644
index 000000000..ef4f8514d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp
@@ -0,0 +1,238 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if !defined(__arm__) && !(defined(LINUX) || defined(ANDROID) || defined(XP_DARWIN))
+#error "This code is for Linux/iOS ARM only. Please check if it works for you, too.\nDepends strongly on gcc behaviour."
+#endif
+
+/* Specify explicitly a symbol for this function, don't try to guess the c++ mangled symbol. */
+static nsresult PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args) asm("_PrepareAndDispatch")
+ATTRIBUTE_USED;
+
+#ifdef __ARM_EABI__
+#define DOUBLEWORD_ALIGN(p) ((uint32_t *)((((uint32_t)(p)) + 7) & 0xfffffff8))
+#else
+#define DOUBLEWORD_ALIGN(p) (p)
+#endif
+
+// Apple's iOS toolchain is lame.
+#ifdef __APPLE__
+#define GNU(str)
+#define APPLE(str) str
+#define UNDERSCORE "__"
+#else
+#define GNU(str) str
+#define APPLE(str)
+#define UNDERSCORE "_"
+#endif
+
+#ifdef __thumb__
+#define THUMB_FUNC ".thumb_func\n"
+#else
+#define THUMB_FUNC
+#endif
+
+static nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int8_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int16_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_I64 : ap = DOUBLEWORD_ALIGN(ap);
+ dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint8_t*) ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint16_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U64 : ap = DOUBLEWORD_ALIGN(ap);
+ dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_DOUBLE : ap = DOUBLEWORD_ALIGN(ap);
+ dp->val.d = *((double*) ap); ap++; break;
+ case nsXPTType::T_BOOL : dp->val.b = *((bool*) ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((char*) ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((wchar_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+/*
+ * This is our shared stub.
+ *
+ * r0 = Self.
+ *
+ * The Rules:
+ * We pass an (undefined) number of arguments into this function.
+ * The first 3 C++ arguments are in r1 - r3, the rest are built
+ * by the calling function on the stack.
+ *
+ * We are allowed to corrupt r0 - r3, ip, and lr.
+ *
+ * Other Info:
+ * We pass the stub number in using `ip'.
+ *
+ * Implementation:
+ * - We save r1 to r3 inclusive onto the stack, which will be
+ * immediately below the caller saved arguments.
+ * - setup r2 (PrepareAndDispatch's args pointer) to point at
+ * the base of all these arguments
+ * - Save LR (for the return address)
+ * - Set r1 (PrepareAndDispatch's methodindex argument) from ip
+ * - r0 is passed through (self)
+ * - Call PrepareAndDispatch
+ * - When the call returns, we return by loading the PC off the
+ * stack, and undoing the stack (one instruction)!
+ *
+ */
+__asm__ ("\n"
+ GNU(".text\n")
+ APPLE(".section __TEXT,__text\n")
+ THUMB_FUNC
+ ".align 2\n"
+ "SharedStub:\n"
+ GNU(".fnstart\n")
+ GNU(".cfi_startproc\n")
+ "stmfd sp!, {r1, r2, r3}\n"
+ GNU(".save {r1, r2, r3}\n")
+ GNU(".cfi_def_cfa_offset 12\n")
+ GNU(".cfi_offset r3, -4\n")
+ GNU(".cfi_offset r2, -8\n")
+ GNU(".cfi_offset r1, -12\n")
+ "mov r2, sp\n"
+ "str lr, [sp, #-4]!\n"
+ GNU(".save {lr}\n")
+ GNU(".cfi_def_cfa_offset 16\n")
+ GNU(".cfi_offset lr, -16\n")
+ "mov r1, ip\n"
+ "bl _PrepareAndDispatch\n"
+ "ldr pc, [sp], #16\n"
+ GNU(".cfi_endproc\n")
+ GNU(".fnend"));
+
+/*
+ * Create sets of stubs to call the SharedStub.
+ * We don't touch the stack here, nor any registers, other than IP.
+ * IP is defined to be corruptable by a called function, so we are
+ * safe to use it.
+ *
+ * This will work with or without optimisation.
+ */
+
+/*
+ * Note : As G++3 ABI contains the length of the functionname in the
+ * mangled name, it is difficult to get a generic assembler mechanism like
+ * in the G++ 2.95 case.
+ * Create names would be like :
+ * _ZN14nsXPTCStubBase5Stub9Ev
+ * _ZN14nsXPTCStubBase6Stub13Ev
+ * _ZN14nsXPTCStubBase7Stub144Ev
+ * Use the assembler directives to get the names right...
+ */
+
+#define STUB_ENTRY(n) \
+ __asm__( \
+ GNU(".section \".text\"\n") \
+ APPLE(".section __TEXT,__text\n") \
+" .align 2\n" \
+" .if ("#n" - 10) < 0\n" \
+" .globl " UNDERSCORE "ZN14nsXPTCStubBase5Stub"#n"Ev\n" \
+ THUMB_FUNC \
+ GNU(".type _ZN14nsXPTCStubBase5Stub"#n"Ev,#function\n") \
+UNDERSCORE "ZN14nsXPTCStubBase5Stub"#n"Ev:\n" \
+" .else\n" \
+" .if ("#n" - 100) < 0\n" \
+" .globl " UNDERSCORE "ZN14nsXPTCStubBase6Stub"#n"Ev\n" \
+ THUMB_FUNC \
+ GNU(".type _ZN14nsXPTCStubBase6Stub"#n"Ev,#function\n") \
+UNDERSCORE "ZN14nsXPTCStubBase6Stub"#n"Ev:\n" \
+" .else\n" \
+" .if ("#n" - 1000) < 0\n" \
+" .globl " UNDERSCORE "ZN14nsXPTCStubBase7Stub"#n"Ev\n" \
+ THUMB_FUNC \
+ GNU(".type _ZN14nsXPTCStubBase7Stub"#n"Ev,#function\n") \
+UNDERSCORE "ZN14nsXPTCStubBase7Stub"#n"Ev:\n" \
+" .else\n" \
+" .err \"stub number "#n"> 1000 not yet supported\"\n" \
+" .endif\n" \
+" .endif\n" \
+" .endif\n" \
+" mov ip, #"#n"\n" \
+" b SharedStub\n\t");
+
+#if 0
+/*
+ * This part is left in as comment : this is how the method definition
+ * should look like.
+ */
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n () \
+{ \
+ __asm__ ( \
+" mov ip, #"#n"\n" \
+" b SharedStub\n\t"); \
+ return 0; /* avoid warnings */ \
+}
+#endif
+
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_arm_netbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm_netbsd.cpp
new file mode 100644
index 000000000..27c2b3d02
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm_netbsd.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+
+nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ nsIInterfaceInfo* iface_info = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->GetInterfaceInfo(&iface_info);
+ NS_ASSERTION(iface_info,"no interface info");
+
+ iface_info->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int8_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int16_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint8_t*) ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint16_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ case nsXPTType::T_BOOL : dp->val.b = *((bool*) ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((char*) ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((wchar_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ NS_RELEASE(iface_info);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+/*
+ * These stubs move just move the values passed in registers onto the stack,
+ * so they are contiguous with values passed on the stack, and then calls
+ * PrepareAndDispatch() to do the dirty work.
+ */
+
+#define STUB_ENTRY(n) \
+__asm__( \
+ ".global _Stub"#n"__14nsXPTCStubBase\n\t" \
+"_Stub"#n"__14nsXPTCStubBase:\n\t" \
+ "stmfd sp!, {r1, r2, r3} \n\t" \
+ "mov ip, sp \n\t" \
+ "stmfd sp!, {fp, ip, lr, pc} \n\t" \
+ "sub fp, ip, #4 \n\t" \
+ "mov r1, #"#n" \n\t" /* = methodIndex */ \
+ "add r2, sp, #16 \n\t" \
+ "bl _PrepareAndDispatch__FP14nsXPTCStubBaseUiPUi \n\t" \
+ "ldmea fp, {fp, sp, lr} \n\t" \
+ "add sp, sp, #12 \n\t" \
+ "mov pc, lr \n\t" \
+);
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_arm_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm_openbsd.cpp
new file mode 100644
index 000000000..f63d0c1e2
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm_openbsd.cpp
@@ -0,0 +1,205 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#ifdef __GNUC__
+/* This tells gcc3.4+ not to optimize away symbols.
+ * @see http://gcc.gnu.org/gcc-3.4/changes.html
+ */
+#define DONT_DROP_OR_WARN __attribute__((used))
+#else
+/* This tells older gccs not to warn about unused vairables.
+ * @see http://docs.freebsd.org/info/gcc/gcc.info.Variable_Attributes.html
+ */
+#define DONT_DROP_OR_WARN __attribute__((unused))
+#endif
+
+/* Specify explicitly a symbol for this function, don't try to guess the c++ mangled symbol. */
+static nsresult PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args) asm("_PrepareAndDispatch")
+DONT_DROP_OR_WARN;
+
+static nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int8_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int16_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint8_t*) ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint16_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ case nsXPTType::T_BOOL : dp->val.b = *((bool*) ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((char*) ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((wchar_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+/*
+ * This is our shared stub.
+ *
+ * r0 = Self.
+ *
+ * The Rules:
+ * We pass an (undefined) number of arguments into this function.
+ * The first 3 C++ arguments are in r1 - r3, the rest are built
+ * by the calling function on the stack.
+ *
+ * We are allowed to corrupt r0 - r3, ip, and lr.
+ *
+ * Other Info:
+ * We pass the stub number in using `ip'.
+ *
+ * Implementation:
+ * - We save r1 to r3 inclusive onto the stack, which will be
+ * immediately below the caller saved arguments.
+ * - setup r2 (PrepareAndDispatch's args pointer) to point at
+ * the base of all these arguments
+ * - Save LR (for the return address)
+ * - Set r1 (PrepareAndDispatch's methodindex argument) from ip
+ * - r0 is passed through (self)
+ * - Call PrepareAndDispatch
+ * - When the call returns, we return by loading the PC off the
+ * stack, and undoing the stack (one instruction)!
+ *
+ */
+__asm__ ("\n\
+ .text \n\
+ .align 2 \n\
+SharedStub: \n\
+ stmfd sp!, {r1, r2, r3} \n\
+ mov r2, sp \n\
+ str lr, [sp, #-4]! \n\
+ mov r1, ip \n\
+ bl _PrepareAndDispatch \n\
+ ldr pc, [sp], #16");
+
+/*
+ * Create sets of stubs to call the SharedStub.
+ * We don't touch the stack here, nor any registers, other than IP.
+ * IP is defined to be corruptable by a called function, so we are
+ * safe to use it.
+ *
+ * This will work with or without optimisation.
+ */
+
+/*
+ * Note : As G++3 ABI contains the length of the functionname in the
+ * mangled name, it is difficult to get a generic assembler mechanism like
+ * in the G++ 2.95 case.
+ * Create names would be like :
+ * _ZN14nsXPTCStubBase5Stub9Ev
+ * _ZN14nsXPTCStubBase6Stub13Ev
+ * _ZN14nsXPTCStubBase7Stub144Ev
+ * Use the assembler directives to get the names right...
+ */
+
+#define STUB_ENTRY(n) \
+ __asm__( \
+ ".section \".text\"\n" \
+" .align 2\n" \
+" .iflt ("#n" - 10)\n" \
+" .globl _ZN14nsXPTCStubBase5Stub"#n"Ev\n" \
+" .type _ZN14nsXPTCStubBase5Stub"#n"Ev,#function\n" \
+"_ZN14nsXPTCStubBase5Stub"#n"Ev:\n" \
+" .else\n" \
+" .iflt ("#n" - 100)\n" \
+" .globl _ZN14nsXPTCStubBase6Stub"#n"Ev\n" \
+" .type _ZN14nsXPTCStubBase6Stub"#n"Ev,#function\n" \
+"_ZN14nsXPTCStubBase6Stub"#n"Ev:\n" \
+" .else\n" \
+" .iflt ("#n" - 1000)\n" \
+" .globl _ZN14nsXPTCStubBase7Stub"#n"Ev\n" \
+" .type _ZN14nsXPTCStubBase7Stub"#n"Ev,#function\n" \
+"_ZN14nsXPTCStubBase7Stub"#n"Ev:\n" \
+" .else\n" \
+" .err \"stub number "#n"> 1000 not yet supported\"\n" \
+" .endif\n" \
+" .endif\n" \
+" .endif\n" \
+" mov ip, #"#n"\n" \
+" b SharedStub\n\t");
+
+#if 0
+/*
+ * This part is left in as comment : this is how the method definition
+ * should look like.
+ */
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n () \
+{ \
+ __asm__ ( \
+" mov ip, #"#n"\n" \
+" b SharedStub\n\t"); \
+ return 0; /* avoid warnings */ \
+}
+#endif
+
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_aarch64.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_aarch64.s
new file mode 100644
index 000000000..6603a4f25
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_aarch64.s
@@ -0,0 +1,39 @@
+# 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/.
+
+ .set NGPREGS,8
+ .set NFPREGS,8
+
+ .section ".text"
+ .globl SharedStub
+ .hidden SharedStub
+ .type SharedStub,@function
+SharedStub:
+ stp x29, x30, [sp,#-16]!
+ mov x29, sp
+
+ sub sp, sp, #8*(NGPREGS+NFPREGS)
+ stp x0, x1, [sp, #64+(0*8)]
+ stp x2, x3, [sp, #64+(2*8)]
+ stp x4, x5, [sp, #64+(4*8)]
+ stp x6, x7, [sp, #64+(6*8)]
+ stp d0, d1, [sp, #(0*8)]
+ stp d2, d3, [sp, #(2*8)]
+ stp d4, d5, [sp, #(4*8)]
+ stp d6, d7, [sp, #(6*8)]
+
+ # methodIndex passed from stub
+ mov w1, w17
+
+ add x2, sp, #16+(8*(NGPREGS+NFPREGS))
+ add x3, sp, #8*NFPREGS
+ add x4, sp, #0
+
+ bl PrepareAndDispatch
+
+ add sp, sp, #8*(NGPREGS+NFPREGS)
+ ldp x29, x30, [sp],#16
+ ret
+
+ .size SharedStub, . - SharedStub
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf32.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf32.s
new file mode 100644
index 000000000..720dd6cc7
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf32.s
@@ -0,0 +1,123 @@
+
+// Select C numeric constant
+ .radix C
+ .psr abi32
+ .psr msb
+// Section has executable code
+ .section .text, "ax","progbits"
+// procedure named 'SharedStub'
+ .proc SharedStub
+// manual bundling
+ .explicit
+
+ .global PrepareAndDispatch
+// .exclass PrepareAndDispatch, @fullyvisible
+ .type PrepareAndDispatch,@function
+
+SharedStub::
+// 10 arguments, first 8 are the input arguments of previous
+// function call. The 9th one is methodIndex and the 10th is the
+// pointer to the remaining input arguments. The last two arguments
+// are passed in memory.
+ .prologue
+ .save ar.pfs , r41
+// allocate 8 input args, 4 local args, and 5 output args
+ alloc r41 = ar.pfs, 8, 4, 5, 0 // M
+ .save rp, r40
+ mov r40 = rp // I
+ addp4 out4 = 28, sp ;; // I
+
+ .save ar.unat, r42
+ mov r42 = ar.unat // M
+ .fframe 144
+ add sp = -144, sp // A
+// unwind table already knows gp, don't need to specify anything
+ add r43 = 0, gp ;; // A
+
+// We have possible 8 integer registers and 8 float registers that could
+// be arguments. We also have a stack region from the previous
+// stack frame that may hold some stack arguments.
+// We need to write the integer registers to a memory region, write
+// the float registers to a memory region (making sure we don't step
+// on NAT while touching the registers). We also mark the memory
+// address of the stack arguments.
+// We then call PrepareAndDispatch() specifying the three memory
+// region pointers.
+
+
+ .body
+ add out0 = 0, in0 // A move self ptr
+// 144 bytes = 16 byte stack header + 64 byte int space + 64 byte float space
+// methodIndex is at 144 + 16 bytes away from current sp
+// (current frame + previous frame header)
+ ld4 out4 = [out4] // A restarg address
+ add r11 = 160, sp ;; // A address of methodIndex
+
+ ld8 out1 = [r11] // M load methodIndex
+// sp + 16 is the start of intargs
+ add out2 = 16, sp // A address of intargs
+// the intargs take up 64 bytes, so sp + 16 + 64 is the start of floatargs
+ add out3 = 80, sp ;; // A address of floatargs
+
+ add r11 = 0, out2 ;; // A
+ st8.spill [r11] = in1, 8 // M
+ add r10 = 0, out3 ;; // A
+
+ st8.spill [r11] = in2, 8 ;; // M
+ st8.spill [r11] = in3, 8 // M
+ nop.i 0 ;; // I
+
+ st8.spill [r11] = in4, 8 ;; // M
+ st8.spill [r11] = in5, 8 // M
+ nop.i 0 ;; // I
+
+ st8.spill [r11] = in6, 8 ;; // M
+ st8.spill [r11] = in7 // M
+ fclass.nm p14,p15 = f8,@nat ;; // F
+
+(p14) stfd [r10] = f8, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 = f9,@nat ;; // F
+
+(p12) stfd [r10] = f9, 8 // M
+(p13) add r10 = 8, r10 // A
+ fclass.nm p14,p15 =f10,@nat ;; // F
+
+(p14) stfd [r10] = f10, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 =f11,@nat ;; // F
+
+(p12) stfd [r10] = f11, 8 // M
+(p13) add r10 = 8, r10 // A
+ fclass.nm p14,p15 =f12,@nat ;; // F
+
+(p14) stfd [r10] = f12, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 =f13,@nat ;; // F
+
+(p12) stfd [r10] = f13, 8 // M
+(p13) add r10 = 8, r10 // A
+ fclass.nm p14,p15 =f14,@nat ;; // F
+
+(p14) stfd [r10] = f14, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 =f15,@nat ;; // F
+
+(p12) stfd [r10] = f15, 8 // M
+(p13) add r10 = 8, r10 // A
+
+// branch to PrepareAndDispatch
+ br.call.dptk.few rp = PrepareAndDispatch ;; // B
+
+// epilog
+ mov ar.unat = r42 // M
+ mov ar.pfs = r41 // I
+ mov rp = r40 ;; // I
+
+ add gp = 0, r43 // A
+ add sp = 144, sp // A
+ br.ret.dptk.few rp ;; // B
+
+ .endp
+
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf64.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf64.s
new file mode 100644
index 000000000..4c07836d7
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ipf64.s
@@ -0,0 +1,124 @@
+
+// Select C numeric constant
+ .radix C
+ .psr abi64
+ .psr lsb
+// Section has executable code
+ .section .text, "ax","progbits"
+// procedure named 'SharedStub'
+ .proc SharedStub
+// manual bundling
+ .explicit
+
+ .global PrepareAndDispatch
+// .exclass PrepareAndDispatch, @fullyvisible
+ .type PrepareAndDispatch,@function
+
+SharedStub::
+// 10 arguments, first 8 are the input arguments of previous
+// function call. The 9th one is methodIndex and the 10th is the
+// pointer to the remaining input arguments. The last two arguments
+// are passed in memory.
+ .prologue
+ .save ar.pfs , r41
+// allocate 8 input args, 4 local args, and 5 output args
+ alloc r41 = ar.pfs, 8, 4, 5, 0 // M
+ .save rp, r40
+ mov r40 = rp // I
+ add out4 = 24, sp ;; // I
+
+ .save ar.unat, r42
+ mov r42 = ar.unat // M
+ .fframe 144
+ add sp = -144, sp // A
+// unwind table already knows gp, don't need to specify anything
+ add r43 = 0, gp ;; // A
+
+// We have possible 8 integer registers and 8 float registers that could
+// be arguments. We also have a stack region from the previous
+// stack frame that may hold some stack arguments.
+// We need to write the integer registers to a memory region, write
+// the float registers to a memory region (making sure we don't step
+// on NAT while touching the registers). We also mark the memory
+// address of the stack arguments.
+// We then call PrepareAndDispatch() specifying the three memory
+// region pointers.
+
+
+ .body
+ add out0 = 0, in0 // A move self ptr
+// 144 bytes = 16 byte stack header + 64 byte int space + 64 byte float space
+// methodIndex is at 144 + 16 bytes away from current sp
+// (current frame + previous frame header)
+ ld8 out4 = [out4] // M restarg address
+ add r11 = 160, sp ;; // A address of methodIndex
+
+ ld8 out1 = [r11] // M load methodIndex
+// sp + 16 is the start of intargs
+ add out2 = 16, sp // A address of intargs
+// the intargs take up 64 bytes, so sp + 16 + 64 is the start of floatargs
+ add out3 = 80, sp ;; // A address of floatargs
+
+ add r11 = 0, out2 ;; // A
+ st8.spill [r11] = in1, 8 // M
+ add r10 = 0, out3 ;; // A
+
+ st8.spill [r11] = in2, 8 ;; // M
+ st8.spill [r11] = in3, 8 // M
+ nop.i 0 ;; // I
+
+ st8.spill [r11] = in4, 8 ;; // M
+ st8.spill [r11] = in5, 8 // M
+ nop.i 0 ;; // I
+
+ st8.spill [r11] = in6, 8 ;; // M
+ st8.spill [r11] = in7 // M
+ fclass.nm p14,p15 = f8,@nat ;; // F
+
+(p14) stfd [r10] = f8, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 = f9,@nat ;; // F
+
+(p12) stfd [r10] = f9, 8 // M
+(p13) add r10 = 8, r10 // A
+ fclass.nm p14,p15 =f10,@nat ;; // F
+
+(p14) stfd [r10] = f10, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 =f11,@nat ;; // F
+
+(p12) stfd [r10] = f11, 8 // M
+(p13) add r10 = 8, r10 // A
+ fclass.nm p14,p15 =f12,@nat ;; // F
+
+(p14) stfd [r10] = f12, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 =f13,@nat ;; // F
+
+(p12) stfd [r10] = f13, 8 // M
+(p13) add r10 = 8, r10 // A
+ fclass.nm p14,p15 =f14,@nat ;; // F
+
+(p14) stfd [r10] = f14, 8 // M
+(p15) add r10 = 8, r10 // A
+ fclass.nm p12,p13 =f15,@nat ;; // F
+
+(p12) stfd [r10] = f15, 8 // M
+(p13) add r10 = 8, r10 // A
+
+// branch to PrepareAndDispatch
+ br.call.dptk.few rp = PrepareAndDispatch ;; // B
+
+// epilog
+ mov ar.unat = r42 // M
+ mov ar.pfs = r41 // I
+ mov rp = r40 ;; // I
+
+ add gp = 0, r43 // A
+ add sp = 144, sp // A
+ br.ret.dptk.few rp ;; // B
+
+ .endp
+
+/* Magic indicating no need for an executable stack */
+.section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.S b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.S
new file mode 100644
index 000000000..d17301634
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.S
@@ -0,0 +1,116 @@
+/* -*- Mode: asm; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* This code is for MIPS using the O32 ABI. */
+
+#ifdef ANDROID
+#include <asm/regdef.h>
+#include <asm/asm.h>
+#include <machine/asm.h>
+#else
+#include <sys/regdef.h>
+#include <sys/asm.h>
+#endif
+
+# NARGSAVE is the argument space in the callers frame, including extra
+# 'shadowed' space for the argument registers. The minimum of 4
+# argument slots is sometimes predefined in the header files.
+#ifndef NARGSAVE
+#define NARGSAVE 4
+#endif
+
+#define LOCALSZ 2 /* gp, ra */
+#define FRAMESZ ((((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK)
+
+#define RAOFF (FRAMESZ - (1*SZREG))
+#define GPOFF (FRAMESZ - (2*SZREG))
+
+#define A0OFF (FRAMESZ + (0*SZREG))
+#define A1OFF (FRAMESZ + (1*SZREG))
+#define A2OFF (FRAMESZ + (2*SZREG))
+#define A3OFF (FRAMESZ + (3*SZREG))
+
+ .text
+
+#define STUB_ENTRY(x) \
+ .if x < 10; \
+ .globl _ZN14nsXPTCStubBase5Stub ##x ##Ev; \
+ .type _ZN14nsXPTCStubBase5Stub ##x ##Ev,@function; \
+ .aent _ZN14nsXPTCStubBase5Stub ##x ##Ev,0; \
+_ZN14nsXPTCStubBase5Stub ##x ##Ev:; \
+ SETUP_GP; \
+ li t0,x; \
+ b sharedstub; \
+ .elseif x < 100; \
+ .globl _ZN14nsXPTCStubBase6Stub ##x ##Ev; \
+ .type _ZN14nsXPTCStubBase6Stub ##x ##Ev,@function; \
+ .aent _ZN14nsXPTCStubBase6Stub ##x ##Ev,0; \
+_ZN14nsXPTCStubBase6Stub ##x ##Ev:; \
+ SETUP_GP; \
+ li t0,x; \
+ b sharedstub; \
+ .elseif x < 1000; \
+ .globl _ZN14nsXPTCStubBase7Stub ##x ##Ev; \
+ .type _ZN14nsXPTCStubBase7Stub ##x ##Ev,@function; \
+ .aent _ZN14nsXPTCStubBase7Stub ##x ##Ev,0; \
+_ZN14nsXPTCStubBase7Stub ##x ##Ev:; \
+ SETUP_GP; \
+ li t0,x; \
+ b sharedstub; \
+ .else; \
+ .err; \
+ .endif
+
+# SENTINEL_ENTRY is handled in the cpp file.
+#define SENTINEL_ENTRY(x)
+
+#
+# open a dummy frame for the function entries
+#
+ .align 2
+ .type dummy,@function
+ .ent dummy, 0
+ .frame sp, FRAMESZ, ra
+dummy:
+ SETUP_GP
+
+#include "xptcstubsdef.inc"
+
+sharedstub:
+ subu sp, FRAMESZ
+
+ # specify the save register mask for gp, ra, a0-a3
+ .mask 0x900000F0, RAOFF-FRAMESZ
+
+ sw ra, RAOFF(sp)
+ SAVE_GP(GPOFF)
+
+ # Micro-optimization: a0 is already loaded, and its slot gets
+ # ignored by PrepareAndDispatch, so no need to save it here.
+ # sw a0, A0OFF(sp)
+ sw a1, A1OFF(sp)
+ sw a2, A2OFF(sp)
+ sw a3, A3OFF(sp)
+
+ la t9, PrepareAndDispatch
+
+ # t0 is methodIndex
+ move a1, t0
+ # have a2 point to the begin of the argument space on stack
+ addiu a2, sp, FRAMESZ
+
+ # PrepareAndDispatch(that, methodIndex, args)
+ jalr t9
+
+ # Micro-optimization: Using jalr explicitly has the side-effect
+ # of not triggering .cprestore. This is ok because we have no
+ # gp reference below this point. It also allows better
+ # instruction sscheduling.
+ # lw gp, GPOFF(fp)
+
+ lw ra, RAOFF(sp)
+ addiu sp, FRAMESZ
+ j ra
+ END(dummy)
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.s.m4 b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.s.m4
new file mode 100644
index 000000000..33c7b1492
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips.s.m4
@@ -0,0 +1,75 @@
+/* -*- Mode: asm; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * Version: MPL 1.1
+ *
+ * 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/. */
+
+/* This code is for MIPS using the O32 ABI. */
+
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+ .text
+ .globl PrepareAndDispatch
+
+NARGSAVE=4 # extra space for the callee to use. gccism
+ # we can put our a0-a3 in our callers space.
+LOCALSZ=2 # gp, ra
+FRAMESZ=(((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
+
+define(STUB_NAME, `Stub'$1`__14nsXPTCStubBase')
+
+define(STUB_ENTRY,
+` .globl 'STUB_NAME($1)`
+ .align 2
+ .type 'STUB_NAME($1)`,@function
+ .ent 'STUB_NAME($1)`, 0
+'STUB_NAME($1)`:
+ .frame sp, FRAMESZ, ra
+ .set noreorder
+ .cpload t9
+ .set reorder
+ subu sp, FRAMESZ
+ .cprestore 16
+ li t0, '$1`
+ b sharedstub
+.end 'STUB_NAME($1)`
+
+')
+
+define(SENTINEL_ENTRY, `')
+
+include(xptcstubsdef.inc)
+
+ .globl sharedstub
+ .ent sharedstub
+sharedstub:
+
+ REG_S ra, 20(sp)
+
+ REG_S a0, 24(sp)
+ REG_S a1, 28(sp)
+ REG_S a2, 32(sp)
+ REG_S a3, 36(sp)
+
+ # t0 is methodIndex
+ move a1, t0
+
+ # put the start of a1, a2, a3, and stack
+ move a2, sp
+ addi a2, 24 # have a2 point to sp + 24 (where a0 is)
+
+ # PrepareAndDispatch(that, methodIndex, args)
+ # a0 a1 a2
+ #
+ jal PrepareAndDispatch
+
+ REG_L ra, 20(sp)
+ REG_L a1, 28(sp)
+ REG_L a2, 32(sp)
+
+ addu sp, FRAMESZ
+ j ra
+
+.end sharedstub
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips64.S b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips64.S
new file mode 100644
index 000000000..11d851536
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_mips64.S
@@ -0,0 +1,111 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 <sys/regdef.h>
+#include <sys/asm.h>
+
+LOCALSZ=16
+FRAMESZ=(((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
+
+A1OFF=FRAMESZ-(9*SZREG)
+A2OFF=FRAMESZ-(8*SZREG)
+A3OFF=FRAMESZ-(7*SZREG)
+A4OFF=FRAMESZ-(6*SZREG)
+A5OFF=FRAMESZ-(5*SZREG)
+A6OFF=FRAMESZ-(4*SZREG)
+A7OFF=FRAMESZ-(3*SZREG)
+GPOFF=FRAMESZ-(2*SZREG)
+RAOFF=FRAMESZ-(1*SZREG)
+
+F13OFF=FRAMESZ-(16*SZREG)
+F14OFF=FRAMESZ-(15*SZREG)
+F15OFF=FRAMESZ-(14*SZREG)
+F16OFF=FRAMESZ-(13*SZREG)
+F17OFF=FRAMESZ-(12*SZREG)
+F18OFF=FRAMESZ-(11*SZREG)
+F19OFF=FRAMESZ-(10*SZREG)
+
+#define SENTINEL_ENTRY(n) /* defined in cpp file, not here */
+
+#define STUB_ENTRY(x) \
+ .if x < 10; \
+ MAKE_STUB(x, _ZN14nsXPTCStubBase5Stub ##x ##Ev); \
+ .elseif x < 100; \
+ MAKE_STUB(x, _ZN14nsXPTCStubBase6Stub ##x ##Ev); \
+ .elseif x < 1000; \
+ MAKE_STUB(x, _ZN14nsXPTCStubBase7Stub ##x ##Ev); \
+ .else; \
+ .err; \
+ .endif
+
+#define MAKE_STUB(x, name) \
+ .globl name; \
+ .type name,@function; \
+ .aent name,0; \
+name:; \
+ PTR_SUBU sp,FRAMESZ; \
+ SETUP_GP64(GPOFF, name); \
+ li t0,x; \
+ b sharedstub; \
+
+#
+# open a dummy frame for the function entries
+#
+ .text
+ .align 2
+ .type dummy,@function
+ .ent dummy, 0
+dummy:
+ .frame sp, FRAMESZ, ra
+ .mask 0x90000FF0, RAOFF-FRAMESZ
+ .fmask 0x000FF000, F19OFF-FRAMESZ
+
+#include "xptcstubsdef.inc"
+
+sharedstub:
+
+ REG_S a1, A1OFF(sp)
+ REG_S a2, A2OFF(sp)
+ REG_S a3, A3OFF(sp)
+ REG_S a4, A4OFF(sp)
+ REG_S a5, A5OFF(sp)
+ REG_S a6, A6OFF(sp)
+ REG_S a7, A7OFF(sp)
+ REG_S ra, RAOFF(sp)
+
+ s.d $f13, F13OFF(sp)
+ s.d $f14, F14OFF(sp)
+ s.d $f15, F15OFF(sp)
+ s.d $f16, F16OFF(sp)
+ s.d $f17, F17OFF(sp)
+ s.d $f18, F18OFF(sp)
+ s.d $f19, F19OFF(sp)
+
+ # t0 is methodIndex
+ move a1, t0
+
+ # a2 is stack address where extra function params
+ # are stored that do not fit in registers
+ move a2, sp
+ PTR_ADDI a2, FRAMESZ
+
+ # a3 is stack address of a1..a7
+ move a3, sp
+ PTR_ADDI a3, A1OFF
+
+ # a4 is stack address of f13..f19
+ move a4, sp
+ PTR_ADDI a4, F13OFF
+
+ # PrepareAndDispatch(that, methodIndex, args, gprArgs, fpArgs)
+ # a0 a1 a2 a3 a4
+ #
+ jal PrepareAndDispatch
+
+ REG_L ra, RAOFF(sp)
+ RESTORE_GP64
+
+ PTR_ADDU sp, FRAMESZ
+ j ra
+ END(dummy)
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_pa32.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_pa32.s
new file mode 100644
index 000000000..9e86848fc
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_pa32.s
@@ -0,0 +1,68 @@
+ .LEVEL 1.1
+
+curframesz .EQU 128
+; SharedStub has stack size of 128 bytes
+
+lastframesz .EQU 64
+; the StubN C++ function has a small stack size of 64 bytes
+
+ .SPACE $TEXT$,SORT=8
+ .SUBSPA $CODE$,QUAD=0,ALIGN=4,ACCESS=0x2c,CODE_ONLY,SORT=24
+SharedStub
+ .PROC
+ .CALLINFO CALLER,FRAME=80,SAVE_RP,ARGS_SAVED
+
+ .ENTRY
+ STW %rp,-20(%sp)
+ LDO 128(%sp),%sp
+
+ STW %r19,-32(%r30)
+ STW %r26,-36-curframesz(%r30) ; save arg0 in previous frame
+
+ LDO -80(%r30),%r28
+ FSTD,MA %fr5,8(%r28) ; save darg0
+ FSTD,MA %fr7,8(%r28) ; save darg1
+ FSTW,MA %fr4L,4(%r28) ; save farg0
+ FSTW,MA %fr5L,4(%r28) ; save farg1
+ FSTW,MA %fr6L,4(%r28) ; save farg2
+ FSTW,MA %fr7L,4(%r28) ; save farg3
+
+ ; Former value of register 26 is already properly saved by StubN,
+ ; but register 25-23 are not because of the arguments mismatch
+ STW %r25,-40-curframesz-lastframesz(%r30) ; save r25
+ STW %r24,-44-curframesz-lastframesz(%r30) ; save r24
+ STW %r23,-48-curframesz-lastframesz(%r30) ; save r23
+ COPY %r26,%r25 ; method index is arg1
+ LDW -36-curframesz-lastframesz(%r30),%r26 ; self is arg0
+ LDO -40-curframesz-lastframesz(%r30),%r24 ; normal args is arg2
+ LDO -80(%r30),%r23 ; floating args is arg3
+
+ BL .+8,%r2
+ ADDIL L'PrepareAndDispatch-$PIC_pcrel$0+4,%r2
+ LDO R'PrepareAndDispatch-$PIC_pcrel$1+8(%r1),%r1
+$PIC_pcrel$0
+ LDSID (%r1),%r31
+$PIC_pcrel$1
+ MTSP %r31,%sr0
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR,RTNVAL=GR
+;in=23-26;out=28;
+ BLE 0(%sr0,%r1)
+ COPY %r31,%r2
+
+ LDW -32(%r30),%r19
+
+ LDW -148(%sp),%rp
+ BVE (%rp)
+ .EXIT
+ LDO -128(%sp),%sp
+
+
+ .PROCEND ;in=26;out=28;
+
+ .ALIGN 8
+ .SPACE $TEXT$
+ .SUBSPA $CODE$
+ .IMPORT PrepareAndDispatch,CODE
+ .EXPORT SharedStub,ENTRY,PRIV_LEV=3,ARGW0=GR,RTNVAL=GR,LONG_RETURN
+ .END
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_parisc_linux.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_parisc_linux.s
new file mode 100644
index 000000000..b45d7763b
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_parisc_linux.s
@@ -0,0 +1,73 @@
+/* -*- Mode: asm; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * Version: MPL 1.1
+ *
+ * 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/. */
+
+ .LEVEL 1.1
+ .TEXT
+ .ALIGN 4
+
+curframesz:
+ .EQU 128
+
+
+; SharedStub has stack size of 128 bytes
+
+lastframesz:
+ .EQU 64
+
+; the StubN C++ function has a small stack size of 64 bytes
+
+
+.globl SharedStub
+ .type SharedStub, @function
+
+SharedStub:
+ .PROC
+ .CALLINFO CALLER,FRAME=80,SAVE_RP
+
+ .ENTRY
+ STW %rp,-20(%sp)
+ LDO 128(%sp),%sp
+
+ STW %r19,-32(%r30)
+ STW %r26,-36-curframesz(%r30) ; save arg0 in previous frame
+
+ LDO -80(%r30),%r28
+ FSTD,MA %fr5,8(%r28) ; save darg0
+ FSTD,MA %fr7,8(%r28) ; save darg1
+ FSTW,MA %fr4L,4(%r28) ; save farg0
+ FSTW,MA %fr5L,4(%r28) ; save farg1
+ FSTW,MA %fr6L,4(%r28) ; save farg2
+ FSTW,MA %fr7L,4(%r28) ; save farg3
+
+ ; Former value of register 26 is already properly saved by StubN,
+ ; but register 25-23 are not because of the argument mismatch
+ STW %r25,-40-curframesz-lastframesz(%r30) ; save r25
+ STW %r24,-44-curframesz-lastframesz(%r30) ; save r24
+ STW %r23,-48-curframesz-lastframesz(%r30) ; save r23
+ COPY %r26,%r25 ; method index is arg1
+ LDW -36-curframesz-lastframesz(%r30),%r26 ; self is arg0
+ LDO -40-curframesz-lastframesz(%r30),%r24 ; normal args is arg2
+ LDO -80(%r30),%r23 ; floating args is arg3
+
+ .CALL ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR,RTNVAL=GR ;in=23-26;out=28;
+ BL PrepareAndDispatch, %r31
+ COPY %r31,%r2
+
+ LDW -32(%r30),%r19
+
+ LDW -148(%sp),%rp
+ LDO -128(%sp),%sp
+
+
+ BV,N (%rp)
+ NOP
+ NOP
+
+ .EXIT
+ .PROCEND ;in=26;out=28;
+
+ .SIZE SharedStub, .-SharedStub
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc64_linux.S b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc64_linux.S
new file mode 100644
index 000000000..877e262b9
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc64_linux.S
@@ -0,0 +1,112 @@
+# 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/.
+
+.set r0,0; .set r1,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+#if _CALL_ELF == 2
+#define STACK_PARAMS 96
+#else
+#define STACK_PARAMS 112
+#endif
+
+#if _CALL_ELF == 2
+ .section ".text"
+ .type SharedStub,@function
+ .globl SharedStub
+ # Make the symbol hidden so that the branch from the stub does
+ # not go via a PLT. This is not only better for performance,
+ # but may be necessary to avoid linker errors since there is
+ # no place to restore the TOC register in a sibling call.
+ .hidden SharedStub
+ .align 2
+SharedStub:
+0: addis 2,12,(.TOC.-0b)@ha
+ addi 2,2,(.TOC.-0b)@l
+ .localentry SharedStub,.-SharedStub
+#else
+ .section ".text"
+ .align 2
+ .globl SharedStub
+ # Make the symbol hidden so that the branch from the stub does
+ # not go via a PLT. This is not only better for performance,
+ # but may be necessary to avoid linker errors since there is
+ # no place to restore the TOC register in a sibling call.
+ .hidden SharedStub
+ .section ".opd","aw"
+ .align 3
+
+SharedStub:
+ .quad .SharedStub,.TOC.@tocbase
+ .previous
+ .type SharedStub,@function
+
+.SharedStub:
+#endif
+ mflr r0
+
+ std r4, -56(r1) # Save all GPRS
+ std r5, -48(r1)
+ std r6, -40(r1)
+ std r7, -32(r1)
+ std r8, -24(r1)
+ std r9, -16(r1)
+ std r10, -8(r1)
+
+ stfd f13, -64(r1) # ... and FPRS
+ stfd f12, -72(r1)
+ stfd f11, -80(r1)
+ stfd f10, -88(r1)
+ stfd f9, -96(r1)
+ stfd f8, -104(r1)
+ stfd f7, -112(r1)
+ stfd f6, -120(r1)
+ stfd f5, -128(r1)
+ stfd f4, -136(r1)
+ stfd f3, -144(r1)
+ stfd f2, -152(r1)
+ stfd f1, -160(r1)
+
+ subi r6,r1,56 # r6 --> gprData
+ subi r7,r1,160 # r7 --> fprData
+ addi r5,r1,STACK_PARAMS # r5 --> extra stack args
+
+ std r0, 16(r1)
+
+ stdu r1,-288(r1)
+ # r3 has the 'self' pointer
+ # already
+
+ mr r4,r11 # r4 is methodIndex selector,
+ # passed via r11 in the
+ # nsNSStubBase::StubXX() call
+
+ bl PrepareAndDispatch
+ nop
+
+ ld 1,0(r1) # restore stack
+ ld r0,16(r1) # restore LR
+ mtlr r0
+ blr
+
+#if _CALL_ELF == 2
+ .size SharedStub,.-SharedStub
+#else
+ .size SharedStub,.-.SharedStub
+#endif
+
+ # Magic indicating no need for an executable stack
+ .section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix.s.m4 b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix.s.m4
new file mode 100644
index 000000000..6dabf334d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix.s.m4
@@ -0,0 +1,119 @@
+# 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+# Define the correct name of the stub function based on the object model
+
+define(STUB_NAME,
+ ifelse(AIX_OBJMODEL, ibm,
+ `Stub'$1`__EI14nsXPTCStubBaseFv',
+ `Stub'$1`__14nsXPTCStubBaseFv'))
+
+define(STUB_ENTRY, `
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.'STUB_NAME($1)`{TC},"'STUB_NAME($1)`"
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .'STUB_NAME($1)`
+ .globl 'STUB_NAME($1)`{DS}
+
+.'STUB_NAME($1)`:
+ li r12, '$1`
+ b .SharedStub
+ nop
+
+
+ .toc
+T.18.'STUB_NAME($1)`:
+ .tc H.18.'STUB_NAME($1)`{TC},'STUB_NAME($1)`{DS}
+ .csect 'STUB_NAME($1)`{DS}
+ .long .'STUB_NAME($1)`
+ .long TOC{TC0}
+ .long 0x00000000
+')
+
+define(SENTINEL_ENTRY, `')
+
+include(xptcstubsdef.inc)
+
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.SharedStub{TC},"SharedStub"
+
+# .text section
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .SharedStub
+ .globl SharedStub{DS}
+ .extern .PrepareAndDispatch
+
+.SharedStub:
+ mflr r0
+ stw r0,8(sp)
+
+ stwu sp,-176(sp) # room for linkage (24), fprData (104), gprData(28)
+ # outgoing params to PrepareAndDispatch (20)
+
+ stw r4,44(sp) # link area (24) + PrepareAndDispatch params (20)
+ stw r5,48(sp)
+ stw r6,52(sp)
+ stw r7,56(sp)
+ stw r8,60(sp)
+ stw r9,64(sp)
+ stw r10,68(sp)
+ stfd f1,72(sp)
+ stfd f2,80(sp)
+ stfd f3,88(sp)
+ stfd f4,96(sp)
+ stfd f5,104(sp)
+ stfd f6,112(sp)
+ stfd f7,120(sp)
+ stfd f8,128(sp)
+ stfd f9,136(sp)
+ stfd f10,144(sp)
+ stfd f11,152(sp)
+ stfd f12,156(sp)
+ stfd f13,164(sp)
+
+ addi r6,sp,44 # gprData
+
+ addi r7,sp,72 # fprData
+ # r3 has the 'self' pointer already
+ mr r4,r12 # methodIndex selector (it is now LATER)
+ addi r5,sp,232 # pointer to callers args area, beyond r3-r10
+ # mapped range
+
+ bl .PrepareAndDispatch
+ nop
+
+
+ lwz r0,184(sp)
+ addi sp,sp,176
+ mtlr r0
+ blr
+
+# .data section
+
+ .toc # 0x00000038
+T.18.SharedStub:
+ .tc H.18.SharedStub{TC},SharedStub{DS}
+
+ .csect SharedStub{DS}
+ .long .SharedStub # "\0\0\0\0"
+ .long TOC{TC0} # "\0\0\0008"
+ .long 0x00000000 # "\0\0\0\0"
+# End csect SharedStub{DS}
+
+# .bss section
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix64.s.m4 b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix64.s.m4
new file mode 100644
index 000000000..24d713cc9
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_aix64.s.m4
@@ -0,0 +1,97 @@
+# 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+# Define the correct name of the stub function based on the object model
+define(STUB_NAME,
+ ifelse(AIX_OBJMODEL, ibm,
+ `Stub'$1`__EI14nsXPTCStubBaseFv',
+ `Stub'$1`__14nsXPTCStubBaseFv'))
+define(STUB_ENTRY, `
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.'STUB_NAME($1)`{TC},"'STUB_NAME($1)`"
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .'STUB_NAME($1)`
+ .globl 'STUB_NAME($1)`{DS}
+.'STUB_NAME($1)`:
+ li r12, '$1`
+ b .SharedStub
+ nop
+ .toc
+T.18.'STUB_NAME($1)`:
+ .tc H.18.'STUB_NAME($1)`{TC},'STUB_NAME($1)`{DS}
+ .csect 'STUB_NAME($1)`{DS}
+ .llong .'STUB_NAME($1)`
+ .llong TOC{TC0}
+ .llong 0x00000000
+')
+define(SENTINEL_ENTRY, `')
+include(xptcstubsdef.inc)
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.SharedStub{TC},"SharedStub"
+# .text section
+ .csect H.10.NO_SYMBOL{PR}
+ .globl .SharedStub
+ .globl SharedStub{DS}
+ .extern .PrepareAndDispatch
+.SharedStub:
+ mflr r0
+ std r0,16(sp)
+ stdu sp,-248(sp) # room for linkage (24*2), fprData (104), gprData(28*2)
+ # outgoing params to PrepareAndDispatch (40)
+ std r4,88(sp) # link area (48) + PrepareAndDispatch params (20)
+ std r5,96(sp)
+ std r6,104(sp)
+ std r7,112(sp)
+ std r8,120(sp)
+ std r9,128(sp)
+ std r10,136(sp)
+ stfd f1,144(sp)
+ stfd f2,152(sp)
+ stfd f3,160(sp)
+ stfd f4,168(sp)
+ stfd f5,176(sp)
+ stfd f6,184(sp)
+ stfd f7,192(sp)
+ stfd f8,200(sp)
+ stfd f9,208(sp)
+ stfd f10,216(sp)
+ stfd f11,224(sp)
+ stfd f12,232(sp)
+ stfd f13,240(sp)
+ addi r6,sp,88 # gprData
+ addi r7,sp,144 # fprData
+ # r3 has the 'self' pointer already
+ mr r4,r12 # methodIndex selector (it is now LATER)
+ addi r5,sp,360 # pointer to callers args area, beyond r3-r10
+ # mapped range
+ bl .PrepareAndDispatch
+ nop
+ ld r0,264(sp)
+ addi sp,sp,248
+ mtlr r0
+ blr
+# .data section
+ .toc # 0x00000038
+T.18.SharedStub:
+ .tc H.18.SharedStub{TC},SharedStub{DS}
+ .csect SharedStub{DS}
+ .llong .SharedStub # "\0\0\0\0"
+ .llong TOC{TC0} # "\0\0\0008"
+ .llong 0x00000000 # "\0\0\0\0"
+# End csect SharedStub{DS}
+# .bss section
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_darwin.s.m4 b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_darwin.s.m4
new file mode 100644
index 000000000..dda537850
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_darwin.s.m4
@@ -0,0 +1,114 @@
+/* -*- Mode: asm -*- */
+/* 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/. */
+
+ .text
+ .globl _SharedStub
+dnl
+define(STUB_MANGLED_ENTRY,
+` .globl '$2`
+ .align 2
+'$2`:
+ addi r12, 0,'$1`
+ b _SharedStub')
+dnl
+define(STUB_ENTRY,
+` .if '$1` < 10
+STUB_MANGLED_ENTRY('$1`, `__ZN14nsXPTCStubBase5Stub'$1`Ev')
+ .elseif '$1` < 100
+STUB_MANGLED_ENTRY('$1`, `__ZN14nsXPTCStubBase6Stub'$1`Ev')
+ .elseif '$1` < 1000
+STUB_MANGLED_ENTRY('$1`, `__ZN14nsXPTCStubBase7Stub'$1`Ev')
+ .else
+ .err "Stub'$1` >= 1000 not yet supported."
+ .endif
+')
+dnl
+define(SENTINEL_ENTRY, `')
+dnl
+include(xptcstubsdef.inc)
+dnl
+// See also xptcstubs_ppc_rhapsody.cpp:PrepareAndDispatch.
+_SharedStub:
+ // Prolog(ue)
+ mflr r0 // Save the link register in the caller's
+ stw r0, 8(r1) // stack frame
+ stwu r1,-176(r1) // Allocate stack space for our own frame and
+ // adjust stack pointer
+
+ // Linkage area, 0(r1) to 24(r1)
+ // Original sp saved at 0(r1)
+
+ // Parameter area, 20 bytes from 24(r1) to
+ // 44(r1) to accomodate 5 arguments passed
+ // to PrepareAndDispatch
+
+ // Local variables, 132 bytes from 44(r1)
+ // to 176(r1), to accomodate 5 words and
+ // 13 doubles
+
+ stw r4, 44(r1) // Save parameters passed in GPRs r4-r10;
+ stw r5, 48(r1) // a pointer to here will be passed to
+ stw r6, 52(r1) // PrepareAndDispatch for access to
+ stw r7, 56(r1) // arguments passed in registers. r3,
+ stw r8, 60(r1) // the self pointer, is used for the
+ stw r9, 64(r1) // call but isn't otherwise needed in
+ stw r10, 68(r1) // PrepareAndDispatch, so it is not saved.
+
+ stfd f1, 72(r1) // Do the same for floating-point parameters
+ stfd f2, 80(r1) // passed in FPRs f1-f13
+ stfd f3, 88(r1)
+ stfd f4, 96(r1)
+ stfd f5, 104(r1)
+ stfd f6, 112(r1)
+ stfd f7, 120(r1)
+ stfd f8, 128(r1)
+ stfd f9, 136(r1)
+ stfd f10, 144(r1)
+ stfd f11, 152(r1)
+ stfd f12, 160(r1)
+ stfd f13, 168(r1)
+
+ // Set up parameters for call to
+ // PrepareAndDispatch. argument=
+ // 0, pointer to self, already in r3
+ mr r4,r12 // 1, stub number
+ addi r5, r1, 204 // 2, pointer to the parameter area in our
+ // caller's stack, for access to
+ // parameters beyond those passed in
+ // registers. Skip past the first parameter
+ // (corresponding to r3) for the same reason
+ // as above. 176 (size of our frame) + 24
+ // (size of caller's linkage) + 4 (skipped
+ // parameter)
+ addi r6, r1, 44 // 3, pointer to saved GPRs
+ addi r7, r1, 72 // 4, pointer to saved FPRs
+
+ bl L_PrepareAndDispatch$stub
+ // Do it
+ nop // Leave room for linker magic
+
+ // Epilog(ue)
+ lwz r0, 184(r1) // Retrieve old link register value
+ addi r1, r1, 176 // Restore stack pointer
+ mtlr r0 // Restore link register
+ blr // Return
+
+.picsymbol_stub
+L_PrepareAndDispatch$stub: // Standard PIC symbol stub
+ .indirect_symbol _PrepareAndDispatch
+ mflr r0
+ bcl 20,31,L1$pb
+L1$pb:
+ mflr r11
+ addis r11,r11,ha16(L1$lz-L1$pb)
+ mtlr r0
+ lwz r12,lo16(L1$lz-L1$pb)(r11)
+ mtctr r12
+ addi r11,r11,lo16(L1$lz-L1$pb)
+ bctr
+.lazy_symbol_pointer
+L1$lz:
+ .indirect_symbol _PrepareAndDispatch
+ .long dyld_stub_binding_helper
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_linux.S b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_linux.S
new file mode 100644
index 000000000..72a5a9f4b
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_linux.S
@@ -0,0 +1,77 @@
+// -*- Mode: Asm -*-
+//
+// 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+ .section ".text"
+ .align 2
+ .globl SharedStub
+ .type SharedStub,@function
+
+SharedStub:
+ stwu sp,-112(sp) // room for
+ // linkage (8),
+ // gprData (32),
+ // fprData (64),
+ // stack alignment(8)
+ mflr r0
+ stw r0,116(sp) // save LR backchain
+
+ stw r4,12(sp) // save GP registers
+ stw r5,16(sp) // (n.b. that we don't save r3
+ stw r6,20(sp) // because PrepareAndDispatch() is savvy)
+ stw r7,24(sp)
+ stw r8,28(sp)
+ stw r9,32(sp)
+ stw r10,36(sp)
+#ifndef __NO_FPRS__
+ stfd f1,40(sp) // save FP registers
+ stfd f2,48(sp)
+ stfd f3,56(sp)
+ stfd f4,64(sp)
+ stfd f5,72(sp)
+ stfd f6,80(sp)
+ stfd f7,88(sp)
+ stfd f8,96(sp)
+#endif
+
+ // r3 has the 'self' pointer already
+
+ mr r4,r11 // r4 <= methodIndex selector, passed
+ // via r11 in the nsXPTCStubBase::StubXX() call
+
+ addi r5,sp,120 // r5 <= pointer to callers args area,
+ // beyond r3-r10/f1-f8 mapped range
+
+ addi r6,sp,8 // r6 <= gprData
+#ifndef __NO_FPRS__
+ addi r7,sp,40 // r7 <= fprData
+#else
+ li r7, 0 // r7 should be unused
+#endif
+
+ bl PrepareAndDispatch@local // Go!
+
+ lwz r0,116(sp) // restore LR
+ mtlr r0
+ la sp,112(sp) // clean up the stack
+ blr
+
+/* Magic indicating no need for an executable stack */
+.section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_netbsd.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_netbsd.s
new file mode 100644
index 000000000..8d43165ba
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_netbsd.s
@@ -0,0 +1,70 @@
+# -*- Mode: Asm -*-
+#
+# 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+ .section ".text"
+ .align 2
+ .globl SharedStub
+ .type SharedStub,@function
+
+SharedStub:
+ stwu sp,-112(sp) # room for
+ # linkage (8),
+ # gprData (32),
+ # fprData (64),
+ # stack alignment(8)
+ mflr r0
+ stw r0,116(sp) # save LR backchain
+
+ stw r4,12(sp) # save GP registers
+ stw r5,16(sp) # (n.b. that we don't save r3
+ stw r6,20(sp) # because PrepareAndDispatch() is savvy)
+ stw r7,24(sp)
+ stw r8,28(sp)
+ stw r9,32(sp)
+ stw r10,36(sp)
+
+ stfd f1,40(sp) # save FP registers
+ stfd f2,48(sp)
+ stfd f3,56(sp)
+ stfd f4,64(sp)
+ stfd f5,72(sp)
+ stfd f6,80(sp)
+ stfd f7,88(sp)
+ stfd f8,96(sp)
+
+ # r3 has the 'self' pointer already
+
+ mr r4,r11 # r4 <= methodIndex selector, passed
+ # via r11 in the nsXPTCStubBase::StubXX() call
+
+ addi r5,sp,120 # r5 <= pointer to callers args area,
+ # beyond r3-r10/f1-f8 mapped range
+
+ addi r6,sp,8 # r6 <= gprData
+ addi r7,sp,40 # r7 <= fprData
+
+ bl PrepareAndDispatch@local # Go!
+
+ lwz r0,116(sp) # restore LR
+ mtlr r0
+ la sp,112(sp) # clean up the stack
+ blr
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_openbsd.S b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_openbsd.S
new file mode 100644
index 000000000..dcca08205
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_ppc_openbsd.S
@@ -0,0 +1,72 @@
+// -*- Mode: Asm -*-
+//
+// 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/.
+
+.set r0,0; .set sp,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+.set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
+.set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
+.set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
+.set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
+.set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
+.set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
+.set f30,30; .set f31,31
+
+ .section ".text"
+ .align 2
+ .globl SharedStub
+ .type SharedStub,@function
+
+SharedStub:
+ stwu sp,-112(sp) // room for
+ // linkage (8),
+ // gprData (32),
+ // fprData (64),
+ // stack alignment(8)
+ mflr r0
+ stw r0,116(sp) // save LR backchain
+
+ stw r4,12(sp) // save GP registers
+ stw r5,16(sp) // (n.b. that we don't save r3
+ stw r6,20(sp) // because PrepareAndDispatch() is savvy)
+ stw r7,24(sp)
+ stw r8,28(sp)
+ stw r9,32(sp)
+ stw r10,36(sp)
+
+ stfd f1,40(sp) // save FP registers
+ stfd f2,48(sp)
+ stfd f3,56(sp)
+ stfd f4,64(sp)
+ stfd f5,72(sp)
+ stfd f6,80(sp)
+ stfd f7,88(sp)
+ stfd f8,96(sp)
+
+ // r3 has the 'self' pointer already
+
+ mr r4,r11 // r4 <= methodIndex selector, passed
+ // via r11 in the nsXPTCStubBase::StubXX() call
+
+ addi r5,sp,120 // r5 <= pointer to callers args area,
+ // beyond r3-r10/f1-f8 mapped range
+
+ addi r6,sp,8 // r6 <= gprData
+ addi r7,sp,40 // r7 <= fprData
+
+ bl PrepareAndDispatch@local // Go!
+
+ lwz r0,116(sp) // restore LR
+ mtlr r0
+ la sp,112(sp) // clean up the stack
+ blr
+
+// Magic indicating no need for an executable stack
+.section .note.GNU-stack, "", @progbits ; .previous
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc64_openbsd.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc64_openbsd.s
new file mode 100644
index 000000000..ab97a890c
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc64_openbsd.s
@@ -0,0 +1,50 @@
+/* -*- Mode: asm; 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/. */
+
+ .global SharedStub
+
+/*
+ in the frame for the function that called SharedStub are the
+ rest of the parameters we need
+
+*/
+
+SharedStub:
+! we don't create a new frame yet, but work within the frame of the calling
+! function to give ourselves the other parameters we want
+
+ mov %o0, %o1 ! shuffle the index up to 2nd place
+ mov %i0, %o0 ! the original 'this'
+ add %fp, 0x7ff + 136, %o2 ! previous stack top adjusted to the first argument slot (beyond 'this')
+
+! save off the original incoming parameters that arrived in
+! registers, the ABI guarantees the space for us to do this
+ stx %i1, [%fp + 0x7ff + 136]
+ stx %i2, [%fp + 0x7ff + 144]
+ stx %i3, [%fp + 0x7ff + 152]
+ stx %i4, [%fp + 0x7ff + 160]
+ stx %i5, [%fp + 0x7ff + 168]
+! now we can build our own stack frame
+ save %sp,-(128 + 64),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 64
+! our function now appears to have been called
+! as SharedStub(nsISupports* that, uint32_t index, uint32_t* args)
+! so we can just copy these through
+
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ call PrepareAndDispatch
+ nop
+ mov %o0,%i0 ! propagate return value
+ b .LL1
+ nop
+.LL1:
+ ret
+ restore
+
+ .size SharedStub, .-SharedStub
+ .type SharedStub, #function
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_netbsd.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_netbsd.s
new file mode 100644
index 000000000..9b448d7c7
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_netbsd.s
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+ .global SharedStub
+
+/*
+ in the frame for the function that called SharedStub are the
+ rest of the parameters we need
+
+*/
+
+SharedStub:
+! we don't create a new frame yet, but work within the frame of the calling
+! function to give ourselves the other parameters we want
+
+ mov %o0, %o1 ! shuffle the index up to 2nd place
+ mov %i0, %o0 ! the original 'this'
+ add %fp, 72, %o2 ! previous stack top adjusted to the first argument slot (beyond 'this')
+! save off the original incoming parameters that arrived in
+! registers, the ABI guarantees the space for us to do this
+ st %i1, [%fp + 72]
+ st %i2, [%fp + 76]
+ st %i3, [%fp + 80]
+ st %i4, [%fp + 84]
+ st %i5, [%fp + 88]
+! now we can build our own stack frame
+ save %sp,-(64 + 32),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 32
+! our function now appears to have been called
+! as SharedStub(nsISupports* that, uint32_t index, uint32_t* args)
+! so we can just copy these through
+
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ call PrepareAndDispatch
+ nop
+ mov %o0,%i0 ! propagate return value
+ b .LL1
+ nop
+.LL1:
+ ret
+ restore
+
+ .size SharedStub, .-SharedStub
+ .type SharedStub, #function
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_openbsd.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_openbsd.s
new file mode 100644
index 000000000..871556d4c
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_openbsd.s
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+ .global SharedStub
+
+/*
+ in the frame for the function that called SharedStub are the
+ rest of the parameters we need
+
+*/
+
+SharedStub:
+! we don't create a new frame yet, but work within the frame of the calling
+! function to give ourselves the other parameters we want
+
+ mov %o0, %o1 ! shuffle the index up to 2nd place
+ mov %i0, %o0 ! the original 'this'
+ add %fp, 72, %o2 ! previous stack top adjusted to the first argument slot (beyond 'this')
+! save off the original incoming parameters that arrived in
+! registers, the ABI guarantees the space for us to do this
+ st %i1, [%fp + 72]
+ st %i2, [%fp + 76]
+ st %i3, [%fp + 80]
+ st %i4, [%fp + 84]
+ st %i5, [%fp + 88]
+! now we can build our own stack frame
+ save %sp,-(64 + 32),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 32
+! our function now appears to have been called
+! as SharedStub(nsISupports* that, uint32_t index, uint32_t* args)
+! so we can just copy these through
+
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ call PrepareAndDispatch
+ nop
+ mov %o0,%i0 ! propagate return value
+ b .LL1
+ nop
+.LL1:
+ ret
+ restore
+
+ .size SharedStub, .-SharedStub
+ .type SharedStub, #function
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_solaris.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_solaris.s
new file mode 100644
index 000000000..9b448d7c7
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparc_solaris.s
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+ .global SharedStub
+
+/*
+ in the frame for the function that called SharedStub are the
+ rest of the parameters we need
+
+*/
+
+SharedStub:
+! we don't create a new frame yet, but work within the frame of the calling
+! function to give ourselves the other parameters we want
+
+ mov %o0, %o1 ! shuffle the index up to 2nd place
+ mov %i0, %o0 ! the original 'this'
+ add %fp, 72, %o2 ! previous stack top adjusted to the first argument slot (beyond 'this')
+! save off the original incoming parameters that arrived in
+! registers, the ABI guarantees the space for us to do this
+ st %i1, [%fp + 72]
+ st %i2, [%fp + 76]
+ st %i3, [%fp + 80]
+ st %i4, [%fp + 84]
+ st %i5, [%fp + 88]
+! now we can build our own stack frame
+ save %sp,-(64 + 32),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 32
+! our function now appears to have been called
+! as SharedStub(nsISupports* that, uint32_t index, uint32_t* args)
+! so we can just copy these through
+
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ call PrepareAndDispatch
+ nop
+ mov %o0,%i0 ! propagate return value
+ b .LL1
+ nop
+.LL1:
+ ret
+ restore
+
+ .size SharedStub, .-SharedStub
+ .type SharedStub, #function
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparcv9_solaris.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparcv9_solaris.s
new file mode 100644
index 000000000..ab97a890c
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_sparcv9_solaris.s
@@ -0,0 +1,50 @@
+/* -*- Mode: asm; 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/. */
+
+ .global SharedStub
+
+/*
+ in the frame for the function that called SharedStub are the
+ rest of the parameters we need
+
+*/
+
+SharedStub:
+! we don't create a new frame yet, but work within the frame of the calling
+! function to give ourselves the other parameters we want
+
+ mov %o0, %o1 ! shuffle the index up to 2nd place
+ mov %i0, %o0 ! the original 'this'
+ add %fp, 0x7ff + 136, %o2 ! previous stack top adjusted to the first argument slot (beyond 'this')
+
+! save off the original incoming parameters that arrived in
+! registers, the ABI guarantees the space for us to do this
+ stx %i1, [%fp + 0x7ff + 136]
+ stx %i2, [%fp + 0x7ff + 144]
+ stx %i3, [%fp + 0x7ff + 152]
+ stx %i4, [%fp + 0x7ff + 160]
+ stx %i5, [%fp + 0x7ff + 168]
+! now we can build our own stack frame
+ save %sp,-(128 + 64),%sp ! room for the register window and
+ ! struct pointer, rounded up to 0 % 64
+! our function now appears to have been called
+! as SharedStub(nsISupports* that, uint32_t index, uint32_t* args)
+! so we can just copy these through
+
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ call PrepareAndDispatch
+ nop
+ mov %o0,%i0 ! propagate return value
+ b .LL1
+ nop
+.LL1:
+ ret
+ restore
+
+ .size SharedStub, .-SharedStub
+ .type SharedStub, #function
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_64_solaris_SUNW.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_64_solaris_SUNW.s
new file mode 100644
index 000000000..aa6d84434
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_64_solaris_SUNW.s
@@ -0,0 +1,63 @@
+#define STUB_ENTRY1(nn) \
+ .globl __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_; \
+ .hidden __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_; \
+ .type __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_, @function; \
+__1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_: \
+ movl $/**/nn/**/, %eax; \
+ jmp SharedStub; \
+ .size __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_, . - __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_ \
+
+#define STUB_ENTRY2(nn) \
+ .globl __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_; \
+ .hidden __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_; \
+ .type __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_, @function; \
+__1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_: \
+ movl $/**/nn/**/, %eax; \
+ jmp SharedStub; \
+ .size __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_, . - __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_ \
+
+#define STUB_ENTRY3(nn) \
+ .globl __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_; \
+ .hidden __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_; \
+ .type __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_, @function; \
+__1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_: \
+ movl $/**/nn/**/, %eax; \
+ jmp SharedStub; \
+ .size __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_, . - __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_ \
+
+// static nsresult SharedStub(uint32_t methodIndex)
+ .type SharedStub, @function;
+ SharedStub:
+ // make room for gpregs (48), fpregs (64)
+ pushq %rbp;
+ movq %rsp,%rbp;
+ subq $112,%rsp;
+ // save GP registers
+ movq %rdi,-112(%rbp);
+ movq %rsi,-104(%rbp);
+ movq %rdx, -96(%rbp);
+ movq %rcx, -88(%rbp);
+ movq %r8 , -80(%rbp);
+ movq %r9 , -72(%rbp);
+ leaq -112(%rbp),%rcx;
+ // save FP registers
+ movsd %xmm0,-64(%rbp);
+ movsd %xmm1,-56(%rbp);
+ movsd %xmm2,-48(%rbp);
+ movsd %xmm3,-40(%rbp);
+ movsd %xmm4,-32(%rbp);
+ movsd %xmm5,-24(%rbp);
+ movsd %xmm6,-16(%rbp);
+ movsd %xmm7, -8(%rbp);
+ leaq -64(%rbp),%r8;
+ // rdi has the 'self' pointer already
+ movl %eax,%esi;
+ leaq 16(%rbp),%rdx;
+ call PrepareAndDispatch@plt;
+ leave;
+ ret;
+ .size SharedStub, . - SharedStub
+
+#define SENTINEL_ENTRY(nn)
+
+#include "xptcstubsdef_asm.solx86"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_solaris_SUNW.s b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_solaris_SUNW.s
new file mode 100644
index 000000000..76bdcf925
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_x86_solaris_SUNW.s
@@ -0,0 +1,78 @@
+#define STUB_ENTRY1(nn) \
+ .globl __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_; \
+ .type __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_, @function; \
+__1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_: \
+ push %ebp; \
+ movl %esp,%ebp; \
+ andl $-16,%esp; \
+ push %ebx; \
+ call .CG4./**/nn/**/; \
+.CG4./**/nn/**/: \
+ pop %ebx; \
+ addl $_GLOBAL_OFFSET_TABLE_+0x1,%ebx; \
+ leal 0xc(%ebp), %ecx; \
+ pushl %ecx; \
+ pushl $/**/nn/**/; \
+ movl 0x8(%ebp), %ecx; \
+ pushl %ecx; \
+ call __1cSPrepareAndDispatch6FpnOnsXPTCStubBase_IpI_I_; \
+ addl $0xc , %esp; \
+ pop %ebx; \
+ movl %ebp,%esp; \
+ pop %ebp; \
+ ret ; \
+ .size __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_, . - __1cOnsXPTCStubBaseFStub/**/nn/**/6M_I_ \
+
+#define STUB_ENTRY2(nn) \
+ .globl __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_; \
+ .type __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_, @function; \
+__1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_: \
+ push %ebp; \
+ movl %esp,%ebp; \
+ andl $-16,%esp; \
+ push %ebx; \
+ call .CG4./**/nn/**/; \
+.CG4./**/nn/**/: \
+ pop %ebx; \
+ addl $_GLOBAL_OFFSET_TABLE_+0x1,%ebx; \
+ leal 0xc(%ebp), %ecx; \
+ pushl %ecx; \
+ pushl $/**/nn/**/; \
+ movl 0x8(%ebp), %ecx; \
+ pushl %ecx; \
+ call __1cSPrepareAndDispatch6FpnOnsXPTCStubBase_IpI_I_; \
+ addl $0xc , %esp; \
+ pop %ebx; \
+ movl %ebp,%esp; \
+ pop %ebp; \
+ ret ; \
+ .size __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_, . - __1cOnsXPTCStubBaseGStub/**/nn/**/6M_I_ \
+
+#define STUB_ENTRY3(nn) \
+ .globl __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_; \
+ .type __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_, @function; \
+__1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_: \
+ push %ebp; \
+ movl %esp,%ebp; \
+ andl $-16,%esp; \
+ push %ebx; \
+ call .CG4./**/nn/**/; \
+.CG4./**/nn/**/: \
+ pop %ebx; \
+ addl $_GLOBAL_OFFSET_TABLE_+0x1,%ebx; \
+ leal 0xc(%ebp), %ecx; \
+ pushl %ecx; \
+ pushl $/**/nn/**/; \
+ movl 0x8(%ebp), %ecx; \
+ pushl %ecx; \
+ call __1cSPrepareAndDispatch6FpnOnsXPTCStubBase_IpI_I_; \
+ addl $0xc , %esp; \
+ pop %ebx; \
+ movl %ebp,%esp; \
+ pop %ebp; \
+ ret ; \
+ .size __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_, . - __1cOnsXPTCStubBaseHStub/**/nn/**/6M_I_ \
+
+#define SENTINEL_ENTRY(nn)
+
+#include "xptcstubsdef_asm.solx86"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_darwin.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_darwin.cpp
new file mode 100644
index 000000000..49edfca44
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_darwin.cpp
@@ -0,0 +1,16 @@
+/* -*- Mode: C -*- */
+/* 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/. */
+
+#if defined(__i386__)
+#include "xptcstubs_gcc_x86_unix.cpp"
+#elif defined(__x86_64__)
+#include "xptcstubs_x86_64_darwin.cpp"
+#elif defined(__ppc__)
+#include "xptcstubs_ppc_rhapsody.cpp"
+#elif defined(__arm__)
+#include "xptcstubs_arm.cpp"
+#else
+#error unknown cpu architecture
+#endif
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_gcc_x86_unix.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_gcc_x86_unix.cpp
new file mode 100644
index 000000000..6811a26ad
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_gcc_x86_unix.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+#include "xptc_gcc_x86_unix.h"
+
+extern "C" {
+static nsresult ATTRIBUTE_USED
+__attribute__ ((regparm (3)))
+PrepareAndDispatch(uint32_t methodIndex, nsXPTCStubBase* self, uint32_t* args)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ dp->val.p = (void*) *ap;
+ switch(type)
+ {
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+} // extern "C"
+
+#if !defined(XP_MACOSX)
+
+#define STUB_HEADER(a, b) ".hidden " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase" #a "Stub" #b "Ev\n\t" \
+ ".type " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase" #a "Stub" #b "Ev,@function\n"
+
+#define STUB_SIZE(a, b) ".size " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase" #a "Stub" #b "Ev,.-" SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase" #a "Stub" #b "Ev\n\t"
+
+#else
+
+#define STUB_HEADER(a, b)
+#define STUB_SIZE(a, b)
+
+#endif
+
+// gcc3 mangling tends to insert the length of the method name
+#define STUB_ENTRY(n) \
+asm(".text\n\t" \
+ ".align 2\n\t" \
+ ".if " #n " < 10\n\t" \
+ ".globl " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
+ STUB_HEADER(5, n) \
+ SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase5Stub" #n "Ev:\n\t" \
+ ".elseif " #n " < 100\n\t" \
+ ".globl " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
+ STUB_HEADER(6, n) \
+ SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase6Stub" #n "Ev:\n\t" \
+ ".elseif " #n " < 1000\n\t" \
+ ".globl " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
+ STUB_HEADER(7, n) \
+ SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase7Stub" #n "Ev:\n\t" \
+ ".else\n\t" \
+ ".err \"stub number " #n " >= 1000 not yet supported\"\n\t" \
+ ".endif\n\t" \
+ "movl $" #n ", %eax\n\t" \
+ "jmp " SYMBOL_UNDERSCORE "SharedStub\n\t" \
+ ".if " #n " < 10\n\t" \
+ STUB_SIZE(5, n) \
+ ".elseif " #n " < 100\n\t" \
+ STUB_SIZE(6, n) \
+ ".else\n\t" \
+ STUB_SIZE(7, n) \
+ ".endif");
+
+// static nsresult SharedStub(uint32_t methodIndex) __attribute__((regparm(1)))
+asm(".text\n\t"
+ ".align 2\n\t"
+#if !defined(XP_MACOSX)
+ ".type " SYMBOL_UNDERSCORE "SharedStub,@function\n\t"
+#endif
+ SYMBOL_UNDERSCORE "SharedStub:\n\t"
+ "leal 0x08(%esp), %ecx\n\t"
+ "movl 0x04(%esp), %edx\n\t"
+ "jmp " SYMBOL_UNDERSCORE "PrepareAndDispatch\n\t"
+#if !defined(XP_MACOSX)
+ ".size " SYMBOL_UNDERSCORE "SharedStub,.-" SYMBOL_UNDERSCORE "SharedStub"
+#endif
+);
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+void
+xptc_dummy()
+{
+}
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ipf32.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ipf32.cpp
new file mode 100644
index 000000000..e2d37e4f5
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ipf32.cpp
@@ -0,0 +1,151 @@
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+// "This code is for IA64 only"
+
+/* Implement shared vtbl methods. */
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
+ uint64_t* intargs, uint64_t* floatargs, uint64_t* restargs)
+{
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ nsresult result = NS_ERROR_FAILURE;
+ uint64_t* iargs = intargs;
+ uint64_t* fargs = floatargs;
+ uint8_t paramCount;
+ uint8_t i;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (! dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for(i = 0; i < paramCount; ++i)
+ {
+ int isfloat = 0;
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+#ifdef __LP64__
+ /* 64 bit pointer mode */
+ dp->val.p = (void*) *iargs;
+#else
+ /* 32 bit pointer mode */
+ uint32_t* adr = (uint32_t*) iargs;
+ dp->val.p = (void*) (*(adr+1));
+#endif
+ }
+ else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *(iargs); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *(iargs); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *(iargs); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *(iargs); break;
+ case nsXPTType::T_U8 : dp->val.u8 = *(iargs); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *(iargs); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *(iargs); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *(iargs); break;
+ case nsXPTType::T_FLOAT :
+ isfloat = 1;
+ if (i < 7)
+ dp->val.f = (float) *((double*) fargs); /* register */
+ else
+ dp->val.u32 = *(fargs); /* memory */
+ break;
+ case nsXPTType::T_DOUBLE :
+ isfloat = 1;
+ dp->val.u64 = *(fargs);
+ break;
+ case nsXPTType::T_BOOL : dp->val.b = *(iargs); break;
+ case nsXPTType::T_CHAR : dp->val.c = *(iargs); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *(iargs); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ if (i < 7)
+ {
+ /* we are parsing register arguments */
+ if (i == 6)
+ {
+ // run out of register arguments, move on to memory arguments
+ iargs = restargs;
+ fargs = restargs;
+ }
+ else
+ {
+ ++iargs; // advance one integer register slot
+ if (isfloat) ++fargs; // advance float register slot if isfloat
+ }
+ }
+ else
+ {
+ /* we are parsing memory arguments */
+ ++iargs;
+ ++fargs;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(uint64_t,uint64_t,uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t *);
+
+/* Variable a0-a7 were put there so we can have access to the 8 input
+ registers on Stubxyz entry */
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n(uint64_t a1, \
+uint64_t a2,uint64_t a3,uint64_t a4,uint64_t a5,uint64_t a6,uint64_t a7, \
+uint64_t a8) \
+{ uint64_t a0 = (uint64_t) this; \
+ return SharedStub(a0,a1,a2,a3,a4,a5,a6,a7,(uint64_t) n, &a8); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ipf64.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ipf64.cpp
new file mode 100644
index 000000000..fea9e8e3d
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ipf64.cpp
@@ -0,0 +1,154 @@
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+// "This code is for IA64 only"
+
+/* Implement shared vtbl methods. */
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
+ uint64_t* intargs, uint64_t* floatargs, uint64_t* restargs)
+{
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ nsresult result = NS_ERROR_FAILURE;
+ uint64_t* iargs = intargs;
+ uint64_t* fargs = floatargs;
+ uint8_t paramCount;
+ uint8_t i;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (! info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (! dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for(i = 0; i < paramCount; ++i)
+ {
+ int isfloat = 0;
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+#ifdef __LP64__
+ /* 64 bit pointer mode */
+ dp->val.p = (void*) *iargs;
+#else
+ /* 32 bit pointer mode */
+ uint32_t* adr = (uint32_t*) iargs;
+ dp->val.p = (void*) (*(adr+1));
+#endif
+ }
+ else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *(iargs); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *(iargs); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *(iargs); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *(iargs); break;
+ case nsXPTType::T_U8 : dp->val.u8 = *(iargs); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *(iargs); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *(iargs); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *(iargs); break;
+ case nsXPTType::T_FLOAT :
+ isfloat = 1;
+ if (i < 7)
+ dp->val.f = (float) *((double*) fargs); /* register */
+ else
+ dp->val.u32 = *(fargs); /* memory */
+ break;
+ case nsXPTType::T_DOUBLE :
+ isfloat = 1;
+ dp->val.u64 = *(fargs);
+ break;
+ case nsXPTType::T_BOOL : dp->val.b = *(iargs); break;
+ case nsXPTType::T_CHAR : dp->val.c = *(iargs); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *(iargs); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ if (i < 7)
+ {
+ /* we are parsing register arguments */
+ if (i == 6)
+ {
+ // run out of register arguments, move on to memory arguments
+ iargs = restargs;
+ fargs = restargs;
+ }
+ else
+ {
+ ++iargs; // advance one integer register slot
+ if (isfloat) ++fargs; // advance float register slot if isfloat
+ }
+ }
+ else
+ {
+ /* we are parsing memory arguments */
+ ++iargs;
+ ++fargs;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(uint64_t,uint64_t,uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t *);
+
+/* Variable a0-a7 were put there so we can have access to the 8 input
+ registers on Stubxyz entry */
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n(uint64_t a1, \
+uint64_t a2,uint64_t a3,uint64_t a4,uint64_t a5,uint64_t a6,uint64_t a7, \
+uint64_t a8) \
+{ uint64_t a0 = (uint64_t) this; \
+ return SharedStub(a0,a1,a2,a3,a4,a5,a6,a7,(uint64_t) n, &a8); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_alpha.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_alpha.cpp
new file mode 100644
index 000000000..d9c41aee7
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_alpha.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+/* Prototype specifies unmangled function name and disables unused warning */
+static nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
+__asm__("PrepareAndDispatch") ATTRIBUTE_USED;
+
+static nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args)
+{
+ const uint8_t PARAM_BUFFER_COUNT = 16;
+ const uint8_t NUM_ARG_REGS = 6-1; // -1 for "this" pointer
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ // args[0] to args[NUM_ARG_REGS] hold floating point register values
+ uint64_t* ap = args + NUM_ARG_REGS;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = (int8_t) *ap; break;
+ case nsXPTType::T_I16 : dp->val.i16 = (int16_t) *ap; break;
+ case nsXPTType::T_I32 : dp->val.i32 = (int32_t) *ap; break;
+ case nsXPTType::T_I64 : dp->val.i64 = (int64_t) *ap; break;
+ case nsXPTType::T_U8 : dp->val.u8 = (uint8_t) *ap; break;
+ case nsXPTType::T_U16 : dp->val.u16 = (uint16_t) *ap; break;
+ case nsXPTType::T_U32 : dp->val.u32 = (uint32_t) *ap; break;
+ case nsXPTType::T_U64 : dp->val.u64 = (uint64_t) *ap; break;
+ case nsXPTType::T_FLOAT :
+ if(i < NUM_ARG_REGS)
+ {
+ // floats passed via registers are stored as doubles
+ // in the first NUM_ARG_REGS entries in args
+ dp->val.u64 = (uint64_t) args[i];
+ dp->val.f = (float) dp->val.d; // convert double to float
+ }
+ else
+ dp->val.u32 = (uint32_t) *ap;
+ break;
+ case nsXPTType::T_DOUBLE :
+ // doubles passed via registers are also stored
+ // in the first NUM_ARG_REGS entries in args
+ dp->val.u64 = (i < NUM_ARG_REGS) ? args[i] : *ap;
+ break;
+ case nsXPTType::T_BOOL : dp->val.b = (bool) *ap; break;
+ case nsXPTType::T_CHAR : dp->val.c = (char) *ap; break;
+ case nsXPTType::T_WCHAR : dp->val.wc = (char16_t) *ap; break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+/*
+ * SharedStub()
+ * Collects arguments and calls PrepareAndDispatch. The "methodIndex" is
+ * passed to this function via $1 to preserve the argument registers.
+ */
+__asm__(
+ "#### SharedStub ####\n"
+".text\n\t"
+ ".align 5\n\t"
+ ".ent SharedStub\n"
+"SharedStub:\n\t"
+ ".frame $30,96,$26,0\n\t"
+ ".mask 0x4000000,-96\n\t"
+ "ldgp $29,0($27)\n"
+"$SharedStub..ng:\n\t"
+ "subq $30,96,$30\n\t"
+ "stq $26,0($30)\n\t"
+ ".prologue 1\n\t"
+
+ /*
+ * Store arguments passed via registers to the stack.
+ * Floating point registers are stored as doubles and converted
+ * to floats in PrepareAndDispatch if necessary.
+ */
+ "stt $f17,16($30)\n\t" /* floating point registers */
+ "stt $f18,24($30)\n\t"
+ "stt $f19,32($30)\n\t"
+ "stt $f20,40($30)\n\t"
+ "stt $f21,48($30)\n\t"
+ "stq $17,56($30)\n\t" /* integer registers */
+ "stq $18,64($30)\n\t"
+ "stq $19,72($30)\n\t"
+ "stq $20,80($30)\n\t"
+ "stq $21,88($30)\n\t"
+
+ /*
+ * Call PrepareAndDispatch function.
+ */
+ "bis $1,$1,$17\n\t" /* pass "methodIndex" */
+ "addq $30,16,$18\n\t" /* pass "args" */
+ "bsr $26,$PrepareAndDispatch..ng\n\t"
+
+ "ldq $26,0($30)\n\t"
+ "addq $30,96,$30\n\t"
+ "ret $31,($26),1\n\t"
+ ".end SharedStub"
+ );
+
+/*
+ * nsresult nsXPTCStubBase::Stub##n()
+ * Sets register $1 to "methodIndex" and jumps to SharedStub.
+ */
+#define STUB_MANGLED_ENTRY(n, symbol) \
+ "#### Stub"#n" ####" "\n\t" \
+ ".text" "\n\t" \
+ ".align 5" "\n\t" \
+ ".globl " symbol "\n\t" \
+ ".ent " symbol "\n" \
+symbol ":" "\n\t" \
+ ".frame $30,0,$26,0" "\n\t" \
+ "ldgp $29,0($27)" "\n" \
+"$" symbol "..ng:" "\n\t" \
+ ".prologue 1" "\n\t" \
+ "lda $1,"#n "\n\t" \
+ "br $31,$SharedStub..ng" "\n\t" \
+ ".end " symbol
+
+#define STUB_ENTRY(n) \
+__asm__( \
+ ".if "#n" < 10" "\n\t" \
+ STUB_MANGLED_ENTRY(n, "_ZN14nsXPTCStubBase5Stub"#n"Ev") "\n\t" \
+ ".elseif "#n" < 100" "\n\t" \
+ STUB_MANGLED_ENTRY(n, "_ZN14nsXPTCStubBase6Stub"#n"Ev") "\n\t" \
+ ".elseif "#n" < 1000" "\n\t" \
+ STUB_MANGLED_ENTRY(n, "_ZN14nsXPTCStubBase7Stub"#n"Ev") "\n\t" \
+ ".else" "\n\t" \
+ ".err \"Stub"#n" >= 1000 not yet supported.\"" "\n\t" \
+ ".endif" \
+ );
+
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_m68k.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_m68k.cpp
new file mode 100644
index 000000000..3720a5c37
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_m68k.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+extern "C" {
+ nsresult ATTRIBUTE_USED
+ PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+ {
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+
+ switch(type)
+ {
+ // the 8 and 16 bit types will have been promoted to 32 bits before
+ // being pushed onto the stack. Since the 68k is big endian, we
+ // need to skip over the leading high order bytes.
+ case nsXPTType::T_I8 : dp->val.i8 = *(((int8_t*) ap) + 3); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *(((int16_t*) ap) + 1); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U8 : dp->val.u8 = *(((uint8_t*) ap) + 3); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *(((uint16_t*)ap) + 1); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ case nsXPTType::T_BOOL : dp->val.b = *((uint32_t* ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *(((char*) ap) + 3); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((wchar_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+ }
+}
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ void *frame = __builtin_frame_address(0); \
+ return PrepareAndDispatch(this, n, (uint32_t*)frame + 3); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390.cpp
new file mode 100644
index 000000000..cf4ab1268
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390.cpp
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+static nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
+ uint32_t* a_gpr, uint64_t *a_fpr, uint32_t *a_ov)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t gpr = 1, fpr = 0;
+
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (gpr < 5)
+ dp->val.p = (void*) *a_gpr++, gpr++;
+ else
+ dp->val.p = (void*) *a_ov++;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 :
+ if (gpr < 5)
+ dp->val.i8 = *((int32_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i8 = *((int32_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_I16 :
+ if (gpr < 5)
+ dp->val.i16 = *((int32_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i16 = *((int32_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_I32 :
+ if (gpr < 5)
+ dp->val.i32 = *((int32_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i32 = *((int32_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_I64 :
+ if (gpr < 4)
+ dp->val.i64 = *((int64_t*) a_gpr), a_gpr+=2, gpr+=2;
+ else
+ dp->val.i64 = *((int64_t*) a_ov ), a_ov+=2, gpr=5;
+ break;
+ case nsXPTType::T_U8 :
+ if (gpr < 5)
+ dp->val.u8 = *((uint32_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u8 = *((uint32_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U16 :
+ if (gpr < 5)
+ dp->val.u16 = *((uint32_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u16 = *((uint32_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U32 :
+ if (gpr < 5)
+ dp->val.u32 = *((uint32_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u32 = *((uint32_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U64 :
+ if (gpr < 4)
+ dp->val.u64 = *((uint64_t*)a_gpr), a_gpr+=2, gpr+=2;
+ else
+ dp->val.u64 = *((uint64_t*)a_ov ), a_ov+=2, gpr=5;
+ break;
+ case nsXPTType::T_FLOAT :
+ if (fpr < 2)
+ dp->val.f = *((float*) a_fpr), a_fpr++, fpr++;
+ else
+ dp->val.f = *((float*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ if (fpr < 2)
+ dp->val.d = *((double*) a_fpr), a_fpr++, fpr++;
+ else
+ dp->val.d = *((double*) a_ov ), a_ov+=2;
+ break;
+ case nsXPTType::T_BOOL :
+ if (gpr < 5)
+ dp->val.b = *((uint32_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.b = *((uint32_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_CHAR :
+ if (gpr < 5)
+ dp->val.c = *((uint32_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.c = *((uint32_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_WCHAR :
+ if (gpr < 5)
+ dp->val.wc = *((uint32_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.wc = *((uint32_t*)a_ov ), a_ov++;
+ break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ uint32_t a_gpr[4]; \
+ uint64_t a_fpr[2]; \
+ uint32_t *a_ov; \
+ \
+ __asm__ __volatile__ \
+ ( \
+ "l %0,0(15)\n\t" \
+ "ahi %0,96\n\t" \
+ "stm 3,6,0(%3)\n\t" \
+ "std 0,%1\n\t" \
+ "std 2,%2\n\t" \
+ : "=&a" (a_ov), \
+ "=m" (a_fpr[0]), \
+ "=m" (a_fpr[1]) \
+ : "a" (a_gpr) \
+ : "memory", "cc", \
+ "3", "4", "5", "6" \
+ ); \
+ \
+ return PrepareAndDispatch(this, n, a_gpr, a_fpr, a_ov); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390x.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390x.cpp
new file mode 100644
index 000000000..189cceb82
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_linux_s390x.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+static nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
+ uint64_t* a_gpr, uint64_t *a_fpr, uint64_t *a_ov)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t gpr = 1, fpr = 0;
+
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (gpr < 5)
+ dp->val.p = (void*) *a_gpr++, gpr++;
+ else
+ dp->val.p = (void*) *a_ov++;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 :
+ if (gpr < 5)
+ dp->val.i8 = *((int64_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i8 = *((int64_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_I16 :
+ if (gpr < 5)
+ dp->val.i16 = *((int64_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i16 = *((int64_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_I32 :
+ if (gpr < 5)
+ dp->val.i32 = *((int64_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i32 = *((int64_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_I64 :
+ if (gpr < 5)
+ dp->val.i64 = *((int64_t*) a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.i64 = *((int64_t*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U8 :
+ if (gpr < 5)
+ dp->val.u8 = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u8 = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U16 :
+ if (gpr < 5)
+ dp->val.u16 = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u16 = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U32 :
+ if (gpr < 5)
+ dp->val.u32 = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u32 = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_U64 :
+ if (gpr < 5)
+ dp->val.u64 = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.u64 = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_FLOAT :
+ if (fpr < 4)
+ dp->val.f = *((float*) a_fpr), a_fpr++, fpr++;
+ else
+ dp->val.f = *(((float*) a_ov )+1), a_ov++;
+ break;
+ case nsXPTType::T_DOUBLE :
+ if (fpr < 4)
+ dp->val.d = *((double*) a_fpr), a_fpr++, fpr++;
+ else
+ dp->val.d = *((double*) a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_BOOL :
+ if (gpr < 5)
+ dp->val.b = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.b = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_CHAR :
+ if (gpr < 5)
+ dp->val.c = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.c = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ case nsXPTType::T_WCHAR :
+ if (gpr < 5)
+ dp->val.wc = *((uint64_t*)a_gpr), a_gpr++, gpr++;
+ else
+ dp->val.wc = *((uint64_t*)a_ov ), a_ov++;
+ break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ uint64_t a_gpr[4]; \
+ uint64_t a_fpr[4]; \
+ uint64_t *a_ov; \
+ \
+ __asm__ __volatile__ \
+ ( \
+ "lg %0,0(15)\n\t" \
+ "aghi %0,160\n\t" \
+ "stmg 3,6,0(%5)\n\t"\
+ "std 0,%1\n\t" \
+ "std 2,%2\n\t" \
+ "std 4,%3\n\t" \
+ "std 6,%4\n\t" \
+ : "=&a" (a_ov), \
+ "=m" (a_fpr[0]), \
+ "=m" (a_fpr[1]), \
+ "=m" (a_fpr[2]), \
+ "=m" (a_fpr[3]) \
+ : "a" (a_gpr) \
+ : "memory", "cc", \
+ "3", "4", "5", "6" \
+ ); \
+ \
+ return PrepareAndDispatch(this, n, a_gpr, a_fpr, a_ov); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_mips.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_mips.cpp
new file mode 100644
index 000000000..75f8562ce
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_mips.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * Version: MPL 1.1
+ *
+ * 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+#include <stdint.h>
+
+/*
+ * This is for MIPS O32 ABI
+ * Args contains a0-3 and then the stack.
+ * Because a0 is 'this', we want to skip it
+ */
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+ args++; // always skip over a0
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+
+ switch(type)
+ {
+ case nsXPTType::T_I64 :
+ if ((intptr_t)ap & 4) ap++;
+ dp->val.i64 = *((int64_t*) ap); ap++;
+ break;
+ case nsXPTType::T_U64 :
+ if ((intptr_t)ap & 4) ap++;
+ dp->val.u64 = *((int64_t*) ap); ap++;
+ break;
+ case nsXPTType::T_DOUBLE:
+ if ((intptr_t)ap & 4) ap++;
+ dp->val.d = *((double*) ap); ap++;
+ break;
+#ifdef IS_LITTLE_ENDIAN
+ default:
+ dp->val.p = (void*) *ap;
+ break;
+#else
+ case nsXPTType::T_I8 : dp->val.i8 = (int8_t) *ap; break;
+ case nsXPTType::T_I16 : dp->val.i16 = (int16_t) *ap; break;
+ case nsXPTType::T_I32 : dp->val.i32 = (int32_t) *ap; break;
+ case nsXPTType::T_U8 : dp->val.u8 = (uint8_t) *ap; break;
+ case nsXPTType::T_U16 : dp->val.u16 = (uint16_t) *ap; break;
+ case nsXPTType::T_U32 : dp->val.u32 = (uint32_t) *ap; break;
+ case nsXPTType::T_BOOL : dp->val.b = (bool) *ap; break;
+ case nsXPTType::T_CHAR : dp->val.c = (char) *ap; break;
+ case nsXPTType::T_WCHAR : dp->val.wc = (wchar_t) *ap; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *(float *) ap; break;
+ default:
+ NS_ASSERTION(0, "bad type");
+ break;
+#endif
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n) // done in the .s file
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_mips64.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_mips64.cpp
new file mode 100644
index 000000000..a4b553940
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_mips64.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if (_MIPS_SIM != _ABIN32) && (_MIPS_SIM != _ABI64)
+#error "This code is for MIPS n32/n64 only"
+#endif
+
+/*
+ * This is for MIPS n32/n64 ABI
+ *
+ * When we're called, the "gp" registers are stored in gprData and
+ * the "fp" registers are stored in fprData. There are 8 regs
+ * available which correspond to the first 7 parameters of the
+ * function and the "this" pointer. If there are additional parms,
+ * they are stored on the stack at address "args".
+ *
+ */
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args,
+ uint64_t *gprData, double *fprData)
+{
+#define PARAM_BUFFER_COUNT 16
+#define PARAM_GPR_COUNT 7
+#define PARAM_FPR_COUNT 7
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint64_t* ap = args;
+ uint32_t iCount = 0;
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.p = (void*)gprData[iCount++];
+ else
+ dp->val.p = (void*)*ap++;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i8 = (int8_t)gprData[iCount++];
+ else
+ dp->val.i8 = (int8_t)*ap++;
+ break;
+
+ case nsXPTType::T_I16:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i16 = (int16_t)gprData[iCount++];
+ else
+ dp->val.i16 = (int16_t)*ap++;
+ break;
+
+ case nsXPTType::T_I32:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i32 = (int32_t)gprData[iCount++];
+ else
+ dp->val.i32 = (int32_t)*ap++;
+ break;
+
+ case nsXPTType::T_I64:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i64 = (int64_t)gprData[iCount++];
+ else
+ dp->val.i64 = (int64_t)*ap++;
+ break;
+
+ case nsXPTType::T_U8:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u8 = (uint8_t)gprData[iCount++];
+ else
+ dp->val.u8 = (uint8_t)*ap++;
+ break;
+
+ case nsXPTType::T_U16:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u16 = (uint16_t)gprData[iCount++];
+ else
+ dp->val.u16 = (uint16_t)*ap++;
+ break;
+
+ case nsXPTType::T_U32:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u32 = (uint32_t)gprData[iCount++];
+ else
+ dp->val.u32 = (uint32_t)*ap++;
+ break;
+
+ case nsXPTType::T_U64:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u64 = (uint64_t)gprData[iCount++];
+ else
+ dp->val.u64 = (uint64_t)*ap++;
+ break;
+
+ case nsXPTType::T_FLOAT:
+ if (iCount < PARAM_FPR_COUNT)
+ dp->val.f = (double)fprData[iCount++];
+ else
+ dp->val.f = *((double*)ap++);
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ if (iCount < PARAM_FPR_COUNT)
+ dp->val.d = (double)fprData[iCount++];
+ else
+ dp->val.d = *((double*)ap++);
+ break;
+
+ case nsXPTType::T_BOOL:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.b = (bool)gprData[iCount++];
+ else
+ dp->val.b = (bool)*ap++;
+ break;
+
+ case nsXPTType::T_CHAR:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.c = (char)gprData[iCount++];
+ else
+ dp->val.c = (char)*ap++;
+ break;
+
+ case nsXPTType::T_WCHAR:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.wc = (wchar_t)gprData[iCount++];
+ else
+ dp->val.wc = (wchar_t)*ap++;
+ break;
+
+ default:
+ NS_ASSERTION(0, "bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n) /* defined in the assembly file */
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ASSERTION(0,"nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_netbsd_m68k.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_netbsd_m68k.cpp
new file mode 100644
index 000000000..680114fd5
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_netbsd_m68k.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+
+#if !defined(__NetBSD__) || !defined(__m68k__)
+#error This code is for NetBSD/m68k only
+#endif
+
+extern "C" {
+ static nsresult ATTRIBUTE_USED
+ PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+ {
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ nsIInterfaceInfo* iface_info = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->GetInterfaceInfo(&iface_info);
+ NS_ASSERTION(iface_info,"no interface info");
+
+ iface_info->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+
+ switch(type)
+ {
+ // the 8 and 16 bit types will have been promoted to 32 bits before
+ // being pushed onto the stack. Since the 68k is big endian, we
+ // need to skip over the leading high order bytes.
+ case nsXPTType::T_I8 : dp->val.i8 = *(((int8_t*) ap) + 3); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *(((int16_t*) ap) + 1); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U8 : dp->val.u8 = *(((uint8_t*) ap) + 3); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *(((uint16_t*)ap) + 1); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ case nsXPTType::T_BOOL : dp->val.b = *(((char*) ap) + 3); break;
+ case nsXPTType::T_CHAR : dp->val.c = *(((char*) ap) + 3); break;
+ // wchar_t is an int (32 bits) on NetBSD
+ case nsXPTType::T_WCHAR : dp->val.wc = *((wchar_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ NS_RELEASE(iface_info);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+ }
+}
+
+#define STUB_ENTRY(n) \
+__asm__( \
+ ".global _Stub"#n"__14nsXPTCStubBase\n\t" \
+"_Stub"#n"__14nsXPTCStubBase:\n\t" \
+ "link a6,#0 \n\t" \
+ "lea a6@(12), a0 \n\t" /* pointer to args */ \
+ "movl a0, sp@- \n\t" \
+ "movl #"#n", sp@- \n\t" /* method index */ \
+ "movl a6@(8), sp@- \n\t" /* this */ \
+ "jbsr _PrepareAndDispatch \n\t" \
+ "unlk a6 \n\t" \
+ "rts \n\t" \
+);
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_pa32.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_pa32.cpp
new file mode 100644
index 000000000..7353709f1
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_pa32.cpp
@@ -0,0 +1,143 @@
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if _HPUX
+#error "This code is for HP-PA RISC 32 bit mode only"
+#endif
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
+ uint32_t* args, uint32_t* floatargs)
+{
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU;
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ int32_t regwords = 1; /* self pointer is not in the variant records */
+ nsresult result = NS_ERROR_FAILURE;
+ uint8_t paramCount;
+ uint8_t i;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (!info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for(i = 0; i < paramCount; ++i, --args)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *args;
+ ++regwords;
+ continue;
+ }
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int32_t*) args); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int32_t*) args); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) args); break;
+ case nsXPTType::T_DOUBLE :
+ if (regwords & 1)
+ {
+ ++regwords; /* align on double word */
+ --args;
+ }
+ if (regwords == 0 || regwords == 2)
+ {
+ dp->val.d=*((double*) (floatargs + regwords));
+ --args;
+ }
+ else
+ {
+ dp->val.d = *((double*) --args);
+ }
+ regwords += 2;
+ continue;
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_I64 :
+ if (regwords & 1)
+ {
+ ++regwords; /* align on double word */
+ --args;
+ }
+ ((DU *)dp)->lo = *((uint32_t*) args);
+ ((DU *)dp)->hi = *((uint32_t*) --args);
+ regwords += 2;
+ continue;
+ case nsXPTType::T_FLOAT :
+ if (regwords >= 4)
+ dp->val.f = *((float*) args);
+ else
+ dp->val.f = *((float*) floatargs+4+regwords);
+ break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint32_t*) args); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint32_t*) args); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*) args); break;
+ case nsXPTType::T_BOOL : dp->val.b = *((uint32_t*) args); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((uint32_t*) args); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((int32_t*) args); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ ++regwords;
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(int);
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ return SharedStub(n); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp
new file mode 100644
index 000000000..95e67cb29
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Implement shared vtbl methods.
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+// The Linux/PPC64 ABI passes the first 8 integral
+// parameters and the first 13 floating point parameters in registers
+// (r3-r10 and f1-f13), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the caller's stack
+// area. The stack pointer has to retain 16-byte alignment.
+
+// The PowerPC64 platform ABI can be found here:
+// http://www.freestandards.org/spec/ELF/ppc64/
+// and in particular:
+// http://www.freestandards.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-CALL
+
+#define PARAM_BUFFER_COUNT 16
+#define GPR_COUNT 7
+#define FPR_COUNT 13
+
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gprData[]' contains the arguments passed in integer registers
+// - 'fprData[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+#include <stdio.h>
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self,
+ uint64_t methodIndex,
+ uint64_t* args,
+ uint64_t *gprData,
+ double *fprData)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (! info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (! dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint64_t* ap = args;
+ uint64_t tempu64;
+
+ for(i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+ if (i < FPR_COUNT)
+ dp->val.d = fprData[i];
+ else
+ dp->val.d = *(double*) ap;
+ } else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+ if (i < FPR_COUNT)
+ dp->val.f = (float) fprData[i]; // in registers floats are passed as doubles
+ else {
+ float *p = (float *)ap;
+#ifndef __LITTLE_ENDIAN__
+ p++;
+#endif
+ dp->val.f = *p;
+ }
+ } else { /* integer type or pointer */
+ if (i < GPR_COUNT)
+ tempu64 = gprData[i];
+ else
+ tempu64 = *ap;
+
+ if (param.IsOut() || !type.IsArithmetic())
+ dp->val.p = (void*) tempu64;
+ else if (type == nsXPTType::T_I8)
+ dp->val.i8 = (int8_t) tempu64;
+ else if (type == nsXPTType::T_I16)
+ dp->val.i16 = (int16_t) tempu64;
+ else if (type == nsXPTType::T_I32)
+ dp->val.i32 = (int32_t) tempu64;
+ else if (type == nsXPTType::T_I64)
+ dp->val.i64 = (int64_t) tempu64;
+ else if (type == nsXPTType::T_U8)
+ dp->val.u8 = (uint8_t) tempu64;
+ else if (type == nsXPTType::T_U16)
+ dp->val.u16 = (uint16_t) tempu64;
+ else if (type == nsXPTType::T_U32)
+ dp->val.u32 = (uint32_t) tempu64;
+ else if (type == nsXPTType::T_U64)
+ dp->val.u64 = (uint64_t) tempu64;
+ else if (type == nsXPTType::T_BOOL)
+ dp->val.b = (bool) tempu64;
+ else if (type == nsXPTType::T_CHAR)
+ dp->val.c = (char) tempu64;
+ else if (type == nsXPTType::T_WCHAR)
+ dp->val.wc = (wchar_t) tempu64;
+ else
+ NS_ERROR("bad type");
+ }
+
+ if (i >= 7)
+ ap++;
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info,
+ dispatchParams);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+// Load r11 with the constant 'n' and branch to SharedStub().
+//
+// XXX Yes, it's ugly that we're relying on gcc's name-mangling here;
+// however, it's quick, dirty, and'll break when the ABI changes on
+// us, which is what we want ;-).
+
+
+// gcc-3 version
+//
+// As G++3 ABI contains the length of the functionname in the mangled
+// name, it is difficult to get a generic assembler mechanism like
+// in the G++ 2.95 case.
+// Create names would be like:
+// _ZN14nsXPTCStubBase5Stub1Ev
+// _ZN14nsXPTCStubBase6Stub12Ev
+// _ZN14nsXPTCStubBase7Stub123Ev
+// _ZN14nsXPTCStubBase8Stub1234Ev
+// etc.
+// Use assembler directives to get the names right...
+
+#if _CALL_ELF == 2
+# define STUB_ENTRY(n) \
+__asm__ ( \
+ ".section \".text\" \n\t" \
+ ".align 2 \n\t" \
+ ".if "#n" < 10 \n\t" \
+ ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t" \
+ "0: addis 2,12,.TOC.-0b@ha \n\t" \
+ "addi 2,2,.TOC.-0b@l \n\t" \
+ ".localentry _ZN14nsXPTCStubBase5Stub"#n"Ev,.-_ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ \
+ ".elseif "#n" < 100 \n\t" \
+ ".globl _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t" \
+ "0: addis 2,12,.TOC.-0b@ha \n\t" \
+ "addi 2,2,.TOC.-0b@l \n\t" \
+ ".localentry _ZN14nsXPTCStubBase6Stub"#n"Ev,.-_ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ \
+ ".elseif "#n" < 1000 \n\t" \
+ ".globl _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t" \
+ "0: addis 2,12,.TOC.-0b@ha \n\t" \
+ "addi 2,2,.TOC.-0b@l \n\t" \
+ ".localentry _ZN14nsXPTCStubBase7Stub"#n"Ev,.-_ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ \
+ ".else \n\t" \
+ ".err \"stub number "#n" >= 1000 not yet supported\"\n" \
+ ".endif \n\t" \
+ \
+ "li 11,"#n" \n\t" \
+ "b SharedStub \n" \
+);
+#else
+# define STUB_ENTRY(n) \
+__asm__ ( \
+ ".section \".toc\",\"aw\" \n\t" \
+ ".section \".text\" \n\t" \
+ ".align 2 \n\t" \
+ ".if "#n" < 10 \n\t" \
+ ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ ".section \".opd\",\"aw\" \n\t" \
+ ".align 3 \n\t" \
+"_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t" \
+ ".quad ._ZN14nsXPTCStubBase5Stub"#n"Ev,.TOC.@tocbase \n\t" \
+ ".previous \n\t" \
+ ".type _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n" \
+"._ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t" \
+ \
+ ".elseif "#n" < 100 \n\t" \
+ ".globl _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ ".section \".opd\",\"aw\" \n\t" \
+ ".align 3 \n\t" \
+"_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t" \
+ ".quad ._ZN14nsXPTCStubBase6Stub"#n"Ev,.TOC.@tocbase \n\t" \
+ ".previous \n\t" \
+ ".type _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n" \
+"._ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t" \
+ \
+ ".elseif "#n" < 1000 \n\t" \
+ ".globl _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ ".section \".opd\",\"aw\" \n\t" \
+ ".align 3 \n\t" \
+"_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t" \
+ ".quad ._ZN14nsXPTCStubBase7Stub"#n"Ev,.TOC.@tocbase \n\t" \
+ ".previous \n\t" \
+ ".type _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n" \
+"._ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t" \
+ \
+ ".else \n\t" \
+ ".err \"stub number "#n" >= 1000 not yet supported\"\n" \
+ ".endif \n\t" \
+ \
+ "li 11,"#n" \n\t" \
+ "b SharedStub \n" \
+);
+#endif
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix.cpp
new file mode 100644
index 000000000..0463c21c5
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix.cpp
@@ -0,0 +1,185 @@
+/* -*- 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if defined(AIX)
+
+/*
+ For PPC (AIX & MAC), the first 8 integral and the first 13 f.p. parameters
+ arrive in a separate chunk of data that has been loaded from the registers.
+ The args pointer has been set to the start of the parameters BEYOND the ones
+ arriving in registers
+*/
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args, uint32_t *gprData, double *fprData)
+{
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo; // have to move 64 bit entities as 32 bit halves since
+ } DU; // stack slots are not guaranteed 16 byte aligned
+
+#define PARAM_BUFFER_COUNT 16
+#define PARAM_GPR_COUNT 7
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ uint32_t iCount = 0;
+ uint32_t fpCount = 0;
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.p = (void*) gprData[iCount++];
+ else
+ dp->val.p = (void*) *ap++;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i8 = (int8_t) gprData[iCount++];
+ else
+ dp->val.i8 = (int8_t) *ap++;
+ break;
+ case nsXPTType::T_I16 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i16 = (int16_t) gprData[iCount++];
+ else
+ dp->val.i16 = (int16_t) *ap++;
+ break;
+ case nsXPTType::T_I32 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i32 = (int32_t) gprData[iCount++];
+ else
+ dp->val.i32 = (int32_t) *ap++;
+ break;
+ case nsXPTType::T_I64 : if (iCount < PARAM_GPR_COUNT)
+ ((DU *)dp)->hi = (int32_t) gprData[iCount++];
+ else
+ ((DU *)dp)->hi = (int32_t) *ap++;
+ if (iCount < PARAM_GPR_COUNT)
+ ((DU *)dp)->lo = (uint32_t) gprData[iCount++];
+ else
+ ((DU *)dp)->lo = (uint32_t) *ap++;
+ break;
+ case nsXPTType::T_U8 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u8 = (uint8_t) gprData[iCount++];
+ else
+ dp->val.u8 = (uint8_t) *ap++;
+ break;
+ case nsXPTType::T_U16 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u16 = (uint16_t) gprData[iCount++];
+ else
+ dp->val.u16 = (uint16_t) *ap++;
+ break;
+ case nsXPTType::T_U32 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u32 = (uint32_t) gprData[iCount++];
+ else
+ dp->val.u32 = (uint32_t) *ap++;
+ break;
+ case nsXPTType::T_U64 : if (iCount < PARAM_GPR_COUNT)
+ ((DU *)dp)->hi = (uint32_t) gprData[iCount++];
+ else
+ ((DU *)dp)->hi = (uint32_t) *ap++;
+ if (iCount < PARAM_GPR_COUNT)
+ ((DU *)dp)->lo = (uint32_t) gprData[iCount++];
+ else
+ ((DU *)dp)->lo = (uint32_t) *ap++;
+ break;
+ case nsXPTType::T_FLOAT : if (fpCount < 13) {
+ dp->val.f = (float) fprData[fpCount++];
+ if (iCount < PARAM_GPR_COUNT)
+ ++iCount;
+ else
+ ++ap;
+ }
+ else
+ dp->val.f = *((float*) ap++);
+ break;
+ case nsXPTType::T_DOUBLE : if (fpCount < 13) {
+ dp->val.d = (double) fprData[fpCount++];
+ if (iCount < PARAM_GPR_COUNT)
+ ++iCount;
+ else
+ ++ap;
+ if (iCount < PARAM_GPR_COUNT)
+ ++iCount;
+ else
+ ++ap;
+ }
+ else {
+ dp->val.f = *((double*) ap);
+ ap += 2;
+ }
+ break;
+ case nsXPTType::T_BOOL : if (iCount < PARAM_GPR_COUNT)
+ dp->val.b = (bool) gprData[iCount++];
+ else
+ dp->val.b = (bool) *ap++;
+ break;
+ case nsXPTType::T_CHAR : if (iCount < PARAM_GPR_COUNT)
+ dp->val.c = (char) gprData[iCount++];
+ else
+ dp->val.c = (char) *ap++;
+ break;
+ case nsXPTType::T_WCHAR : if (iCount < PARAM_GPR_COUNT)
+ dp->val.wc = (wchar_t) gprData[iCount++];
+ else
+ dp->val.wc = (wchar_t) *ap++;
+ break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex,info,dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n)
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* AIX */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix64.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix64.cpp
new file mode 100644
index 000000000..08eb557ab
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_aix64.cpp
@@ -0,0 +1,172 @@
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if defined(AIX)
+
+/*
+ For PPC (AIX & MAC), the first 8 integral and the first 13 f.p. parameters
+ arrive in a separate chunk of data that has been loaded from the registers.
+ The args pointer has been set to the start of the parameters BEYOND the ones
+ arriving in registers
+*/
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint64_t methodIndex, uint64_t* args, uint64_t *gprData, double *fprData)
+{
+
+#define PARAM_BUFFER_COUNT 16
+#define PARAM_GPR_COUNT 7
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint64_t* ap = args;
+ uint32_t iCount = 0;
+ uint32_t fpCount = 0;
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.p = (void*) gprData[iCount++];
+ else
+ dp->val.p = (void*) *ap++;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i8 = (int8_t) gprData[iCount++];
+ else
+ dp->val.i8 = (int8_t) *ap++;
+ break;
+ case nsXPTType::T_I16 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i16 = (int16_t) gprData[iCount++];
+ else
+ dp->val.i16 = (int16_t) *ap++;
+ break;
+ case nsXPTType::T_I32 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i32 = (int32_t) gprData[iCount++];
+ else
+ dp->val.i32 = (int32_t) *ap++;
+ break;
+ case nsXPTType::T_I64 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.i64 = (int64_t) gprData[iCount++];
+ else
+ dp->val.i64 = (int64_t) *ap++;
+ break;
+ case nsXPTType::T_U8 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u8 = (uint8_t) gprData[iCount++];
+ else
+ dp->val.u8 = (uint8_t) *ap++;
+ break;
+ case nsXPTType::T_U16 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u16 = (uint16_t) gprData[iCount++];
+ else
+ dp->val.u16 = (uint16_t) *ap++;
+ break;
+ case nsXPTType::T_U32 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u32 = (uint32_t) gprData[iCount++];
+ else
+ dp->val.u32 = (uint32_t) *ap++;
+ break;
+ case nsXPTType::T_U64 : if (iCount < PARAM_GPR_COUNT)
+ dp->val.u64 = (uint64_t) gprData[iCount++];
+ else
+ dp->val.u64 = (uint64_t) *ap++;
+ break;
+ case nsXPTType::T_FLOAT : if (fpCount < 13) {
+ dp->val.f = (float) fprData[fpCount++];
+ if (iCount < PARAM_GPR_COUNT)
+ ++iCount;
+ else
+ ++ap;
+ }
+ else
+ dp->val.f = *((float*) ap++);
+ break;
+ case nsXPTType::T_DOUBLE : if (fpCount < 13) {
+ dp->val.d = (double) fprData[fpCount++];
+ if (iCount < PARAM_GPR_COUNT)
+ ++iCount;
+ else
+ ++ap;
+ if (iCount < PARAM_GPR_COUNT)
+ ++iCount;
+ else
+ ++ap;
+ }
+ else {
+ dp->val.f = *((double*) ap);
+ ap += 2;
+ }
+ break;
+ case nsXPTType::T_BOOL : if (iCount < PARAM_GPR_COUNT)
+ dp->val.b = (bool) gprData[iCount++];
+ else
+ dp->val.b = (bool) *ap++;
+ break;
+ case nsXPTType::T_CHAR : if (iCount < PARAM_GPR_COUNT)
+ dp->val.c = (char) gprData[iCount++];
+ else
+ dp->val.c = (char) *ap++;
+ break;
+ case nsXPTType::T_WCHAR : if (iCount < PARAM_GPR_COUNT)
+ dp->val.wc = (wchar_t) gprData[iCount++];
+ else
+ dp->val.wc = (wchar_t) *ap++;
+ break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex,info,dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n)
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* AIX */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_linux.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_linux.cpp
new file mode 100644
index 000000000..86405a8ca
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_linux.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Implement shared vtbl methods.
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+// The Linux/PPC ABI (aka PPC/SYSV ABI) passes the first 8 integral
+// parameters and the first 8 floating point parameters in registers
+// (r3-r10 and f1-f8), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the callers stack
+// area. The stack pointer has to retain 16-byte alignment, longlongs
+// and doubles are aligned on 8-byte boundaries.
+#ifndef __NO_FPRS__
+#define PARAM_BUFFER_COUNT 16
+#define GPR_COUNT 8
+#define FPR_COUNT 8
+#else
+#define PARAM_BUFFER_COUNT 8
+#define GPR_COUNT 8
+#endif
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gprData[]' contains the arguments passed in integer registers
+// - 'fprData[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self,
+ uint32_t methodIndex,
+ uint32_t* args,
+ uint32_t *gprData,
+ double *fprData)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (! info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (! dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t* ap = args;
+ uint32_t gpr = 1; // skip one GPR register
+#ifndef __NO_FPRS__
+ uint32_t fpr = 0;
+#endif
+ uint32_t tempu32;
+ uint64_t tempu64;
+
+ for(i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+#ifndef __NO_FPRS__
+ if (fpr < FPR_COUNT)
+ dp->val.d = fprData[fpr++];
+#else
+ if (gpr & 1)
+ gpr++;
+ if (gpr + 1 < GPR_COUNT) {
+ dp->val.d = *(double*) &gprData[gpr];
+ gpr += 2;
+ }
+#endif
+ else {
+ if ((uint32_t) ap & 4) ap++; // doubles are 8-byte aligned on stack
+ dp->val.d = *(double*) ap;
+ ap += 2;
+ }
+ continue;
+ }
+ else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+#ifndef __NO_FPRS__
+ if (fpr < FPR_COUNT)
+ dp->val.f = (float) fprData[fpr++]; // in registers floats are passed as doubles
+#else
+ if (gpr < GPR_COUNT)
+ dp->val.f = *(float*) &gprData[gpr++];
+#endif
+ else
+ dp->val.f = *(float*) ap++;
+ continue;
+ }
+ else if (!param.IsOut() && (type == nsXPTType::T_I64
+ || type == nsXPTType::T_U64)) {
+ if (gpr & 1) gpr++; // longlongs are aligned in odd/even register pairs, eg. r5/r6
+ if ((gpr + 1) < GPR_COUNT) {
+ tempu64 = *(uint64_t*) &gprData[gpr];
+ gpr += 2;
+ }
+ else {
+ if ((uint32_t) ap & 4) ap++; // longlongs are 8-byte aligned on stack
+ tempu64 = *(uint64_t*) ap;
+ ap += 2;
+ }
+ }
+ else {
+ if (gpr < GPR_COUNT)
+ tempu32 = gprData[gpr++];
+ else
+ tempu32 = *ap++;
+ }
+
+ if(param.IsOut() || !type.IsArithmetic()) {
+ if (type == nsXPTType::T_JSVAL)
+ dp->val.p = *((void**) tempu32);
+ else
+ dp->val.p = (void*) tempu32;
+ continue;
+ }
+
+ switch(type) {
+ case nsXPTType::T_I8: dp->val.i8 = (int8_t) tempu32; break;
+ case nsXPTType::T_I16: dp->val.i16 = (int16_t) tempu32; break;
+ case nsXPTType::T_I32: dp->val.i32 = (int32_t) tempu32; break;
+ case nsXPTType::T_I64: dp->val.i64 = (int64_t) tempu64; break;
+ case nsXPTType::T_U8: dp->val.u8 = (uint8_t) tempu32; break;
+ case nsXPTType::T_U16: dp->val.u16 = (uint16_t) tempu32; break;
+ case nsXPTType::T_U32: dp->val.u32 = (uint32_t) tempu32; break;
+ case nsXPTType::T_U64: dp->val.u64 = (uint64_t) tempu64; break;
+ case nsXPTType::T_BOOL: dp->val.b = (bool) tempu32; break;
+ case nsXPTType::T_CHAR: dp->val.c = (char) tempu32; break;
+ case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) tempu32; break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex,
+ info,
+ dispatchParams);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+// Load r11 with the constant 'n' and branch to SharedStub().
+//
+// XXX Yes, it's ugly that we're relying on gcc's name-mangling here;
+// however, it's quick, dirty, and'll break when the ABI changes on
+// us, which is what we want ;-).
+
+// gcc-3 version
+//
+// As G++3 ABI contains the length of the functionname in the mangled
+// name, it is difficult to get a generic assembler mechanism like
+// in the G++ 2.95 case.
+// Create names would be like:
+// _ZN14nsXPTCStubBase5Stub1Ev
+// _ZN14nsXPTCStubBase6Stub12Ev
+// _ZN14nsXPTCStubBase7Stub123Ev
+// _ZN14nsXPTCStubBase8Stub1234Ev
+// etc.
+// Use assembler directives to get the names right...
+
+# define STUB_ENTRY(n) \
+__asm__ ( \
+ ".align 2 \n\t" \
+ ".if "#n" < 10 \n\t" \
+ ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t" \
+ \
+ ".elseif "#n" < 100 \n\t" \
+ ".globl _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t" \
+ \
+ ".elseif "#n" < 1000 \n\t" \
+ ".globl _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t" \
+ \
+ ".else \n\t" \
+ ".err \"stub number "#n" >= 1000 not yet supported\"\n" \
+ ".endif \n\t" \
+ \
+ "li 11,"#n" \n\t" \
+ "b SharedStub@local \n" \
+);
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_netbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_netbsd.cpp
new file mode 100644
index 000000000..ec359fc99
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_netbsd.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Implement shared vtbl methods.
+
+#include "xptcprivate.h"
+
+// The Linux/PPC ABI (aka PPC/SYSV ABI) passes the first 8 integral
+// parameters and the first 8 floating point parameters in registers
+// (r3-r10 and f1-f8), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the callers stack
+// area. The stack pointer has to retain 16-byte alignment, longlongs
+// and doubles are aligned on 8-byte boundaries.
+
+#define PARAM_BUFFER_COUNT 16
+#define GPR_COUNT 8
+#define FPR_COUNT 8
+
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gprData[]' contains the arguments passed in integer registers
+// - 'fprData[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self,
+ uint32_t methodIndex,
+ uint32_t* args,
+ uint32_t *gprData,
+ double *fprData)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ nsIInterfaceInfo* iface_info = nullptr;
+ const nsXPTMethodInfo* info;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->GetInterfaceInfo(&iface_info);
+ NS_ASSERTION(iface_info,"no interface info");
+ if (! iface_info)
+ return NS_ERROR_UNEXPECTED;
+
+ iface_info->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (! info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (! dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t* ap = args;
+ uint32_t gpr = 1; // skip one GPR register
+ uint32_t fpr = 0;
+ uint32_t tempu32;
+ uint64_t tempu64;
+
+ for(i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+ if (fpr < FPR_COUNT)
+ dp->val.d = fprData[fpr++];
+ else {
+ if ((uint32_t) ap & 4) ap++; // doubles are 8-byte aligned on stack
+ dp->val.d = *(double*) ap;
+ ap += 2;
+ if (gpr < GPR_COUNT)
+ gpr += 2;
+ }
+ continue;
+ }
+ else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+ if (fpr < FPR_COUNT)
+ dp->val.f = (float) fprData[fpr++]; // in registers floats are passed as doubles
+ else {
+ dp->val.f = *(float*) ap;
+ ap += 1;
+ if (gpr < GPR_COUNT)
+ gpr += 1;
+ }
+ continue;
+ }
+ else if (!param.IsOut() && (type == nsXPTType::T_I64
+ || type == nsXPTType::T_U64)) {
+ if (gpr & 1) gpr++; // longlongs are aligned in odd/even register pairs, eg. r5/r6
+ if ((gpr + 1) < GPR_COUNT) {
+ tempu64 = *(uint64_t*) &gprData[gpr];
+ gpr += 2;
+ }
+ else {
+ if ((uint32_t) ap & 4) ap++; // longlongs are 8-byte aligned on stack
+ tempu64 = *(uint64_t*) ap;
+ ap += 2;
+ }
+ }
+ else {
+ if (gpr < GPR_COUNT)
+ tempu32 = gprData[gpr++];
+ else
+ tempu32 = *ap++;
+ }
+
+ if(param.IsOut() || !type.IsArithmetic()) {
+ if (type == nsXPTType::T_JSVAL)
+ dp->val.p = *((void**) tempu32);
+ else
+ dp->val.p = (void*) tempu32;
+ continue;
+ }
+
+ switch(type) {
+ case nsXPTType::T_I8: dp->val.i8 = (int8_t) tempu32; break;
+ case nsXPTType::T_I16: dp->val.i16 = (int16_t) tempu32; break;
+ case nsXPTType::T_I32: dp->val.i32 = (int32_t) tempu32; break;
+ case nsXPTType::T_I64: dp->val.i64 = (int64_t) tempu64; break;
+ case nsXPTType::T_U8: dp->val.u8 = (uint8_t) tempu32; break;
+ case nsXPTType::T_U16: dp->val.u16 = (uint16_t) tempu32; break;
+ case nsXPTType::T_U32: dp->val.u32 = (uint32_t) tempu32; break;
+ case nsXPTType::T_U64: dp->val.u64 = (uint64_t) tempu64; break;
+ case nsXPTType::T_BOOL: dp->val.b = (bool) tempu32; break;
+ case nsXPTType::T_CHAR: dp->val.c = (char) tempu32; break;
+ case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) tempu32; break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ NS_RELEASE(iface_info);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+// Load r11 with the constant 'n' and branch to SharedStub().
+//
+// XXX Yes, it's ugly that we're relying on gcc's name-mangling here;
+// however, it's quick, dirty, and'll break when the ABI changes on
+// us, which is what we want ;-).
+
+#define STUB_ENTRY(n) \
+__asm__ ( \
+ ".section \".text\" \n\t" \
+ ".align 2 \n\t" \
+ ".globl Stub"#n"__14nsXPTCStubBase \n\t" \
+ ".type Stub"#n"__14nsXPTCStubBase,@function \n\n" \
+ \
+"Stub"#n"__14nsXPTCStubBase: \n\t" \
+ "li 11,"#n" \n\t" \
+ "b SharedStub@local \n" \
+);
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_openbsd.cpp
new file mode 100644
index 000000000..62a693643
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_openbsd.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// Implement shared vtbl methods.
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+// The Linux/PPC ABI (aka PPC/SYSV ABI) passes the first 8 integral
+// parameters and the first 8 floating point parameters in registers
+// (r3-r10 and f1-f8), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the callers stack
+// area. The stack pointer has to retain 16-byte alignment, longlongs
+// and doubles are aligned on 8-byte boundaries.
+
+#define PARAM_BUFFER_COUNT 16
+#define GPR_COUNT 8
+#define FPR_COUNT 8
+
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gprData[]' contains the arguments passed in integer registers
+// - 'fprData[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self,
+ uint32_t methodIndex,
+ uint32_t* args,
+ uint32_t *gprData,
+ double *fprData)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (! info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t* ap = args;
+ uint32_t gpr = 1; // skip one GPR register
+ uint32_t fpr = 0;
+ uint32_t tempu32;
+ uint64_t tempu64;
+
+ for(i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+ if (fpr < FPR_COUNT)
+ dp->val.d = fprData[fpr++];
+ else {
+ if ((uint32_t) ap & 4) ap++; // doubles are 8-byte aligned on stack
+ dp->val.d = *(double*) ap;
+ ap += 2;
+ }
+ continue;
+ }
+ else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+ if (fpr < FPR_COUNT)
+ dp->val.f = (float) fprData[fpr++]; // in registers floats are passed as doubles
+ else
+ dp->val.f = *(float*) ap++;
+ continue;
+ }
+ else if (!param.IsOut() && (type == nsXPTType::T_I64
+ || type == nsXPTType::T_U64)) {
+ if (gpr & 1) gpr++; // longlongs are aligned in odd/even register pairs, eg. r5/r6
+ if ((gpr + 1) < GPR_COUNT) {
+ tempu64 = *(uint64_t*) &gprData[gpr];
+ gpr += 2;
+ }
+ else {
+ if ((uint32_t) ap & 4) ap++; // longlongs are 8-byte aligned on stack
+ tempu64 = *(uint64_t*) ap;
+ ap += 2;
+ }
+ }
+ else {
+ if (gpr < GPR_COUNT)
+ tempu32 = gprData[gpr++];
+ else
+ tempu32 = *ap++;
+ }
+
+ if(param.IsOut() || !type.IsArithmetic()) {
+ if (type == nsXPTType::T_JSVAL)
+ dp->val.p = *((void**) tempu32);
+ else
+ dp->val.p = (void*) tempu32;
+ continue;
+ }
+
+ switch(type) {
+ case nsXPTType::T_I8: dp->val.i8 = (int8_t) tempu32; break;
+ case nsXPTType::T_I16: dp->val.i16 = (int16_t) tempu32; break;
+ case nsXPTType::T_I32: dp->val.i32 = (int32_t) tempu32; break;
+ case nsXPTType::T_I64: dp->val.i64 = (int64_t) tempu64; break;
+ case nsXPTType::T_U8: dp->val.u8 = (uint8_t) tempu32; break;
+ case nsXPTType::T_U16: dp->val.u16 = (uint16_t) tempu32; break;
+ case nsXPTType::T_U32: dp->val.u32 = (uint32_t) tempu32; break;
+ case nsXPTType::T_U64: dp->val.u64 = (uint64_t) tempu64; break;
+ case nsXPTType::T_BOOL: dp->val.b = (bool) tempu32; break;
+ case nsXPTType::T_CHAR: dp->val.c = (char) tempu32; break;
+ case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) tempu32; break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex,
+ info,
+ dispatchParams);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+// Load r11 with the constant 'n' and branch to SharedStub().
+//
+// XXX Yes, it's ugly that we're relying on gcc's name-mangling here;
+// however, it's quick, dirty, and'll break when the ABI changes on
+// us, which is what we want ;-).
+
+
+// gcc-3 version
+//
+// As G++3 ABI contains the length of the functionname in the mangled
+// name, it is difficult to get a generic assembler mechanism like
+// in the G++ 2.95 case.
+// Create names would be like:
+// _ZN14nsXPTCStubBase5Stub1Ev
+// _ZN14nsXPTCStubBase6Stub12Ev
+// _ZN14nsXPTCStubBase7Stub123Ev
+// _ZN14nsXPTCStubBase8Stub1234Ev
+// etc.
+// Use assembler directives to get the names right...
+
+# define STUB_ENTRY(n) \
+__asm__ ( \
+ ".align 2 \n\t" \
+ ".if "#n" < 10 \n\t" \
+ ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t" \
+ \
+ ".elseif "#n" < 100 \n\t" \
+ ".globl _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t" \
+ \
+ ".elseif "#n" < 1000 \n\t" \
+ ".globl _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
+ ".type _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n" \
+"_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t" \
+ \
+ ".else \n\t" \
+ ".err \"stub number "#n" >= 1000 not yet supported\"\n" \
+ ".endif \n\t" \
+ \
+ "li 11,"#n" \n\t" \
+ "b SharedStub@local \n" \
+);
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_rhapsody.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_rhapsody.cpp
new file mode 100644
index 000000000..4c485a275
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc_rhapsody.cpp
@@ -0,0 +1,165 @@
+/* -*- Mode: C -*- */
+/* 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+/* Under the Mac OS X PowerPC ABI, the first 8 integer and 13 floating point
+ * parameters are delivered in registers and are not on the stack, although
+ * stack space is allocated for them. The integer parameters are delivered
+ * in GPRs r3 through r10. The first 8 words of the parameter area on the
+ * stack shadow these registers. A word will either be in a register or on
+ * the stack, but not in both. Although the first floating point parameters
+ * are passed in floating point registers, GPR space and stack space is
+ * reserved for them as well.
+ *
+ * SharedStub has passed pointers to the parameter section of the stack
+ * and saved copies of the GPRs and FPRs used for parameter passing. We
+ * don't care about the first parameter (which is delivered here as the self
+ * pointer), so SharedStub pointed us past that. argsGPR thus points to GPR
+ * r4 (corresponding to the first argument after the self pointer) and
+ * argsStack points to the parameter section of the caller's stack frame
+ * reserved for the same argument. This way, it is possible to reference
+ * either argsGPR or argsStack with the same index.
+ *
+ * Contrary to the assumption made by the previous implementation, the
+ * Mac OS X PowerPC ABI doesn't impose any special alignment restrictions on
+ * parameter sections of stacks. Values that are 64 bits wide appear on the
+ * stack without any special padding.
+ *
+ * See also xptcstubs_asm_ppc_darwin.s.m4:_SharedStub.
+ *
+ * ABI reference:
+ * http://developer.apple.com/documentation/DeveloperTools/Conceptual/
+ * MachORuntime/PowerPCConventions/chapter_3_section_1.html */
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(
+ nsXPTCStubBase *self,
+ uint32_t methodIndex,
+ uint32_t *argsStack,
+ uint32_t *argsGPR,
+ double *argsFPR) {
+#define PARAM_BUFFER_COUNT 16
+#define PARAM_FPR_COUNT 13
+#define PARAM_GPR_COUNT 7
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant *dispatchParams = nullptr;
+ const nsXPTMethodInfo *methodInfo;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+ uint32_t argIndex = 0;
+ uint32_t fprIndex = 0;
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU;
+
+ NS_ASSERTION(self, "no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &methodInfo);
+ NS_ASSERTION(methodInfo, "no method info");
+
+ paramCount = methodInfo->GetParamCount();
+
+ if(paramCount > PARAM_BUFFER_COUNT) {
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ }
+ else {
+ dispatchParams = paramBuffer;
+ }
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ for(i = 0; i < paramCount; i++, argIndex++) {
+ const nsXPTParamInfo &param = methodInfo->GetParam(i);
+ const nsXPTType &type = param.GetType();
+ nsXPTCMiniVariant *dp = &dispatchParams[i];
+ uint32_t theParam;
+
+ if(argIndex < PARAM_GPR_COUNT)
+ theParam = argsGPR[argIndex];
+ else
+ theParam = argsStack[argIndex];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ dp->val.p = (void *) theParam;
+ else {
+ switch(type) {
+ case nsXPTType::T_I8:
+ dp->val.i8 = (int8_t) theParam;
+ break;
+ case nsXPTType::T_I16:
+ dp->val.i16 = (int16_t) theParam;
+ break;
+ case nsXPTType::T_I32:
+ dp->val.i32 = (int32_t) theParam;
+ break;
+ case nsXPTType::T_U8:
+ dp->val.u8 = (uint8_t) theParam;
+ break;
+ case nsXPTType::T_U16:
+ dp->val.u16 = (uint16_t) theParam;
+ break;
+ case nsXPTType::T_U32:
+ dp->val.u32 = (uint32_t) theParam;
+ break;
+ case nsXPTType::T_I64:
+ case nsXPTType::T_U64:
+ ((DU *)dp)->hi = (uint32_t) theParam;
+ if(++argIndex < PARAM_GPR_COUNT)
+ ((DU *)dp)->lo = (uint32_t) argsGPR[argIndex];
+ else
+ ((DU *)dp)->lo = (uint32_t) argsStack[argIndex];
+ break;
+ case nsXPTType::T_BOOL:
+ dp->val.b = (bool) theParam;
+ break;
+ case nsXPTType::T_CHAR:
+ dp->val.c = (char) theParam;
+ break;
+ case nsXPTType::T_WCHAR:
+ dp->val.wc = (wchar_t) theParam;
+ break;
+ case nsXPTType::T_FLOAT:
+ if(fprIndex < PARAM_FPR_COUNT)
+ dp->val.f = (float) argsFPR[fprIndex++];
+ else
+ dp->val.f = *(float *) &argsStack[argIndex];
+ break;
+ case nsXPTType::T_DOUBLE:
+ if(fprIndex < PARAM_FPR_COUNT)
+ dp->val.d = argsFPR[fprIndex++];
+ else
+ dp->val.d = *(double *) &argsStack[argIndex];
+ argIndex++;
+ break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+ }
+
+ result = self->mOuter->
+ CallMethod((uint16_t)methodIndex, methodInfo, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n)
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc64_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc64_openbsd.cpp
new file mode 100644
index 000000000..b8a09c97e
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc64_openbsd.cpp
@@ -0,0 +1,104 @@
+/* -*- 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if defined(sparc) || defined(__sparc__)
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint64_t methodIndex, uint64_t* args)
+{
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint64_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_BOOL : dp->val.b = *((int64_t*) ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((uint64_t*) ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((int64_t*) ap); break;
+ case nsXPTType::T_I8 : dp->val.i8 = *((int64_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int64_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int64_t*) ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint64_t*) ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint64_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint64_t*)ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*) ap); break;
+ case nsXPTType::T_FLOAT : dp->val.f = ((float*) ap)[1]; break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(int, int*);
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ int dummy; /* defeat tail-call optimization */ \
+ return SharedStub(n, &dummy); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* sparc || __sparc__ */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_netbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_netbsd.cpp
new file mode 100644
index 000000000..22bf94d49
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_netbsd.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+
+#if defined(sparc) || defined(__sparc__)
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ nsIInterfaceInfo* iface_info = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->GetInterfaceInfo(&iface_info);
+ NS_ASSERTION(iface_info,"no interface info");
+
+ iface_info->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (type == nsXPTType::T_JSVAL)
+ dp->val.p = *((void**) *ap);
+ else
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int32_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int32_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_DOUBLE :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_I64 : ((DU *)dp)->hi = ((DU *)ap)->hi;
+ ((DU *)dp)->lo = ((DU *)ap)->lo;
+ ap++;
+ break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_BOOL : dp->val.b = *((uint32_t*)ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((uint32_t*)ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((int32_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ NS_RELEASE(iface_info);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(int, int*);
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ int dummy; /* defeat tail-call optimization */ \
+ return SharedStub(n, &dummy); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* sparc || __sparc__ */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_openbsd.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_openbsd.cpp
new file mode 100644
index 000000000..55d814b85
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_openbsd.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+
+#if defined(sparc) || defined(__sparc__)
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ nsIInterfaceInfo* iface_info = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->GetInterfaceInfo(&iface_info);
+ NS_ASSERTION(iface_info,"no interface info");
+
+ iface_info->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (type == nsXPTType::T_JSVAL)
+ dp->val.p = *((void**) *ap);
+ else
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int32_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int32_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_DOUBLE :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_I64 : ((DU *)dp)->hi = ((DU *)ap)->hi;
+ ((DU *)dp)->lo = ((DU *)ap)->lo;
+ ap++;
+ break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_BOOL : dp->val.b = *((uint32_t*)ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((uint32_t*)ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((int32_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ NS_RELEASE(iface_info);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(int, int*);
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ int dummy; /* defeat tail-call optimization */ \
+ return SharedStub(n, &dummy); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* sparc || __sparc__ */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_solaris.cpp
new file mode 100644
index 000000000..61f3df4ff
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparc_solaris.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if defined(sparc) || defined(__sparc__)
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+
+ typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+ } DU; // have to move 64 bit entities as 32 bit halves since
+ // stack slots are not guaranteed 16 byte aligned
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (type == nsXPTType::T_JSVAL)
+ dp->val.p = *((void**) *ap);
+ else
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int32_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int32_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_DOUBLE :
+ case nsXPTType::T_U64 :
+ case nsXPTType::T_I64 : ((DU *)dp)->hi = ((DU *)ap)->hi;
+ ((DU *)dp)->lo = ((DU *)ap)->lo;
+ ap++;
+ break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_BOOL : dp->val.b = *((uint32_t*)ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((uint32_t*)ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((int32_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(int, int*);
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ int dummy; /* defeat tail-call optimization */ \
+ return SharedStub(n, &dummy); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* sparc || __sparc__ */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_sparcv9_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparcv9_solaris.cpp
new file mode 100644
index 000000000..583ce9864
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_sparcv9_solaris.cpp
@@ -0,0 +1,101 @@
+/* -*- 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#if defined(sparc) || defined(__sparc__)
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint64_t methodIndex, uint64_t* args)
+{
+
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint64_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int64_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int64_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int64_t*) ap); break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint64_t*)ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint64_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint64_t*)ap); break;
+ case nsXPTType::T_FLOAT : dp->val.f = ((float*) ap)[1]; break;
+ case nsXPTType::T_BOOL : dp->val.b = *((uint64_t*)ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((uint64_t*)ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((int64_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+extern "C" nsresult SharedStub(int, int*);
+
+#define STUB_ENTRY(n) \
+nsresult nsXPTCStubBase::Stub##n() \
+{ \
+ int dummy; /* defeat tail-call optimization */ \
+ return SharedStub(n, &dummy); \
+}
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+#endif /* sparc || __sparc__ */
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_darwin.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_darwin.cpp
new file mode 100644
index 000000000..ae058567f
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_darwin.cpp
@@ -0,0 +1,190 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+// Implement shared vtbl methods.
+
+// Keep this in sync with the linux version.
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+// The Darwin/x86-64 ABI passes the first 6 integer parameters and the
+// first 8 floating point parameters in registers (rdi, rsi, rdx, rcx,
+// r8, r9 and xmm0-xmm7), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the callers stack
+// area.
+
+const uint32_t PARAM_BUFFER_COUNT = 16;
+const uint32_t GPR_COUNT = 6;
+const uint32_t FPR_COUNT = 8;
+
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gpregs[]' contains the arguments passed in integer registers
+// - 'fpregs[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
+ uint64_t * args, uint64_t * gpregs, double *fpregs)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (!info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if (paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint64_t* ap = args;
+ uint32_t nr_gpr = 1; // skip one GPR register for 'that'
+ uint32_t nr_fpr = 0;
+ uint64_t value;
+
+ for (i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+ if (nr_fpr < FPR_COUNT)
+ dp->val.d = fpregs[nr_fpr++];
+ else
+ dp->val.d = *(double*) ap++;
+ continue;
+ }
+ else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+ if (nr_fpr < FPR_COUNT)
+ // The value in %xmm register is already prepared to
+ // be retrieved as a float. Therefore, we pass the
+ // value verbatim, as a double without conversion.
+ dp->val.d = fpregs[nr_fpr++];
+ else
+ dp->val.f = *(float*) ap++;
+ continue;
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ value = gpregs[nr_gpr++];
+ else
+ value = *ap++;
+ }
+
+ if (param.IsOut() || !type.IsArithmetic()) {
+ dp->val.p = (void*) value;
+ continue;
+ }
+
+ switch (type) {
+ case nsXPTType::T_I8: dp->val.i8 = (int8_t) value; break;
+ case nsXPTType::T_I16: dp->val.i16 = (int16_t) value; break;
+ case nsXPTType::T_I32: dp->val.i32 = (int32_t) value; break;
+ case nsXPTType::T_I64: dp->val.i64 = (int64_t) value; break;
+ case nsXPTType::T_U8: dp->val.u8 = (uint8_t) value; break;
+ case nsXPTType::T_U16: dp->val.u16 = (uint16_t) value; break;
+ case nsXPTType::T_U32: dp->val.u32 = (uint32_t) value; break;
+ case nsXPTType::T_U64: dp->val.u64 = (uint64_t) value; break;
+ // Cast to uint8_t first, to remove garbage on upper 56 bits.
+ case nsXPTType::T_BOOL: dp->val.b = (bool)(uint8_t) value; break;
+ case nsXPTType::T_CHAR: dp->val.c = (char) value; break;
+ case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) value; break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+// Darwin/x86-64 uses gcc >= 4.2
+
+#define STUB_ENTRY(n) \
+asm(".section __TEXT,__text\n\t" \
+ ".align 3\n\t" \
+ ".if " #n " < 10\n\t" \
+ ".globl __ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
+ "__ZN14nsXPTCStubBase5Stub" #n "Ev:\n\t" \
+ ".elseif " #n " < 100\n\t" \
+ ".globl __ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
+ "__ZN14nsXPTCStubBase6Stub" #n "Ev:\n\t" \
+ ".elseif " #n " < 1000\n\t" \
+ ".globl __ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
+ "__ZN14nsXPTCStubBase7Stub" #n "Ev:\n\t" \
+ ".else\n\t" \
+ ".err \"stub number " #n " >= 1000 not yet supported\"\n\t" \
+ ".endif\n\t" \
+ "movl $" #n ", %eax\n\t" \
+ "jmp SharedStub\n\t");
+
+// static nsresult SharedStub(uint32_t methodIndex)
+asm(".section __TEXT,__text\n\t"
+ ".align 3\n\t"
+ "SharedStub:\n\t"
+ // make room for gpregs (48), fpregs (64)
+ "pushq %rbp\n\t"
+ "movq %rsp,%rbp\n\t"
+ "subq $112,%rsp\n\t"
+ // save GP registers
+ "movq %rdi,-112(%rbp)\n\t"
+ "movq %rsi,-104(%rbp)\n\t"
+ "movq %rdx, -96(%rbp)\n\t"
+ "movq %rcx, -88(%rbp)\n\t"
+ "movq %r8 , -80(%rbp)\n\t"
+ "movq %r9 , -72(%rbp)\n\t"
+ "leaq -112(%rbp),%rcx\n\t"
+ // save FP registers
+ "movsd %xmm0,-64(%rbp)\n\t"
+ "movsd %xmm1,-56(%rbp)\n\t"
+ "movsd %xmm2,-48(%rbp)\n\t"
+ "movsd %xmm3,-40(%rbp)\n\t"
+ "movsd %xmm4,-32(%rbp)\n\t"
+ "movsd %xmm5,-24(%rbp)\n\t"
+ "movsd %xmm6,-16(%rbp)\n\t"
+ "movsd %xmm7, -8(%rbp)\n\t"
+ "leaq -64(%rbp),%r8\n\t"
+ // rdi has the 'self' pointer already
+ "movl %eax,%esi\n\t"
+ "leaq 16(%rbp),%rdx\n\t"
+ "call _PrepareAndDispatch\n\t"
+ "leave\n\t"
+ "ret\n\t");
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_linux.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_linux.cpp
new file mode 100644
index 000000000..01044363b
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_linux.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+// Implement shared vtbl methods.
+
+// Keep this in sync with the darwin version.
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+// The Linux/x86-64 ABI passes the first 6 integer parameters and the
+// first 8 floating point parameters in registers (rdi, rsi, rdx, rcx,
+// r8, r9 and xmm0-xmm7), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the callers stack
+// area.
+
+const uint32_t PARAM_BUFFER_COUNT = 16;
+const uint32_t GPR_COUNT = 6;
+const uint32_t FPR_COUNT = 8;
+
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gpregs[]' contains the arguments passed in integer registers
+// - 'fpregs[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
+ uint64_t * args, uint64_t * gpregs, double *fpregs)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (!info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if (paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint64_t* ap = args;
+ uint32_t nr_gpr = 1; // skip one GPR register for 'that'
+ uint32_t nr_fpr = 0;
+ uint64_t value;
+
+ for (i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+ if (nr_fpr < FPR_COUNT)
+ dp->val.d = fpregs[nr_fpr++];
+ else
+ dp->val.d = *(double*) ap++;
+ continue;
+ }
+ else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+ if (nr_fpr < FPR_COUNT)
+ // The value in %xmm register is already prepared to
+ // be retrieved as a float. Therefore, we pass the
+ // value verbatim, as a double without conversion.
+ dp->val.d = fpregs[nr_fpr++];
+ else
+ dp->val.f = *(float*) ap++;
+ continue;
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ value = gpregs[nr_gpr++];
+ else
+ value = *ap++;
+ }
+
+ if (param.IsOut() || !type.IsArithmetic()) {
+ dp->val.p = (void*) value;
+ continue;
+ }
+
+ switch (type) {
+ case nsXPTType::T_I8: dp->val.i8 = (int8_t) value; break;
+ case nsXPTType::T_I16: dp->val.i16 = (int16_t) value; break;
+ case nsXPTType::T_I32: dp->val.i32 = (int32_t) value; break;
+ case nsXPTType::T_I64: dp->val.i64 = (int64_t) value; break;
+ case nsXPTType::T_U8: dp->val.u8 = (uint8_t) value; break;
+ case nsXPTType::T_U16: dp->val.u16 = (uint16_t) value; break;
+ case nsXPTType::T_U32: dp->val.u32 = (uint32_t) value; break;
+ case nsXPTType::T_U64: dp->val.u64 = (uint64_t) value; break;
+ // Cast to uint8_t first, to remove garbage on upper 56 bits.
+ case nsXPTType::T_BOOL: dp->val.b = (bool)(uint8_t) value; break;
+ case nsXPTType::T_CHAR: dp->val.c = (char) value; break;
+ case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) value; break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+// Linux/x86-64 uses gcc >= 3.1
+#define STUB_ENTRY(n) \
+asm(".section \".text\"\n\t" \
+ ".align 2\n\t" \
+ ".if " #n " < 10\n\t" \
+ ".globl _ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
+ ".hidden _ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
+ ".type _ZN14nsXPTCStubBase5Stub" #n "Ev,@function\n" \
+ "_ZN14nsXPTCStubBase5Stub" #n "Ev:\n\t" \
+ ".elseif " #n " < 100\n\t" \
+ ".globl _ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
+ ".hidden _ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
+ ".type _ZN14nsXPTCStubBase6Stub" #n "Ev,@function\n" \
+ "_ZN14nsXPTCStubBase6Stub" #n "Ev:\n\t" \
+ ".elseif " #n " < 1000\n\t" \
+ ".globl _ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
+ ".hidden _ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
+ ".type _ZN14nsXPTCStubBase7Stub" #n "Ev,@function\n" \
+ "_ZN14nsXPTCStubBase7Stub" #n "Ev:\n\t" \
+ ".else\n\t" \
+ ".err \"stub number " #n " >= 1000 not yet supported\"\n\t" \
+ ".endif\n\t" \
+ "movl $" #n ", %eax\n\t" \
+ "jmp SharedStub\n\t" \
+ ".if " #n " < 10\n\t" \
+ ".size _ZN14nsXPTCStubBase5Stub" #n "Ev,.-_ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
+ ".elseif " #n " < 100\n\t" \
+ ".size _ZN14nsXPTCStubBase6Stub" #n "Ev,.-_ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
+ ".else\n\t" \
+ ".size _ZN14nsXPTCStubBase7Stub" #n "Ev,.-_ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
+ ".endif");
+
+// static nsresult SharedStub(uint32_t methodIndex)
+asm(".section \".text\"\n\t"
+ ".align 2\n\t"
+ ".type SharedStub,@function\n\t"
+ "SharedStub:\n\t"
+ // make room for gpregs (48), fpregs (64)
+ "pushq %rbp\n\t"
+ "movq %rsp,%rbp\n\t"
+ "subq $112,%rsp\n\t"
+ // save GP registers
+ "movq %rdi,-112(%rbp)\n\t"
+ "movq %rsi,-104(%rbp)\n\t"
+ "movq %rdx, -96(%rbp)\n\t"
+ "movq %rcx, -88(%rbp)\n\t"
+ "movq %r8 , -80(%rbp)\n\t"
+ "movq %r9 , -72(%rbp)\n\t"
+ "leaq -112(%rbp),%rcx\n\t"
+ // save FP registers
+ "movsd %xmm0,-64(%rbp)\n\t"
+ "movsd %xmm1,-56(%rbp)\n\t"
+ "movsd %xmm2,-48(%rbp)\n\t"
+ "movsd %xmm3,-40(%rbp)\n\t"
+ "movsd %xmm4,-32(%rbp)\n\t"
+ "movsd %xmm5,-24(%rbp)\n\t"
+ "movsd %xmm6,-16(%rbp)\n\t"
+ "movsd %xmm7, -8(%rbp)\n\t"
+ "leaq -64(%rbp),%r8\n\t"
+ // rdi has the 'self' pointer already
+ "movl %eax,%esi\n\t"
+ "leaq 16(%rbp),%rdx\n\t"
+ "call PrepareAndDispatch@plt\n\t"
+ "leave\n\t"
+ "ret\n\t"
+ ".size SharedStub,.-SharedStub");
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_solaris.cpp
new file mode 100644
index 000000000..677fa9960
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_64_solaris.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+// Implement shared vtbl methods.
+
+// Keep this in sync with the darwin version.
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+// The Linux/x86-64 ABI passes the first 6 integer parameters and the
+// first 8 floating point parameters in registers (rdi, rsi, rdx, rcx,
+// r8, r9 and xmm0-xmm7), no stack space is allocated for these by the
+// caller. The rest of the parameters are passed in the callers stack
+// area.
+
+const uint32_t PARAM_BUFFER_COUNT = 16;
+const uint32_t GPR_COUNT = 6;
+const uint32_t FPR_COUNT = 8;
+
+// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
+//
+// - 'args[]' contains the arguments passed on stack
+// - 'gpregs[]' contains the arguments passed in integer registers
+// - 'fpregs[]' contains the arguments passed in floating point registers
+//
+// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
+// and then the method gets called.
+
+extern "C" nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
+ uint64_t * args, uint64_t * gpregs, double *fpregs)
+{
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint32_t paramCount;
+ uint32_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+ if (!info)
+ return NS_ERROR_UNEXPECTED;
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if (paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+ if (!dispatchParams)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint64_t* ap = args;
+ uint32_t nr_gpr = 1; // skip one GPR register for 'that'
+ uint32_t nr_fpr = 0;
+ uint64_t value;
+
+ for (i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
+ if (nr_fpr < FPR_COUNT)
+ dp->val.d = fpregs[nr_fpr++];
+ else
+ dp->val.d = *(double*) ap++;
+ continue;
+ }
+ else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
+ if (nr_fpr < FPR_COUNT)
+ // The value in %xmm register is already prepared to
+ // be retrieved as a float. Therefore, we pass the
+ // value verbatim, as a double without conversion.
+ dp->val.d = fpregs[nr_fpr++];
+ else
+ dp->val.f = *(float*) ap++;
+ continue;
+ }
+ else {
+ if (nr_gpr < GPR_COUNT)
+ value = gpregs[nr_gpr++];
+ else
+ value = *ap++;
+ }
+
+ if (param.IsOut() || !type.IsArithmetic()) {
+ dp->val.p = (void*) value;
+ continue;
+ }
+
+ switch (type) {
+ case nsXPTType::T_I8: dp->val.i8 = (int8_t) value; break;
+ case nsXPTType::T_I16: dp->val.i16 = (int16_t) value; break;
+ case nsXPTType::T_I32: dp->val.i32 = (int32_t) value; break;
+ case nsXPTType::T_I64: dp->val.i64 = (int64_t) value; break;
+ case nsXPTType::T_U8: dp->val.u8 = (uint8_t) value; break;
+ case nsXPTType::T_U16: dp->val.u16 = (uint16_t) value; break;
+ case nsXPTType::T_U32: dp->val.u32 = (uint32_t) value; break;
+ case nsXPTType::T_U64: dp->val.u64 = (uint64_t) value; break;
+ // Cast to uint8_t first, to remove garbage on upper 56 bits.
+ case nsXPTType::T_BOOL: dp->val.b = (bool)(uint8_t) value; break;
+ case nsXPTType::T_CHAR: dp->val.c = (char) value; break;
+ case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) value; break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
+
+ if (dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n)
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_solaris.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_solaris.cpp
new file mode 100644
index 000000000..39eec5e54
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_x86_solaris.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+nsresult ATTRIBUTE_USED
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint32_t* args)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no interface info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ dp->val.p = (void*) *ap;
+ switch(type)
+ {
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n)
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
diff --git a/xpcom/reflect/xptcall/md/win32/moz.build b/xpcom/reflect/xptcall/md/win32/moz.build
new file mode 100644
index 000000000..5dbe41d76
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/moz.build
@@ -0,0 +1,45 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['TARGET_CPU'] == 'x86_64':
+ if CONFIG['GNU_CXX']:
+ SOURCES += [
+ 'xptcinvoke_x86_64.cpp',
+ 'xptcstubs_x86_64_gnu.cpp',
+ ]
+ SOURCES += [
+ 'xptcinvoke_asm_x86_64_gnu.s'
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke_x86_64.cpp',
+ 'xptcstubs_x86_64.cpp'
+ ]
+ SOURCES += [
+ 'xptcinvoke_asm_x86_64.asm',
+ 'xptcstubs_asm_x86_64.asm'
+ ]
+else:
+ if CONFIG['GNU_CXX']:
+ SOURCES += [
+ 'xptcinvoke_x86_gnu.cpp',
+ 'xptcstubs.cpp',
+ ]
+ else:
+ SOURCES += [
+ 'xptcinvoke.cpp',
+ 'xptcinvoke_asm_x86_msvc.asm',
+ 'xptcstubs.cpp',
+ ]
+ SOURCES['xptcinvoke.cpp'].no_pgo = True
+ SOURCES['xptcinvoke_asm_x86_msvc.asm'].flags += ['-safeseh']
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../..',
+ '/xpcom/reflect/xptinfo',
+]
diff --git a/xpcom/reflect/xptcall/md/win32/xptcinvoke.cpp b/xpcom/reflect/xptcall/md/win32/xptcinvoke.cpp
new file mode 100644
index 000000000..faec869f6
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcinvoke.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+#ifndef WIN32
+#error "This code is for Win32 only"
+#endif
+
+extern "C" void __fastcall
+invoke_copy_to_stack(uint32_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ for(; paramCount > 0; paramCount--, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int8_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int16_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint8_t*) d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint16_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ case nsXPTType::T_BOOL : *((bool*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((char*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
diff --git a/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64.asm b/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64.asm
new file mode 100644
index 000000000..bf7c2ef0c
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64.asm
@@ -0,0 +1,107 @@
+; 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/.
+
+extrn invoke_copy_to_stack:PROC
+
+
+.CODE
+
+;
+;XPTC__InvokebyIndex(nsISupports* that, uint32_t methodIndex,
+; uint32_t paramCount, nsXPTCVariant* params)
+;
+
+XPTC__InvokebyIndex PROC FRAME
+
+ ; store register parameters
+
+ mov qword ptr [rsp+32], r9 ; params
+ mov dword ptr [rsp+24], r8d ; paramCount
+ mov dword ptr [rsp+16], edx ; methodIndex
+ mov qword ptr [rsp+8], rcx ; that
+
+ push rbp
+ .PUSHREG rbp
+ mov rbp, rsp ; store current RSP to RBP
+ .SETFRAME rbp, 0
+ .ENDPROLOG
+
+ sub rsp, 32
+
+ ; maybe we don't have any parameters to copy
+
+ test r8d, r8d
+ jz noparams
+
+ ;
+ ; Build stack for stdcall
+ ;
+
+ ; 1st parameter is space for parameters
+
+ mov eax, r8d
+ or eax, 1
+ shl rax, 3 ; *= 8
+ sub rsp, rax
+ mov rcx, rsp
+
+ ; 2nd parameter is parameter count
+
+ mov edx, r8d
+
+ ; 3rd parameter is params
+
+ mov r8, r9
+
+ sub rsp, 40
+ call invoke_copy_to_stack ; rcx = d
+ ; edx = paramCount
+ ; r8 = s
+ add rsp, 32
+
+ ; Current stack is the following.
+ ;
+ ; 0h: [space (for this)]
+ ; 8h: [1st parameter]
+ ; 10h: [2nd parameter]
+ ; 18h: [3rd parameter]
+ ; 20h: [4th parameter]
+ ; ...
+ ;
+ ; On Win64 ABI, the first 4 parameters are passed using registers,
+ ; and others are on stack.
+
+ ; 1st, 2nd and 3rd arguments are passed via registers
+
+ mov rdx, qword ptr [rsp+8] ; 1st parameter
+ movsd xmm1, qword ptr [rsp+8] ; for double
+
+ mov r8, qword ptr [rsp+16] ; 2nd parameter
+ movsd xmm2, qword ptr [rsp+16] ; for double
+
+ mov r9, qword ptr [rsp+24] ; 3rd parameter
+ movsd xmm3, qword ptr [rsp+24] ; for double
+
+ ; rcx register is this
+
+ mov rcx, qword ptr [rbp+8+8] ; that
+
+noparams:
+
+ ; calculate call address
+
+ mov r11, qword ptr [rcx]
+ mov eax, dword ptr [rbp+16+8] ; methodIndex
+
+ call qword ptr [r11+rax*8] ; stdcall, i.e. callee cleans up stack.
+
+ mov rsp, rbp
+ pop rbp
+
+ ret
+
+XPTC__InvokebyIndex ENDP
+
+
+END
diff --git a/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64_gnu.s b/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64_gnu.s
new file mode 100644
index 000000000..006e0444b
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_64_gnu.s
@@ -0,0 +1,110 @@
+# 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/.
+
+.extern invoke_copy_to_stack
+
+
+.text
+.intel_syntax noprefix
+
+#
+#_XPTC__InvokebyIndex(nsISupports* that, uint32_t methodIndex,
+# uint32_t paramCount, nsXPTCVariant* params)
+#
+
+.globl XPTC__InvokebyIndex
+.def XPTC__InvokebyIndex
+ .scl 3
+ .type 46
+.endef
+XPTC__InvokebyIndex:
+
+ # store register parameters
+
+ mov qword ptr [rsp+32], r9 # params
+ mov dword ptr [rsp+24], r8d # paramCount
+ mov dword ptr [rsp+16], edx # methodIndex
+ mov qword ptr [rsp+8], rcx # that
+
+ push rbp
+ # .PUSHREG rbp
+ mov rbp, rsp # store current RSP to RBP
+ # .SETFRAME rbp, 0
+ # .ENDPROLOG
+
+ sub rsp, 32
+
+ # maybe we don't have any parameters to copy
+
+ test r8d, r8d
+ jz noparams
+
+ #
+ # Build stack for stdcall
+ #
+
+ # 1st parameter is space for parameters
+
+ mov eax, r8d
+ or eax, 1
+ shl rax, 3 # *= 8
+ sub rsp, rax
+ mov rcx, rsp
+
+ # 2nd parameter is parameter count
+
+ mov edx, r8d
+
+ # 3rd parameter is params
+
+ mov r8, r9
+
+ sub rsp, 40
+ call invoke_copy_to_stack # rcx = d
+ # edx = paramCount
+ # r8 = s
+ add rsp, 32
+
+ # Current stack is the following.
+ #
+ # 0h: [space (for this)]
+ # 8h: [1st parameter]
+ # 10h: [2nd parameter]
+ # 18h: [3rd parameter]
+ # 20h: [4th parameter]
+ # ...
+ #
+ # On Win64 ABI, the first 4 parameters are passed using registers,
+ # and others are on stack.
+
+ # 1st, 2nd and 3rd arguments are passed via registers
+
+ mov rdx, qword ptr [rsp+8] # 1st parameter
+ movsd xmm1, qword ptr [rsp+8] # for double
+
+ mov r8, qword ptr [rsp+16] # 2nd parameter
+ movsd xmm2, qword ptr [rsp+16] # for double
+
+ mov r9, qword ptr [rsp+24] # 3rd parameter
+ movsd xmm3, qword ptr [rsp+24] # for double
+
+ # rcx register is this
+
+ mov rcx, qword ptr [rbp+8+8] # that
+
+noparams:
+
+ # calculate call address
+
+ mov r11, qword ptr [rcx]
+ mov eax, dword ptr [rbp+16+8] # methodIndex
+
+ call qword ptr [r11+rax*8] # stdcall, i.e. callee cleans up stack.
+
+ mov rsp, rbp
+ pop rbp
+
+ ret
+
+
diff --git a/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_msvc.asm b/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_msvc.asm
new file mode 100644
index 000000000..f3b7a1826
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcinvoke_asm_x86_msvc.asm
@@ -0,0 +1,63 @@
+; 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/.
+
+ TITLE xptcinvoke_asm_x86_msvc.asm
+ .686P
+ .model flat
+
+PUBLIC _NS_InvokeByIndex
+EXTRN @invoke_copy_to_stack@12:PROC
+
+;
+; extern "C" nsresult __cdecl
+; NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+; uint32_t paramCount, nsXPTCVariant* params)
+;
+
+_TEXT SEGMENT
+_NS_InvokeByIndex PROC
+
+ ; Build frame
+ push ebp
+ mov ebp, esp
+
+ ; Save paramCount for later
+ mov edx, dword ptr [ebp+16]
+
+ ; Do we have any parameters?
+ test edx, edx
+ jz noparams
+
+ ; Build call for copy_to_stack, which is __fastcall
+
+ ; Allocate space for parameters. 8 is the biggest size
+ ; any parameter can be, so assume that all our parameters
+ ; are that large.
+ mov eax, edx
+ shl eax, 3
+ sub esp, eax
+
+ mov ecx, esp
+ push dword ptr [ebp+20]
+ call @invoke_copy_to_stack@12
+noparams:
+ ; Push the `this' parameter for the call.
+ mov ecx, dword ptr [ebp+8]
+ push ecx
+
+ ; Load the vtable.
+ mov edx, dword ptr [ecx]
+
+ ; Call the vtable index at `methodIndex'.
+ mov eax, dword ptr [ebp+12]
+ call dword ptr [edx+eax*4]
+
+ ; Reset and return.
+ mov esp, ebp
+ pop ebp
+ ret
+_NS_InvokeByIndex ENDP
+_TEXT ENDS
+
+END
diff --git a/xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_64.cpp b/xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_64.cpp
new file mode 100644
index 000000000..36b454b35
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_64.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+extern "C" void
+invoke_copy_to_stack(uint64_t* d, uint32_t paramCount, nsXPTCVariant* s)
+{
+ for(; paramCount > 0; paramCount--, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+
+ /*
+ * AMD64 platform uses 8 bytes align.
+ */
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int8_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int16_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; break;
+ case nsXPTType::T_U8 : *((uint8_t*) d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint16_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; break;
+ case nsXPTType::T_BOOL : *((bool*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((char*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+
+extern "C" nsresult
+XPTC__InvokebyIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+extern "C"
+EXPORT_XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params)
+{
+ return XPTC__InvokebyIndex(that, methodIndex, paramCount, params);
+}
+
diff --git a/xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_gnu.cpp b/xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_gnu.cpp
new file mode 100644
index 000000000..0e73e99f3
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcinvoke_x86_gnu.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Platform specific code to invoke XPCOM methods on native objects */
+
+#include "xptcprivate.h"
+
+extern "C" {
+void __attribute__ ((__used__)) __attribute__ ((regparm(3)))
+invoke_copy_to_stack(uint32_t paramCount, nsXPTCVariant* s, uint32_t* d)
+{
+ for(uint32_t i = paramCount; i >0; i--, d++, s++)
+ {
+ if(s->IsPtrData())
+ {
+ *((void**)d) = s->ptr;
+ continue;
+ }
+
+ switch(s->type)
+ {
+ case nsXPTType::T_I8 : *((int8_t*) d) = s->val.i8; break;
+ case nsXPTType::T_I16 : *((int16_t*) d) = s->val.i16; break;
+ case nsXPTType::T_I32 : *((int32_t*) d) = s->val.i32; break;
+ case nsXPTType::T_I64 : *((int64_t*) d) = s->val.i64; d++; break;
+ case nsXPTType::T_U8 : *((uint8_t*) d) = s->val.u8; break;
+ case nsXPTType::T_U16 : *((uint16_t*)d) = s->val.u16; break;
+ case nsXPTType::T_U32 : *((uint32_t*)d) = s->val.u32; break;
+ case nsXPTType::T_U64 : *((uint64_t*)d) = s->val.u64; d++; break;
+ case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
+ case nsXPTType::T_DOUBLE : *((double*) d) = s->val.d; d++; break;
+ case nsXPTType::T_BOOL : *((bool*) d) = s->val.b; break;
+ case nsXPTType::T_CHAR : *((char*) d) = s->val.c; break;
+ case nsXPTType::T_WCHAR : *((wchar_t*) d) = s->val.wc; break;
+ default:
+ // all the others are plain pointer types
+ *((void**)d) = s->val.p;
+ break;
+ }
+ }
+}
+} // extern "C"
+
+/*
+ EXPORT_XPCOM_API(nsresult)
+ NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+ Each param takes at most two 4-byte words.
+ It doesn't matter if we push too many words, and calculating the exact
+ amount takes time.
+
+ that = ebp + 0x08
+ methodIndex = ebp + 0x0c
+ paramCount = ebp + 0x10
+ params = ebp + 0x14
+
+*/
+
+__asm__ (
+ ".text\n\t"
+/* alignment here seems unimportant here; this was 16, now it's 2 which
+ is what xptcstubs uses. */
+ ".align 2\n\t"
+ ".globl _NS_InvokeByIndex\n\t"
+ "_NS_InvokeByIndex:\n\t"
+ "pushl %ebp\n\t"
+ "movl %esp, %ebp\n\t"
+ "movl 0x10(%ebp), %eax\n\t"
+ "leal 0(,%eax,8),%edx\n\t"
+
+ /* set up call frame for method. */
+ "subl %edx, %esp\n\t" /* make room for params. */
+/* Align to maximum x86 data size: 128 bits == 16 bytes == XMM register size.
+ * This is to avoid protection faults where SSE+ alignment of stack pointer
+ * is assumed and required, e.g. by GCC4's -ftree-vectorize option.
+ */
+ "andl $0xfffffff0, %esp\n\t" /* drop(?) stack ptr to 128-bit align */
+/* $esp should be aligned to a 16-byte boundary here (note we include an
+ * additional 4 bytes in a later push instruction). This will ensure $ebp
+ * in the function called below is aligned to a 0x8 boundary. SSE instructions
+ * like movapd/movdqa expect memory operand to be aligned on a 16-byte
+ * boundary. The GCC compiler will generate the memory operand using $ebp
+ * with an 8-byte offset.
+ */
+ "subl $0xc, %esp\n\t" /* lower again; push/call below will re-align */
+ "movl %esp, %ecx\n\t" /* ecx = d */
+ "movl 8(%ebp), %edx\n\t" /* edx = this */
+ "pushl %edx\n\t" /* push this. esp % 16 == 0 */
+
+ "movl 0x14(%ebp), %edx\n\t"
+ "call _invoke_copy_to_stack\n\t"
+ "movl 0x08(%ebp), %ecx\n\t" /* 'that' */
+ "movl (%ecx), %edx\n\t"
+ "movl 0x0c(%ebp), %eax\n\t" /* function index */
+ "leal (%edx,%eax,4), %edx\n\t"
+ "call *(%edx)\n\t"
+ "movl %ebp, %esp\n\t"
+ "popl %ebp\n\t"
+ "ret\n"
+ ".section .drectve\n\t"
+ ".ascii \" -export:NS_InvokeByIndex\"\n\t"
+ ".text\n\t"
+);
diff --git a/xpcom/reflect/xptcall/md/win32/xptcstubs.cpp b/xpcom/reflect/xptcall/md/win32/xptcstubs.cpp
new file mode 100644
index 000000000..e525aebd7
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcstubs.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#ifndef WIN32
+#error "This code is for Win32 only"
+#endif
+
+extern "C" {
+
+#if !defined(__GNUC__) && !defined(__clang__)
+static
+#endif
+nsresult __stdcall
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex,
+ uint32_t* args, uint32_t* stackBytesToPop)
+{
+#define PARAM_BUFFER_COUNT 16
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ // If anything fails before stackBytesToPop can be set then
+ // the failure is completely catastrophic!
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ // setup variant array pointer
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint32_t* ap = args;
+ for(i = 0; i < paramCount; i++, ap++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ dp->val.p = (void*) *ap;
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8 : dp->val.i8 = *((int8_t*) ap); break;
+ case nsXPTType::T_I16 : dp->val.i16 = *((int16_t*) ap); break;
+ case nsXPTType::T_I32 : dp->val.i32 = *((int32_t*) ap); break;
+ case nsXPTType::T_I64 : dp->val.i64 = *((int64_t*) ap); ap++; break;
+ case nsXPTType::T_U8 : dp->val.u8 = *((uint8_t*) ap); break;
+ case nsXPTType::T_U16 : dp->val.u16 = *((uint16_t*)ap); break;
+ case nsXPTType::T_U32 : dp->val.u32 = *((uint32_t*)ap); break;
+ case nsXPTType::T_U64 : dp->val.u64 = *((uint64_t*)ap); ap++; break;
+ case nsXPTType::T_FLOAT : dp->val.f = *((float*) ap); break;
+ case nsXPTType::T_DOUBLE : dp->val.d = *((double*) ap); ap++; break;
+ case nsXPTType::T_BOOL : dp->val.b = *((bool*) ap); break;
+ case nsXPTType::T_CHAR : dp->val.c = *((char*) ap); break;
+ case nsXPTType::T_WCHAR : dp->val.wc = *((wchar_t*) ap); break;
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+ *stackBytesToPop = ((uint32_t)ap) - ((uint32_t)args);
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+} // extern "C"
+
+// declspec(naked) is broken in gcc and clang-cl
+#if !defined(__GNUC__) && !defined(__clang__)
+static
+__declspec(naked)
+void SharedStub(void)
+{
+ __asm {
+ push ebp // set up simple stack frame
+ mov ebp, esp // stack has: ebp/vtbl_index/retaddr/this/args
+ push ecx // make room for a ptr
+ lea eax, [ebp-4] // pointer to stackBytesToPop
+ push eax
+ lea eax, [ebp+12] // pointer to args
+ push eax
+ push ecx // vtbl_index
+ mov eax, [ebp+8] // this
+ push eax
+ call PrepareAndDispatch
+ mov edx, [ebp+4] // return address
+ mov ecx, [ebp-4] // stackBytesToPop
+ add ecx, 8 // for 'this' and return address
+ mov esp, ebp
+ pop ebp
+ add esp, ecx // fix up stack pointer
+ jmp edx // simulate __stdcall return
+ }
+}
+
+// these macros get expanded (many times) in the file #included below
+#define STUB_ENTRY(n) \
+__declspec(naked) nsresult __stdcall nsXPTCStubBase::Stub##n() \
+{ __asm mov ecx, n __asm jmp SharedStub }
+
+#else
+
+asm(".text\n\t"
+ ".align 4\n\t"
+ "SharedStub:\n\t"
+ "push %ebp\n\t"
+ "mov %esp, %ebp\n\t"
+ "push %ecx\n\t"
+ "lea -4(%ebp), %eax\n\t"
+ "push %eax\n\t"
+ "lea 12(%ebp), %eax\n\t"
+ "push %eax\n\t"
+ "push %ecx\n\t"
+ "movl 8(%ebp), %eax\n\t"
+ "push %eax\n\t"
+ "call _PrepareAndDispatch@16\n\t"
+ "mov 4(%ebp), %edx\n\t"
+ "mov -4(%ebp), %ecx\n\t"
+ "add $8, %ecx\n\t"
+ "mov %ebp, %esp\n\t"
+ "pop %ebp\n\t"
+ "add %ecx, %esp\n\t"
+ "jmp *%edx"
+);
+
+// The clang-cl specific code below is required because mingw uses the gcc name
+// mangling, but clang-cl implements the MSVC name mangling.
+
+#ifdef __clang__
+
+#define STUB_ENTRY(n) \
+asm(".text\n\t" \
+ ".align 4\n\t" \
+ ".globl \"?Stub" #n "@nsXPTCStubBase@@UAG?AW4nsresult@@XZ\"\n\t" \
+ ".def \"?Stub" #n "@nsXPTCStubBase@@UAG?AW4nsresult@@XZ\"; \n\t" \
+ ".scl 2\n\t" \
+ ".type 46\n\t" \
+ ".endef\n\t" \
+ "\"?Stub" #n "@nsXPTCStubBase@@UAG?AW4nsresult@@XZ\":\n\t" \
+ "mov $" #n ", %ecx\n\t" \
+ "jmp SharedStub");
+
+#else
+
+#define STUB_ENTRY(n) \
+asm(".text\n\t" \
+ ".align 4\n\t" \
+ ".if " #n " < 10\n\t" \
+ ".globl __ZN14nsXPTCStubBase5Stub" #n "Ev@4\n\t" \
+ ".def __ZN14nsXPTCStubBase5Stub" #n "Ev@4; \n\t" \
+ ".scl 3\n\t" \
+ ".type 46\n\t" \
+ ".endef\n\t" \
+ "__ZN14nsXPTCStubBase5Stub" #n "Ev@4:\n\t" \
+ ".elseif " #n " < 100\n\t" \
+ ".globl __ZN14nsXPTCStubBase6Stub" #n "Ev@4\n\t" \
+ ".def __ZN14nsXPTCStubBase6Stub" #n "Ev@4\n\t" \
+ ".scl 3\n\t" \
+ ".type 46\n\t" \
+ ".endef\n\t" \
+ "__ZN14nsXPTCStubBase6Stub" #n "Ev@4:\n\t" \
+ ".elseif " #n " < 1000\n\t" \
+ ".globl __ZN14nsXPTCStubBase7Stub" #n "Ev@4\n\t" \
+ ".def __ZN14nsXPTCStubBase7Stub" #n "Ev@4\n\t" \
+ ".scl 3\n\t" \
+ ".type 46\n\t" \
+ ".endef\n\t" \
+ "__ZN14nsXPTCStubBase7Stub" #n "Ev@4:\n\t" \
+ ".else\n\t" \
+ ".err \"stub number " #n " >= 1000 not yet supported\"\n\t" \
+ ".endif\n\t" \
+ "mov $" #n ", %ecx\n\t" \
+ "jmp SharedStub");
+
+#endif
+
+#endif /* __GNUC__ || __clang__ */
+
+#define SENTINEL_ENTRY(n) \
+nsresult __stdcall nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4035) // OK to have no return value
+#endif
+#include "xptcstubsdef.inc"
+#ifdef _MSC_VER
+#pragma warning(default : 4035) // restore default
+#endif
+
+void
+#ifdef __GNUC__
+__cdecl
+#endif
+xptc_dummy()
+{
+}
diff --git a/xpcom/reflect/xptcall/md/win32/xptcstubs_asm_x86_64.asm b/xpcom/reflect/xptcall/md/win32/xptcstubs_asm_x86_64.asm
new file mode 100644
index 000000000..db2cc16be
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcstubs_asm_x86_64.asm
@@ -0,0 +1,335 @@
+; 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/.
+
+extrn PrepareAndDispatch:PROC
+
+.code
+
+SharedStub PROC FRAME
+ sub rsp, 104
+ .ALLOCSTACK 104
+ .ENDPROLOG
+
+ ; rcx is this pointer. Need backup for optimized build
+
+ mov qword ptr [rsp+88], rcx
+
+ ;
+ ; fist 4 parameters (1st is "this" pointer) are passed in registers.
+ ;
+
+ ; for floating value
+
+ movsd qword ptr [rsp+64], xmm1
+ movsd qword ptr [rsp+72], xmm2
+ movsd qword ptr [rsp+80], xmm3
+
+ ; for integer value
+
+ mov qword ptr [rsp+40], rdx
+ mov qword ptr [rsp+48], r8
+ mov qword ptr [rsp+56], r9
+
+ ;
+ ; Call PrepareAndDispatch function
+ ;
+
+ ; 5th parameter (floating parameters) of PrepareAndDispatch
+
+ lea r9, qword ptr [rsp+64]
+ mov qword ptr [rsp+32], r9
+
+ ; 4th parameter (normal parameters) of PrepareAndDispatch
+
+ lea r9, qword ptr [rsp+40]
+
+ ; 3rd parameter (pointer to args on stack)
+
+ lea r8, qword ptr [rsp+40+104]
+
+ ; 2nd parameter (vtbl_index)
+
+ mov rdx, r11
+
+ ; 1st parameter (this) (rcx)
+
+ call PrepareAndDispatch
+
+ ; restore rcx
+
+ mov rcx, qword ptr [rsp+88]
+
+ ;
+ ; clean up register
+ ;
+
+ add rsp, 104+8
+
+ ; set return address
+
+ mov rdx, qword ptr [rsp-8]
+
+ ; simulate __stdcall return
+
+ jmp rdx
+
+SharedStub ENDP
+
+
+STUBENTRY MACRO functionname, paramcount
+functionname PROC
+ mov r11, paramcount
+ jmp SharedStub
+functionname ENDP
+ENDM
+
+ STUBENTRY ?Stub3@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 3
+ STUBENTRY ?Stub4@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 4
+ STUBENTRY ?Stub5@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 5
+ STUBENTRY ?Stub6@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 6
+ STUBENTRY ?Stub7@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 7
+ STUBENTRY ?Stub8@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 8
+ STUBENTRY ?Stub9@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 9
+ STUBENTRY ?Stub10@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 10
+ STUBENTRY ?Stub11@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 11
+ STUBENTRY ?Stub12@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 12
+ STUBENTRY ?Stub13@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 13
+ STUBENTRY ?Stub14@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 14
+ STUBENTRY ?Stub15@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 15
+ STUBENTRY ?Stub16@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 16
+ STUBENTRY ?Stub17@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 17
+ STUBENTRY ?Stub18@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 18
+ STUBENTRY ?Stub19@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 19
+ STUBENTRY ?Stub20@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 20
+ STUBENTRY ?Stub21@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 21
+ STUBENTRY ?Stub22@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 22
+ STUBENTRY ?Stub23@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 23
+ STUBENTRY ?Stub24@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 24
+ STUBENTRY ?Stub25@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 25
+ STUBENTRY ?Stub26@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 26
+ STUBENTRY ?Stub27@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 27
+ STUBENTRY ?Stub28@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 28
+ STUBENTRY ?Stub29@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 29
+ STUBENTRY ?Stub30@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 30
+ STUBENTRY ?Stub31@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 31
+ STUBENTRY ?Stub32@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 32
+ STUBENTRY ?Stub33@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 33
+ STUBENTRY ?Stub34@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 34
+ STUBENTRY ?Stub35@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 35
+ STUBENTRY ?Stub36@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 36
+ STUBENTRY ?Stub37@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 37
+ STUBENTRY ?Stub38@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 38
+ STUBENTRY ?Stub39@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 39
+ STUBENTRY ?Stub40@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 40
+ STUBENTRY ?Stub41@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 41
+ STUBENTRY ?Stub42@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 42
+ STUBENTRY ?Stub43@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 43
+ STUBENTRY ?Stub44@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 44
+ STUBENTRY ?Stub45@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 45
+ STUBENTRY ?Stub46@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 46
+ STUBENTRY ?Stub47@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 47
+ STUBENTRY ?Stub48@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 48
+ STUBENTRY ?Stub49@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 49
+ STUBENTRY ?Stub50@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 50
+ STUBENTRY ?Stub51@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 51
+ STUBENTRY ?Stub52@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 52
+ STUBENTRY ?Stub53@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 53
+ STUBENTRY ?Stub54@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 54
+ STUBENTRY ?Stub55@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 55
+ STUBENTRY ?Stub56@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 56
+ STUBENTRY ?Stub57@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 57
+ STUBENTRY ?Stub58@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 58
+ STUBENTRY ?Stub59@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 59
+ STUBENTRY ?Stub60@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 60
+ STUBENTRY ?Stub61@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 61
+ STUBENTRY ?Stub62@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 62
+ STUBENTRY ?Stub63@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 63
+ STUBENTRY ?Stub64@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 64
+ STUBENTRY ?Stub65@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 65
+ STUBENTRY ?Stub66@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 66
+ STUBENTRY ?Stub67@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 67
+ STUBENTRY ?Stub68@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 68
+ STUBENTRY ?Stub69@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 69
+ STUBENTRY ?Stub70@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 70
+ STUBENTRY ?Stub71@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 71
+ STUBENTRY ?Stub72@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 72
+ STUBENTRY ?Stub73@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 73
+ STUBENTRY ?Stub74@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 74
+ STUBENTRY ?Stub75@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 75
+ STUBENTRY ?Stub76@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 76
+ STUBENTRY ?Stub77@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 77
+ STUBENTRY ?Stub78@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 78
+ STUBENTRY ?Stub79@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 79
+ STUBENTRY ?Stub80@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 80
+ STUBENTRY ?Stub81@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 81
+ STUBENTRY ?Stub82@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 82
+ STUBENTRY ?Stub83@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 83
+ STUBENTRY ?Stub84@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 84
+ STUBENTRY ?Stub85@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 85
+ STUBENTRY ?Stub86@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 86
+ STUBENTRY ?Stub87@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 87
+ STUBENTRY ?Stub88@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 88
+ STUBENTRY ?Stub89@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 89
+ STUBENTRY ?Stub90@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 90
+ STUBENTRY ?Stub91@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 91
+ STUBENTRY ?Stub92@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 92
+ STUBENTRY ?Stub93@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 93
+ STUBENTRY ?Stub94@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 94
+ STUBENTRY ?Stub95@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 95
+ STUBENTRY ?Stub96@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 96
+ STUBENTRY ?Stub97@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 97
+ STUBENTRY ?Stub98@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 98
+ STUBENTRY ?Stub99@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 99
+ STUBENTRY ?Stub100@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 100
+ STUBENTRY ?Stub101@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 101
+ STUBENTRY ?Stub102@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 102
+ STUBENTRY ?Stub103@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 103
+ STUBENTRY ?Stub104@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 104
+ STUBENTRY ?Stub105@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 105
+ STUBENTRY ?Stub106@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 106
+ STUBENTRY ?Stub107@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 107
+ STUBENTRY ?Stub108@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 108
+ STUBENTRY ?Stub109@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 109
+ STUBENTRY ?Stub110@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 110
+ STUBENTRY ?Stub111@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 111
+ STUBENTRY ?Stub112@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 112
+ STUBENTRY ?Stub113@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 113
+ STUBENTRY ?Stub114@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 114
+ STUBENTRY ?Stub115@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 115
+ STUBENTRY ?Stub116@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 116
+ STUBENTRY ?Stub117@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 117
+ STUBENTRY ?Stub118@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 118
+ STUBENTRY ?Stub119@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 119
+ STUBENTRY ?Stub120@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 120
+ STUBENTRY ?Stub121@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 121
+ STUBENTRY ?Stub122@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 122
+ STUBENTRY ?Stub123@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 123
+ STUBENTRY ?Stub124@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 124
+ STUBENTRY ?Stub125@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 125
+ STUBENTRY ?Stub126@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 126
+ STUBENTRY ?Stub127@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 127
+ STUBENTRY ?Stub128@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 128
+ STUBENTRY ?Stub129@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 129
+ STUBENTRY ?Stub130@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 130
+ STUBENTRY ?Stub131@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 131
+ STUBENTRY ?Stub132@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 132
+ STUBENTRY ?Stub133@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 133
+ STUBENTRY ?Stub134@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 134
+ STUBENTRY ?Stub135@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 135
+ STUBENTRY ?Stub136@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 136
+ STUBENTRY ?Stub137@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 137
+ STUBENTRY ?Stub138@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 138
+ STUBENTRY ?Stub139@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 139
+ STUBENTRY ?Stub140@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 140
+ STUBENTRY ?Stub141@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 141
+ STUBENTRY ?Stub142@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 142
+ STUBENTRY ?Stub143@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 143
+ STUBENTRY ?Stub144@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 144
+ STUBENTRY ?Stub145@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 145
+ STUBENTRY ?Stub146@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 146
+ STUBENTRY ?Stub147@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 147
+ STUBENTRY ?Stub148@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 148
+ STUBENTRY ?Stub149@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 149
+ STUBENTRY ?Stub150@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 150
+ STUBENTRY ?Stub151@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 151
+ STUBENTRY ?Stub152@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 152
+ STUBENTRY ?Stub153@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 153
+ STUBENTRY ?Stub154@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 154
+ STUBENTRY ?Stub155@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 155
+ STUBENTRY ?Stub156@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 156
+ STUBENTRY ?Stub157@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 157
+ STUBENTRY ?Stub158@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 158
+ STUBENTRY ?Stub159@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 159
+ STUBENTRY ?Stub160@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 160
+ STUBENTRY ?Stub161@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 161
+ STUBENTRY ?Stub162@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 162
+ STUBENTRY ?Stub163@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 163
+ STUBENTRY ?Stub164@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 164
+ STUBENTRY ?Stub165@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 165
+ STUBENTRY ?Stub166@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 166
+ STUBENTRY ?Stub167@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 167
+ STUBENTRY ?Stub168@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 168
+ STUBENTRY ?Stub169@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 169
+ STUBENTRY ?Stub170@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 170
+ STUBENTRY ?Stub171@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 171
+ STUBENTRY ?Stub172@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 172
+ STUBENTRY ?Stub173@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 173
+ STUBENTRY ?Stub174@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 174
+ STUBENTRY ?Stub175@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 175
+ STUBENTRY ?Stub176@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 176
+ STUBENTRY ?Stub177@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 177
+ STUBENTRY ?Stub178@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 178
+ STUBENTRY ?Stub179@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 179
+ STUBENTRY ?Stub180@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 180
+ STUBENTRY ?Stub181@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 181
+ STUBENTRY ?Stub182@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 182
+ STUBENTRY ?Stub183@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 183
+ STUBENTRY ?Stub184@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 184
+ STUBENTRY ?Stub185@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 185
+ STUBENTRY ?Stub186@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 186
+ STUBENTRY ?Stub187@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 187
+ STUBENTRY ?Stub188@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 188
+ STUBENTRY ?Stub189@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 189
+ STUBENTRY ?Stub190@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 190
+ STUBENTRY ?Stub191@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 191
+ STUBENTRY ?Stub192@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 192
+ STUBENTRY ?Stub193@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 193
+ STUBENTRY ?Stub194@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 194
+ STUBENTRY ?Stub195@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 195
+ STUBENTRY ?Stub196@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 196
+ STUBENTRY ?Stub197@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 197
+ STUBENTRY ?Stub198@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 198
+ STUBENTRY ?Stub199@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 199
+ STUBENTRY ?Stub200@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 200
+ STUBENTRY ?Stub201@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 201
+ STUBENTRY ?Stub202@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 202
+ STUBENTRY ?Stub203@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 203
+ STUBENTRY ?Stub204@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 204
+ STUBENTRY ?Stub205@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 205
+ STUBENTRY ?Stub206@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 206
+ STUBENTRY ?Stub207@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 207
+ STUBENTRY ?Stub208@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 208
+ STUBENTRY ?Stub209@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 209
+ STUBENTRY ?Stub210@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 210
+ STUBENTRY ?Stub211@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 211
+ STUBENTRY ?Stub212@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 212
+ STUBENTRY ?Stub213@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 213
+ STUBENTRY ?Stub214@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 214
+ STUBENTRY ?Stub215@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 215
+ STUBENTRY ?Stub216@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 216
+ STUBENTRY ?Stub217@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 217
+ STUBENTRY ?Stub218@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 218
+ STUBENTRY ?Stub219@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 219
+ STUBENTRY ?Stub220@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 220
+ STUBENTRY ?Stub221@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 221
+ STUBENTRY ?Stub222@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 222
+ STUBENTRY ?Stub223@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 223
+ STUBENTRY ?Stub224@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 224
+ STUBENTRY ?Stub225@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 225
+ STUBENTRY ?Stub226@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 226
+ STUBENTRY ?Stub227@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 227
+ STUBENTRY ?Stub228@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 228
+ STUBENTRY ?Stub229@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 229
+ STUBENTRY ?Stub230@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 230
+ STUBENTRY ?Stub231@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 231
+ STUBENTRY ?Stub232@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 232
+ STUBENTRY ?Stub233@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 233
+ STUBENTRY ?Stub234@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 234
+ STUBENTRY ?Stub235@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 235
+ STUBENTRY ?Stub236@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 236
+ STUBENTRY ?Stub237@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 237
+ STUBENTRY ?Stub238@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 238
+ STUBENTRY ?Stub239@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 239
+ STUBENTRY ?Stub240@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 240
+ STUBENTRY ?Stub241@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 241
+ STUBENTRY ?Stub242@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 242
+ STUBENTRY ?Stub243@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 243
+ STUBENTRY ?Stub244@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 244
+ STUBENTRY ?Stub245@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 245
+ STUBENTRY ?Stub246@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 246
+ STUBENTRY ?Stub247@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 247
+ STUBENTRY ?Stub248@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 248
+ STUBENTRY ?Stub249@nsXPTCStubBase@@UEAA?AW4nsresult@@XZ, 249
+
+END
diff --git a/xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64.cpp b/xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64.cpp
new file mode 100644
index 000000000..8ff479d1e
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64.cpp
@@ -0,0 +1,197 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implement shared vtbl methods. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+/*
+ * This is for Windows XP 64-Bit Edition / Server 2003 for AMD64 or later.
+ */
+
+extern "C" nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args,
+ uint64_t *gprData, double *fprData)
+{
+#define PARAM_BUFFER_COUNT 16
+//
+// "this" pointer is first parameter, so parameter count is 3.
+//
+#define PARAM_GPR_COUNT 3
+#define PARAM_FPR_COUNT 3
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self,"no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info,"no method info");
+
+ paramCount = info->GetParamCount();
+
+ //
+ // setup variant array pointer
+ //
+
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint64_t* ap = args;
+ uint32_t iCount = 0;
+
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.p = (void*)gprData[iCount++];
+ else
+ dp->val.p = (void*)*ap++;
+
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i8 = (int8_t)gprData[iCount++];
+ else
+ dp->val.i8 = *((int8_t*)ap++);
+ break;
+
+ case nsXPTType::T_I16:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i16 = (int16_t)gprData[iCount++];
+ else
+ dp->val.i16 = *((int16_t*)ap++);
+ break;
+
+ case nsXPTType::T_I32:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i32 = (int32_t)gprData[iCount++];
+ else
+ dp->val.i32 = *((int32_t*)ap++);
+ break;
+
+ case nsXPTType::T_I64:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i64 = (int64_t)gprData[iCount++];
+ else
+ dp->val.i64 = *((int64_t*)ap++);
+ break;
+
+ case nsXPTType::T_U8:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u8 = (uint8_t)gprData[iCount++];
+ else
+ dp->val.u8 = *((uint8_t*)ap++);
+ break;
+
+ case nsXPTType::T_U16:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u16 = (uint16_t)gprData[iCount++];
+ else
+ dp->val.u16 = *((uint16_t*)ap++);
+ break;
+
+ case nsXPTType::T_U32:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u32 = (uint32_t)gprData[iCount++];
+ else
+ dp->val.u32 = *((uint32_t*)ap++);
+ break;
+
+ case nsXPTType::T_U64:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u64 = (uint64_t)gprData[iCount++];
+ else
+ dp->val.u64 = *((uint64_t*)ap++);
+ break;
+
+ case nsXPTType::T_FLOAT:
+ if (iCount < PARAM_FPR_COUNT)
+ // The value in xmm register is already prepared to
+ // be retrieved as a float. Therefore, we pass the
+ // value verbatim, as a double without conversion.
+ dp->val.d = (double)fprData[iCount++];
+ else
+ dp->val.f = *((float*)ap++);
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ if (iCount < PARAM_FPR_COUNT)
+ dp->val.d = (double)fprData[iCount++];
+ else
+ dp->val.d = *((double*)ap++);
+ break;
+
+ case nsXPTType::T_BOOL:
+ if (iCount < PARAM_GPR_COUNT)
+ // We need cast to uint8_t to remove garbage on upper 56-bit
+ // at first.
+ dp->val.b = (bool)(uint8_t)gprData[iCount++];
+ else
+ dp->val.b = *((bool*)ap++);
+ break;
+
+ case nsXPTType::T_CHAR:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.c = (char)gprData[iCount++];
+ else
+ dp->val.c = *((char*)ap++);
+ break;
+
+ case nsXPTType::T_WCHAR:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.wc = (wchar_t)gprData[iCount++];
+ else
+ dp->val.wc = *((wchar_t*)ap++);
+ break;
+
+ default:
+ NS_ERROR("bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+#define STUB_ENTRY(n) /* defined in the assembly file */
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ERROR("nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
+void
+xptc_dummy()
+{
+}
+
diff --git a/xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64_gnu.cpp b/xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64_gnu.cpp
new file mode 100644
index 000000000..2676334d2
--- /dev/null
+++ b/xpcom/reflect/xptcall/md/win32/xptcstubs_x86_64_gnu.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "xptcprivate.h"
+#include "xptiprivate.h"
+
+/*
+ * This is for Windows 64 bit (x86_64) using GCC syntax
+ * Code was copied from the MSVC version.
+ */
+
+#if !defined(_AMD64_) || !defined(__GNUC__)
+# error xptcstubs_x86_64_gnu.cpp being used unexpectedly
+#endif
+
+extern "C" nsresult __attribute__((__used__))
+PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
+ uint64_t * args, uint64_t * gprData, double *fprData)
+{
+#define PARAM_BUFFER_COUNT 16
+//
+// "this" pointer is first parameter, so parameter count is 3.
+//
+#define PARAM_GPR_COUNT 3
+#define PARAM_FPR_COUNT 3
+
+ nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+ nsXPTCMiniVariant* dispatchParams = nullptr;
+ const nsXPTMethodInfo* info = nullptr;
+ uint8_t paramCount;
+ uint8_t i;
+ nsresult result = NS_ERROR_FAILURE;
+
+ NS_ASSERTION(self, "no self");
+
+ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+ NS_ASSERTION(info, "no method info");
+
+ paramCount = info->GetParamCount();
+
+ //
+ // setup variant array pointer
+ //
+
+ if(paramCount > PARAM_BUFFER_COUNT)
+ dispatchParams = new nsXPTCMiniVariant[paramCount];
+ else
+ dispatchParams = paramBuffer;
+
+ NS_ASSERTION(dispatchParams,"no place for params");
+
+ uint64_t* ap = args;
+ uint32_t iCount = 0;
+
+ for(i = 0; i < paramCount; i++)
+ {
+ const nsXPTParamInfo& param = info->GetParam(i);
+ const nsXPTType& type = param.GetType();
+ nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+ if(param.IsOut() || !type.IsArithmetic())
+ {
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.p = (void*)gprData[iCount++];
+ else
+ dp->val.p = (void*)*ap++;
+
+ continue;
+ }
+ // else
+ switch(type)
+ {
+ case nsXPTType::T_I8:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i8 = (int8_t)gprData[iCount++];
+ else
+ dp->val.i8 = *((int8_t*)ap++);
+ break;
+
+ case nsXPTType::T_I16:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i16 = (int16_t)gprData[iCount++];
+ else
+ dp->val.i16 = *((int16_t*)ap++);
+ break;
+
+ case nsXPTType::T_I32:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i32 = (int32_t)gprData[iCount++];
+ else
+ dp->val.i32 = *((int32_t*)ap++);
+ break;
+
+ case nsXPTType::T_I64:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.i64 = (int64_t)gprData[iCount++];
+ else
+ dp->val.i64 = *((int64_t*)ap++);
+ break;
+
+ case nsXPTType::T_U8:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u8 = (uint8_t)gprData[iCount++];
+ else
+ dp->val.u8 = *((uint8_t*)ap++);
+ break;
+
+ case nsXPTType::T_U16:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u16 = (uint16_t)gprData[iCount++];
+ else
+ dp->val.u16 = *((uint16_t*)ap++);
+ break;
+
+ case nsXPTType::T_U32:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u32 = (uint32_t)gprData[iCount++];
+ else
+ dp->val.u32 = *((uint32_t*)ap++);
+ break;
+
+ case nsXPTType::T_U64:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.u64 = (uint64_t)gprData[iCount++];
+ else
+ dp->val.u64 = *((uint64_t*)ap++);
+ break;
+
+ case nsXPTType::T_FLOAT:
+ if (iCount < PARAM_FPR_COUNT)
+ dp->val.f = (float)fprData[iCount++];
+ else
+ dp->val.f = *((float*)ap++);
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ if (iCount < PARAM_FPR_COUNT)
+ dp->val.d = (double)fprData[iCount++];
+ else
+ dp->val.d = *((double*)ap++);
+ break;
+
+ case nsXPTType::T_BOOL:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.b = (bool)gprData[iCount++];
+ else
+ dp->val.b = *((bool*)ap++);
+ break;
+
+ case nsXPTType::T_CHAR:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.c = (char)gprData[iCount++];
+ else
+ dp->val.c = *((char*)ap++);
+ break;
+
+ case nsXPTType::T_WCHAR:
+ if (iCount < PARAM_GPR_COUNT)
+ dp->val.wc = (wchar_t)gprData[iCount++];
+ else
+ dp->val.wc = *((wchar_t*)ap++);
+ break;
+
+ default:
+ NS_ASSERTION(0, "bad type");
+ break;
+ }
+ }
+
+ result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+ if(dispatchParams != paramBuffer)
+ delete [] dispatchParams;
+
+ return result;
+}
+
+__asm__ (
+ ".text\n"
+ ".intel_syntax noprefix\n" /* switch to Intel syntax to look like the MSVC assembly */
+ ".globl SharedStub\n"
+ ".def SharedStub ; .scl 3 ; .type 46 ; .endef \n"
+ "SharedStub:\n"
+ "sub rsp, 104\n"
+
+ /* rcx is this pointer. Need backup for optimized build */
+
+ "mov qword ptr [rsp+88], rcx\n"
+
+ /*
+ * fist 4 parameters (1st is "this" pointer) are passed in registers.
+ */
+
+ /* for floating value */
+
+ "movsd qword ptr [rsp+64], xmm1\n"
+ "movsd qword ptr [rsp+72], xmm2\n"
+ "movsd qword ptr [rsp+80], xmm3\n"
+
+ /* for integer value */
+
+ "mov qword ptr [rsp+40], rdx\n"
+ "mov qword ptr [rsp+48], r8\n"
+ "mov qword ptr [rsp+56], r9\n"
+
+ /*
+ * Call PrepareAndDispatch function
+ */
+
+ /* 5th parameter (floating parameters) of PrepareAndDispatch */
+
+ "lea r9, qword ptr [rsp+64]\n"
+ "mov qword ptr [rsp+32], r9\n"
+
+ /* 4th parameter (normal parameters) of PrepareAndDispatch */
+
+ "lea r9, qword ptr [rsp+40]\n"
+
+ /* 3rd parameter (pointer to args on stack) */
+
+ "lea r8, qword ptr [rsp+40+104]\n"
+
+ /* 2nd parameter (vtbl_index) */
+
+ "mov rdx, r11\n"
+
+ /* 1st parameter (this) (rcx) */
+
+ "call PrepareAndDispatch\n"
+
+ /* restore rcx */
+
+ "mov rcx, qword ptr [rsp+88]\n"
+
+ /*
+ * clean up register
+ */
+
+ "add rsp, 104+8\n"
+
+ /* set return address */
+
+ "mov rdx, qword ptr [rsp-8]\n"
+
+ /* simulate __stdcall return */
+
+ "jmp rdx\n"
+
+ /* back to AT&T syntax */
+ ".att_syntax\n"
+);
+
+#define STUB_ENTRY(n) \
+asm(".intel_syntax noprefix\n" /* this is in intel syntax */ \
+ ".text\n" \
+ ".align 2\n" \
+ ".if " #n " < 10\n" \
+ ".globl _ZN14nsXPTCStubBase5Stub" #n "Ev@4\n" \
+ ".def _ZN14nsXPTCStubBase5Stub" #n "Ev@4\n" \
+ ".scl 3\n" /* private */ \
+ ".type 46\n" /* function returning unsigned int */ \
+ ".endef\n" \
+ "_ZN14nsXPTCStubBase5Stub" #n "Ev@4:\n" \
+ ".elseif " #n " < 100\n" \
+ ".globl _ZN14nsXPTCStubBase6Stub" #n "Ev@4\n" \
+ ".def _ZN14nsXPTCStubBase6Stub" #n "Ev@4\n" \
+ ".scl 3\n" /* private */\
+ ".type 46\n" /* function returning unsigned int */ \
+ ".endef\n" \
+ "_ZN14nsXPTCStubBase6Stub" #n "Ev@4:\n" \
+ ".elseif " #n " < 1000\n" \
+ ".globl _ZN14nsXPTCStubBase7Stub" #n "Ev@4\n" \
+ ".def _ZN14nsXPTCStubBase7Stub" #n "Ev@4\n" \
+ ".scl 3\n" /* private */ \
+ ".type 46\n" /* function returning unsigned int */ \
+ ".endef\n" \
+ "_ZN14nsXPTCStubBase7Stub" #n "Ev@4:\n" \
+ ".else\n" \
+ ".err \"stub number " #n " >= 1000 not yet supported\"\n" \
+ ".endif\n" \
+ "mov r11, " #n "\n" \
+ "jmp SharedStub\n" \
+ ".att_syntax\n" /* back to AT&T syntax */ \
+ "");
+
+#define SENTINEL_ENTRY(n) \
+nsresult nsXPTCStubBase::Sentinel##n() \
+{ \
+ NS_ASSERTION(0,"nsXPTCStubBase::Sentinel called"); \
+ return NS_ERROR_NOT_IMPLEMENTED; \
+}
+
+#include "xptcstubsdef.inc"
+
diff --git a/xpcom/reflect/xptcall/moz.build b/xpcom/reflect/xptcall/moz.build
new file mode 100644
index 000000000..fee7a3352
--- /dev/null
+++ b/xpcom/reflect/xptcall/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += ['md']
+
+SOURCES += [
+ 'xptcall.cpp',
+]
+
+EXPORTS += [
+ 'xptcall.h',
+ 'xptcstubsdecl.inc',
+ 'xptcstubsdef.inc',
+]
+
+LOCAL_INCLUDES += [
+ '/xpcom/reflect/xptinfo',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/xpcom/reflect/xptcall/porting.html b/xpcom/reflect/xptcall/porting.html
new file mode 100644
index 000000000..0073c604e
--- /dev/null
+++ b/xpcom/reflect/xptcall/porting.html
@@ -0,0 +1,216 @@
+<!-- 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/. -->
+
+<html>
+<head>
+<title>xptcall Porting Guide</title>
+</head>
+<body bgcolor = "white">
+<h2><center>xptcall Porting Guide</center></h2>
+
+<h3>Overview</h3>
+
+<blockquote>
+
+<a href="http://www.mozilla.org/scriptable/xptcall-faq.html"> xptcall</a> is a
+library that supports both invoking methods on arbitrary xpcom objects and
+implementing classes whose objects can impersonate any xpcom interface. It does
+this using platform specific assembly language code. This code needs to be
+ported to all platforms that want to support xptcall (and thus mozilla).
+
+</blockquote>
+
+<h3>The tree</h3>
+
+<blockquote>
+<pre>
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall">mozilla/xpcom/reflect/xptcall</a>
+ +--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/public">public</a> // exported headers
+ +--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/src">src</a> // core source
+ | \--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md">md</a> // platform specific parts
+ | +--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/mac">mac</a> // mac ppc
+ | +--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a> // all unix
+ | \--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/win32">win32</a> // win32
+ | +--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/test">test</a> // simple tests to get started
+ \--<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/tests">tests</a> // full tests via api
+</pre>
+
+Porters are free to create subdirectories under the base <code>md</code>
+directory for their given platforms and to integrate into the build system as
+appropriate for their platform.
+
+</blockquote>
+
+<h3>Theory of operation</h3>
+
+<blockquote>
+
+There are really two pieces of functionality: <i>invoke</i> and <i>stubs</i>...
+
+<p>
+
+The <b><i>invoke</i></b> functionality requires the implementation of the
+following on each platform (from <a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/xptcall.h">xptcall/xptcall.h</a>):
+
+<pre>
+XPTC_PUBLIC_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+</pre>
+
+Calling code is expected to supply an array of <code>nsXPTCVariant</code>
+structs. These are discriminated unions describing the type and value of each
+parameter of the target function. The platform specific code then builds a call
+frame and invokes the method indicated by the index <code>methodIndex</code> on
+the xpcom interface <code>that</code>.
+
+<p>
+
+Here are examples of this implementation for
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/win32/xptcinvoke.cpp">Win32</a>
+and
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix/xptcinvoke_unixish_x86.cpp">Linux x86, NetBSD x86, and FreeBSD</a>.
+
+Both of these implementations use the basic strategy of: figure out how much
+stack space is needed for the params, make the space in a new frame, copy the
+params to that space, invoke the method, cleanup and return. C++ is used where
+appropriate, Assembly language is used where necessary. Inline assembly language is used here,
+but it is equally valid to use separate assembly language source files. Porters
+can decide how best to do this for their platforms.
+
+<p>
+
+The <b><i>stubs</i></b> functionality is more complex. The goal here is a class
+whose vtbl can look like the vtbl of any arbitrary xpcom interface. Objects of
+this class can then be built to impersonate any xpcom object. The base interface
+for this is (from <a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/xptcall.h">xptcall/xptcall.h</a>):
+
+<pre>
+class nsXPTCStubBase : public nsISupports
+{
+public:
+ // Include generated vtbl stub declarations.
+ // These are virtual and *also* implemented by this class..
+#include "xptcstubsdecl.inc"
+
+ // The following methods must be provided by inheritor of this class.
+
+ // return a refcounted pointer to the InterfaceInfo for this object
+ // NOTE: on some platforms this MUST not fail or we crash!
+ NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo** info) = 0;
+
+ // call this method and return result
+ NS_IMETHOD CallMethod(uint16_t methodIndex,
+ const nsXPTMethodInfo* info,
+ nsXPTCMiniVariant* params) = 0;
+};
+</pre>
+
+Code that wishes to make use of this <i>stubs</i> functionality (such as
+<a href="http://www.mozilla.org/scriptable/">XPConnect</a>) implement a class
+which inherits from <code>nsXPTCStubBase</code> and implements the
+<code>GetInterfaceInfo</code> and <code>CallMethod</code> to let the
+platform specific code know how to get interface information and how to dispatch methods
+once their parameters have been pulled out of the platform specific calling
+frame.
+
+<p>
+
+Porters of this functionality implement the platform specific code for the
+<i>stub</i> methods that fill the vtbl for this class. The idea here is that the
+class has a vtbl full of a large number of generic stubs. All instances of this
+class share that vtbl and the same stubs. The stubs forward calls to a platform
+specific method that uses the interface information supplied by
+the overridden <code>GetInterfaceInfo</code> to extract the parameters and build
+an array of platform independent <code>nsXPTCMiniVariant</code> structs which
+are in turn passed on to the overridden <code>CallMethod</code>. The
+platform dependent code is responsible for doing any cleanup and returning.
+
+<p>
+
+The stub methods are declared in <a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/xptcstubsdecl.inc">xptcall/xptcstubsdecl.inc</a>.
+These are '#included' into the declaration of <code>nsXPTCStubBase</code>. A
+similar include file (<a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/xptcstubsdef.inc">xptcall/xptcstubsdef.inc</a>)
+is expanded using platform specific macros to define the stub functions. These
+'.inc' files are checked into cvs. However, they can be regenerated as necessary
+(i.e. to change the number of stubs or to change their specific declaration)
+using the Perl script <a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/genstubs.pl">xptcall/genstubs.pl</a>.
+
+<p>
+
+Here are examples of this implementation for <a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/win32/xptcstubs.cpp">Win32</a>
+and <a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix/xptcstubs_unixish_x86.cpp">Linux x86, NetBSD x86, and FreeBSD</a>.
+Both of these examples use inline assembly language. That is just how I
+decided to do it. You can do it as you choose.
+
+<p>
+
+The Win32 version is somewhat tighter because the __declspec(naked) feature
+allows for very small stubs. However, the __stdcall requires the callee to clean
+up the stack, so it is imperative that the interface information scheme allow
+the code to determine the correct stack pointer fixup for return without fail,
+else the process will crash.
+
+<p>
+
+I opted to use inline assembler for the gcc Linux x86 port. I ended up with
+larger stubs than I would have preferred rather than battle the compiler over
+what would happen to the stack before my asm code began running.
+
+<p>
+
+I believe that the non-assembly parts of these files can be copied and reused
+with minimal (but not zero) platform specific tweaks. Feel free to copy and
+paste as necessary. Please remember that safety and reliability are more
+important than speed optimizations. This code is primarily used to connect XPCOM
+components with JavaScript; function call overhead is a <b>tiny</b> part of the
+time involved.
+
+<p>
+
+I put together
+<a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/test">xptcall/md/test
+</a> as a place to evolve the basic functionality as a port is coming together.
+Not all of the functionality is exercised, but it is a place to get started.
+<a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/tests">xptcall/tests
+</a> has an api level test for <code>NS_InvokeByIndex</code>, but no tests for
+the <i>stubs</i> functionality. Such a test ought to be written, but this has not
+yet been done.
+
+<p>
+
+A full 'test' at this point requires building the client and running the
+XPConnect test called <i>TestXPC</i> in
+<a
+href="http://lxr.mozilla.org/mozilla/source/js/xpconnect/tests">mozilla/js/xpconnect/tests
+</a>.
+
+<p>
+
+Getting these ports done is very important. Please let <a
+href="mailto:jband@netscape.com">me</a> know if you are interested in doing one.
+I'll answer any questions as I get them.
+
+<p>
+
+<a
+href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/status.html">
+Porting Status
+</a>
+
+</blockquote>
+
+<hr>
+<b>Author:</b> <a href="mailto:jband@netscape.com">John Bandhauer &lt;jband@netscape.com&gt;</a><br>
+<b>Last modified:</b> 31 May 1999
+
+</body>
+</html>
diff --git a/xpcom/reflect/xptcall/status.html b/xpcom/reflect/xptcall/status.html
new file mode 100644
index 000000000..65de20596
--- /dev/null
+++ b/xpcom/reflect/xptcall/status.html
@@ -0,0 +1,412 @@
+<!-- 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/. -->
+
+<html>
+<head>
+<title>xptcall Porting Status</title>
+</head>
+<body bgcolor = "white">
+<h2><center>xptcall Porting Status</center></h2>
+
+<h3>What is this?</h3>
+
+This is a status page for the multiplatform porting of xptcall.
+xptcall has a
+<a href="http://www.mozilla.org/scriptable/xptcall-faq.html">FAQ</a>
+and a
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/porting.html">Porting Guide</a>.
+
+<p>
+
+This is being maintained by <a href="mailto:jband@netscape.com">John Bandhauer &lt;jband@netscape.com&gt;</a>.
+Feel free to email me with questions or to volunteer to contribute xptcall code for any platform.
+
+<p>
+
+<a href="mailto:shaver@mozilla.org">Mike Shaver &lt;shaver@mozilla.org&gt;</a>
+is the best contact regarding 'nix (Unix, Linux, Finux, etc.) ports of xptcall.
+
+<h3>Status</h3>
+
+<table BORDER="1">
+<TR align="left" BGCOLOR="yellow">
+<TH>Status</TH>
+<TH>Platform</TH>
+<TH><img src="http://tinderbox.mozilla.org/star.gif">Contributors and <font color="red"><b>?</b></font> Possible Contributors</TH>
+<TH>Notes</TH>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Win32 x86</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:jband@netscape.com">John Bandhauer &lt;jband@netscape.com&gt;</a>
+</TD>
+<TD>
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/win32">win32</a></TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Linux x86</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:jband@netscape.com">John Bandhauer &lt;jband@netscape.com&gt;</a><br>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:drepper@cygnus.com">Ulrich Drepper &lt;drepper@cygnus.com&gt;</a>
+</TD>
+<TD><a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a>
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>FreeBSD and NetBSD x86</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:toshok@hungry.com">Christoph Toshok &lt;toshok@hungry.com&gt;</a>,<BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:jband@netscape.com">John Bandhauer &lt;jband@netscape.com&gt;</a></TD>
+<TD><a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a> (same as Linux 86 code)</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>BSD/OS x86</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:bert_driehuis@nl.compuware.com">Bert Driehuis &lt;bert_driehuis@nl.compuware.com&gt;</a></TD>
+<TD><a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a> (same as Linux 86 code)
+Bert contributed patches that *should* do the right thing for all the unixish-x86
+versions of this code for GCC 2.7 or 2.8 vs. EGCS 1.1. He notes that the vtbl
+scheme is different. He is hoping that others will help test the changes using
+these two compilers on the various platforms where this same code is used.
+<a href="news://news.mozilla.org/372DD257.4248C821%40nl.compuware.com">Bert's details</a>
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Mac PPC</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+ <a href="mailto:rogerl@netscape.com">Roger Lawrence &lt;rogerl@netscape.com&gt;</a>,<BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:beard@netscape.com">Patrick Beard &lt;beard@netscape.com&gt;</a>
+</TD>
+<TD><a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/mac">mac</a> (passing tests and checked in)</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Solaris Sparc</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:rogerl@netscape.com">Roger Lawrence &lt;rogerl@netscape.com&gt;</a>,<BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:mcafee@netscape.com">Chris McAfee &lt;mcafee@netscape.com&gt;</a>
+</TD>
+<TD><a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a> This is checked in and working.</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Solaris Sparc v9 (64bit)</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:pavlov@netscape.com">Stuart Parmenter &lt;pavlov@netscape.com&gt;</a>,<BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:cls@seawood.org">Chris Seawood &lt;cls@seawood.org&gt;</a>
+</TD>
+<TD><a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a> This is checked in and (pavlov claims!) working.</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>OS/2</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:mjf35@cam.ac.uk">John Fairhurst &lt;mjf35@cam.ac.uk&gt;</a></TD>
+<TD>I never heard exactly who did what. But mozilla has been working on OS/2
+for a long time now.
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>OpenVMS Alpha</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:colin@theblakes.com">Colin R. Blake &lt;colin@theblakes.com&gt;</a></TD>
+<TD>
+Colin says this is passing all the tests on OpenVMS Alpha!
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>NT Alpha</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:bob@guiduck.com">bob meader &lt;bob@guiduck.com&gt;</a></TD>
+<TD>
+bob writes:<br>
+Enclosed is xptcall for alpha/nt target..
+<p>
+It is a variation of the IRIS port (only targeted for win32).
+<p>
+Notice the last 2 files (the change to mozilla\xpcom\build\makefile.win and
+mozilla\xpcom\build) are needed because I was unable to figure how to do a
+"declspecexport" from the assembler ASAXP ... if some knows how to do that then
+those last 2 files won't be needed.
+<p>
+I have had someone look over this code at bridge.com (the entry point to
+compaq/gem compiler team) and this code was given the OK. I consider it "done".
+<p>
+This code lives in the files where the name includes 'alpha' in the <a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/win32">win32</a> directory.<BR>
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Linux ARM</TD>
+<TD><img alt="Started" title="Started" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:sh990154@mail.uni-greifswald.de">Stefan Hanske&lt;sh990154@mail.uni-greifswald.de&gt;</a><BR>
+<font color="red"><b>?</b></font>
+<a href="mailto:willy@bofh.ai">Matthew Wilcox &lt;willy@bofh.ai&gt;</a></TD>
+<TD>
+Stefan's code is checked in and he says it is working.
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Linux Sparc</TD>
+<TD>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:anton@progsoc.uts.edu.au">Anton Blanchard &lt;anton@progsoc.uts.edu.au&gt;</a>,
+<BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:rogerl@netscape.com">Roger Lawrence &lt;rogerl@netscape.com&gt;</a>,
+<BR>
+<img alt="Maybe" title="Maybe" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:ehle.3@osu.eduehle.3@osu.edu">Brandon Ehle &lt;ehle.3@osu.edu&gt;</a>
+</TD>
+<TD>
+Anton contributed patches to Roger's Sparc code. Anton says it works and passes the tests!<b>
+(24-Aug-1999) Brandon writes: I've finished testing XPTCALL Sparc Linux on 12 different Sparc machines and it checks out good.
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Linux PPC</TD>
+<TD>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:beard@netscape.com">Patrick Beard &lt;beard@netscape.com&gt;</a><BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:waterson@netscape.com">Chris Waterson &lt;waterson@netscape.com&gt;</a><BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:Franz.Sirl-kernel@lauterbach.com">Franz Sirl &lt;Franz.Sirl-kernel@lauterbach.com&gt;</a><BR>
+<font color="red"><b>?</b></font>
+<a href="mailto:jsproul@condor.fddi.wesleyan.edu">Jason Y. Sproul &lt;jsproul@condor.fddi.wesleyan.edu&gt;</a><BR>
+ <font color="red"><b>?</b></font>
+ <a href="mailto:darkmane@w-link.net">Sean Chitwood &lt;darkmane@w-link.net&gt;</a></TD>
+<TD>
+waterson said: <b>Mozilla runs on Linux/PPC</b>
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Linux Alpha</TD>
+<TD>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:glen.nakamura@usa.net">Glen Nakamura &lt;glen.nakamura@usa.net&gt;</a><BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:morrildl@nycap.rr.com">Dan Morril &lt;morrildl@nycap.rr.com&gt;</a><BR>
+</TD>
+<TD>
+Glen writes:
+<p>
+I am attaching a patch which contains my Linux Alpha xptcall code.
+It passes TestXPTCInvoke and TestXPC on my machine which runs
+kernel 2.2.7, glibc 2.1.1, and egcs 1.1.2. I have not tested it
+with older GNU compilers such as gcc 2.8.x. From looking at the
+Linux x86 code, I gather that the vtable layout is a little different
+for those compilers and the code will need minor modifications
+in order to work properly.
+<p>
+I am not sure how much of the code can be used for OpenVMS Alpha
+and/or Digital UNIX. Currently the code is dependent on the g++
+name mangling convention and a few GNU extensions so I'm not sure
+how useful it will be for the other systems. Hopefully the
+comments in the code are detailed enough to help people attempting
+a port.
+<p>
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>SunOS x86</TD>
+<TD>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:aljones@lbl.gov">Arthur Jones &lt;aljones@lbl.gov&gt;</a><BR>
+<font color="red"><b>?</b></font>
+<a href="mailto:ppokorny@mindspring.com">Philip Pokorny &lt;ppokorny@mindspring.com&gt;</a><BR>
+</TD>
+<TD>
+The word I hear is that this is working and done
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>HP-UX</TD>
+<TD>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:wang@cup.hp.com">Thomas Wang &lt;wang@cup.hp.com&gt;</a><BR>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:mgleeson1@netscape.com">Mike Gleeson &lt;mgleeson1@netscape.com&gt;</a>
+</TD>
+<TD>I hear that this code is checked in and working. Though, there is some
+doubt - see bug
+#<a href="http://bugzilla.mozilla.org/show_bug.cgi?id=17997">17997</a>
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>AIX PPC</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:jdunn@netscape.com">Jim Dunn &lt;jdunn@netscape.com&gt;</a></TD>
+<TD>Philip K. Warren writes: <BR>
+
+We have gone through several releases of AIX without any problems.
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Irix</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:jasonh@m7.engr.sgi.com">Jason Heirtzler &lt;jasonh@m7.engr.sgi.com&gt;</a><BR>
+</TD>
+<TD>Jason has declared this done. Jason is no longer working at SGI and will
+not be maintaining this code. There is some doubt as to whether or not this is
+working for everyone - see bug
+#<a href="http://bugzilla.mozilla.org/show_bug.cgi?id=10061">10061</a>.
+<a href="mailto:shaver@mozilla.org">Mike&nbsp;Shaver&nbsp;&lt;shaver@mozilla.org&gt;</a>
+is the interim maintainer until someone more suitable can be found.
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>BeOS x86</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:duncan@be.com">Duncan Wilcox &lt;duncan@be.com&gt;</a><BR>
+</TD>
+<TD>
+<a href="http://lxr.mozilla.org/mozilla/source/xpcom/reflect/xptcall/md/unix">unix</a> (yet another reuse of the Linux 86 code!)<BR>
+Duncan says this is all working. He did the code for old cfront style 'this' adjustment for others to use too!
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="red"><font color="white"><b>HELP!</b></font></TD>
+<TD>BeOS PPC</TD>
+<TD align="center">-</TD>
+<TD align="center">-</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Compaq Tru64 UNIX (Digital UNIX)</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:streeter@zk3.dec.com">Steve Streeter &lt;streeter@zk3.dec.com&gt;</a><BR>
+</TD>
+<TD>Code passes the tests and is checked in.</TD>
+</TR>
+
+<TR>
+<TD bgcolor="khaki"><font color="black"><b>Working</b></font></TD>
+<TD>Neutrio x86</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:Jerry.Kirk@Nexwarecorp.com">Jerry L. Kirk &lt;Jerry.Kirk@Nexwarecorp.com&gt;</a><BR>
+</TD>
+<TD>
+Patches for xptc*_unixish_x86.cpp checked in. Waiting for verification that this is really finished.
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="khaki"><font color="black"><b>Investigating</b></font></TD>
+<TD>SCO UW7 and OSR5</TD>
+<TD>
+<img alt="Investigating" title="Investigating" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:jkj@sco.com">J. Kean Johnston &lt;jkj@sco.com&gt;</a><BR>
+<img alt="Investigating" title="Investigating" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:evanh@sco.com">Evan Hunt &lt;evanh@sco.com&gt;</a><BR>
+</TD>
+<TD>Recent (Feb-2001) email from jkj@sco.com suggests that work will be occuring soon.</TD>
+</TR>
+
+<TR>
+<TD bgcolor="khaki"><font color="black"><b>Works</b></font></TD>
+<TD>NetBSD/m68k</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:khym@bga.com">Dave Huang &lt;khym@bga.com&gt;</a><BR>
+</TD>
+<TD>
+Dave's changes are in the tree. Waiting for verification that it is really finished.</TD>
+</TR>
+
+<TR>
+<TD bgcolor="khaki"><font color="black"><b>Partially Working</b></font></TD>
+<TD>NetBSD/arm32</TD>
+<TD><img alt="Investigating" title="Investigating" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:mpumford@black-star.demon.co.uk">Mike Pumford &lt;mpumford@black-star.demon.co.uk&gt;</a>
+</TD>
+<TD>Mike writes:<BR>
+I have started porting to the platform based on the code for Linux ARM. The
+InvokeByIndex code works correctly when used with TestXPTCInvoke. I am
+currently working on making TestXPC function correctly.
+<P>
+I am doing the porting work with egcs-1.1.2 on NetBSD 1.4P (NetBSD-current
+snapshot from a couple of days ago).
+</TD>
+</TR>
+
+<TR>
+<TD bgcolor="green"><font color="white"><b>Done</b></font></TD>
+<TD>Linux ia64</TD>
+<TD><img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+HP<br>
+<img alt="Contributed code!" title="Contributed code!" src="http://tinderbox.mozilla.org/star.gif">
+<a href="mailto:drepper@redhat.com">Ulrich Drepper &lt;drepper@redhat.com&gt;</a>
+</TD>
+<TD><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=40950#c15">bug 40950 comment 15</a></TD>
+</TR>
+
+
+<TR>
+<TD bgcolor="red"><font color="white"><b>HELP!</b></font></TD>
+<TD>All others!</TD>
+<TD align="center">-</TD>
+<TD align="center">-</TD>
+</TR>
+
+
+</table>
+
+<p>
+
+<b>Note:</b> I've used the symbol (<font color="red"><b>?</b></font>) to
+indicate people who have expressed an interest in <i>possibly</i> contributing code.
+Just because these people are listed here does not mean that they have commited
+themselves to do the work. If <b>you</b> would like to contribute then let me
+know. Feel free to email these folks and offer to help or find out what's going
+on. We're all in this together.
+
+<p>
+
+<hr>
+<b>Author:</b> <a href="mailto:jband@netscape.com">John Bandhauer &lt;jband@netscape.com&gt;</a><br>
+<b>Last modified:</b> 3 February 2003
+
+</body>
+</html>
diff --git a/xpcom/reflect/xptcall/xptcall.cpp b/xpcom/reflect/xptcall/xptcall.cpp
new file mode 100644
index 000000000..1ea0ac34d
--- /dev/null
+++ b/xpcom/reflect/xptcall/xptcall.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* entry point wrappers. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+#include "nsPrintfCString.h"
+
+using namespace mozilla;
+
+NS_IMETHODIMP
+nsXPTCStubBase::QueryInterface(REFNSIID aIID,
+ void **aInstancePtr)
+{
+ if (aIID.Equals(mEntry->IID())) {
+ NS_ADDREF_THIS();
+ *aInstancePtr = static_cast<nsISupports*>(this);
+ return NS_OK;
+ }
+
+ return mOuter->QueryInterface(aIID, aInstancePtr);
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsXPTCStubBase::AddRef()
+{
+ return mOuter->AddRef();
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsXPTCStubBase::Release()
+{
+ return mOuter->Release();
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_GetXPTCallStub(REFNSIID aIID, nsIXPTCProxy* aOuter,
+ nsISomeInterface* *aResult)
+{
+ if (NS_WARN_IF(!aOuter) || NS_WARN_IF(!aResult))
+ return NS_ERROR_INVALID_ARG;
+
+ XPTInterfaceInfoManager *iim =
+ XPTInterfaceInfoManager::GetSingleton();
+ if (NS_WARN_IF(!iim))
+ return NS_ERROR_NOT_INITIALIZED;
+
+ xptiInterfaceEntry *iie = iim->GetInterfaceEntryForIID(&aIID);
+ if (!iie || !iie->EnsureResolved() || iie->GetBuiltinClassFlag())
+ return NS_ERROR_FAILURE;
+
+ if (iie->GetHasNotXPCOMFlag()) {
+#ifdef DEBUG
+ nsPrintfCString msg("XPTCall will not implement interface %s because of [notxpcom] members.", iie->GetTheName());
+ NS_WARNING(msg.get());
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = new nsXPTCStubBase(aOuter, iie);
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(void)
+NS_DestroyXPTCallStub(nsISomeInterface* aStub)
+{
+ nsXPTCStubBase* stub = static_cast<nsXPTCStubBase*>(aStub);
+ delete(stub);
+}
+
+EXPORT_XPCOM_API(size_t)
+NS_SizeOfIncludingThisXPTCallStub(const nsISomeInterface* aStub,
+ mozilla::MallocSizeOf aMallocSizeOf)
+{
+ // We could cast aStub to nsXPTCStubBase, but that class doesn't seem to own anything,
+ // so just measure the size of the object itself.
+ return aMallocSizeOf(aStub);
+}
diff --git a/xpcom/reflect/xptcall/xptcall.h b/xpcom/reflect/xptcall/xptcall.h
new file mode 100644
index 000000000..1f2367b5d
--- /dev/null
+++ b/xpcom/reflect/xptcall/xptcall.h
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Public declarations for xptcall. */
+
+#ifndef xptcall_h___
+#define xptcall_h___
+
+#include "nscore.h"
+#include "nsISupports.h"
+#include "xpt_struct.h"
+#include "xptinfo.h"
+#include "js/Value.h"
+#include "mozilla/MemoryReporting.h"
+
+struct nsXPTCMiniVariant
+{
+// No ctors or dtors so that we can use arrays of these on the stack
+// with no penalty.
+ union
+ {
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ float f;
+ double d;
+ bool b;
+ char c;
+ char16_t wc;
+ void* p;
+
+ // Types below here are unknown to the assembly implementations, and
+ // therefore _must_ be passed with indirect semantics. We put them in
+ // the union here for type safety, so that we can avoid void* tricks.
+ JS::Value j;
+ } val;
+};
+
+struct nsXPTCVariant : public nsXPTCMiniVariant
+{
+// No ctors or dtors so that we can use arrays of these on the stack
+// with no penalty.
+
+ // inherits 'val' here
+ void* ptr;
+ nsXPTType type;
+ uint8_t flags;
+
+ enum
+ {
+ //
+ // Bitflag definitions
+ //
+
+ // Indicates that ptr (above, and distinct from val.p) is the value that
+ // should be passed on the stack.
+ //
+ // In theory, ptr could point anywhere. But in practice it always points
+ // to &val. So this flag is used to pass 'val' by reference, letting us
+ // avoid the extra allocation we would incur if we were to use val.p.
+ //
+ // Various parts of XPConnect assume that ptr==&val, so we enforce it
+ // explicitly with SetIndirect() and IsIndirect().
+ //
+ // Since ptr always points to &val, the semantics of this flag are kind of
+ // dumb, since the ptr field is unnecessary. But changing them would
+ // require changing dozens of assembly files, so they're likely to stay
+ // the way they are.
+ PTR_IS_DATA = 0x1,
+
+ // Indicates that the value we hold requires some sort of cleanup (memory
+ // deallocation, interface release, JS::Value unrooting, etc). The precise
+ // cleanup that is performed depends on the 'type' field above.
+ // If the value is an array, this flag specifies whether the elements
+ // within the array require cleanup (we always clean up the array itself,
+ // so this flag would be redundant for that purpose).
+ VAL_NEEDS_CLEANUP = 0x2
+ };
+
+ void ClearFlags() {flags = 0;}
+ void SetIndirect() {ptr = &val; flags |= PTR_IS_DATA;}
+ void SetValNeedsCleanup() {flags |= VAL_NEEDS_CLEANUP;}
+
+ bool IsIndirect() const {return 0 != (flags & PTR_IS_DATA);}
+ bool DoesValNeedCleanup() const {return 0 != (flags & VAL_NEEDS_CLEANUP);}
+
+ // Internal use only. Use IsIndirect() instead.
+ bool IsPtrData() const {return 0 != (flags & PTR_IS_DATA);}
+
+ void Init(const nsXPTCMiniVariant& mv, const nsXPTType& t, uint8_t f)
+ {
+ type = t;
+ flags = f;
+
+ if(f & PTR_IS_DATA)
+ {
+ ptr = mv.val.p;
+ val.p = nullptr;
+ }
+ else
+ {
+ ptr = nullptr;
+ val.p = nullptr; // make sure 'val.p' is always initialized
+ switch(t.TagPart()) {
+ case nsXPTType::T_I8: val.i8 = mv.val.i8; break;
+ case nsXPTType::T_I16: val.i16 = mv.val.i16; break;
+ case nsXPTType::T_I32: val.i32 = mv.val.i32; break;
+ case nsXPTType::T_I64: val.i64 = mv.val.i64; break;
+ case nsXPTType::T_U8: val.u8 = mv.val.u8; break;
+ case nsXPTType::T_U16: val.u16 = mv.val.u16; break;
+ case nsXPTType::T_U32: val.u32 = mv.val.u32; break;
+ case nsXPTType::T_U64: val.u64 = mv.val.u64; break;
+ case nsXPTType::T_FLOAT: val.f = mv.val.f; break;
+ case nsXPTType::T_DOUBLE: val.d = mv.val.d; break;
+ case nsXPTType::T_BOOL: val.b = mv.val.b; break;
+ case nsXPTType::T_CHAR: val.c = mv.val.c; break;
+ case nsXPTType::T_WCHAR: val.wc = mv.val.wc; break;
+ case nsXPTType::T_VOID: /* fall through */
+ case nsXPTType::T_IID: /* fall through */
+ case nsXPTType::T_DOMSTRING: /* fall through */
+ case nsXPTType::T_CHAR_STR: /* fall through */
+ case nsXPTType::T_WCHAR_STR: /* fall through */
+ case nsXPTType::T_INTERFACE: /* fall through */
+ case nsXPTType::T_INTERFACE_IS: /* fall through */
+ case nsXPTType::T_ARRAY: /* fall through */
+ case nsXPTType::T_PSTRING_SIZE_IS: /* fall through */
+ case nsXPTType::T_PWSTRING_SIZE_IS: /* fall through */
+ case nsXPTType::T_UTF8STRING: /* fall through */
+ case nsXPTType::T_CSTRING: /* fall through */
+ default: val.p = mv.val.p; break;
+ }
+ }
+ }
+};
+
+class nsIXPTCProxy : public nsISupports
+{
+public:
+ NS_IMETHOD CallMethod(uint16_t aMethodIndex,
+ const XPTMethodDescriptor *aInfo,
+ nsXPTCMiniVariant *aParams) = 0;
+};
+
+/**
+ * This is a typedef to avoid confusion between the canonical
+ * nsISupports* that provides object identity and an interface pointer
+ * for inheriting interfaces that aren't known at compile-time.
+ */
+typedef nsISupports nsISomeInterface;
+
+/**
+ * Get a proxy object to implement the specified interface.
+ *
+ * @param aIID The IID of the interface to implement.
+ * @param aOuter An object to receive method calls from the proxy object.
+ * The stub forwards QueryInterface/AddRef/Release to the
+ * outer object. The proxy object does not hold a reference to
+ * the outer object; it is the caller's responsibility to
+ * ensure that this pointer remains valid until the stub has
+ * been destroyed.
+ * @param aStub Out parameter for the new proxy object. The object is
+ * not addrefed. The object never destroys itself. It must be
+ * explicitly destroyed by calling
+ * NS_DestroyXPTCallStub when it is no longer needed.
+ */
+XPCOM_API(nsresult)
+NS_GetXPTCallStub(REFNSIID aIID, nsIXPTCProxy* aOuter,
+ nsISomeInterface* *aStub);
+
+/**
+ * Destroys an XPTCall stub previously created with NS_GetXPTCallStub.
+ */
+XPCOM_API(void)
+NS_DestroyXPTCallStub(nsISomeInterface* aStub);
+
+/**
+ * Measures the size of an XPTCall stub previously created with NS_GetXPTCallStub.
+ */
+XPCOM_API(size_t)
+NS_SizeOfIncludingThisXPTCallStub(const nsISomeInterface* aStub, mozilla::MallocSizeOf aMallocSizeOf);
+
+XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ uint32_t paramCount, nsXPTCVariant* params);
+
+#endif /* xptcall_h___ */
diff --git a/xpcom/reflect/xptcall/xptcprivate.h b/xpcom/reflect/xptcall/xptcprivate.h
new file mode 100644
index 000000000..7805792e7
--- /dev/null
+++ b/xpcom/reflect/xptcall/xptcprivate.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* All the xptcall private declarations - only include locally. */
+
+#ifndef xptcprivate_h___
+#define xptcprivate_h___
+
+#include "xptcall.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Attributes.h"
+
+class xptiInterfaceEntry;
+
+#if !defined(__ia64) || (!defined(__hpux) && !defined(__linux__) && !defined(__FreeBSD__))
+#define STUB_ENTRY(n) NS_IMETHOD Stub##n() = 0;
+#else
+#define STUB_ENTRY(n) NS_IMETHOD Stub##n(uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t) = 0;
+#endif
+
+#define SENTINEL_ENTRY(n) NS_IMETHOD Sentinel##n() = 0;
+
+class nsIXPTCStubBase : public nsISupports
+{
+public:
+#include "xptcstubsdef.inc"
+};
+
+#undef STUB_ENTRY
+#undef SENTINEL_ENTRY
+
+#if !defined(__ia64) || (!defined(__hpux) && !defined(__linux__) && !defined(__FreeBSD__))
+#define STUB_ENTRY(n) NS_IMETHOD Stub##n() override;
+#else
+#define STUB_ENTRY(n) NS_IMETHOD Stub##n(uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t) override;
+#endif
+
+#define SENTINEL_ENTRY(n) NS_IMETHOD Sentinel##n() override;
+
+class nsXPTCStubBase final : public nsIXPTCStubBase
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+#include "xptcstubsdef.inc"
+
+ nsXPTCStubBase(nsIXPTCProxy* aOuter, xptiInterfaceEntry *aEntry)
+ : mOuter(aOuter), mEntry(aEntry) { MOZ_COUNT_CTOR(nsXPTCStubBase); }
+
+ nsIXPTCProxy* mOuter;
+ xptiInterfaceEntry* mEntry;
+
+ ~nsXPTCStubBase() { MOZ_COUNT_DTOR(nsXPTCStubBase); }
+};
+
+#undef STUB_ENTRY
+#undef SENTINEL_ENTRY
+
+#if defined(__clang__) || defined(__GNUC__)
+#define ATTRIBUTE_USED __attribute__ ((__used__))
+#else
+#define ATTRIBUTE_USED
+#endif
+
+#endif /* xptcprivate_h___ */
diff --git a/xpcom/reflect/xptcall/xptcstubsdecl.inc b/xpcom/reflect/xptcall/xptcstubsdecl.inc
new file mode 100644
index 000000000..91d35fe32
--- /dev/null
+++ b/xpcom/reflect/xptcall/xptcstubsdecl.inc
@@ -0,0 +1,761 @@
+/* generated file - DO NOT EDIT */
+
+/* includes 247 stub entries, and 5 sentinel entries */
+
+/*
+* declarations of normal stubs...
+* 0 is QueryInterface
+* 1 is AddRef
+* 2 is Release
+*/
+#if !defined(__ia64) || (!defined(__hpux) && !defined(__linux__) && !defined(__FreeBSD__))
+NS_IMETHOD Stub3();
+NS_IMETHOD Stub4();
+NS_IMETHOD Stub5();
+NS_IMETHOD Stub6();
+NS_IMETHOD Stub7();
+NS_IMETHOD Stub8();
+NS_IMETHOD Stub9();
+NS_IMETHOD Stub10();
+NS_IMETHOD Stub11();
+NS_IMETHOD Stub12();
+NS_IMETHOD Stub13();
+NS_IMETHOD Stub14();
+NS_IMETHOD Stub15();
+NS_IMETHOD Stub16();
+NS_IMETHOD Stub17();
+NS_IMETHOD Stub18();
+NS_IMETHOD Stub19();
+NS_IMETHOD Stub20();
+NS_IMETHOD Stub21();
+NS_IMETHOD Stub22();
+NS_IMETHOD Stub23();
+NS_IMETHOD Stub24();
+NS_IMETHOD Stub25();
+NS_IMETHOD Stub26();
+NS_IMETHOD Stub27();
+NS_IMETHOD Stub28();
+NS_IMETHOD Stub29();
+NS_IMETHOD Stub30();
+NS_IMETHOD Stub31();
+NS_IMETHOD Stub32();
+NS_IMETHOD Stub33();
+NS_IMETHOD Stub34();
+NS_IMETHOD Stub35();
+NS_IMETHOD Stub36();
+NS_IMETHOD Stub37();
+NS_IMETHOD Stub38();
+NS_IMETHOD Stub39();
+NS_IMETHOD Stub40();
+NS_IMETHOD Stub41();
+NS_IMETHOD Stub42();
+NS_IMETHOD Stub43();
+NS_IMETHOD Stub44();
+NS_IMETHOD Stub45();
+NS_IMETHOD Stub46();
+NS_IMETHOD Stub47();
+NS_IMETHOD Stub48();
+NS_IMETHOD Stub49();
+NS_IMETHOD Stub50();
+NS_IMETHOD Stub51();
+NS_IMETHOD Stub52();
+NS_IMETHOD Stub53();
+NS_IMETHOD Stub54();
+NS_IMETHOD Stub55();
+NS_IMETHOD Stub56();
+NS_IMETHOD Stub57();
+NS_IMETHOD Stub58();
+NS_IMETHOD Stub59();
+NS_IMETHOD Stub60();
+NS_IMETHOD Stub61();
+NS_IMETHOD Stub62();
+NS_IMETHOD Stub63();
+NS_IMETHOD Stub64();
+NS_IMETHOD Stub65();
+NS_IMETHOD Stub66();
+NS_IMETHOD Stub67();
+NS_IMETHOD Stub68();
+NS_IMETHOD Stub69();
+NS_IMETHOD Stub70();
+NS_IMETHOD Stub71();
+NS_IMETHOD Stub72();
+NS_IMETHOD Stub73();
+NS_IMETHOD Stub74();
+NS_IMETHOD Stub75();
+NS_IMETHOD Stub76();
+NS_IMETHOD Stub77();
+NS_IMETHOD Stub78();
+NS_IMETHOD Stub79();
+NS_IMETHOD Stub80();
+NS_IMETHOD Stub81();
+NS_IMETHOD Stub82();
+NS_IMETHOD Stub83();
+NS_IMETHOD Stub84();
+NS_IMETHOD Stub85();
+NS_IMETHOD Stub86();
+NS_IMETHOD Stub87();
+NS_IMETHOD Stub88();
+NS_IMETHOD Stub89();
+NS_IMETHOD Stub90();
+NS_IMETHOD Stub91();
+NS_IMETHOD Stub92();
+NS_IMETHOD Stub93();
+NS_IMETHOD Stub94();
+NS_IMETHOD Stub95();
+NS_IMETHOD Stub96();
+NS_IMETHOD Stub97();
+NS_IMETHOD Stub98();
+NS_IMETHOD Stub99();
+NS_IMETHOD Stub100();
+NS_IMETHOD Stub101();
+NS_IMETHOD Stub102();
+NS_IMETHOD Stub103();
+NS_IMETHOD Stub104();
+NS_IMETHOD Stub105();
+NS_IMETHOD Stub106();
+NS_IMETHOD Stub107();
+NS_IMETHOD Stub108();
+NS_IMETHOD Stub109();
+NS_IMETHOD Stub110();
+NS_IMETHOD Stub111();
+NS_IMETHOD Stub112();
+NS_IMETHOD Stub113();
+NS_IMETHOD Stub114();
+NS_IMETHOD Stub115();
+NS_IMETHOD Stub116();
+NS_IMETHOD Stub117();
+NS_IMETHOD Stub118();
+NS_IMETHOD Stub119();
+NS_IMETHOD Stub120();
+NS_IMETHOD Stub121();
+NS_IMETHOD Stub122();
+NS_IMETHOD Stub123();
+NS_IMETHOD Stub124();
+NS_IMETHOD Stub125();
+NS_IMETHOD Stub126();
+NS_IMETHOD Stub127();
+NS_IMETHOD Stub128();
+NS_IMETHOD Stub129();
+NS_IMETHOD Stub130();
+NS_IMETHOD Stub131();
+NS_IMETHOD Stub132();
+NS_IMETHOD Stub133();
+NS_IMETHOD Stub134();
+NS_IMETHOD Stub135();
+NS_IMETHOD Stub136();
+NS_IMETHOD Stub137();
+NS_IMETHOD Stub138();
+NS_IMETHOD Stub139();
+NS_IMETHOD Stub140();
+NS_IMETHOD Stub141();
+NS_IMETHOD Stub142();
+NS_IMETHOD Stub143();
+NS_IMETHOD Stub144();
+NS_IMETHOD Stub145();
+NS_IMETHOD Stub146();
+NS_IMETHOD Stub147();
+NS_IMETHOD Stub148();
+NS_IMETHOD Stub149();
+NS_IMETHOD Stub150();
+NS_IMETHOD Stub151();
+NS_IMETHOD Stub152();
+NS_IMETHOD Stub153();
+NS_IMETHOD Stub154();
+NS_IMETHOD Stub155();
+NS_IMETHOD Stub156();
+NS_IMETHOD Stub157();
+NS_IMETHOD Stub158();
+NS_IMETHOD Stub159();
+NS_IMETHOD Stub160();
+NS_IMETHOD Stub161();
+NS_IMETHOD Stub162();
+NS_IMETHOD Stub163();
+NS_IMETHOD Stub164();
+NS_IMETHOD Stub165();
+NS_IMETHOD Stub166();
+NS_IMETHOD Stub167();
+NS_IMETHOD Stub168();
+NS_IMETHOD Stub169();
+NS_IMETHOD Stub170();
+NS_IMETHOD Stub171();
+NS_IMETHOD Stub172();
+NS_IMETHOD Stub173();
+NS_IMETHOD Stub174();
+NS_IMETHOD Stub175();
+NS_IMETHOD Stub176();
+NS_IMETHOD Stub177();
+NS_IMETHOD Stub178();
+NS_IMETHOD Stub179();
+NS_IMETHOD Stub180();
+NS_IMETHOD Stub181();
+NS_IMETHOD Stub182();
+NS_IMETHOD Stub183();
+NS_IMETHOD Stub184();
+NS_IMETHOD Stub185();
+NS_IMETHOD Stub186();
+NS_IMETHOD Stub187();
+NS_IMETHOD Stub188();
+NS_IMETHOD Stub189();
+NS_IMETHOD Stub190();
+NS_IMETHOD Stub191();
+NS_IMETHOD Stub192();
+NS_IMETHOD Stub193();
+NS_IMETHOD Stub194();
+NS_IMETHOD Stub195();
+NS_IMETHOD Stub196();
+NS_IMETHOD Stub197();
+NS_IMETHOD Stub198();
+NS_IMETHOD Stub199();
+NS_IMETHOD Stub200();
+NS_IMETHOD Stub201();
+NS_IMETHOD Stub202();
+NS_IMETHOD Stub203();
+NS_IMETHOD Stub204();
+NS_IMETHOD Stub205();
+NS_IMETHOD Stub206();
+NS_IMETHOD Stub207();
+NS_IMETHOD Stub208();
+NS_IMETHOD Stub209();
+NS_IMETHOD Stub210();
+NS_IMETHOD Stub211();
+NS_IMETHOD Stub212();
+NS_IMETHOD Stub213();
+NS_IMETHOD Stub214();
+NS_IMETHOD Stub215();
+NS_IMETHOD Stub216();
+NS_IMETHOD Stub217();
+NS_IMETHOD Stub218();
+NS_IMETHOD Stub219();
+NS_IMETHOD Stub220();
+NS_IMETHOD Stub221();
+NS_IMETHOD Stub222();
+NS_IMETHOD Stub223();
+NS_IMETHOD Stub224();
+NS_IMETHOD Stub225();
+NS_IMETHOD Stub226();
+NS_IMETHOD Stub227();
+NS_IMETHOD Stub228();
+NS_IMETHOD Stub229();
+NS_IMETHOD Stub230();
+NS_IMETHOD Stub231();
+NS_IMETHOD Stub232();
+NS_IMETHOD Stub233();
+NS_IMETHOD Stub234();
+NS_IMETHOD Stub235();
+NS_IMETHOD Stub236();
+NS_IMETHOD Stub237();
+NS_IMETHOD Stub238();
+NS_IMETHOD Stub239();
+NS_IMETHOD Stub240();
+NS_IMETHOD Stub241();
+NS_IMETHOD Stub242();
+NS_IMETHOD Stub243();
+NS_IMETHOD Stub244();
+NS_IMETHOD Stub245();
+NS_IMETHOD Stub246();
+NS_IMETHOD Stub247();
+NS_IMETHOD Stub248();
+NS_IMETHOD Stub249();
+#else
+NS_IMETHOD Stub3(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub4(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub5(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub6(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub7(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub8(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub9(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub10(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub11(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub12(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub13(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub14(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub15(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub16(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub17(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub18(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub19(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub20(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub21(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub22(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub23(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub24(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub25(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub26(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub27(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub28(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub29(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub30(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub31(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub32(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub33(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub34(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub35(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub36(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub37(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub38(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub39(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub40(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub41(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub42(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub43(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub44(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub45(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub46(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub47(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub48(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub49(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub50(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub51(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub52(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub53(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub54(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub55(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub56(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub57(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub58(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub59(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub60(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub61(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub62(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub63(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub64(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub65(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub66(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub67(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub68(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub69(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub70(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub71(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub72(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub73(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub74(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub75(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub76(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub77(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub78(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub79(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub80(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub81(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub82(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub83(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub84(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub85(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub86(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub87(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub88(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub89(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub90(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub91(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub92(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub93(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub94(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub95(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub96(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub97(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub98(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub99(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub100(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub101(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub102(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub103(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub104(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub105(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub106(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub107(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub108(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub109(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub110(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub111(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub112(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub113(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub114(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub115(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub116(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub117(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub118(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub119(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub120(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub121(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub122(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub123(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub124(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub125(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub126(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub127(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub128(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub129(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub130(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub131(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub132(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub133(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub134(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub135(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub136(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub137(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub138(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub139(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub140(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub141(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub142(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub143(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub144(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub145(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub146(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub147(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub148(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub149(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub150(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub151(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub152(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub153(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub154(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub155(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub156(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub157(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub158(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub159(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub160(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub161(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub162(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub163(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub164(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub165(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub166(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub167(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub168(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub169(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub170(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub171(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub172(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub173(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub174(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub175(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub176(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub177(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub178(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub179(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub180(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub181(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub182(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub183(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub184(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub185(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub186(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub187(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub188(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub189(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub190(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub191(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub192(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub193(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub194(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub195(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub196(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub197(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub198(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub199(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub200(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub201(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub202(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub203(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub204(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub205(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub206(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub207(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub208(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub209(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub210(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub211(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub212(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub213(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub214(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub215(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub216(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub217(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub218(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub219(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub220(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub221(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub222(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub223(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub224(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub225(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub226(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub227(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub228(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub229(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub230(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub231(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub232(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub233(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub234(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub235(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub236(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub237(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub238(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub239(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub240(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub241(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub242(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub243(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub244(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub245(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub246(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub247(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub248(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+NS_IMETHOD Stub249(uint64_t,uint64_t,
+ uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t);
+#endif
+
+/* declarations of sentinel stubs */
+NS_IMETHOD Sentinel0();
+NS_IMETHOD Sentinel1();
+NS_IMETHOD Sentinel2();
+NS_IMETHOD Sentinel3();
+NS_IMETHOD Sentinel4();
diff --git a/xpcom/reflect/xptcall/xptcstubsdef.inc b/xpcom/reflect/xptcall/xptcstubsdef.inc
new file mode 100644
index 000000000..2df455406
--- /dev/null
+++ b/xpcom/reflect/xptcall/xptcstubsdef.inc
@@ -0,0 +1,252 @@
+STUB_ENTRY(3)
+STUB_ENTRY(4)
+STUB_ENTRY(5)
+STUB_ENTRY(6)
+STUB_ENTRY(7)
+STUB_ENTRY(8)
+STUB_ENTRY(9)
+STUB_ENTRY(10)
+STUB_ENTRY(11)
+STUB_ENTRY(12)
+STUB_ENTRY(13)
+STUB_ENTRY(14)
+STUB_ENTRY(15)
+STUB_ENTRY(16)
+STUB_ENTRY(17)
+STUB_ENTRY(18)
+STUB_ENTRY(19)
+STUB_ENTRY(20)
+STUB_ENTRY(21)
+STUB_ENTRY(22)
+STUB_ENTRY(23)
+STUB_ENTRY(24)
+STUB_ENTRY(25)
+STUB_ENTRY(26)
+STUB_ENTRY(27)
+STUB_ENTRY(28)
+STUB_ENTRY(29)
+STUB_ENTRY(30)
+STUB_ENTRY(31)
+STUB_ENTRY(32)
+STUB_ENTRY(33)
+STUB_ENTRY(34)
+STUB_ENTRY(35)
+STUB_ENTRY(36)
+STUB_ENTRY(37)
+STUB_ENTRY(38)
+STUB_ENTRY(39)
+STUB_ENTRY(40)
+STUB_ENTRY(41)
+STUB_ENTRY(42)
+STUB_ENTRY(43)
+STUB_ENTRY(44)
+STUB_ENTRY(45)
+STUB_ENTRY(46)
+STUB_ENTRY(47)
+STUB_ENTRY(48)
+STUB_ENTRY(49)
+STUB_ENTRY(50)
+STUB_ENTRY(51)
+STUB_ENTRY(52)
+STUB_ENTRY(53)
+STUB_ENTRY(54)
+STUB_ENTRY(55)
+STUB_ENTRY(56)
+STUB_ENTRY(57)
+STUB_ENTRY(58)
+STUB_ENTRY(59)
+STUB_ENTRY(60)
+STUB_ENTRY(61)
+STUB_ENTRY(62)
+STUB_ENTRY(63)
+STUB_ENTRY(64)
+STUB_ENTRY(65)
+STUB_ENTRY(66)
+STUB_ENTRY(67)
+STUB_ENTRY(68)
+STUB_ENTRY(69)
+STUB_ENTRY(70)
+STUB_ENTRY(71)
+STUB_ENTRY(72)
+STUB_ENTRY(73)
+STUB_ENTRY(74)
+STUB_ENTRY(75)
+STUB_ENTRY(76)
+STUB_ENTRY(77)
+STUB_ENTRY(78)
+STUB_ENTRY(79)
+STUB_ENTRY(80)
+STUB_ENTRY(81)
+STUB_ENTRY(82)
+STUB_ENTRY(83)
+STUB_ENTRY(84)
+STUB_ENTRY(85)
+STUB_ENTRY(86)
+STUB_ENTRY(87)
+STUB_ENTRY(88)
+STUB_ENTRY(89)
+STUB_ENTRY(90)
+STUB_ENTRY(91)
+STUB_ENTRY(92)
+STUB_ENTRY(93)
+STUB_ENTRY(94)
+STUB_ENTRY(95)
+STUB_ENTRY(96)
+STUB_ENTRY(97)
+STUB_ENTRY(98)
+STUB_ENTRY(99)
+STUB_ENTRY(100)
+STUB_ENTRY(101)
+STUB_ENTRY(102)
+STUB_ENTRY(103)
+STUB_ENTRY(104)
+STUB_ENTRY(105)
+STUB_ENTRY(106)
+STUB_ENTRY(107)
+STUB_ENTRY(108)
+STUB_ENTRY(109)
+STUB_ENTRY(110)
+STUB_ENTRY(111)
+STUB_ENTRY(112)
+STUB_ENTRY(113)
+STUB_ENTRY(114)
+STUB_ENTRY(115)
+STUB_ENTRY(116)
+STUB_ENTRY(117)
+STUB_ENTRY(118)
+STUB_ENTRY(119)
+STUB_ENTRY(120)
+STUB_ENTRY(121)
+STUB_ENTRY(122)
+STUB_ENTRY(123)
+STUB_ENTRY(124)
+STUB_ENTRY(125)
+STUB_ENTRY(126)
+STUB_ENTRY(127)
+STUB_ENTRY(128)
+STUB_ENTRY(129)
+STUB_ENTRY(130)
+STUB_ENTRY(131)
+STUB_ENTRY(132)
+STUB_ENTRY(133)
+STUB_ENTRY(134)
+STUB_ENTRY(135)
+STUB_ENTRY(136)
+STUB_ENTRY(137)
+STUB_ENTRY(138)
+STUB_ENTRY(139)
+STUB_ENTRY(140)
+STUB_ENTRY(141)
+STUB_ENTRY(142)
+STUB_ENTRY(143)
+STUB_ENTRY(144)
+STUB_ENTRY(145)
+STUB_ENTRY(146)
+STUB_ENTRY(147)
+STUB_ENTRY(148)
+STUB_ENTRY(149)
+STUB_ENTRY(150)
+STUB_ENTRY(151)
+STUB_ENTRY(152)
+STUB_ENTRY(153)
+STUB_ENTRY(154)
+STUB_ENTRY(155)
+STUB_ENTRY(156)
+STUB_ENTRY(157)
+STUB_ENTRY(158)
+STUB_ENTRY(159)
+STUB_ENTRY(160)
+STUB_ENTRY(161)
+STUB_ENTRY(162)
+STUB_ENTRY(163)
+STUB_ENTRY(164)
+STUB_ENTRY(165)
+STUB_ENTRY(166)
+STUB_ENTRY(167)
+STUB_ENTRY(168)
+STUB_ENTRY(169)
+STUB_ENTRY(170)
+STUB_ENTRY(171)
+STUB_ENTRY(172)
+STUB_ENTRY(173)
+STUB_ENTRY(174)
+STUB_ENTRY(175)
+STUB_ENTRY(176)
+STUB_ENTRY(177)
+STUB_ENTRY(178)
+STUB_ENTRY(179)
+STUB_ENTRY(180)
+STUB_ENTRY(181)
+STUB_ENTRY(182)
+STUB_ENTRY(183)
+STUB_ENTRY(184)
+STUB_ENTRY(185)
+STUB_ENTRY(186)
+STUB_ENTRY(187)
+STUB_ENTRY(188)
+STUB_ENTRY(189)
+STUB_ENTRY(190)
+STUB_ENTRY(191)
+STUB_ENTRY(192)
+STUB_ENTRY(193)
+STUB_ENTRY(194)
+STUB_ENTRY(195)
+STUB_ENTRY(196)
+STUB_ENTRY(197)
+STUB_ENTRY(198)
+STUB_ENTRY(199)
+STUB_ENTRY(200)
+STUB_ENTRY(201)
+STUB_ENTRY(202)
+STUB_ENTRY(203)
+STUB_ENTRY(204)
+STUB_ENTRY(205)
+STUB_ENTRY(206)
+STUB_ENTRY(207)
+STUB_ENTRY(208)
+STUB_ENTRY(209)
+STUB_ENTRY(210)
+STUB_ENTRY(211)
+STUB_ENTRY(212)
+STUB_ENTRY(213)
+STUB_ENTRY(214)
+STUB_ENTRY(215)
+STUB_ENTRY(216)
+STUB_ENTRY(217)
+STUB_ENTRY(218)
+STUB_ENTRY(219)
+STUB_ENTRY(220)
+STUB_ENTRY(221)
+STUB_ENTRY(222)
+STUB_ENTRY(223)
+STUB_ENTRY(224)
+STUB_ENTRY(225)
+STUB_ENTRY(226)
+STUB_ENTRY(227)
+STUB_ENTRY(228)
+STUB_ENTRY(229)
+STUB_ENTRY(230)
+STUB_ENTRY(231)
+STUB_ENTRY(232)
+STUB_ENTRY(233)
+STUB_ENTRY(234)
+STUB_ENTRY(235)
+STUB_ENTRY(236)
+STUB_ENTRY(237)
+STUB_ENTRY(238)
+STUB_ENTRY(239)
+STUB_ENTRY(240)
+STUB_ENTRY(241)
+STUB_ENTRY(242)
+STUB_ENTRY(243)
+STUB_ENTRY(244)
+STUB_ENTRY(245)
+STUB_ENTRY(246)
+STUB_ENTRY(247)
+STUB_ENTRY(248)
+STUB_ENTRY(249)
+SENTINEL_ENTRY(0)
+SENTINEL_ENTRY(1)
+SENTINEL_ENTRY(2)
+SENTINEL_ENTRY(3)
+SENTINEL_ENTRY(4)
diff --git a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
new file mode 100644
index 000000000..14e719f6c
--- /dev/null
+++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
@@ -0,0 +1,700 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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 "ShimInterfaceInfo.h"
+
+#include "nsIBrowserBoxObject.h"
+#include "nsIContainerBoxObject.h"
+#include "nsIDOMAnimationEvent.h"
+#include "nsIDOMAttr.h"
+#include "nsIDOMBeforeUnloadEvent.h"
+#include "nsIDOMCanvasRenderingContext2D.h"
+#include "nsIDOMCDATASection.h"
+#include "nsIDOMCharacterData.h"
+#include "nsIDOMClientRect.h"
+#include "nsIDOMClientRectList.h"
+#include "nsIDOMClipboardEvent.h"
+#include "nsIDOMCommandEvent.h"
+#include "nsIDOMComment.h"
+#include "nsIDOMCSSPrimitiveValue.h"
+#include "nsIDOMCSSStyleDeclaration.h"
+#include "nsIDOMCSSStyleSheet.h"
+#include "nsIDOMCSSValue.h"
+#include "nsIDOMCSSValueList.h"
+#include "nsIDOMCustomEvent.h"
+#include "nsIDOMDataContainerEvent.h"
+#ifdef MOZ_WEBRTC
+#include "nsIDOMDataChannel.h"
+#endif
+#include "nsIDOMDataTransfer.h"
+#include "nsIDOMDOMCursor.h"
+#include "nsIDOMDOMException.h"
+#include "nsIDOMDOMRequest.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMDocumentFragment.h"
+#include "nsIDOMDocumentType.h"
+#include "nsIDOMDocumentXBL.h"
+#include "nsIDOMDragEvent.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMFileList.h"
+#include "nsIDOMFocusEvent.h"
+#include "nsIDOMFormData.h"
+#include "nsIDOMGeoPositionError.h"
+#include "nsIDOMHistory.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsIDOMHTMLAppletElement.h"
+#include "nsIDOMHTMLAreaElement.h"
+#include "nsIDOMHTMLBaseElement.h"
+#include "nsIDOMHTMLBodyElement.h"
+#include "nsIDOMHTMLButtonElement.h"
+#include "nsIDOMHTMLCanvasElement.h"
+#include "nsIDOMHTMLCollection.h"
+#include "nsIDOMHTMLDirectoryElement.h"
+#include "nsIDOMHTMLDocument.h"
+#include "nsIDOMHTMLElement.h"
+#include "nsIDOMHTMLEmbedElement.h"
+#include "nsIDOMHTMLFieldSetElement.h"
+#include "nsIDOMHTMLFormElement.h"
+#include "nsIDOMHTMLFrameElement.h"
+#include "nsIDOMHTMLFrameSetElement.h"
+#include "nsIDOMHTMLHRElement.h"
+#include "nsIDOMHTMLHeadElement.h"
+#include "nsIDOMHTMLHtmlElement.h"
+#include "nsIDOMHTMLIFrameElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsIDOMHTMLLIElement.h"
+#include "nsIDOMHTMLLabelElement.h"
+#include "nsIDOMHTMLLinkElement.h"
+#include "nsIDOMHTMLMapElement.h"
+#include "nsIDOMHTMLMediaElement.h"
+#include "nsIDOMHTMLMenuElement.h"
+#include "nsIDOMHTMLMenuItemElement.h"
+#include "nsIDOMHTMLMetaElement.h"
+#include "nsIDOMHTMLOListElement.h"
+#include "nsIDOMHTMLObjectElement.h"
+#include "nsIDOMHTMLOptGroupElement.h"
+#include "nsIDOMHTMLOptionElement.h"
+#include "nsIDOMHTMLOptionsCollection.h"
+#include "nsIDOMHTMLParagraphElement.h"
+#include "nsIDOMHTMLPreElement.h"
+#include "nsIDOMHTMLQuoteElement.h"
+#include "nsIDOMHTMLScriptElement.h"
+#include "nsIDOMHTMLSelectElement.h"
+#include "nsIDOMHTMLSourceElement.h"
+#include "nsIDOMHTMLStyleElement.h"
+#include "nsIDOMHTMLTableCellElement.h"
+#include "nsIDOMHTMLTextAreaElement.h"
+#include "nsIDOMHTMLUListElement.h"
+#include "nsIDOMKeyEvent.h"
+#include "nsIDOMMediaList.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsIDOMMouseScrollEvent.h"
+#include "nsIDOMMutationEvent.h"
+#include "nsIDOMMozNamedAttrMap.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMNodeIterator.h"
+#include "nsIDOMNotifyPaintEvent.h"
+#include "nsIDOMNSEvent.h"
+#include "nsIDOMOfflineResourceList.h"
+#include "nsIDOMPaintRequest.h"
+#include "nsIDOMParser.h"
+#include "nsIDOMProcessingInstruction.h"
+#include "nsIDOMRange.h"
+#include "nsIDOMRect.h"
+#include "nsIDOMScreen.h"
+#include "nsIDOMScrollAreaEvent.h"
+#include "nsIDOMSerializer.h"
+#include "nsIDOMSimpleGestureEvent.h"
+#include "nsIDOMStyleSheet.h"
+#include "nsIDOMStyleSheetList.h"
+#include "nsIDOMSVGElement.h"
+#include "nsIDOMSVGLength.h"
+#include "nsIDOMText.h"
+#include "nsIDOMTimeEvent.h"
+#include "nsIDOMTimeRanges.h"
+#include "nsIDOMTransitionEvent.h"
+#include "nsIDOMTreeWalker.h"
+#include "nsIDOMUIEvent.h"
+#include "nsIDOMValidityState.h"
+#include "nsIDOMWheelEvent.h"
+#include "nsIDOMXMLDocument.h"
+#include "nsIDOMXPathEvaluator.h"
+#include "nsIDOMXPathResult.h"
+#include "nsIDOMXULCommandEvent.h"
+#include "nsIDOMXULDocument.h"
+#include "nsIDOMXULElement.h"
+#include "nsIListBoxObject.h"
+#include "nsIMenuBoxObject.h"
+#include "nsIScrollBoxObject.h"
+#include "nsISelection.h"
+#include "nsITreeBoxObject.h"
+#include "nsIXMLHttpRequest.h"
+
+#include "mozilla/dom/AnimationEventBinding.h"
+#include "mozilla/dom/AttrBinding.h"
+#include "mozilla/dom/BeforeUnloadEventBinding.h"
+#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
+#include "mozilla/dom/CDATASectionBinding.h"
+#include "mozilla/dom/CharacterDataBinding.h"
+#include "mozilla/dom/DOMRectBinding.h"
+#include "mozilla/dom/DOMRectListBinding.h"
+#include "mozilla/dom/ClipboardEventBinding.h"
+#include "mozilla/dom/CommandEventBinding.h"
+#include "mozilla/dom/CommentBinding.h"
+#include "mozilla/dom/ContainerBoxObjectBinding.h"
+#include "mozilla/dom/CSSPrimitiveValueBinding.h"
+#include "mozilla/dom/CSSStyleDeclarationBinding.h"
+#include "mozilla/dom/CSSStyleSheetBinding.h"
+#include "mozilla/dom/CSSValueBinding.h"
+#include "mozilla/dom/CSSValueListBinding.h"
+#include "mozilla/dom/CustomEventBinding.h"
+#ifdef MOZ_WEBRTC
+#include "mozilla/dom/DataChannelBinding.h"
+#endif
+#include "mozilla/dom/DataContainerEventBinding.h"
+#include "mozilla/dom/DataTransferBinding.h"
+#include "mozilla/dom/DOMCursorBinding.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/dom/DOMParserBinding.h"
+#include "mozilla/dom/DOMRequestBinding.h"
+#include "mozilla/dom/DocumentBinding.h"
+#include "mozilla/dom/DocumentFragmentBinding.h"
+#include "mozilla/dom/DocumentTypeBinding.h"
+#include "mozilla/dom/DocumentBinding.h"
+#include "mozilla/dom/DragEventBinding.h"
+#include "mozilla/dom/ElementBinding.h"
+#include "mozilla/dom/EventBinding.h"
+#include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/FileListBinding.h"
+#include "mozilla/dom/FocusEventBinding.h"
+#include "mozilla/dom/FormDataBinding.h"
+#include "mozilla/dom/HistoryBinding.h"
+#include "mozilla/dom/HTMLAnchorElementBinding.h"
+#include "mozilla/dom/HTMLAppletElementBinding.h"
+#include "mozilla/dom/HTMLAreaElementBinding.h"
+#include "mozilla/dom/HTMLBaseElementBinding.h"
+#include "mozilla/dom/HTMLBodyElementBinding.h"
+#include "mozilla/dom/HTMLButtonElementBinding.h"
+#include "mozilla/dom/HTMLCanvasElementBinding.h"
+#include "mozilla/dom/HTMLCollectionBinding.h"
+#include "mozilla/dom/HTMLDirectoryElementBinding.h"
+#include "mozilla/dom/HTMLDocumentBinding.h"
+#include "mozilla/dom/HTMLElementBinding.h"
+#include "mozilla/dom/HTMLEmbedElementBinding.h"
+#include "mozilla/dom/HTMLFieldSetElementBinding.h"
+#include "mozilla/dom/HTMLFormElementBinding.h"
+#include "mozilla/dom/HTMLFrameElementBinding.h"
+#include "mozilla/dom/HTMLFrameSetElementBinding.h"
+#include "mozilla/dom/HTMLHRElementBinding.h"
+#include "mozilla/dom/HTMLHeadElementBinding.h"
+#include "mozilla/dom/HTMLHtmlElementBinding.h"
+#include "mozilla/dom/HTMLIFrameElementBinding.h"
+#include "mozilla/dom/HTMLImageElementBinding.h"
+#include "mozilla/dom/HTMLInputElementBinding.h"
+#include "mozilla/dom/HTMLLIElementBinding.h"
+#include "mozilla/dom/HTMLLabelElementBinding.h"
+#include "mozilla/dom/HTMLLinkElementBinding.h"
+#include "mozilla/dom/HTMLMapElementBinding.h"
+#include "mozilla/dom/HTMLMediaElementBinding.h"
+#include "mozilla/dom/HTMLMenuElementBinding.h"
+#include "mozilla/dom/HTMLMenuItemElementBinding.h"
+#include "mozilla/dom/HTMLMetaElementBinding.h"
+#include "mozilla/dom/HTMLOListElementBinding.h"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "mozilla/dom/HTMLOptGroupElementBinding.h"
+#include "mozilla/dom/HTMLOptionElementBinding.h"
+#include "mozilla/dom/HTMLOptionsCollectionBinding.h"
+#include "mozilla/dom/HTMLParagraphElementBinding.h"
+#include "mozilla/dom/HTMLPreElementBinding.h"
+#include "mozilla/dom/HTMLQuoteElementBinding.h"
+#include "mozilla/dom/HTMLScriptElementBinding.h"
+#include "mozilla/dom/HTMLSelectElementBinding.h"
+#include "mozilla/dom/HTMLSourceElementBinding.h"
+#include "mozilla/dom/HTMLStyleElementBinding.h"
+#include "mozilla/dom/HTMLTableCellElementBinding.h"
+#include "mozilla/dom/HTMLTextAreaElementBinding.h"
+#include "mozilla/dom/HTMLUListElementBinding.h"
+#include "mozilla/dom/KeyEventBinding.h"
+#include "mozilla/dom/ListBoxObjectBinding.h"
+#include "mozilla/dom/MediaListBinding.h"
+#include "mozilla/dom/MessageEventBinding.h"
+#include "mozilla/dom/MenuBoxObjectBinding.h"
+#include "mozilla/dom/MouseEventBinding.h"
+#include "mozilla/dom/MouseScrollEventBinding.h"
+#include "mozilla/dom/MutationEventBinding.h"
+#include "mozilla/dom/NamedNodeMapBinding.h"
+#include "mozilla/dom/NodeIteratorBinding.h"
+#include "mozilla/dom/NodeBinding.h"
+#include "mozilla/dom/NotifyPaintEventBinding.h"
+#include "mozilla/dom/EventBinding.h"
+#include "mozilla/dom/OfflineResourceListBinding.h"
+#include "mozilla/dom/PaintRequestBinding.h"
+#include "mozilla/dom/PositionErrorBinding.h"
+#include "mozilla/dom/ProcessingInstructionBinding.h"
+#include "mozilla/dom/RangeBinding.h"
+#include "mozilla/dom/RectBinding.h"
+#include "mozilla/dom/ScreenBinding.h"
+#include "mozilla/dom/ScrollBoxObjectBinding.h"
+#include "mozilla/dom/SelectionBinding.h"
+#include "mozilla/dom/ScrollAreaEventBinding.h"
+#include "mozilla/dom/SimpleGestureEventBinding.h"
+#include "mozilla/dom/StorageEventBinding.h"
+#include "mozilla/dom/StyleSheetBinding.h"
+#include "mozilla/dom/StyleSheetListBinding.h"
+#include "mozilla/dom/SVGElementBinding.h"
+#include "mozilla/dom/SVGLengthBinding.h"
+#include "mozilla/dom/TextBinding.h"
+#include "mozilla/dom/TimeEventBinding.h"
+#include "mozilla/dom/TimeRangesBinding.h"
+#include "mozilla/dom/TransitionEventBinding.h"
+#include "mozilla/dom/TreeBoxObjectBinding.h"
+#include "mozilla/dom/TreeWalkerBinding.h"
+#include "mozilla/dom/UIEventBinding.h"
+#include "mozilla/dom/ValidityStateBinding.h"
+#include "mozilla/dom/WheelEventBinding.h"
+#include "mozilla/dom/XMLDocumentBinding.h"
+#include "mozilla/dom/XMLHttpRequestEventTargetBinding.h"
+#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
+#include "mozilla/dom/XMLSerializerBinding.h"
+#include "mozilla/dom/XPathEvaluatorBinding.h"
+#include "mozilla/dom/XPathResultBinding.h"
+#include "mozilla/dom/XULCommandEventBinding.h"
+#include "mozilla/dom/XULDocumentBinding.h"
+#include "mozilla/dom/XULElementBinding.h"
+
+using namespace mozilla;
+
+struct ComponentsInterfaceShimEntry {
+ constexpr
+ ComponentsInterfaceShimEntry(const char* aName, const nsIID& aIID,
+ const dom::NativePropertyHooks* aNativePropHooks)
+ : geckoName(aName), iid(aIID), nativePropHooks(aNativePropHooks) {}
+
+ const char *geckoName;
+ const nsIID& iid;
+ const dom::NativePropertyHooks* nativePropHooks;
+};
+
+#define DEFINE_SHIM_WITH_CUSTOM_INTERFACE(geckoName, domName) \
+ { #geckoName, NS_GET_IID(geckoName), \
+ mozilla::dom::domName ## Binding::sNativePropertyHooks }
+#define DEFINE_SHIM(name) \
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOM ## name, name)
+
+/**
+ * These shim entries allow us to make old XPIDL interfaces implementing DOM
+ * APIs as non-scriptable in order to save some runtime memory on Firefox OS,
+ * without breaking the entries under Components.interfaces which might both
+ * be used by our code and add-ons. Specifically, the shim entries provide
+ * the following:
+ *
+ * * Components.interfaces.nsIFoo entries. These entries basically work
+ * almost exactly as the usual ones that you would get through the
+ * XPIDL machinery. Specifically, they have the right name, they reflect
+ * the right IID, and they will work properly when passed to QueryInterface.
+ *
+ * * Components.interfaces.nsIFoo.CONSTANT values. These entries will have
+ * the right name and the right value for most integer types. Note that
+ * support for non-numerical constants is untested and will probably not
+ * work out of the box.
+ *
+ * FAQ:
+ * * When should I add an entry to the list here?
+ * Only if you're making an XPIDL interfaces which has a corresponding
+ * WebIDL interface non-scriptable.
+ * * When should I remove an entry from this list?
+ * If you are completely removing an XPIDL interface from the code base. If
+ * you forget to do so, the compiler will remind you.
+ * * How should I add an entry to the list here?
+ * First, make sure that the XPIDL interface in question is non-scriptable
+ * and also has a corresponding WebIDL interface. Then, add two include
+ * entries above, one for the XPIDL interface and one for the WebIDL
+ * interface, and add a shim entry below. If the name of the XPIDL
+ * interface only has an "nsIDOM" prefix prepended to the WebIDL name, you
+ * can use the DEFINE_SHIM macro and pass in the name of the WebIDL
+ * interface. Otherwise, use DEFINE_SHIM_WITH_CUSTOM_INTERFACE.
+ */
+
+const ComponentsInterfaceShimEntry kComponentsInterfaceShimMap[] =
+{
+ DEFINE_SHIM(AnimationEvent),
+ DEFINE_SHIM(Attr),
+ DEFINE_SHIM(BeforeUnloadEvent),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIBrowserBoxObject, ContainerBoxObject),
+ DEFINE_SHIM(CanvasRenderingContext2D),
+ DEFINE_SHIM(CDATASection),
+ DEFINE_SHIM(CharacterData),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMClientRect, DOMRectReadOnly),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMClientRectList, DOMRectList),
+ DEFINE_SHIM(ClipboardEvent),
+ DEFINE_SHIM(CommandEvent),
+ DEFINE_SHIM(Comment),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIContainerBoxObject, ContainerBoxObject),
+ DEFINE_SHIM(CSSPrimitiveValue),
+ DEFINE_SHIM(CSSStyleDeclaration),
+ DEFINE_SHIM(CSSStyleSheet),
+ DEFINE_SHIM(CSSValue),
+ DEFINE_SHIM(CSSValueList),
+ DEFINE_SHIM(CustomEvent),
+#ifdef MOZ_WEBRTC
+ DEFINE_SHIM(DataChannel),
+#endif
+ DEFINE_SHIM(DataContainerEvent),
+ DEFINE_SHIM(DataTransfer),
+ DEFINE_SHIM(DOMCursor),
+ DEFINE_SHIM(DOMException),
+ DEFINE_SHIM(DOMRequest),
+ DEFINE_SHIM(Document),
+ DEFINE_SHIM(DocumentFragment),
+ DEFINE_SHIM(DocumentType),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMDocumentXBL, Document),
+ DEFINE_SHIM(DragEvent),
+ DEFINE_SHIM(Element),
+ DEFINE_SHIM(Event),
+ DEFINE_SHIM(EventTarget),
+ DEFINE_SHIM(FileList),
+ DEFINE_SHIM(FocusEvent),
+ DEFINE_SHIM(FormData),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMGeoPositionError, PositionError),
+ DEFINE_SHIM(History),
+ DEFINE_SHIM(HTMLAnchorElement),
+ DEFINE_SHIM(HTMLAppletElement),
+ DEFINE_SHIM(HTMLAreaElement),
+ DEFINE_SHIM(HTMLBaseElement),
+ DEFINE_SHIM(HTMLBodyElement),
+ DEFINE_SHIM(HTMLButtonElement),
+ DEFINE_SHIM(HTMLCanvasElement),
+ DEFINE_SHIM(HTMLCollection),
+ DEFINE_SHIM(HTMLDirectoryElement),
+ DEFINE_SHIM(HTMLDocument),
+ DEFINE_SHIM(HTMLElement),
+ DEFINE_SHIM(HTMLEmbedElement),
+ DEFINE_SHIM(HTMLFieldSetElement),
+ DEFINE_SHIM(HTMLFormElement),
+ DEFINE_SHIM(HTMLFrameElement),
+ DEFINE_SHIM(HTMLFrameSetElement),
+ DEFINE_SHIM(HTMLHRElement),
+ DEFINE_SHIM(HTMLHeadElement),
+ DEFINE_SHIM(HTMLHtmlElement),
+ DEFINE_SHIM(HTMLIFrameElement),
+ DEFINE_SHIM(HTMLImageElement),
+ DEFINE_SHIM(HTMLInputElement),
+ DEFINE_SHIM(HTMLLIElement),
+ DEFINE_SHIM(HTMLLabelElement),
+ DEFINE_SHIM(HTMLLinkElement),
+ DEFINE_SHIM(HTMLMapElement),
+ DEFINE_SHIM(HTMLMediaElement),
+ DEFINE_SHIM(HTMLMenuElement),
+ DEFINE_SHIM(HTMLMenuItemElement),
+ DEFINE_SHIM(HTMLMetaElement),
+ DEFINE_SHIM(HTMLOListElement),
+ DEFINE_SHIM(HTMLObjectElement),
+ DEFINE_SHIM(HTMLOptGroupElement),
+ DEFINE_SHIM(HTMLOptionElement),
+ DEFINE_SHIM(HTMLOptionsCollection),
+ DEFINE_SHIM(HTMLParagraphElement),
+ DEFINE_SHIM(HTMLPreElement),
+ DEFINE_SHIM(HTMLQuoteElement),
+ DEFINE_SHIM(HTMLScriptElement),
+ DEFINE_SHIM(HTMLSelectElement),
+ DEFINE_SHIM(HTMLSourceElement),
+ DEFINE_SHIM(HTMLStyleElement),
+ DEFINE_SHIM(HTMLTableCellElement),
+ DEFINE_SHIM(HTMLTextAreaElement),
+ DEFINE_SHIM(HTMLUListElement),
+ DEFINE_SHIM(KeyEvent),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIListBoxObject, ListBoxObject),
+ DEFINE_SHIM(MediaList),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIMenuBoxObject, MenuBoxObject),
+ DEFINE_SHIM(MouseEvent),
+ DEFINE_SHIM(MouseScrollEvent),
+ DEFINE_SHIM(MutationEvent),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMMozNamedAttrMap, NamedNodeMap),
+ DEFINE_SHIM(NodeIterator),
+ DEFINE_SHIM(Node),
+ DEFINE_SHIM(NotifyPaintEvent),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMNSEvent, Event),
+ DEFINE_SHIM(OfflineResourceList),
+ DEFINE_SHIM(PaintRequest),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMParser, DOMParser),
+ DEFINE_SHIM(ProcessingInstruction),
+ DEFINE_SHIM(Range),
+ DEFINE_SHIM(Rect),
+ DEFINE_SHIM(Screen),
+ DEFINE_SHIM(ScrollAreaEvent),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIScrollBoxObject, ScrollBoxObject),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMSerializer, XMLSerializer),
+ DEFINE_SHIM(SimpleGestureEvent),
+ DEFINE_SHIM(StyleSheet),
+ DEFINE_SHIM(StyleSheetList),
+ DEFINE_SHIM(SVGElement),
+ DEFINE_SHIM(SVGLength),
+ DEFINE_SHIM(Text),
+ DEFINE_SHIM(TimeEvent),
+ DEFINE_SHIM(TimeRanges),
+ DEFINE_SHIM(TransitionEvent),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsITreeBoxObject, TreeBoxObject),
+ DEFINE_SHIM(TreeWalker),
+ DEFINE_SHIM(UIEvent),
+ DEFINE_SHIM(ValidityState),
+ DEFINE_SHIM(WheelEvent),
+ DEFINE_SHIM(XMLDocument),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIXMLHttpRequestEventTarget, XMLHttpRequestEventTarget),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIXMLHttpRequestUpload, XMLHttpRequestUpload),
+ DEFINE_SHIM(XPathEvaluator),
+ DEFINE_SHIM(XPathResult),
+ DEFINE_SHIM(XULCommandEvent),
+ DEFINE_SHIM(XULDocument),
+ DEFINE_SHIM(XULElement),
+ DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsISelection, Selection),
+};
+
+#undef DEFINE_SHIM
+#undef DEFINE_SHIM_WITH_CUSTOM_INTERFACE
+
+NS_IMPL_ISUPPORTS(ShimInterfaceInfo, nsISupports, nsIInterfaceInfo)
+
+already_AddRefed<ShimInterfaceInfo>
+ShimInterfaceInfo::MaybeConstruct(const char* aName, JSContext* cx)
+{
+ RefPtr<ShimInterfaceInfo> info;
+ for (uint32_t i = 0; i < ArrayLength(kComponentsInterfaceShimMap); ++i) {
+ if (!strcmp(aName, kComponentsInterfaceShimMap[i].geckoName)) {
+ const ComponentsInterfaceShimEntry& shimEntry =
+ kComponentsInterfaceShimMap[i];
+ info = new ShimInterfaceInfo(shimEntry.iid,
+ shimEntry.geckoName,
+ shimEntry.nativePropHooks);
+ break;
+ }
+ }
+ return info.forget();
+}
+
+ShimInterfaceInfo::ShimInterfaceInfo(const nsIID& aIID,
+ const char* aName,
+ const mozilla::dom::NativePropertyHooks* aNativePropHooks)
+ : mIID(aIID)
+ , mName(aName)
+ , mNativePropHooks(aNativePropHooks)
+{
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetName(char** aName)
+{
+ *aName = ToNewCString(mName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetInterfaceIID(nsIID** aIID)
+{
+ *aIID = static_cast<nsIID*> (nsMemory::Clone(&mIID, sizeof(mIID)));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::IsScriptable(bool* aRetVal)
+{
+ // This class should pretend that the interface is scriptable because
+ // that's what nsJSIID assumes.
+ *aRetVal = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::IsBuiltinClass(bool* aRetVal)
+{
+ *aRetVal = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::IsMainProcessScriptableOnly(bool* aRetVal)
+{
+ *aRetVal = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetParent(nsIInterfaceInfo** aParent)
+{
+ *aParent = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetMethodCount(uint16_t* aCount)
+{
+ // Pretend we don't have any methods.
+ *aCount = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetConstantCount(uint16_t* aCount)
+{
+ // We assume that we never have interfaces with more than UINT16_MAX
+ // constants defined on them.
+ uint16_t count = 0;
+
+ // NOTE: The structure of this loop must be kept in sync with the loop
+ // in GetConstant.
+ const mozilla::dom::NativePropertyHooks* propHooks = mNativePropHooks;
+ do {
+ const mozilla::dom::NativeProperties* props[] = {
+ propHooks->mNativeProperties.regular,
+ propHooks->mNativeProperties.chromeOnly
+ };
+ for (size_t i = 0; i < ArrayLength(props); ++i) {
+ auto prop = props[i];
+ if (prop && prop->HasConstants()) {
+ for (auto cs = prop->Constants()->specs; cs->name; ++cs) {
+ // We have found one constant here. We explicitly do not
+ // bother calling isEnabled() here because it's OK to define
+ // potentially extra constants on these shim interfaces.
+ ++count;
+ }
+ }
+ }
+ } while ((propHooks = propHooks->mProtoHooks));
+ *aCount = count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetMethodInfo(uint16_t aIndex, const nsXPTMethodInfo** aInfo)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetMethodInfoForName(const char* aName, uint16_t* aIndex, const nsXPTMethodInfo** aInfo)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetConstant(uint16_t aIndex, JS::MutableHandleValue aConstant,
+ char** aName)
+{
+ // We assume that we never have interfaces with more than UINT16_MAX
+ // constants defined on them.
+ uint16_t index = 0;
+
+ // NOTE: The structure of this loop must be kept in sync with the loop
+ // in GetConstantCount.
+ const mozilla::dom::NativePropertyHooks* propHooks = mNativePropHooks;
+ do {
+ const mozilla::dom::NativeProperties* props[] = {
+ propHooks->mNativeProperties.regular,
+ propHooks->mNativeProperties.chromeOnly
+ };
+ for (size_t i = 0; i < ArrayLength(props); ++i) {
+ auto prop = props[i];
+ if (prop && prop->HasConstants()) {
+ for (auto cs = prop->Constants()->specs; cs->name; ++cs) {
+ // We have found one constant here. We explicitly do not
+ // bother calling isEnabled() here because it's OK to define
+ // potentially extra constants on these shim interfaces.
+ if (index == aIndex) {
+ aConstant.set(cs->value);
+ *aName = ToNewCString(nsDependentCString(cs->name));
+ return NS_OK;
+ }
+ ++index;
+ }
+ }
+ }
+ } while ((propHooks = propHooks->mProtoHooks));
+
+ // aIndex was bigger than the number of constants we have.
+ return NS_ERROR_INVALID_ARG;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetInfoForParam(uint16_t aIndex, const nsXPTParamInfo* aParam, nsIInterfaceInfo** aRetVal)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetIIDForParam(uint16_t aIndex, const nsXPTParamInfo* aParam, nsIID** aRetVal)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetTypeForParam(uint16_t aInex, const nsXPTParamInfo* aParam, uint16_t aDimension, nsXPTType* aRetVal)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetSizeIsArgNumberForParam(uint16_t aInex, const nsXPTParamInfo* aParam, uint16_t aDimension, uint8_t* aRetVal)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetInterfaceIsArgNumberForParam(uint16_t aInex, const nsXPTParamInfo* aParam, uint8_t* aRetVal)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::IsIID(const nsIID* aIID, bool* aRetVal)
+{
+ *aRetVal = mIID.Equals(*aIID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetNameShared(const char** aName)
+{
+ *aName = mName.get();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::GetIIDShared(const nsIID** aIID)
+{
+ *aIID = &mIID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::IsFunction(bool* aRetVal)
+{
+ *aRetVal = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ShimInterfaceInfo::HasAncestor(const nsIID* aIID, bool* aRetVal)
+{
+ *aRetVal = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(nsresult)
+ShimInterfaceInfo::GetIIDForParamNoAlloc(uint16_t aIndex, const nsXPTParamInfo* aInfo, nsIID* aIID)
+{
+ MOZ_ASSERT(false, "This should never be called");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/xpcom/reflect/xptinfo/ShimInterfaceInfo.h b/xpcom/reflect/xptinfo/ShimInterfaceInfo.h
new file mode 100644
index 000000000..868f503a5
--- /dev/null
+++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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 ShimInterfaceInfo_h
+#define ShimInterfaceInfo_h
+
+#include "mozilla/Attributes.h"
+#include "nsIInterfaceInfo.h"
+#include "nsString.h"
+#include "nsID.h"
+#include "nsTArray.h"
+#include "xptinfo.h"
+#include "nsAutoPtr.h"
+#include "js/RootingAPI.h"
+
+namespace mozilla {
+namespace dom {
+struct NativePropertyHooks;
+} // namespace dom
+} // namespace mozilla
+
+class ShimInterfaceInfo final : public nsIInterfaceInfo
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINTERFACEINFO
+
+ // Construct a ShimInterfaceInfo object if we have a shim available for aName.
+ // Otherwise, returns nullptr.
+ static already_AddRefed<ShimInterfaceInfo>
+ MaybeConstruct(const char* aName, JSContext* cx);
+
+private:
+ ShimInterfaceInfo(const nsIID& aIID,
+ const char* aName,
+ const mozilla::dom::NativePropertyHooks* aNativePropHooks);
+
+ ~ShimInterfaceInfo() {}
+
+private:
+ nsIID mIID;
+ nsAutoCString mName;
+ const mozilla::dom::NativePropertyHooks* mNativePropHooks;
+};
+
+#endif
diff --git a/xpcom/reflect/xptinfo/TODO b/xpcom/reflect/xptinfo/TODO
new file mode 100644
index 000000000..50215a4fb
--- /dev/null
+++ b/xpcom/reflect/xptinfo/TODO
@@ -0,0 +1,20 @@
+/* jband - 03/24/00 - */
+
+- DOCS
+- improve error handling
+ - should some errors really be warnings?
+ - should autoreg support additional channel to receive warnings so that
+ an installer can decide whether or not to accept the consequences of
+ leaving the newly installed files in place?
+- verification of interfaces (warnings and/or errors)
+ - verify that repeated interfaces are identical in all ways
+ - verify that interface names are always one-to-one with iids
+- check for truncated xpt files and version problems
+ - http://bugzilla.mozilla.org/show_bug.cgi?id=33193
+- TESTS!
+ - e.g. verify the merge stuff really works for various inputs.
+ - we really need a set of .xpt and .zip files and code that does an array
+ of autoreg and interfaceinof use activitities to test various corners
+ of the system.
+- better autoreg logging
+- use only 32 bits for file size?
diff --git a/xpcom/reflect/xptinfo/XPTInterfaceInfoManager.h b/xpcom/reflect/xptinfo/XPTInterfaceInfoManager.h
new file mode 100644
index 000000000..e72153ad2
--- /dev/null
+++ b/xpcom/reflect/xptinfo/XPTInterfaceInfoManager.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_XPTInterfaceInfoManager_h_
+#define mozilla_XPTInterfaceInfoManager_h_
+
+#include "nsIInterfaceInfoManager.h"
+#include "nsIMemoryReporter.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsDataHashtable.h"
+
+template<typename T> class nsCOMArray;
+class nsIMemoryReporter;
+struct XPTHeader;
+struct XPTInterfaceDirectoryEntry;
+class xptiInterfaceEntry;
+class xptiInterfaceInfo;
+class xptiTypelibGuts;
+
+namespace mozilla {
+
+class XPTInterfaceInfoManager final
+ : public nsIInterfaceInfoManager
+ , public nsIMemoryReporter
+{
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINTERFACEINFOMANAGER
+ NS_DECL_NSIMEMORYREPORTER
+
+public:
+ // GetSingleton() is infallible
+ static XPTInterfaceInfoManager* GetSingleton();
+ static void FreeInterfaceInfoManager();
+
+ void GetScriptableInterfaces(nsCOMArray<nsIInterfaceInfo>& aInterfaces);
+
+ void RegisterBuffer(char *buf, uint32_t length);
+
+ static Mutex& GetResolveLock()
+ {
+ return GetSingleton()->mResolveLock;
+ }
+
+ xptiInterfaceEntry* GetInterfaceEntryForIID(const nsIID *iid);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+private:
+ XPTInterfaceInfoManager();
+ ~XPTInterfaceInfoManager();
+
+ void InitMemoryReporter();
+
+ void RegisterXPTHeader(XPTHeader* aHeader);
+
+ // idx is the index of this interface in the XPTHeader
+ void VerifyAndAddEntryIfNew(XPTInterfaceDirectoryEntry* iface,
+ uint16_t idx,
+ xptiTypelibGuts* typelib);
+
+private:
+
+ class xptiWorkingSet
+ {
+ public:
+ xptiWorkingSet();
+ ~xptiWorkingSet();
+
+ bool IsValid() const;
+
+ void InvalidateInterfaceInfos();
+ void ClearHashTables();
+
+ // utility methods...
+
+ enum {NOT_FOUND = 0xffffffff};
+
+ // Directory stuff...
+
+ uint32_t GetDirectoryCount();
+ nsresult GetCloneOfDirectoryAt(uint32_t i, nsIFile** dir);
+ nsresult GetDirectoryAt(uint32_t i, nsIFile** dir);
+ bool FindDirectory(nsIFile* dir, uint32_t* index);
+ bool FindDirectoryOfFile(nsIFile* file, uint32_t* index);
+ bool DirectoryAtMatchesPersistentDescriptor(uint32_t i, const char* desc);
+
+ private:
+ uint32_t mFileCount;
+ uint32_t mMaxFileCount;
+
+ public:
+ // XXX make these private with accessors
+ // mTableMonitor must be held across:
+ // * any read from or write to mIIDTable or mNameTable
+ // * any writing to the links between an xptiInterfaceEntry
+ // and its xptiInterfaceInfo (mEntry/mInfo)
+ mozilla::ReentrantMonitor mTableReentrantMonitor;
+ nsDataHashtable<nsIDHashKey, xptiInterfaceEntry*> mIIDTable;
+ nsDataHashtable<nsDepCharHashKey, xptiInterfaceEntry*> mNameTable;
+ };
+
+ // XXX xptiInterfaceInfo want's to poke at the working set itself
+ friend class ::xptiInterfaceInfo;
+ friend class ::xptiInterfaceEntry;
+ friend class ::xptiTypelibGuts;
+
+ xptiWorkingSet mWorkingSet;
+ Mutex mResolveLock;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/reflect/xptinfo/moz.build b/xpcom/reflect/xptinfo/moz.build
new file mode 100644
index 000000000..320949782
--- /dev/null
+++ b/xpcom/reflect/xptinfo/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'ShimInterfaceInfo.cpp',
+ 'xptiInterfaceInfo.cpp',
+ 'xptiInterfaceInfoManager.cpp',
+ 'xptiTypelibGuts.cpp',
+ 'xptiWorkingSet.cpp',
+]
+
+XPIDL_SOURCES += [
+ 'nsIInterfaceInfo.idl',
+ 'nsIInterfaceInfoManager.idl',
+]
+
+XPIDL_MODULE = 'xpcom_xpti'
+
+EXPORTS += [
+ 'xptinfo.h',
+]
+
+EXPORTS.mozilla += [
+ 'XPTInterfaceInfoManager.h',
+]
+
+LOCAL_INCLUDES += [
+ '/dom/base',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/xpcom/reflect/xptinfo/nsIInterfaceInfo.idl b/xpcom/reflect/xptinfo/nsIInterfaceInfo.idl
new file mode 100644
index 000000000..8b902d5ee
--- /dev/null
+++ b/xpcom/reflect/xptinfo/nsIInterfaceInfo.idl
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsIInterfaceInfo public declaration. */
+
+
+#include "nsISupports.idl"
+
+// forward declaration of non-XPCOM types
+
+[ptr] native nsXPTMethodInfoPtr(nsXPTMethodInfo);
+[ptr] native nsXPTParamInfoPtr(nsXPTParamInfo);
+ native nsXPTType(nsXPTType);
+
+// We bend the rules to do a [shared] nsIID (but this is never scriptable)
+[ptr] native nsIIDPtrShared(nsIID);
+
+%{C++
+class nsXPTMethodInfo;
+class nsXPTParamInfo;
+class nsXPTType;
+%}
+
+[builtinclass, uuid(3820e663-8e22-4789-b470-56bcf7083f2b)]
+interface nsIInterfaceInfo : nsISupports
+{
+ readonly attribute string name;
+ readonly attribute nsIIDPtr InterfaceIID;
+
+ boolean isScriptable();
+ boolean isBuiltinClass();
+
+ readonly attribute nsIInterfaceInfo parent;
+
+ /**
+ * These include counts for parent (and all ancestors).
+ */
+ readonly attribute uint16_t methodCount;
+ readonly attribute uint16_t constantCount;
+
+ /**
+ * These include methods and constants for parent (and all ancestors).
+ *
+ * These do *not* make copies ***explicit bending of XPCOM rules***.
+ */
+
+ void getMethodInfo(in uint16_t index,
+ [shared, retval] out nsXPTMethodInfoPtr info);
+
+ void getMethodInfoForName(in string methodName, out uint16_t index,
+ [shared, retval] out nsXPTMethodInfoPtr info);
+
+ void getConstant(in uint16_t index,
+ out jsval constant,
+ out string name);
+
+
+ /**
+ * Get the interface information or iid associated with a param of some
+ * method in this interface.
+ */
+
+ nsIInterfaceInfo getInfoForParam(in uint16_t methodIndex,
+ [const] in nsXPTParamInfoPtr param);
+
+ nsIIDPtr getIIDForParam(in uint16_t methodIndex,
+ [const] in nsXPTParamInfoPtr param);
+
+
+ /**
+ * These do *not* make copies ***explicit bending of XPCOM rules***.
+ */
+
+ nsXPTType getTypeForParam(in uint16_t methodIndex,
+ [const] in nsXPTParamInfoPtr param,
+ in uint16_t dimension);
+
+ uint8_t getSizeIsArgNumberForParam(in uint16_t methodIndex,
+ [const] in nsXPTParamInfoPtr param,
+ in uint16_t dimension);
+
+ uint8_t getInterfaceIsArgNumberForParam(in uint16_t methodIndex,
+ [const] in nsXPTParamInfoPtr param);
+
+ boolean isIID(in nsIIDPtr IID);
+
+ void getNameShared([shared,retval] out string name);
+ void getIIDShared([shared,retval] out nsIIDPtrShared iid);
+
+ boolean isFunction();
+
+ boolean hasAncestor(in nsIIDPtr iid);
+
+ [notxpcom] nsresult getIIDForParamNoAlloc(in uint16_t methodIndex,
+ [const] in nsXPTParamInfoPtr param,
+ out nsIID iid);
+
+ boolean isMainProcessScriptableOnly();
+};
diff --git a/xpcom/reflect/xptinfo/nsIInterfaceInfoManager.idl b/xpcom/reflect/xptinfo/nsIInterfaceInfoManager.idl
new file mode 100644
index 000000000..bc63aed49
--- /dev/null
+++ b/xpcom/reflect/xptinfo/nsIInterfaceInfoManager.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsIInterfaceInfoManager public declaration. */
+
+
+#include "nsISupports.idl"
+
+interface nsIInterfaceInfo;
+
+[builtinclass, uuid(1d53d8d9-1d92-428f-b5cc-198b55e897d7)]
+interface nsIInterfaceInfoManager : nsISupports
+{
+ nsIInterfaceInfo getInfoForIID(in nsIIDPtr iid);
+ nsIInterfaceInfo getInfoForName(in string name);
+};
+
+%{C++
+#define NS_INTERFACEINFOMANAGER_SERVICE_CID \
+ { /* 13bef784-f8e0-4f96-85c1-09f9ef4f9a19 */ \
+ 0x13bef784, 0xf8e0, 0x4f96, \
+ {0x85, 0xc1, 0x09, 0xf9, 0xef, 0x4f, 0x9a, 0x19} }
+
+#define NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID \
+ "@mozilla.org/xpti/interfaceinfomanager-service;1"
+%}
diff --git a/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
new file mode 100644
index 000000000..8ea80fda1
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
@@ -0,0 +1,742 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implementation of xptiInterfaceEntry and xptiInterfaceInfo. */
+
+#include "xptiprivate.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+#include "mozilla/PodOperations.h"
+#include "jsapi.h"
+
+using namespace mozilla;
+
+/* static */ xptiInterfaceEntry*
+xptiInterfaceEntry::Create(const char* name, const nsID& iid,
+ XPTInterfaceDescriptor* aDescriptor,
+ xptiTypelibGuts* aTypelib)
+{
+ int namelen = strlen(name);
+ void* place =
+ XPT_CALLOC8(gXPTIStructArena, sizeof(xptiInterfaceEntry) + namelen);
+ if (!place) {
+ return nullptr;
+ }
+ return new (place) xptiInterfaceEntry(name, namelen, iid, aDescriptor,
+ aTypelib);
+}
+
+xptiInterfaceEntry::xptiInterfaceEntry(const char* name,
+ size_t nameLength,
+ const nsID& iid,
+ XPTInterfaceDescriptor* aDescriptor,
+ xptiTypelibGuts* aTypelib)
+ : mIID(iid)
+ , mDescriptor(aDescriptor)
+ , mTypelib(aTypelib)
+ , mParent(nullptr)
+ , mInfo(nullptr)
+ , mMethodBaseIndex(0)
+ , mConstantBaseIndex(0)
+ , mFlags(0)
+{
+ memcpy(mName, name, nameLength);
+ SetResolvedState(PARTIALLY_RESOLVED);
+}
+
+bool
+xptiInterfaceEntry::Resolve()
+{
+ MutexAutoLock lock(XPTInterfaceInfoManager::GetResolveLock());
+ return ResolveLocked();
+}
+
+bool
+xptiInterfaceEntry::ResolveLocked()
+{
+ int resolvedState = GetResolveState();
+
+ if(resolvedState == FULLY_RESOLVED)
+ return true;
+ if(resolvedState == RESOLVE_FAILED)
+ return false;
+
+ NS_ASSERTION(GetResolveState() == PARTIALLY_RESOLVED, "bad state!");
+
+ // Finish out resolution by finding parent and Resolving it so
+ // we can set the info we get from it.
+
+ uint16_t parent_index = mDescriptor->parent_interface;
+
+ if(parent_index)
+ {
+ xptiInterfaceEntry* parent =
+ mTypelib->GetEntryAt(parent_index - 1);
+
+ if(!parent || !parent->EnsureResolvedLocked())
+ {
+ SetResolvedState(RESOLVE_FAILED);
+ return false;
+ }
+
+ mParent = parent;
+ if (parent->GetHasNotXPCOMFlag()) {
+ SetHasNotXPCOMFlag();
+ } else {
+ for (uint16_t idx = 0; idx < mDescriptor->num_methods; ++idx) {
+ nsXPTMethodInfo* method = reinterpret_cast<nsXPTMethodInfo*>(
+ mDescriptor->method_descriptors + idx);
+ if (method->IsNotXPCOM()) {
+ SetHasNotXPCOMFlag();
+ break;
+ }
+ }
+ }
+
+
+ mMethodBaseIndex =
+ parent->mMethodBaseIndex +
+ parent->mDescriptor->num_methods;
+
+ mConstantBaseIndex =
+ parent->mConstantBaseIndex +
+ parent->mDescriptor->num_constants;
+
+ }
+ LOG_RESOLVE(("+ complete resolve of %s\n", mName));
+
+ SetResolvedState(FULLY_RESOLVED);
+ return true;
+}
+
+/**************************************************/
+// These non-virtual methods handle the delegated nsIInterfaceInfo methods.
+
+nsresult
+xptiInterfaceEntry::GetName(char **name)
+{
+ // It is not necessary to Resolve because this info is read from manifest.
+ *name = (char*) nsMemory::Clone(mName, strlen(mName)+1);
+ return *name ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+xptiInterfaceEntry::GetIID(nsIID **iid)
+{
+ // It is not necessary to Resolve because this info is read from manifest.
+ *iid = (nsIID*) nsMemory::Clone(&mIID, sizeof(nsIID));
+ return *iid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+xptiInterfaceEntry::IsScriptable(bool* result)
+{
+ // It is not necessary to Resolve because this info is read from manifest.
+ *result = GetScriptableFlag();
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::IsFunction(bool* result)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ *result = XPT_ID_IS_FUNCTION(mDescriptor->flags);
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetMethodCount(uint16_t* count)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ *count = mMethodBaseIndex +
+ mDescriptor->num_methods;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetConstantCount(uint16_t* count)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(!count)
+ return NS_ERROR_UNEXPECTED;
+
+ *count = mConstantBaseIndex +
+ mDescriptor->num_constants;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetMethodInfo(uint16_t index, const nsXPTMethodInfo** info)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(index < mMethodBaseIndex)
+ return mParent->GetMethodInfo(index, info);
+
+ if(index >= mMethodBaseIndex +
+ mDescriptor->num_methods)
+ {
+ NS_ERROR("bad param");
+ *info = nullptr;
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // else...
+ *info = reinterpret_cast<nsXPTMethodInfo*>
+ (&mDescriptor->method_descriptors[index - mMethodBaseIndex]);
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetMethodInfoForName(const char* methodName, uint16_t *index,
+ const nsXPTMethodInfo** result)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ // This is a slow algorithm, but this is not expected to be called much.
+ for(uint16_t i = 0; i < mDescriptor->num_methods; ++i)
+ {
+ const nsXPTMethodInfo* info;
+ info = reinterpret_cast<nsXPTMethodInfo*>
+ (&mDescriptor->
+ method_descriptors[i]);
+ if (PL_strcmp(methodName, info->GetName()) == 0) {
+ *index = i + mMethodBaseIndex;
+ *result = info;
+ return NS_OK;
+ }
+ }
+
+ if(mParent)
+ return mParent->GetMethodInfoForName(methodName, index, result);
+ else
+ {
+ *index = 0;
+ *result = 0;
+ return NS_ERROR_INVALID_ARG;
+ }
+}
+
+nsresult
+xptiInterfaceEntry::GetConstant(uint16_t index, JS::MutableHandleValue constant,
+ char** name)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(index < mConstantBaseIndex)
+ return mParent->GetConstant(index, constant, name);
+
+ if(index >= mConstantBaseIndex +
+ mDescriptor->num_constants)
+ {
+ NS_PRECONDITION(0, "bad param");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const auto& c = mDescriptor->const_descriptors[index - mConstantBaseIndex];
+ AutoJSContext cx;
+ JS::Rooted<JS::Value> v(cx);
+ v.setUndefined();
+
+ switch (c.type.prefix.flags) {
+ case nsXPTType::T_I8:
+ {
+ v.setInt32(c.value.i8);
+ break;
+ }
+ case nsXPTType::T_U8:
+ {
+ v.setInt32(c.value.ui8);
+ break;
+ }
+ case nsXPTType::T_I16:
+ {
+ v.setInt32(c.value.i16);
+ break;
+ }
+ case nsXPTType::T_U16:
+ {
+ v.setInt32(c.value.ui16);
+ break;
+ }
+ case nsXPTType::T_I32:
+ {
+ v = JS_NumberValue(c.value.i32);
+ break;
+ }
+ case nsXPTType::T_U32:
+ {
+ v = JS_NumberValue(c.value.ui32);
+ break;
+ }
+ default:
+ {
+#ifdef DEBUG
+ NS_ERROR("Non-numeric constant found in interface.");
+#endif
+ }
+ }
+
+ constant.set(v);
+ *name = ToNewCString(nsDependentCString(c.name));
+
+ return NS_OK;
+}
+
+// this is a private helper
+
+nsresult
+xptiInterfaceEntry::GetInterfaceIndexForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param,
+ uint16_t* interfaceIndex)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(methodIndex < mMethodBaseIndex)
+ return mParent->GetInterfaceIndexForParam(methodIndex, param,
+ interfaceIndex);
+
+ if(methodIndex >= mMethodBaseIndex +
+ mDescriptor->num_methods)
+ {
+ NS_ERROR("bad param");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const XPTTypeDescriptor *td = &param->type;
+
+ while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) {
+ td = &mDescriptor->additional_types[td->u.array.additional_type];
+ }
+
+ if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_TYPE) {
+ NS_ERROR("not an interface");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *interfaceIndex = (td->u.iface.iface_hi8 << 8) | td->u.iface.iface_lo8;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetEntryForParam(uint16_t methodIndex,
+ const nsXPTParamInfo * param,
+ xptiInterfaceEntry** entry)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(methodIndex < mMethodBaseIndex)
+ return mParent->GetEntryForParam(methodIndex, param, entry);
+
+ uint16_t interfaceIndex = 0;
+ nsresult rv = GetInterfaceIndexForParam(methodIndex, param,
+ &interfaceIndex);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ xptiInterfaceEntry* theEntry = mTypelib->GetEntryAt(interfaceIndex - 1);
+
+ // This can happen if a declared interface is not available at runtime.
+ if(!theEntry)
+ {
+ *entry = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ *entry = theEntry;
+ return NS_OK;
+}
+
+already_AddRefed<ShimInterfaceInfo>
+xptiInterfaceEntry::GetShimForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param)
+{
+ if(methodIndex < mMethodBaseIndex) {
+ return mParent->GetShimForParam(methodIndex, param);
+ }
+
+ uint16_t interfaceIndex = 0;
+ nsresult rv = GetInterfaceIndexForParam(methodIndex, param,
+ &interfaceIndex);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ const char* shimName = mTypelib->GetEntryNameAt(interfaceIndex - 1);
+ RefPtr<ShimInterfaceInfo> shim =
+ ShimInterfaceInfo::MaybeConstruct(shimName, nullptr);
+ return shim.forget();
+}
+
+nsresult
+xptiInterfaceEntry::GetInfoForParam(uint16_t methodIndex,
+ const nsXPTParamInfo *param,
+ nsIInterfaceInfo** info)
+{
+ xptiInterfaceEntry* entry;
+ nsresult rv = GetEntryForParam(methodIndex, param, &entry);
+ if (NS_FAILED(rv)) {
+ RefPtr<ShimInterfaceInfo> shim = GetShimForParam(methodIndex, param);
+ if (!shim) {
+ return rv;
+ }
+
+ shim.forget(info);
+ return NS_OK;
+ }
+
+ *info = entry->InterfaceInfo().take();
+
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetIIDForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param, nsIID** iid)
+{
+ xptiInterfaceEntry* entry;
+ nsresult rv = GetEntryForParam(methodIndex, param, &entry);
+ if (NS_FAILED(rv)) {
+ RefPtr<ShimInterfaceInfo> shim = GetShimForParam(methodIndex, param);
+ if (!shim) {
+ return rv;
+ }
+
+ return shim->GetInterfaceIID(iid);
+ }
+ return entry->GetIID(iid);
+}
+
+nsresult
+xptiInterfaceEntry::GetIIDForParamNoAlloc(uint16_t methodIndex,
+ const nsXPTParamInfo * param,
+ nsIID *iid)
+{
+ xptiInterfaceEntry* entry;
+ nsresult rv = GetEntryForParam(methodIndex, param, &entry);
+ if (NS_FAILED(rv)) {
+ RefPtr<ShimInterfaceInfo> shim = GetShimForParam(methodIndex, param);
+ if (!shim) {
+ return rv;
+ }
+
+ const nsIID* shimIID;
+ DebugOnly<nsresult> rv2 = shim->GetIIDShared(&shimIID);
+ MOZ_ASSERT(NS_SUCCEEDED(rv2));
+ *iid = *shimIID;
+ return NS_OK;
+ }
+ *iid = entry->mIID;
+ return NS_OK;
+}
+
+// this is a private helper
+nsresult
+xptiInterfaceEntry::GetTypeInArray(const nsXPTParamInfo* param,
+ uint16_t dimension,
+ const XPTTypeDescriptor** type)
+{
+ NS_ASSERTION(IsFullyResolved(), "bad state");
+
+ const XPTTypeDescriptor *td = &param->type;
+ const XPTTypeDescriptor *additional_types =
+ mDescriptor->additional_types;
+
+ for (uint16_t i = 0; i < dimension; i++) {
+ if(XPT_TDP_TAG(td->prefix) != TD_ARRAY) {
+ NS_ERROR("bad dimension");
+ return NS_ERROR_INVALID_ARG;
+ }
+ td = &additional_types[td->u.array.additional_type];
+ }
+
+ *type = td;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetTypeForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param,
+ uint16_t dimension,
+ nsXPTType* type)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(methodIndex < mMethodBaseIndex)
+ return mParent->
+ GetTypeForParam(methodIndex, param, dimension, type);
+
+ if(methodIndex >= mMethodBaseIndex +
+ mDescriptor->num_methods)
+ {
+ NS_ERROR("bad index");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const XPTTypeDescriptor *td;
+
+ if(dimension) {
+ nsresult rv = GetTypeInArray(param, dimension, &td);
+ if(NS_FAILED(rv))
+ return rv;
+ }
+ else
+ td = &param->type;
+
+ *type = nsXPTType(td->prefix);
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetSizeIsArgNumberForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param,
+ uint16_t dimension,
+ uint8_t* argnum)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(methodIndex < mMethodBaseIndex)
+ return mParent->
+ GetSizeIsArgNumberForParam(methodIndex, param, dimension, argnum);
+
+ if(methodIndex >= mMethodBaseIndex +
+ mDescriptor->num_methods)
+ {
+ NS_ERROR("bad index");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const XPTTypeDescriptor *td;
+
+ if(dimension) {
+ nsresult rv = GetTypeInArray(param, dimension, &td);
+ if(NS_FAILED(rv))
+ return rv;
+ }
+ else
+ td = &param->type;
+
+ // verify that this is a type that has size_is
+ switch (XPT_TDP_TAG(td->prefix)) {
+ case TD_ARRAY:
+ *argnum = td->u.array.argnum;
+ break;
+ case TD_PSTRING_SIZE_IS:
+ case TD_PWSTRING_SIZE_IS:
+ *argnum = td->u.pstring_is.argnum;
+ break;
+ default:
+ NS_ERROR("not a size_is");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetInterfaceIsArgNumberForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param,
+ uint8_t* argnum)
+{
+ if(!EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+
+ if(methodIndex < mMethodBaseIndex)
+ return mParent->
+ GetInterfaceIsArgNumberForParam(methodIndex, param, argnum);
+
+ if(methodIndex >= mMethodBaseIndex +
+ mDescriptor->num_methods)
+ {
+ NS_ERROR("bad index");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const XPTTypeDescriptor *td = &param->type;
+
+ while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) {
+ td = &mDescriptor->additional_types[td->u.array.additional_type];
+ }
+
+ if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_IS_TYPE) {
+ NS_ERROR("not an iid_is");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *argnum = td->u.interface_is.argnum;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::IsIID(const nsIID * iid, bool *_retval)
+{
+ // It is not necessary to Resolve because this info is read from manifest.
+ *_retval = mIID.Equals(*iid);
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetNameShared(const char **name)
+{
+ // It is not necessary to Resolve because this info is read from manifest.
+ *name = mName;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::GetIIDShared(const nsIID * *iid)
+{
+ // It is not necessary to Resolve because this info is read from manifest.
+ *iid = &mIID;
+ return NS_OK;
+}
+
+nsresult
+xptiInterfaceEntry::HasAncestor(const nsIID * iid, bool *_retval)
+{
+ *_retval = false;
+
+ for(xptiInterfaceEntry* current = this;
+ current;
+ current = current->mParent)
+ {
+ if(current->mIID.Equals(*iid))
+ {
+ *_retval = true;
+ break;
+ }
+ if(!current->EnsureResolved())
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+/***************************************************/
+
+already_AddRefed<xptiInterfaceInfo>
+xptiInterfaceEntry::InterfaceInfo()
+{
+#ifdef DEBUG
+ XPTInterfaceInfoManager::GetSingleton()->mWorkingSet.mTableReentrantMonitor.
+ AssertCurrentThreadIn();
+#endif
+
+ if(!mInfo)
+ {
+ mInfo = new xptiInterfaceInfo(this);
+ }
+
+ RefPtr<xptiInterfaceInfo> info = mInfo;
+ return info.forget();
+}
+
+void
+xptiInterfaceEntry::LockedInvalidateInterfaceInfo()
+{
+ if(mInfo)
+ {
+ mInfo->Invalidate();
+ mInfo = nullptr;
+ }
+}
+
+bool
+xptiInterfaceInfo::BuildParent()
+{
+ mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager::GetSingleton()->
+ mWorkingSet.mTableReentrantMonitor);
+ NS_ASSERTION(mEntry &&
+ mEntry->IsFullyResolved() &&
+ !mParent &&
+ mEntry->Parent(),
+ "bad BuildParent call");
+ mParent = mEntry->Parent()->InterfaceInfo();
+ return true;
+}
+
+/***************************************************************************/
+
+NS_IMPL_QUERY_INTERFACE(xptiInterfaceInfo, nsIInterfaceInfo)
+
+xptiInterfaceInfo::xptiInterfaceInfo(xptiInterfaceEntry* entry)
+ : mEntry(entry)
+{
+}
+
+xptiInterfaceInfo::~xptiInterfaceInfo()
+{
+ NS_ASSERTION(!mEntry, "bad state in dtor");
+}
+
+void
+xptiInterfaceInfo::Invalidate()
+{
+ mParent = nullptr;
+ mEntry = nullptr;
+}
+
+MozExternalRefCountType
+xptiInterfaceInfo::AddRef(void)
+{
+ nsrefcnt cnt = ++mRefCnt;
+ NS_LOG_ADDREF(this, cnt, "xptiInterfaceInfo", sizeof(*this));
+ return cnt;
+}
+
+MozExternalRefCountType
+xptiInterfaceInfo::Release(void)
+{
+ xptiInterfaceEntry* entry = mEntry;
+ nsrefcnt cnt = --mRefCnt;
+ NS_LOG_RELEASE(this, cnt, "xptiInterfaceInfo");
+ if(!cnt)
+ {
+ mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager::
+ GetSingleton()->mWorkingSet.
+ mTableReentrantMonitor);
+
+ // If InterfaceInfo added and *released* a reference before we
+ // acquired the monitor then 'this' might already be dead. In that
+ // case we would not want to try to access any instance data. We
+ // would want to bail immediately. If 'this' is already dead then the
+ // entry will no longer have a pointer to 'this'. So, we can protect
+ // ourselves from danger without more aggressive locking.
+ if(entry && !entry->InterfaceInfoEquals(this))
+ return 0;
+
+ // If InterfaceInfo added a reference before we acquired the monitor
+ // then we want to bail out of here without destorying the object.
+ if(mRefCnt)
+ return 1;
+
+ if(mEntry)
+ {
+ mEntry->LockedInterfaceInfoDeathNotification();
+ mEntry = nullptr;
+ }
+
+ delete this;
+ return 0;
+ }
+ return cnt;
+}
+
+/***************************************************************************/
diff --git a/xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp b/xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp
new file mode 100644
index 000000000..600a99b94
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Implementation of xptiInterfaceInfoManager. */
+
+#include "mozilla/XPTInterfaceInfoManager.h"
+
+#include "mozilla/FileUtils.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/StaticPtr.h"
+
+#include "xptiprivate.h"
+#include "nsDependentString.h"
+#include "nsString.h"
+#include "nsArrayEnumerator.h"
+#include "nsDirectoryService.h"
+#include "nsIMemoryReporter.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(
+ XPTInterfaceInfoManager,
+ nsIInterfaceInfoManager,
+ nsIMemoryReporter)
+
+static StaticRefPtr<XPTInterfaceInfoManager> gInterfaceInfoManager;
+
+size_t
+XPTInterfaceInfoManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+ // The entries themselves are allocated out of an arena accounted
+ // for elsewhere, so don't measure them
+ n += mWorkingSet.mIIDTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ n += mWorkingSet.mNameTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ return n;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(XPTIMallocSizeOf)
+
+NS_IMETHODIMP
+XPTInterfaceInfoManager::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ size_t amount = SizeOfIncludingThis(XPTIMallocSizeOf);
+
+ // Measure gXPTIStructArena here, too. This is a bit grotty because it
+ // doesn't belong to the XPTIInterfaceInfoManager, but there's no
+ // obviously better place to measure it.
+ amount += XPT_SizeOfArenaIncludingThis(gXPTIStructArena, XPTIMallocSizeOf);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/xpti-working-set", KIND_HEAP, UNITS_BYTES, amount,
+ "Memory used by the XPCOM typelib system.");
+
+ return NS_OK;
+}
+
+// static
+XPTInterfaceInfoManager*
+XPTInterfaceInfoManager::GetSingleton()
+{
+ if (!gInterfaceInfoManager) {
+ gInterfaceInfoManager = new XPTInterfaceInfoManager();
+ gInterfaceInfoManager->InitMemoryReporter();
+ }
+ return gInterfaceInfoManager;
+}
+
+void
+XPTInterfaceInfoManager::FreeInterfaceInfoManager()
+{
+ gInterfaceInfoManager = nullptr;
+}
+
+XPTInterfaceInfoManager::XPTInterfaceInfoManager()
+ : mWorkingSet(),
+ mResolveLock("XPTInterfaceInfoManager.mResolveLock")
+{
+}
+
+XPTInterfaceInfoManager::~XPTInterfaceInfoManager()
+{
+ // We only do this on shutdown of the service.
+ mWorkingSet.InvalidateInterfaceInfos();
+
+ UnregisterWeakMemoryReporter(this);
+}
+
+void
+XPTInterfaceInfoManager::InitMemoryReporter()
+{
+ RegisterWeakMemoryReporter(this);
+}
+
+void
+XPTInterfaceInfoManager::RegisterBuffer(char *buf, uint32_t length)
+{
+ XPTState state;
+ XPT_InitXDRState(&state, buf, length);
+
+ XPTCursor curs;
+ NotNull<XPTCursor*> cursor = WrapNotNull(&curs);
+ if (!XPT_MakeCursor(&state, XPT_HEADER, 0, cursor)) {
+ return;
+ }
+
+ XPTHeader *header = nullptr;
+ if (XPT_DoHeader(gXPTIStructArena, cursor, &header)) {
+ RegisterXPTHeader(header);
+ }
+}
+
+void
+XPTInterfaceInfoManager::RegisterXPTHeader(XPTHeader* aHeader)
+{
+ if (aHeader->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) {
+ NS_ASSERTION(!aHeader->num_interfaces,"bad libxpt");
+ LOG_AUTOREG((" file is version %d.%d Type file of version %d.0 or higher can not be read.\n", (int)header->major_version, (int)header->minor_version, (int)XPT_MAJOR_INCOMPATIBLE_VERSION));
+ }
+
+ xptiTypelibGuts* typelib = xptiTypelibGuts::Create(aHeader);
+
+ ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+ for(uint16_t k = 0; k < aHeader->num_interfaces; k++)
+ VerifyAndAddEntryIfNew(aHeader->interface_directory + k, k, typelib);
+}
+
+void
+XPTInterfaceInfoManager::VerifyAndAddEntryIfNew(XPTInterfaceDirectoryEntry* iface,
+ uint16_t idx,
+ xptiTypelibGuts* typelib)
+{
+ if (!iface->interface_descriptor)
+ return;
+
+ // The number of maximum methods is not arbitrary. It is the same value as
+ // in xpcom/reflect/xptcall/genstubs.pl; do not change this value
+ // without changing that one or you WILL see problems.
+ if (iface->interface_descriptor->num_methods > 250 &&
+ !(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags))) {
+ NS_ASSERTION(0, "Too many methods to handle for the stub, cannot load");
+ fprintf(stderr, "ignoring too large interface: %s\n", iface->name);
+ return;
+ }
+
+ mWorkingSet.mTableReentrantMonitor.AssertCurrentThreadIn();
+ xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(iface->iid);
+ if (entry) {
+ // XXX validate this info to find possible inconsistencies
+ LOG_AUTOREG((" ignoring repeated interface: %s\n", iface->name));
+ return;
+ }
+
+ // Build a new xptiInterfaceEntry object and hook it up.
+
+ entry = xptiInterfaceEntry::Create(iface->name,
+ iface->iid,
+ iface->interface_descriptor,
+ typelib);
+ if (!entry)
+ return;
+
+ //XXX We should SetHeader too as part of the validation, no?
+ entry->SetScriptableFlag(XPT_ID_IS_SCRIPTABLE(iface->interface_descriptor->flags));
+ entry->SetBuiltinClassFlag(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags));
+ entry->SetMainProcessScriptableOnlyFlag(
+ XPT_ID_IS_MAIN_PROCESS_SCRIPTABLE_ONLY(iface->interface_descriptor->flags));
+
+ mWorkingSet.mIIDTable.Put(entry->IID(), entry);
+ mWorkingSet.mNameTable.Put(entry->GetTheName(), entry);
+
+ typelib->SetEntryAt(idx, entry);
+
+ LOG_AUTOREG((" added interface: %s\n", iface->name));
+}
+
+// this is a private helper
+static nsresult
+EntryToInfo(xptiInterfaceEntry* entry, nsIInterfaceInfo **_retval)
+{
+ if (!entry) {
+ *_retval = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<xptiInterfaceInfo> info = entry->InterfaceInfo();
+ info.forget(_retval);
+ return NS_OK;
+}
+
+xptiInterfaceEntry*
+XPTInterfaceInfoManager::GetInterfaceEntryForIID(const nsIID *iid)
+{
+ ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+ return mWorkingSet.mIIDTable.Get(*iid);
+}
+
+NS_IMETHODIMP
+XPTInterfaceInfoManager::GetInfoForIID(const nsIID * iid, nsIInterfaceInfo **_retval)
+{
+ NS_ASSERTION(iid, "bad param");
+ NS_ASSERTION(_retval, "bad param");
+
+ ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+ xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid);
+ return EntryToInfo(entry, _retval);
+}
+
+NS_IMETHODIMP
+XPTInterfaceInfoManager::GetInfoForName(const char *name, nsIInterfaceInfo **_retval)
+{
+ NS_ASSERTION(name, "bad param");
+ NS_ASSERTION(_retval, "bad param");
+
+ ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+ xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name);
+ return EntryToInfo(entry, _retval);
+}
+
+void
+XPTInterfaceInfoManager::GetScriptableInterfaces(nsCOMArray<nsIInterfaceInfo>& aInterfaces)
+{
+ // I didn't want to incur the size overhead of using nsHashtable just to
+ // make building an enumerator easier. So, this code makes a snapshot of
+ // the table using an nsCOMArray and builds an enumerator for that.
+ // We can afford this transient cost.
+
+ ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+ aInterfaces.SetCapacity(mWorkingSet.mNameTable.Count());
+ for (auto iter = mWorkingSet.mNameTable.Iter(); !iter.Done(); iter.Next()) {
+ xptiInterfaceEntry* entry = iter.UserData();
+ if (entry->GetScriptableFlag()) {
+ nsCOMPtr<nsIInterfaceInfo> ii = entry->InterfaceInfo();
+ aInterfaces.AppendElement(ii);
+ }
+ }
+}
diff --git a/xpcom/reflect/xptinfo/xptiTypelibGuts.cpp b/xpcom/reflect/xptinfo/xptiTypelibGuts.cpp
new file mode 100644
index 000000000..e2965d0b1
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptiTypelibGuts.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implementation of xptiTypelibGuts. */
+
+#include "xptiprivate.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+
+using namespace mozilla;
+
+// Ensure through static analysis that xptiTypelibGuts won't have a vtable.
+template <class T>
+class MOZ_NEEDS_NO_VTABLE_TYPE CheckNoVTable
+{
+};
+CheckNoVTable<xptiTypelibGuts> gChecker;
+
+// static
+xptiTypelibGuts*
+xptiTypelibGuts::Create(XPTHeader* aHeader)
+{
+ NS_ASSERTION(aHeader, "bad param");
+ size_t n = sizeof(xptiTypelibGuts) +
+ sizeof(xptiInterfaceEntry*) * (aHeader->num_interfaces - 1);
+ void* place = XPT_CALLOC8(gXPTIStructArena, n);
+ if (!place)
+ return nullptr;
+ return new(place) xptiTypelibGuts(aHeader);
+}
+
+xptiInterfaceEntry*
+xptiTypelibGuts::GetEntryAt(uint16_t i)
+{
+ static const nsID zeroIID =
+ { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } };
+
+ NS_ASSERTION(mHeader, "bad state");
+ NS_ASSERTION(i < GetEntryCount(), "bad index");
+
+ xptiInterfaceEntry* r = mEntryArray[i];
+ if (r)
+ return r;
+
+ XPTInterfaceDirectoryEntry* iface = mHeader->interface_directory + i;
+
+ XPTInterfaceInfoManager::xptiWorkingSet& set =
+ XPTInterfaceInfoManager::GetSingleton()->mWorkingSet;
+
+ {
+ ReentrantMonitorAutoEnter monitor(set.mTableReentrantMonitor);
+ if (iface->iid.Equals(zeroIID))
+ r = set.mNameTable.Get(iface->name);
+ else
+ r = set.mIIDTable.Get(iface->iid);
+ }
+
+ if (r)
+ SetEntryAt(i, r);
+
+ return r;
+}
+
+const char*
+xptiTypelibGuts::GetEntryNameAt(uint16_t i)
+{
+ NS_ASSERTION(mHeader, "bad state");
+ NS_ASSERTION(i < GetEntryCount(), "bad index");
+
+ XPTInterfaceDirectoryEntry* iface = mHeader->interface_directory + i;
+
+ return iface->name;
+}
diff --git a/xpcom/reflect/xptinfo/xptiWorkingSet.cpp b/xpcom/reflect/xptinfo/xptiWorkingSet.cpp
new file mode 100644
index 000000000..10f81a4b2
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptiWorkingSet.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Implementation of xptiWorkingSet. */
+
+#include "mozilla/XPTInterfaceInfoManager.h"
+
+#include "xptiprivate.h"
+#include "nsString.h"
+
+using namespace mozilla;
+
+static const size_t XPTI_ARENA8_BLOCK_SIZE = 16 * 1024;
+static const size_t XPTI_ARENA1_BLOCK_SIZE = 8 * 1024;
+
+static const uint32_t XPTI_HASHTABLE_LENGTH = 1024;
+
+XPTInterfaceInfoManager::xptiWorkingSet::xptiWorkingSet()
+ : mTableReentrantMonitor("xptiWorkingSet::mTableReentrantMonitor")
+ , mIIDTable(XPTI_HASHTABLE_LENGTH)
+ , mNameTable(XPTI_HASHTABLE_LENGTH)
+{
+ MOZ_COUNT_CTOR(xptiWorkingSet);
+
+ gXPTIStructArena = XPT_NewArena(XPTI_ARENA8_BLOCK_SIZE,
+ XPTI_ARENA1_BLOCK_SIZE);
+}
+
+void
+XPTInterfaceInfoManager::xptiWorkingSet::InvalidateInterfaceInfos()
+{
+ ReentrantMonitorAutoEnter monitor(mTableReentrantMonitor);
+ for (auto iter = mNameTable.Iter(); !iter.Done(); iter.Next()) {
+ xptiInterfaceEntry* entry = iter.UserData();
+ entry->LockedInvalidateInterfaceInfo();
+ }
+}
+
+XPTInterfaceInfoManager::xptiWorkingSet::~xptiWorkingSet()
+{
+ MOZ_COUNT_DTOR(xptiWorkingSet);
+
+ // Only destroy the arena if we're doing leak stats. Why waste shutdown
+ // time touching pages if we don't have to?
+#ifdef NS_FREE_PERMANENT_DATA
+ XPT_DestroyArena(gXPTIStructArena);
+#endif
+}
+
+XPTArena* gXPTIStructArena;
diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h
new file mode 100644
index 000000000..1207a42bb
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* XPTI_PUBLIC_API and XPTI_GetInterfaceInfoManager declarations. */
+
+#ifndef xptiinfo_h___
+#define xptiinfo_h___
+
+#include "nscore.h"
+#include "xpt_struct.h"
+
+// Flyweight wrapper classes for xpt_struct.h structs.
+// Everything here is dependent upon - and sensitive to changes in -
+// xpcom/typelib/xpt/xpt_struct.h!
+
+class nsXPTType : public XPTTypeDescriptorPrefix
+{
+// NO DATA - this a flyweight wrapper
+public:
+ nsXPTType()
+ {} // random contents
+ MOZ_IMPLICIT nsXPTType(const XPTTypeDescriptorPrefix& prefix)
+ {*(XPTTypeDescriptorPrefix*)this = prefix;}
+
+ MOZ_IMPLICIT nsXPTType(const uint8_t& prefix)
+ {*(uint8_t*)this = prefix;}
+
+ nsXPTType& operator=(uint8_t val)
+ {flags = val; return *this;}
+
+ nsXPTType& operator=(const nsXPTType& other)
+ {flags = other.flags; return *this;}
+
+ operator uint8_t() const
+ {return flags;}
+
+ // 'Arithmetic' here roughly means that the value is self-contained and
+ // doesn't depend on anything else in memory (ie: not a pointer, not an
+ // XPCOM object, not a jsval, etc).
+ //
+ // Supposedly this terminology comes from Harbison/Steele, but it's still
+ // a rather crappy name. We'd change it if it wasn't used all over the
+ // place in xptcall. :-(
+ bool IsArithmetic() const
+ {return flags <= T_WCHAR;}
+
+ // We used to abuse 'pointer' flag bit in typelib format quite extensively.
+ // We've gotten rid of most of the cases, but there's still a fair amount
+ // of refactoring to be done in XPCWrappedJSClass before we can safely stop
+ // asking about this. In the mean time, we've got a temporary version of
+ // IsPointer() that should be equivalent to what's in the typelib.
+ bool deprecated_IsPointer() const
+ {return !IsArithmetic() && TagPart() != T_JSVAL;}
+
+ bool IsInterfacePointer() const
+ { switch (TagPart()) {
+ default:
+ return false;
+ case T_INTERFACE:
+ case T_INTERFACE_IS:
+ return true;
+ }
+ }
+
+ bool IsArray() const
+ {return TagPart() == T_ARRAY;}
+
+ // 'Dependent' means that params of this type are dependent upon other
+ // params. e.g. an T_INTERFACE_IS is dependent upon some other param at
+ // runtime to say what the interface type of this param really is.
+ bool IsDependent() const
+ { switch (TagPart()) {
+ default:
+ return false;
+ case T_INTERFACE_IS:
+ case TD_ARRAY:
+ case T_PSTRING_SIZE_IS:
+ case T_PWSTRING_SIZE_IS:
+ return true;
+ }
+ }
+
+ uint8_t TagPart() const
+ {return (uint8_t) (flags & XPT_TDP_TAGMASK);}
+
+ enum
+ {
+ T_I8 = TD_INT8 ,
+ T_I16 = TD_INT16 ,
+ T_I32 = TD_INT32 ,
+ T_I64 = TD_INT64 ,
+ T_U8 = TD_UINT8 ,
+ T_U16 = TD_UINT16 ,
+ T_U32 = TD_UINT32 ,
+ T_U64 = TD_UINT64 ,
+ T_FLOAT = TD_FLOAT ,
+ T_DOUBLE = TD_DOUBLE ,
+ T_BOOL = TD_BOOL ,
+ T_CHAR = TD_CHAR ,
+ T_WCHAR = TD_WCHAR ,
+ T_VOID = TD_VOID ,
+ T_IID = TD_PNSIID ,
+ T_DOMSTRING = TD_DOMSTRING ,
+ T_CHAR_STR = TD_PSTRING ,
+ T_WCHAR_STR = TD_PWSTRING ,
+ T_INTERFACE = TD_INTERFACE_TYPE ,
+ T_INTERFACE_IS = TD_INTERFACE_IS_TYPE,
+ T_ARRAY = TD_ARRAY ,
+ T_PSTRING_SIZE_IS = TD_PSTRING_SIZE_IS ,
+ T_PWSTRING_SIZE_IS = TD_PWSTRING_SIZE_IS ,
+ T_UTF8STRING = TD_UTF8STRING ,
+ T_CSTRING = TD_CSTRING ,
+ T_ASTRING = TD_ASTRING ,
+ T_JSVAL = TD_JSVAL
+ };
+// NO DATA - this a flyweight wrapper
+};
+
+class nsXPTParamInfo : public XPTParamDescriptor
+{
+// NO DATA - this a flyweight wrapper
+public:
+ MOZ_IMPLICIT nsXPTParamInfo(const XPTParamDescriptor& desc)
+ {*(XPTParamDescriptor*)this = desc;}
+
+
+ bool IsIn() const {return 0 != (XPT_PD_IS_IN(flags));}
+ bool IsOut() const {return 0 != (XPT_PD_IS_OUT(flags));}
+ bool IsRetval() const {return 0 != (XPT_PD_IS_RETVAL(flags));}
+ bool IsShared() const {return 0 != (XPT_PD_IS_SHARED(flags));}
+
+ // Dipper types are one of the more inscrutable aspects of xpidl. In a
+ // nutshell, dippers are empty container objects, created and passed by
+ // the caller, and filled by the callee. The callee receives a fully-
+ // formed object, and thus does not have to construct anything. But
+ // the object is functionally empty, and the callee is responsible for
+ // putting something useful inside of it.
+ //
+ // XPIDL decides which types to make dippers. The list of these types
+ // is given in the isDipperType() function in typelib.py, and is currently
+ // limited to 4 string types.
+ //
+ // When a dipper type is declared as an 'out' parameter, xpidl internally
+ // converts it to an 'in', and sets the XPT_PD_DIPPER flag on it. For this
+ // reason, dipper types are sometimes referred to as 'out parameters
+ // masquerading as in'. The burden of maintaining this illusion falls mostly
+ // on XPConnect, which creates the empty containers, and harvest the results
+ // after the call.
+ bool IsDipper() const {return 0 != (XPT_PD_IS_DIPPER(flags));}
+ bool IsOptional() const {return 0 != (XPT_PD_IS_OPTIONAL(flags));}
+ const nsXPTType GetType() const {return type.prefix;}
+
+ bool IsStringClass() const {
+ switch (GetType().TagPart()) {
+ case nsXPTType::T_ASTRING:
+ case nsXPTType::T_DOMSTRING:
+ case nsXPTType::T_UTF8STRING:
+ case nsXPTType::T_CSTRING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Whether this parameter is passed indirectly on the stack. This mainly
+ // applies to out/inout params, but we use it unconditionally for certain
+ // types.
+ bool IsIndirect() const {return IsOut() ||
+ GetType().TagPart() == nsXPTType::T_JSVAL;}
+
+ // NOTE: other activities on types are done via methods on nsIInterfaceInfo
+
+private:
+ nsXPTParamInfo(); // no implementation
+// NO DATA - this a flyweight wrapper
+};
+
+class nsXPTMethodInfo : public XPTMethodDescriptor
+{
+// NO DATA - this a flyweight wrapper
+public:
+ MOZ_IMPLICIT nsXPTMethodInfo(const XPTMethodDescriptor& desc)
+ {*(XPTMethodDescriptor*)this = desc;}
+
+ bool IsGetter() const {return 0 != (XPT_MD_IS_GETTER(flags) );}
+ bool IsSetter() const {return 0 != (XPT_MD_IS_SETTER(flags) );}
+ bool IsNotXPCOM() const {return 0 != (XPT_MD_IS_NOTXPCOM(flags));}
+ bool IsHidden() const {return 0 != (XPT_MD_IS_HIDDEN(flags) );}
+ bool WantsOptArgc() const {return 0 != (XPT_MD_WANTS_OPT_ARGC(flags));}
+ bool WantsContext() const {return 0 != (XPT_MD_WANTS_CONTEXT(flags));}
+ const char* GetName() const {return name;}
+ uint8_t GetParamCount() const {return num_args;}
+ /* idx was index before I got _sick_ of the warnings on Unix, sorry jband */
+ const nsXPTParamInfo GetParam(uint8_t idx) const
+ {
+ NS_PRECONDITION(idx < GetParamCount(),"bad arg");
+ return params[idx];
+ }
+ const nsXPTParamInfo GetResult() const
+ {return result;}
+private:
+ nsXPTMethodInfo(); // no implementation
+// NO DATA - this a flyweight wrapper
+};
+
+
+// forward declaration
+struct nsXPTCMiniVariant;
+
+class nsXPTConstant : public XPTConstDescriptor
+{
+// NO DATA - this a flyweight wrapper
+public:
+ MOZ_IMPLICIT nsXPTConstant(const XPTConstDescriptor& desc)
+ {*(XPTConstDescriptor*)this = desc;}
+
+ const char* GetName() const
+ {return name;}
+
+ const nsXPTType GetType() const
+ {return type.prefix;}
+
+ // XXX this is ugly. But sometimes you gotta do what you gotta do.
+ // A reinterpret_cast won't do the trick here. And this plain C cast
+ // works correctly and is safe enough.
+ // See http://bugzilla.mozilla.org/show_bug.cgi?id=49641
+ const nsXPTCMiniVariant* GetValue() const
+ {return (nsXPTCMiniVariant*) &value;}
+private:
+ nsXPTConstant(); // no implementation
+// NO DATA - this a flyweight wrapper
+};
+
+#endif /* xptiinfo_h___ */
diff --git a/xpcom/reflect/xptinfo/xptiprivate.h b/xpcom/reflect/xptinfo/xptiprivate.h
new file mode 100644
index 000000000..c32ef9c77
--- /dev/null
+++ b/xpcom/reflect/xptinfo/xptiprivate.h
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Library-private header for Interface Info system. */
+
+#ifndef xptiprivate_h___
+#define xptiprivate_h___
+
+#include "nscore.h"
+#include <new>
+#include "nsISupports.h"
+
+// this after nsISupports, to pick up IID
+// so that xpt stuff doesn't try to define it itself...
+#include "xpt_struct.h"
+#include "xpt_xdr.h"
+
+#include "nsIInterfaceInfo.h"
+#include "nsIInterfaceInfoManager.h"
+#include "xptinfo.h"
+#include "ShimInterfaceInfo.h"
+
+#include "nsIServiceManager.h"
+#include "nsIFile.h"
+#include "nsIDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIWeakReference.h"
+
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+
+#include "js/TypeDecls.h"
+
+#include "nsCRT.h"
+#include "nsMemory.h"
+
+#include "nsCOMArray.h"
+#include "nsQuickSort.h"
+
+#include "nsXPIDLString.h"
+
+#include "nsIInputStream.h"
+
+#include "nsHashKeys.h"
+#include "nsDataHashtable.h"
+#include "plstr.h"
+#include "prprf.h"
+#include "prio.h"
+#include "prtime.h"
+#include "prenv.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/***************************************************************************/
+
+#if 0 && defined(DEBUG_jband)
+#define LOG_RESOLVE(x) printf x
+#define LOG_LOAD(x) printf x
+#define LOG_AUTOREG(x) do{printf x; xptiInterfaceInfoManager::WriteToLog x;}while(0)
+#else
+#define LOG_RESOLVE(x) ((void)0)
+#define LOG_LOAD(x) ((void)0)
+#define LOG_AUTOREG(x) ((void)0)
+#endif
+
+#if 1 && defined(DEBUG_jband)
+#define SHOW_INFO_COUNT_STATS
+#endif
+
+/***************************************************************************/
+
+class xptiInterfaceInfo;
+class xptiInterfaceEntry;
+class xptiTypelibGuts;
+
+extern XPTArena* gXPTIStructArena;
+
+/***************************************************************************/
+
+/***************************************************************************/
+
+// No virtuals.
+// These are always constructed in the struct arena using placement new.
+// dtor need not be called.
+
+class xptiTypelibGuts
+{
+public:
+ static xptiTypelibGuts* Create(XPTHeader* aHeader);
+
+ XPTHeader* GetHeader() {return mHeader;}
+ uint16_t GetEntryCount() const {return mHeader->num_interfaces;}
+
+ void SetEntryAt(uint16_t i, xptiInterfaceEntry* ptr)
+ {
+ NS_ASSERTION(mHeader,"bad state!");
+ NS_ASSERTION(i < GetEntryCount(),"bad param!");
+ mEntryArray[i] = ptr;
+ }
+
+ xptiInterfaceEntry* GetEntryAt(uint16_t i);
+ const char* GetEntryNameAt(uint16_t i);
+
+private:
+ explicit xptiTypelibGuts(XPTHeader* aHeader)
+ : mHeader(aHeader)
+ { }
+ ~xptiTypelibGuts();
+
+private:
+ XPTHeader* mHeader; // hold pointer into arena
+ xptiInterfaceEntry* mEntryArray[1]; // Always last. Sized to fit.
+};
+
+/***************************************************************************/
+
+/***************************************************************************/
+
+// This class exists to help xptiInterfaceInfo store a 4-state (2 bit) value
+// and a set of bitflags in one 8bit value. See below.
+
+class xptiInfoFlags
+{
+ enum {STATE_MASK = 3};
+public:
+ explicit xptiInfoFlags(uint8_t n) : mData(n) {}
+ xptiInfoFlags(const xptiInfoFlags& r) : mData(r.mData) {}
+
+ static uint8_t GetStateMask()
+ {return uint8_t(STATE_MASK);}
+
+ void Clear()
+ {mData = 0;}
+
+ uint8_t GetData() const
+ {return mData;}
+
+ uint8_t GetState() const
+ {return mData & GetStateMask();}
+
+ void SetState(uint8_t state)
+ {mData &= ~GetStateMask(); mData |= state;}
+
+ void SetFlagBit(uint8_t flag, bool on)
+ {if(on)
+ mData |= ~GetStateMask() & flag;
+ else
+ mData &= GetStateMask() | ~flag;}
+
+ bool GetFlagBit(uint8_t flag) const
+ {return (mData & flag) ? true : false;}
+
+private:
+ uint8_t mData;
+};
+
+/****************************************************/
+
+// No virtual methods.
+// We always create in the struct arena and construct using "placement new".
+// No members need dtor calls.
+
+class xptiInterfaceEntry
+{
+public:
+ static xptiInterfaceEntry* Create(const char* name,
+ const nsID& iid,
+ XPTInterfaceDescriptor* aDescriptor,
+ xptiTypelibGuts* aTypelib);
+
+ enum {
+ PARTIALLY_RESOLVED = 1,
+ FULLY_RESOLVED = 2,
+ RESOLVE_FAILED = 3
+ };
+
+ // Additional bit flags...
+ enum {SCRIPTABLE = 4, BUILTINCLASS = 8, HASNOTXPCOM = 16,
+ MAIN_PROCESS_SCRIPTABLE_ONLY = 32};
+
+ uint8_t GetResolveState() const {return mFlags.GetState();}
+
+ bool IsFullyResolved() const
+ {return GetResolveState() == (uint8_t) FULLY_RESOLVED;}
+
+ void SetScriptableFlag(bool on)
+ {mFlags.SetFlagBit(uint8_t(SCRIPTABLE),on);}
+ bool GetScriptableFlag() const
+ {return mFlags.GetFlagBit(uint8_t(SCRIPTABLE));}
+ void SetBuiltinClassFlag(bool on)
+ {mFlags.SetFlagBit(uint8_t(BUILTINCLASS),on);}
+ bool GetBuiltinClassFlag() const
+ {return mFlags.GetFlagBit(uint8_t(BUILTINCLASS));}
+ void SetMainProcessScriptableOnlyFlag(bool on)
+ {mFlags.SetFlagBit(uint8_t(MAIN_PROCESS_SCRIPTABLE_ONLY),on);}
+ bool GetMainProcessScriptableOnlyFlag() const
+ {return mFlags.GetFlagBit(uint8_t(MAIN_PROCESS_SCRIPTABLE_ONLY));}
+
+
+ // AddRef/Release are special and are not considered for the NOTXPCOM flag.
+ void SetHasNotXPCOMFlag()
+ {
+ mFlags.SetFlagBit(HASNOTXPCOM, true);
+ }
+ bool GetHasNotXPCOMFlag() const
+ {
+ return mFlags.GetFlagBit(HASNOTXPCOM);
+ }
+
+ const nsID* GetTheIID() const {return &mIID;}
+ const char* GetTheName() const {return mName;}
+
+ bool EnsureResolved()
+ {return IsFullyResolved() ? true : Resolve();}
+
+ already_AddRefed<xptiInterfaceInfo> InterfaceInfo();
+ bool InterfaceInfoEquals(const xptiInterfaceInfo* info) const
+ {return info == mInfo;}
+
+ void LockedInvalidateInterfaceInfo();
+ void LockedInterfaceInfoDeathNotification() {mInfo = nullptr;}
+
+ xptiInterfaceEntry* Parent() const {
+ NS_ASSERTION(IsFullyResolved(), "Parent() called while not resolved?");
+ return mParent;
+ }
+
+ const nsID& IID() const { return mIID; }
+
+ //////////////////////
+ // These non-virtual methods handle the delegated nsIInterfaceInfo methods.
+
+ nsresult GetName(char * *aName);
+ nsresult GetIID(nsIID * *aIID);
+ nsresult IsScriptable(bool *_retval);
+ nsresult IsBuiltinClass(bool *_retval) {
+ *_retval = GetBuiltinClassFlag();
+ return NS_OK;
+ }
+ nsresult IsMainProcessScriptableOnly(bool *_retval) {
+ *_retval = GetMainProcessScriptableOnlyFlag();
+ return NS_OK;
+ }
+ // Except this one.
+ //nsresult GetParent(nsIInterfaceInfo * *aParent);
+ nsresult GetMethodCount(uint16_t *aMethodCount);
+ nsresult GetConstantCount(uint16_t *aConstantCount);
+ nsresult GetMethodInfo(uint16_t index, const nsXPTMethodInfo * *info);
+ nsresult GetMethodInfoForName(const char *methodName, uint16_t *index, const nsXPTMethodInfo * *info);
+ nsresult GetConstant(uint16_t index, JS::MutableHandleValue, char** constant);
+ nsresult GetInfoForParam(uint16_t methodIndex, const nsXPTParamInfo * param, nsIInterfaceInfo **_retval);
+ nsresult GetIIDForParam(uint16_t methodIndex, const nsXPTParamInfo * param, nsIID * *_retval);
+ nsresult GetTypeForParam(uint16_t methodIndex, const nsXPTParamInfo * param, uint16_t dimension, nsXPTType *_retval);
+ nsresult GetSizeIsArgNumberForParam(uint16_t methodIndex, const nsXPTParamInfo * param, uint16_t dimension, uint8_t *_retval);
+ nsresult GetInterfaceIsArgNumberForParam(uint16_t methodIndex, const nsXPTParamInfo * param, uint8_t *_retval);
+ nsresult IsIID(const nsIID * IID, bool *_retval);
+ nsresult GetNameShared(const char **name);
+ nsresult GetIIDShared(const nsIID * *iid);
+ nsresult IsFunction(bool *_retval);
+ nsresult HasAncestor(const nsIID * iid, bool *_retval);
+ nsresult GetIIDForParamNoAlloc(uint16_t methodIndex, const nsXPTParamInfo * param, nsIID *iid);
+
+private:
+ xptiInterfaceEntry(const char* name,
+ size_t nameLength,
+ const nsID& iid,
+ XPTInterfaceDescriptor* aDescriptor,
+ xptiTypelibGuts* aTypelib);
+ ~xptiInterfaceEntry();
+
+ void SetResolvedState(int state)
+ {mFlags.SetState(uint8_t(state));}
+
+ bool Resolve();
+
+ // We only call these "*Locked" variants after locking. This is done to
+ // allow reentrace as files are loaded and various interfaces resolved
+ // without having to worry about the locked state.
+
+ bool EnsureResolvedLocked()
+ {return IsFullyResolved() ? true : ResolveLocked();}
+ bool ResolveLocked();
+
+ // private helpers
+
+ nsresult GetEntryForParam(uint16_t methodIndex,
+ const nsXPTParamInfo * param,
+ xptiInterfaceEntry** entry);
+
+ nsresult GetTypeInArray(const nsXPTParamInfo* param,
+ uint16_t dimension,
+ const XPTTypeDescriptor** type);
+
+ nsresult GetInterfaceIndexForParam(uint16_t methodIndex,
+ const nsXPTParamInfo* param,
+ uint16_t* interfaceIndex);
+
+ already_AddRefed<ShimInterfaceInfo>
+ GetShimForParam(uint16_t methodIndex, const nsXPTParamInfo* param);
+
+private:
+ nsID mIID;
+ XPTInterfaceDescriptor* mDescriptor;
+
+ xptiTypelibGuts* mTypelib;
+
+ xptiInterfaceEntry* mParent; // Valid only when fully resolved
+
+ xptiInterfaceInfo* MOZ_UNSAFE_REF("The safety of this pointer is ensured "
+ "by the semantics of xptiWorkingSet.")
+ mInfo; // May come and go.
+
+ uint16_t mMethodBaseIndex;
+ uint16_t mConstantBaseIndex;
+
+ xptiInfoFlags mFlags;
+
+ char mName[1]; // Always last. Sized to fit.
+};
+
+class xptiInterfaceInfo final : public nsIInterfaceInfo
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // Use delegation to implement (most!) of nsIInterfaceInfo.
+ NS_IMETHOD GetName(char * *aName) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetName(aName); }
+ NS_IMETHOD GetInterfaceIID(nsIID * *aIID) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetIID(aIID); }
+ NS_IMETHOD IsScriptable(bool *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsScriptable(_retval); }
+ NS_IMETHOD IsBuiltinClass(bool *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsBuiltinClass(_retval); }
+ NS_IMETHOD IsMainProcessScriptableOnly(bool *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsMainProcessScriptableOnly(_retval); }
+ // Except this one.
+ NS_IMETHOD GetParent(nsIInterfaceInfo * *aParent) override
+ {
+ if(!EnsureResolved() || !EnsureParent())
+ return NS_ERROR_UNEXPECTED;
+ NS_IF_ADDREF(*aParent = mParent);
+ return NS_OK;
+ }
+ NS_IMETHOD GetMethodCount(uint16_t *aMethodCount) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetMethodCount(aMethodCount); }
+ NS_IMETHOD GetConstantCount(uint16_t *aConstantCount) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetConstantCount(aConstantCount); }
+ NS_IMETHOD GetMethodInfo(uint16_t index, const nsXPTMethodInfo * *info) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetMethodInfo(index, info); }
+ NS_IMETHOD GetMethodInfoForName(const char *methodName, uint16_t *index, const nsXPTMethodInfo * *info) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetMethodInfoForName(methodName, index, info); }
+ NS_IMETHOD GetConstant(uint16_t index, JS::MutableHandleValue constant, char** name) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetConstant(index, constant, name); }
+ NS_IMETHOD GetInfoForParam(uint16_t methodIndex, const nsXPTParamInfo * param, nsIInterfaceInfo **_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetInfoForParam(methodIndex, param, _retval); }
+ NS_IMETHOD GetIIDForParam(uint16_t methodIndex, const nsXPTParamInfo * param, nsIID * *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetIIDForParam(methodIndex, param, _retval); }
+ NS_IMETHOD GetTypeForParam(uint16_t methodIndex, const nsXPTParamInfo * param, uint16_t dimension, nsXPTType *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetTypeForParam(methodIndex, param, dimension, _retval); }
+ NS_IMETHOD GetSizeIsArgNumberForParam(uint16_t methodIndex, const nsXPTParamInfo * param, uint16_t dimension, uint8_t *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetSizeIsArgNumberForParam(methodIndex, param, dimension, _retval); }
+ NS_IMETHOD GetInterfaceIsArgNumberForParam(uint16_t methodIndex, const nsXPTParamInfo * param, uint8_t *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetInterfaceIsArgNumberForParam(methodIndex, param, _retval); }
+ NS_IMETHOD IsIID(const nsIID * IID, bool *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsIID(IID, _retval); }
+ NS_IMETHOD GetNameShared(const char **name) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetNameShared(name); }
+ NS_IMETHOD GetIIDShared(const nsIID * *iid) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetIIDShared(iid); }
+ NS_IMETHOD IsFunction(bool *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->IsFunction(_retval); }
+ NS_IMETHOD HasAncestor(const nsIID * iid, bool *_retval) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->HasAncestor(iid, _retval); }
+ NS_IMETHOD GetIIDForParamNoAlloc(uint16_t methodIndex, const nsXPTParamInfo * param, nsIID *iid) override { return !mEntry ? NS_ERROR_UNEXPECTED : mEntry->GetIIDForParamNoAlloc(methodIndex, param, iid); }
+
+public:
+ explicit xptiInterfaceInfo(xptiInterfaceEntry* entry);
+
+ void Invalidate();
+
+private:
+
+ ~xptiInterfaceInfo();
+
+ // Note that mParent might still end up as nullptr if we don't have one.
+ bool EnsureParent()
+ {
+ NS_ASSERTION(mEntry && mEntry->IsFullyResolved(), "bad EnsureParent call");
+ return mParent || !mEntry->Parent() || BuildParent();
+ }
+
+ bool EnsureResolved()
+ {
+ return mEntry && mEntry->EnsureResolved();
+ }
+
+ bool BuildParent();
+
+ xptiInterfaceInfo(); // not implemented
+
+private:
+ xptiInterfaceEntry* mEntry;
+ RefPtr<xptiInterfaceInfo> mParent;
+};
+
+/***************************************************************************/
+
+#endif /* xptiprivate_h___ */
diff --git a/xpcom/rust/nsstring/Cargo.toml b/xpcom/rust/nsstring/Cargo.toml
new file mode 100644
index 000000000..d86a1ad26
--- /dev/null
+++ b/xpcom/rust/nsstring/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "nsstring"
+version = "0.1.0"
+authors = ["nobody@mozilla.com"]
+license = "MPL-2.0"
+description = "Rust bindings to xpcom string types"
+
+[dependencies]
diff --git a/xpcom/rust/nsstring/gtest/Cargo.toml b/xpcom/rust/nsstring/gtest/Cargo.toml
new file mode 100644
index 000000000..44897ec98
--- /dev/null
+++ b/xpcom/rust/nsstring/gtest/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "nsstring-gtest"
+version = "0.1.0"
+authors = ["nobody@mozilla.com"]
+license = "MPL-2.0"
+description = "Tests for rust bindings to xpcom string types"
+
+[dependencies]
+nsstring = { path = "../" }
+
+[lib]
+path = "test.rs"
diff --git a/xpcom/rust/nsstring/gtest/Test.cpp b/xpcom/rust/nsstring/gtest/Test.cpp
new file mode 100644
index 000000000..93d2ee1d7
--- /dev/null
+++ b/xpcom/rust/nsstring/gtest/Test.cpp
@@ -0,0 +1,131 @@
+#include "gtest/gtest.h"
+#include <stdint.h>
+#include "nsString.h"
+
+extern "C" {
+ // This function is called by the rust code in test.rs if a non-fatal test
+ // failure occurs.
+ void GTest_ExpectFailure(const char* aMessage) {
+ EXPECT_STREQ(aMessage, "");
+ }
+}
+
+#define SIZE_ALIGN_CHECK(Clazz) \
+ extern "C" void Rust_Test_ReprSizeAlign_##Clazz(size_t* size, size_t* align); \
+ TEST(RustNsString, ReprSizeAlign_##Clazz) { \
+ size_t size, align; \
+ Rust_Test_ReprSizeAlign_##Clazz(&size, &align); \
+ EXPECT_EQ(size, sizeof(Clazz)); \
+ EXPECT_EQ(align, alignof(Clazz)); \
+ }
+
+SIZE_ALIGN_CHECK(nsString)
+SIZE_ALIGN_CHECK(nsCString)
+SIZE_ALIGN_CHECK(nsFixedString)
+SIZE_ALIGN_CHECK(nsFixedCString)
+
+#define MEMBER_CHECK(Clazz, Member) \
+ extern "C" void Rust_Test_Member_##Clazz##_##Member(size_t* size, \
+ size_t* align, \
+ size_t* offset); \
+ TEST(RustNsString, ReprMember_##Clazz##_##Member) { \
+ class Hack : public Clazz { \
+ public: \
+ static void RunTest() { \
+ size_t size, align, offset; \
+ Rust_Test_Member_##Clazz##_##Member(&size, &align, &offset); \
+ EXPECT_EQ(size, sizeof(mozilla::DeclVal<Hack>().Member)); \
+ EXPECT_EQ(size, alignof(decltype(mozilla::DeclVal<Hack>().Member))); \
+ EXPECT_EQ(offset, offsetof(Hack, Member)); \
+ } \
+ }; \
+ static_assert(sizeof(Clazz) == sizeof(Hack), "Hack matches class"); \
+ Hack::RunTest(); \
+ }
+
+MEMBER_CHECK(nsString, mData)
+MEMBER_CHECK(nsString, mLength)
+MEMBER_CHECK(nsString, mFlags)
+MEMBER_CHECK(nsCString, mData)
+MEMBER_CHECK(nsCString, mLength)
+MEMBER_CHECK(nsCString, mFlags)
+MEMBER_CHECK(nsFixedString, mFixedCapacity)
+MEMBER_CHECK(nsFixedString, mFixedBuf)
+MEMBER_CHECK(nsFixedCString, mFixedCapacity)
+MEMBER_CHECK(nsFixedCString, mFixedBuf)
+
+extern "C" void Rust_Test_NsStringFlags(uint32_t* f_none,
+ uint32_t* f_terminated,
+ uint32_t* f_voided,
+ uint32_t* f_shared,
+ uint32_t* f_owned,
+ uint32_t* f_fixed,
+ uint32_t* f_literal,
+ uint32_t* f_class_fixed);
+TEST(RustNsString, NsStringFlags) {
+ uint32_t f_none, f_terminated, f_voided, f_shared, f_owned, f_fixed, f_literal, f_class_fixed;
+ Rust_Test_NsStringFlags(&f_none, &f_terminated,
+ &f_voided, &f_shared,
+ &f_owned, &f_fixed,
+ &f_literal, &f_class_fixed);
+ EXPECT_EQ(f_none, nsAString::F_NONE);
+ EXPECT_EQ(f_none, nsACString::F_NONE);
+ EXPECT_EQ(f_terminated, nsAString::F_TERMINATED);
+ EXPECT_EQ(f_terminated, nsACString::F_TERMINATED);
+ EXPECT_EQ(f_voided, nsAString::F_VOIDED);
+ EXPECT_EQ(f_voided, nsACString::F_VOIDED);
+ EXPECT_EQ(f_shared, nsAString::F_SHARED);
+ EXPECT_EQ(f_shared, nsACString::F_SHARED);
+ EXPECT_EQ(f_owned, nsAString::F_OWNED);
+ EXPECT_EQ(f_owned, nsACString::F_OWNED);
+ EXPECT_EQ(f_fixed, nsAString::F_FIXED);
+ EXPECT_EQ(f_fixed, nsACString::F_FIXED);
+ EXPECT_EQ(f_literal, nsAString::F_LITERAL);
+ EXPECT_EQ(f_literal, nsACString::F_LITERAL);
+ EXPECT_EQ(f_class_fixed, nsAString::F_CLASS_FIXED);
+ EXPECT_EQ(f_class_fixed, nsACString::F_CLASS_FIXED);
+}
+
+extern "C" void Rust_StringFromCpp(const nsACString* aCStr, const nsAString* aStr);
+TEST(RustNsString, StringFromCpp) {
+ nsAutoCString foo;
+ foo.AssignASCII("Hello, World!");
+
+ nsAutoString bar;
+ bar.AssignASCII("Hello, World!");
+
+ Rust_StringFromCpp(&foo, &bar);
+}
+
+extern "C" void Rust_AssignFromRust(nsACString* aCStr, nsAString* aStr);
+TEST(RustNsString, AssignFromRust) {
+ nsAutoCString cs;
+ nsAutoString s;
+ Rust_AssignFromRust(&cs, &s);
+ EXPECT_TRUE(cs.EqualsASCII("Hello, World!"));
+ EXPECT_TRUE(s.EqualsASCII("Hello, World!"));
+}
+
+extern "C" {
+ void Cpp_AssignFromCpp(nsACString* aCStr, nsAString* aStr) {
+ aCStr->AssignASCII("Hello, World!");
+ aStr->AssignASCII("Hello, World!");
+ }
+}
+extern "C" void Rust_AssignFromCpp();
+TEST(RustNsString, AssignFromCpp) {
+ Rust_AssignFromCpp();
+}
+extern "C" void Rust_FixedAssignFromCpp();
+TEST(RustNsString, FixedAssignFromCpp) {
+ Rust_FixedAssignFromCpp();
+}
+extern "C" void Rust_AutoAssignFromCpp();
+TEST(RustNsString, AutoAssignFromCpp) {
+ Rust_AutoAssignFromCpp();
+}
+
+extern "C" void Rust_StringWrite();
+TEST(RustNsString, StringWrite) {
+ Rust_StringWrite();
+}
diff --git a/xpcom/rust/nsstring/gtest/moz.build b/xpcom/rust/nsstring/gtest/moz.build
new file mode 100644
index 000000000..5bed9e57e
--- /dev/null
+++ b/xpcom/rust/nsstring/gtest/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['MOZ_RUST']:
+ UNIFIED_SOURCES += [
+ 'Test.cpp'
+ ]
+
+FINAL_LIBRARY = 'xul-gtest'
diff --git a/xpcom/rust/nsstring/gtest/test.rs b/xpcom/rust/nsstring/gtest/test.rs
new file mode 100644
index 000000000..2968a1be7
--- /dev/null
+++ b/xpcom/rust/nsstring/gtest/test.rs
@@ -0,0 +1,112 @@
+#![allow(non_snake_case)]
+
+#[macro_use]
+extern crate nsstring;
+
+use std::fmt::Write;
+use std::ffi::CString;
+use std::os::raw::c_char;
+use nsstring::*;
+
+fn nonfatal_fail(msg: String) {
+ extern "C" {
+ fn GTest_ExpectFailure(message: *const c_char);
+ }
+ unsafe {
+ GTest_ExpectFailure(CString::new(msg).unwrap().as_ptr());
+ }
+}
+
+/// This macro checks if the two arguments are equal, and causes a non-fatal
+/// GTest test failure if they are not.
+macro_rules! expect_eq {
+ ($x:expr, $y:expr) => {
+ match (&$x, &$y) {
+ (x, y) => if *x != *y {
+ nonfatal_fail(format!("check failed: (`{:?}` == `{:?}`) at {}:{}",
+ x, y, file!(), line!()))
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern fn Rust_StringFromCpp(cs: *const nsACString, s: *const nsAString) {
+ unsafe {
+ expect_eq!(&*cs, "Hello, World!");
+ expect_eq!(&*s, "Hello, World!");
+ }
+}
+
+#[no_mangle]
+pub extern fn Rust_AssignFromRust(cs: *mut nsACString, s: *mut nsAString) {
+ unsafe {
+ (*cs).assign(&nsCString::from("Hello, World!"));
+ expect_eq!(&*cs, "Hello, World!");
+ (*s).assign(&nsString::from("Hello, World!"));
+ expect_eq!(&*s, "Hello, World!");
+ }
+}
+
+extern "C" {
+ fn Cpp_AssignFromCpp(cs: *mut nsACString, s: *mut nsAString);
+}
+
+#[no_mangle]
+pub extern fn Rust_AssignFromCpp() {
+ let mut cs = nsCString::new();
+ let mut s = nsString::new();
+ unsafe {
+ Cpp_AssignFromCpp(&mut *cs, &mut *s);
+ }
+ expect_eq!(cs, "Hello, World!");
+ expect_eq!(s, "Hello, World!");
+}
+
+#[no_mangle]
+pub extern fn Rust_FixedAssignFromCpp() {
+ let mut cs_buf: [u8; 64] = [0; 64];
+ let cs_buf_ptr = &cs_buf as *const _ as usize;
+ let mut s_buf: [u16; 64] = [0; 64];
+ let s_buf_ptr = &s_buf as *const _ as usize;
+ let mut cs = nsFixedCString::new(&mut cs_buf);
+ let mut s = nsFixedString::new(&mut s_buf);
+ unsafe {
+ Cpp_AssignFromCpp(&mut *cs, &mut *s);
+ }
+ expect_eq!(cs, "Hello, World!");
+ expect_eq!(s, "Hello, World!");
+ expect_eq!(cs.as_ptr() as usize, cs_buf_ptr);
+ expect_eq!(s.as_ptr() as usize, s_buf_ptr);
+}
+
+#[no_mangle]
+pub extern fn Rust_AutoAssignFromCpp() {
+ ns_auto_cstring!(cs);
+ ns_auto_string!(s);
+ unsafe {
+ Cpp_AssignFromCpp(&mut *cs, &mut *s);
+ }
+ expect_eq!(cs, "Hello, World!");
+ expect_eq!(s, "Hello, World!");
+}
+
+#[no_mangle]
+pub extern fn Rust_StringWrite() {
+ ns_auto_cstring!(cs);
+ ns_auto_string!(s);
+
+ write!(s, "a").unwrap();
+ write!(cs, "a").unwrap();
+ expect_eq!(s, "a");
+ expect_eq!(cs, "a");
+ write!(s, "bc").unwrap();
+ write!(cs, "bc").unwrap();
+ expect_eq!(s, "abc");
+ expect_eq!(cs, "abc");
+ write!(s, "{}", 123).unwrap();
+ write!(cs, "{}", 123).unwrap();
+ expect_eq!(s, "abc123");
+ expect_eq!(cs, "abc123");
+}
+
diff --git a/xpcom/rust/nsstring/src/lib.rs b/xpcom/rust/nsstring/src/lib.rs
new file mode 100644
index 000000000..cd518f3c5
--- /dev/null
+++ b/xpcom/rust/nsstring/src/lib.rs
@@ -0,0 +1,853 @@
+//! This module provides rust bindings for the XPCOM string types.
+//!
+//! # TL;DR (what types should I use)
+//!
+//! Use `&{mut,} nsA[C]String` for functions in rust which wish to take or
+//! mutate XPCOM strings. The other string types `Deref` to this type.
+//!
+//! Use `ns[C]String<'a>` for string struct members which don't leave rust, and
+//! as an intermediate between rust string data structures (such as `String`,
+//! `Vec<u16>`, `&str`, and `&[u16]`) and `&{mut,} nsA[C]String` (using
+//! `ns[C]String::from(value)`). These conversions, when possible, will not
+//! perform any allocations.
+//!
+//! Use `nsFixed[C]String` or `ns_auto_[c]string!` for dynamic stack allocated
+//! strings which are expected to hold short string values.
+//!
+//! Use `*{const,mut} nsA[C]String` (`{const,} nsA[C]String*` in C++) for
+//! function arguments passed across the rust/C++ language boundary.
+//!
+//! Use `ns[C]StringRepr` for string struct members which are shared between
+//! rust and C++, but be careful, because this type lacks a `Drop`
+//! implementation.
+//!
+//! # String Types
+//!
+//! ## `nsA[C]String`
+//!
+//! The core types in this module are `nsAString` and `nsACString`. These types
+//! are zero-sized as far as rust is concerned, and are safe to pass around
+//! behind both references (in rust code), and pointers (in C++ code). They
+//! represent a handle to a XPCOM string which holds either `u16` or `u8`
+//! characters respectively. The backing character buffer is guaranteed to live
+//! as long as the reference to the `nsAString` or `nsACString`.
+//!
+//! These types in rust are simply used as dummy types. References to them
+//! represent a pointer to the beginning of a variable-sized `#[repr(C)]` struct
+//! which is common between both C++ and Rust implementations. In C++, their
+//! corresponding types are also named `nsAString` or `nsACString`, and they are
+//! defined within the `nsTSubstring.{cpp,h}` file.
+//!
+//! ### Valid Operations
+//!
+//! An `&nsA[C]String` acts like rust's `&str`, in that it is a borrowed
+//! reference to the backing data. When used as an argument to other functions
+//! on `&mut nsA[C]String`, optimizations can be performed to avoid copying
+//! buffers, as information about the backing storage is preserved.
+//!
+//! An `&mut nsA[C]String` acts like rust's `&mut Cow<str>`, in that it is a
+//! mutable reference to a potentially borrowed string, which when modified will
+//! ensure that it owns its own backing storage. This type can be appended to
+//! with the methods `.append`, `.append_utf{8,16}`, and with the `write!`
+//! macro, and can be assigned to with `.assign`.
+//!
+//! ## `ns[C]String<'a>`
+//!
+//! This type is an maybe-owned string type. It acts similarially to a
+//! `Cow<[{u8,u16}]>`. This type provides `Deref` and `DerefMut` implementations
+//! to `nsA[C]String`, which provides the methods for manipulating this type.
+//! This type's lifetime parameter, `'a`, represents the lifetime of the backing
+//! storage. When modified this type may re-allocate in order to ensure that it
+//! does not mutate its backing storage.
+//!
+//! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
+//! creates an empty `ns[C]String<'static>`, or through one of the provided
+//! `From` implementations. Both string types may be constructed `From<&'a
+//! str>`, with `nsCString` having a `'a` lifetime, as the storage is shared
+//! with the `str`, while `nsString` has a `'static` lifetime, as its storage
+//! has to be transcoded.
+//!
+//! When passing this type by reference, prefer passing a `&nsA[C]String` or
+//! `&mut nsA[C]String`. to passing this type.
+//!
+//! This type is _not_ `#[repr(C)]`, as it has a `Drop` impl, which in versions
+//! of `rustc < 1.13` adds drop flags to the struct, which messes up the layout,
+//! making it unsafe to pass across the FFI boundary. The rust compiler will
+//! warn if this type appears in `extern "C"` function definitions.
+//!
+//! When passing this type across the language boundary, pass it as `*const
+//! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
+//! mutable reference.
+//!
+//! This type is similar to the C++ type of the same name.
+//!
+//! ## `nsFixed[C]String<'a>`
+//!
+//! This type is a string type with fixed backing storage. It is created with
+//! `nsFixed[C]String::new(buffer)`, passing a mutable reference to a buffer as
+//! the argument. This buffer will be used as backing storage whenever the
+//! resulting string will fit within it, falling back to heap allocations only
+//! when the string size exceeds that of the backing buffer.
+//!
+//! Like `ns[C]String`, this type dereferences to `nsA[C]String` which provides
+//! the methods for manipulating the type, and is not `#[repr(C)]`.
+//!
+//! When passing this type by reference, prefer passing a `&nsA[C]String` or
+//! `&mut nsA[C]String`. to passing this type.
+//!
+//! This type is _not_ `#[repr(C)]`, as it has a `Drop` impl, which in versions
+//! of `rustc < 1.13` adds drop flags to the struct, which messes up the layout,
+//! making it unsafe to pass across the FFI boundary. The rust compiler will
+//! warn if this type appears in `extern "C"` function definitions.
+//!
+//! When passing this type across the language boundary, pass it as `*const
+//! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
+//! mutable reference.
+//!
+//! This type is similar to the C++ type of the same name.
+//!
+//! ## `ns_auto_[c]string!($name)`
+//!
+//! This is a helper macro which defines a fixed size, (currently 64 character),
+//! backing array on the stack, and defines a local variable with name `$name`
+//! which is a `nsFixed[C]String` using this buffer as its backing storage.
+//!
+//! Usage of this macro is similar to the C++ type `nsAuto[C]String`, but could
+//! not be implemented as a basic type due to the differences between rust and
+//! C++'s move semantics.
+//!
+//! ## `ns[C]StringRepr`
+//!
+//! This type represents a C++ `ns[C]String`. This type is `#[repr(C)]` and is
+//! safe to use in struct definitions which are shared across the language
+//! boundary. It automatically dereferences to `&{mut,} nsA[C]String`, and thus
+//! can be treated similarially to `ns[C]String`.
+//!
+//! If this type is dropped in rust, it will not free its backing storage. This
+//! is because types implementing `Drop` have a drop flag added, which messes up
+//! the layout of this type. When drop flags are removed, which should happen in
+//! `rustc 1.13` (see rust-lang/rust#35764), this type will likely be removed,
+//! and replaced with direct usage of `ns[C]String<'a>`, as its layout may be
+//! identical. This module provides rust bindings to our xpcom ns[C]String
+//! types.
+
+#![allow(non_camel_case_types)]
+
+use std::ops::{Deref, DerefMut};
+use std::marker::PhantomData;
+use std::slice;
+use std::ptr;
+use std::mem;
+use std::fmt;
+use std::cmp;
+use std::str;
+use std::u32;
+
+//////////////////////////////////
+// Internal Implemenation Flags //
+//////////////////////////////////
+
+const F_NONE: u32 = 0; // no flags
+
+// data flags are in the lower 16-bits
+const F_TERMINATED: u32 = 1 << 0; // IsTerminated returns true
+const F_VOIDED: u32 = 1 << 1; // IsVoid returns true
+const F_SHARED: u32 = 1 << 2; // mData points to a heap-allocated, shared buffer
+const F_OWNED: u32 = 1 << 3; // mData points to a heap-allocated, raw buffer
+const F_FIXED: u32 = 1 << 4; // mData points to a fixed-size writable, dependent buffer
+const F_LITERAL: u32 = 1 << 5; // mData points to a string literal; F_TERMINATED will also be set
+
+// class flags are in the upper 16-bits
+const F_CLASS_FIXED: u32 = 1 << 16; // indicates that |this| is of type nsTFixedString
+
+////////////////////////////////////
+// Generic String Bindings Macros //
+////////////////////////////////////
+
+macro_rules! define_string_types {
+ {
+ char_t = $char_t: ty;
+ AString = $AString: ident;
+ String = $String: ident;
+ FixedString = $FixedString: ident;
+
+ StringRepr = $StringRepr: ident;
+ FixedStringRepr = $FixedStringRepr: ident;
+ AutoStringRepr = $AutoStringRepr: ident;
+ } => {
+ /// The representation of a ns[C]String type in C++. This type is
+ /// used internally by our definition of ns[C]String to ensure layout
+ /// compatibility with the C++ ns[C]String type.
+ ///
+ /// This type may also be used in place of a C++ ns[C]String inside of
+ /// struct definitions which are shared with C++, as it has identical
+ /// layout to our ns[C]String type. Due to drop flags, our ns[C]String
+ /// type does not have identical layout. When drop flags are removed,
+ /// this type will likely be made a private implementation detail, and
+ /// its uses will be replaced with `ns[C]String`.
+ ///
+ /// This struct will leak its data if dropped from rust. See the module
+ /// documentation for more information on this type.
+ #[repr(C)]
+ pub struct $StringRepr {
+ data: *const $char_t,
+ length: u32,
+ flags: u32,
+ }
+
+ impl Deref for $StringRepr {
+ type Target = $AString;
+ fn deref(&self) -> &$AString {
+ unsafe {
+ mem::transmute(self)
+ }
+ }
+ }
+
+ impl DerefMut for $StringRepr {
+ fn deref_mut(&mut self) -> &mut $AString {
+ unsafe {
+ mem::transmute(self)
+ }
+ }
+ }
+
+ /// The representation of a nsFixed[C]String type in C++. This type is
+ /// used internally by our definition of nsFixed[C]String to ensure layout
+ /// compatibility with the C++ nsFixed[C]String type.
+ #[repr(C)]
+ struct $FixedStringRepr {
+ base: $StringRepr,
+ capacity: u32,
+ buffer: *mut $char_t,
+ }
+
+ /// This type is the abstract type which is used for interacting with
+ /// strings in rust. Each string type can derefence to an instance of
+ /// this type, which provides the useful operations on strings.
+ ///
+ /// NOTE: Rust thinks this type has a size of 0, because the data
+ /// associated with it is not necessarially safe to move. It is not safe
+ /// to construct a nsAString yourself, unless it is received by
+ /// dereferencing one of these types.
+ ///
+ /// NOTE: The `[u8; 0]` member is zero sized, and only exists to prevent
+ /// the construction by code outside of this module. It is used instead
+ /// of a private `()` member because the `improper_ctypes` lint complains
+ /// about some ZST members in `extern "C"` function declarations.
+ #[repr(C)]
+ pub struct $AString {
+ _prohibit_constructor: [u8; 0],
+ }
+
+ impl Deref for $AString {
+ type Target = [$char_t];
+ fn deref(&self) -> &[$char_t] {
+ unsafe {
+ // This is legal, as all $AString values actually point to a
+ // $StringRepr
+ let this: &$StringRepr = mem::transmute(self);
+ if this.data.is_null() {
+ debug_assert!(this.length == 0);
+ // Use an arbitrary non-null value as the pointer
+ slice::from_raw_parts(0x1 as *const $char_t, 0)
+ } else {
+ slice::from_raw_parts(this.data, this.length as usize)
+ }
+ }
+ }
+ }
+
+ impl cmp::PartialEq for $AString {
+ fn eq(&self, other: &$AString) -> bool {
+ &self[..] == &other[..]
+ }
+ }
+
+ impl cmp::PartialEq<[$char_t]> for $AString {
+ fn eq(&self, other: &[$char_t]) -> bool {
+ &self[..] == other
+ }
+ }
+
+ impl<'a> cmp::PartialEq<$String<'a>> for $AString {
+ fn eq(&self, other: &$String<'a>) -> bool {
+ self.eq(&**other)
+ }
+ }
+
+ impl<'a> cmp::PartialEq<$FixedString<'a>> for $AString {
+ fn eq(&self, other: &$FixedString<'a>) -> bool {
+ self.eq(&**other)
+ }
+ }
+
+ pub struct $String<'a> {
+ hdr: $StringRepr,
+ _marker: PhantomData<&'a [$char_t]>,
+ }
+
+ impl $String<'static> {
+ pub fn new() -> $String<'static> {
+ $String {
+ hdr: $StringRepr {
+ data: ptr::null(),
+ length: 0,
+ flags: F_NONE,
+ },
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ impl<'a> Deref for $String<'a> {
+ type Target = $AString;
+ fn deref(&self) -> &$AString {
+ &self.hdr
+ }
+ }
+
+ impl<'a> DerefMut for $String<'a> {
+ fn deref_mut(&mut self) -> &mut $AString {
+ &mut self.hdr
+ }
+ }
+
+ impl<'a> From<&'a String> for $String<'a> {
+ fn from(s: &'a String) -> $String<'a> {
+ $String::from(&s[..])
+ }
+ }
+
+ impl<'a> From<&'a Vec<$char_t>> for $String<'a> {
+ fn from(s: &'a Vec<$char_t>) -> $String<'a> {
+ $String::from(&s[..])
+ }
+ }
+
+ impl<'a> From<&'a [$char_t]> for $String<'a> {
+ fn from(s: &'a [$char_t]) -> $String<'a> {
+ assert!(s.len() < (u32::MAX as usize));
+ $String {
+ hdr: $StringRepr {
+ data: s.as_ptr(),
+ length: s.len() as u32,
+ flags: F_NONE,
+ },
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ impl From<Box<[$char_t]>> for $String<'static> {
+ fn from(s: Box<[$char_t]>) -> $String<'static> {
+ assert!(s.len() < (u32::MAX as usize));
+ // SAFETY NOTE: This method produces an F_OWNED ns[C]String from
+ // a Box<[$char_t]>. this is only safe because in the Gecko
+ // tree, we use the same allocator for Rust code as for C++
+ // code, meaning that our box can be legally freed with
+ // libc::free().
+ let length = s.len() as u32;
+ let ptr = s.as_ptr();
+ mem::forget(s);
+ $String {
+ hdr: $StringRepr {
+ data: ptr,
+ length: length,
+ flags: F_OWNED,
+ },
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ impl From<Vec<$char_t>> for $String<'static> {
+ fn from(s: Vec<$char_t>) -> $String<'static> {
+ s.into_boxed_slice().into()
+ }
+ }
+
+ impl<'a> From<&'a $AString> for $String<'static> {
+ fn from(s: &'a $AString) -> $String<'static> {
+ let mut string = $String::new();
+ string.assign(s);
+ string
+ }
+ }
+
+ impl<'a> fmt::Write for $String<'a> {
+ fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+ $AString::write_str(self, s)
+ }
+ }
+
+ impl<'a> fmt::Display for $String<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ <$AString as fmt::Display>::fmt(self, f)
+ }
+ }
+
+ impl<'a> fmt::Debug for $String<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ <$AString as fmt::Debug>::fmt(self, f)
+ }
+ }
+
+ impl<'a> cmp::PartialEq for $String<'a> {
+ fn eq(&self, other: &$String<'a>) -> bool {
+ $AString::eq(self, other)
+ }
+ }
+
+ impl<'a> cmp::PartialEq<[$char_t]> for $String<'a> {
+ fn eq(&self, other: &[$char_t]) -> bool {
+ $AString::eq(self, other)
+ }
+ }
+
+ impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $String<'a> {
+ fn eq(&self, other: &&'b [$char_t]) -> bool {
+ $AString::eq(self, *other)
+ }
+ }
+
+ impl<'a> cmp::PartialEq<str> for $String<'a> {
+ fn eq(&self, other: &str) -> bool {
+ $AString::eq(self, other)
+ }
+ }
+
+ impl<'a, 'b> cmp::PartialEq<&'b str> for $String<'a> {
+ fn eq(&self, other: &&'b str) -> bool {
+ $AString::eq(self, *other)
+ }
+ }
+
+ impl<'a> Drop for $String<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ self.finalize();
+ }
+ }
+ }
+
+ /// A nsFixed[C]String is a string which uses a fixed size mutable
+ /// backing buffer for storing strings which will fit within that
+ /// buffer, rather than using heap allocations.
+ pub struct $FixedString<'a> {
+ hdr: $FixedStringRepr,
+ _marker: PhantomData<&'a mut [$char_t]>,
+ }
+
+ impl<'a> $FixedString<'a> {
+ pub fn new(buf: &'a mut [$char_t]) -> $FixedString<'a> {
+ let len = buf.len();
+ assert!(len < (u32::MAX as usize));
+ let buf_ptr = buf.as_mut_ptr();
+ $FixedString {
+ hdr: $FixedStringRepr {
+ base: $StringRepr {
+ data: ptr::null(),
+ length: 0,
+ flags: F_CLASS_FIXED,
+ },
+ capacity: len as u32,
+ buffer: buf_ptr,
+ },
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ impl<'a> Deref for $FixedString<'a> {
+ type Target = $AString;
+ fn deref(&self) -> &$AString {
+ &self.hdr.base
+ }
+ }
+
+ impl<'a> DerefMut for $FixedString<'a> {
+ fn deref_mut(&mut self) -> &mut $AString {
+ &mut self.hdr.base
+ }
+ }
+
+ impl<'a> fmt::Write for $FixedString<'a> {
+ fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+ $AString::write_str(self, s)
+ }
+ }
+
+ impl<'a> fmt::Display for $FixedString<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ <$AString as fmt::Display>::fmt(self, f)
+ }
+ }
+
+ impl<'a> fmt::Debug for $FixedString<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ <$AString as fmt::Debug>::fmt(self, f)
+ }
+ }
+
+ impl<'a> cmp::PartialEq for $FixedString<'a> {
+ fn eq(&self, other: &$FixedString<'a>) -> bool {
+ $AString::eq(self, other)
+ }
+ }
+
+ impl<'a> cmp::PartialEq<[$char_t]> for $FixedString<'a> {
+ fn eq(&self, other: &[$char_t]) -> bool {
+ $AString::eq(self, other)
+ }
+ }
+
+ impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $FixedString<'a> {
+ fn eq(&self, other: &&'b [$char_t]) -> bool {
+ $AString::eq(self, *other)
+ }
+ }
+
+ impl<'a> cmp::PartialEq<str> for $FixedString<'a> {
+ fn eq(&self, other: &str) -> bool {
+ $AString::eq(self, other)
+ }
+ }
+
+ impl<'a, 'b> cmp::PartialEq<&'b str> for $FixedString<'a> {
+ fn eq(&self, other: &&'b str) -> bool {
+ $AString::eq(self, *other)
+ }
+ }
+
+ impl<'a> Drop for $FixedString<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ self.finalize();
+ }
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////
+// Bindings for nsCString (u8 char type) //
+///////////////////////////////////////////
+
+define_string_types! {
+ char_t = u8;
+
+ AString = nsACString;
+ String = nsCString;
+ FixedString = nsFixedCString;
+
+ StringRepr = nsCStringRepr;
+ FixedStringRepr = nsFixedCStringRepr;
+ AutoStringRepr = nsAutoCStringRepr;
+}
+
+impl nsACString {
+ /// Leaves the nsACString in an unstable state with a dangling data pointer.
+ /// Should only be used in drop implementations of rust types which wrap
+ /// this type.
+ unsafe fn finalize(&mut self) {
+ Gecko_FinalizeCString(self);
+ }
+
+ pub fn assign(&mut self, other: &nsACString) {
+ unsafe {
+ Gecko_AssignCString(self as *mut _, other as *const _);
+ }
+ }
+
+ pub fn assign_utf16(&mut self, other: &nsAString) {
+ self.assign(&nsCString::new());
+ self.append_utf16(other);
+ }
+
+ pub fn append(&mut self, other: &nsACString) {
+ unsafe {
+ Gecko_AppendCString(self as *mut _, other as *const _);
+ }
+ }
+
+ pub fn append_utf16(&mut self, other: &nsAString) {
+ unsafe {
+ Gecko_AppendUTF16toCString(self as *mut _, other as *const _);
+ }
+ }
+
+ pub unsafe fn as_str_unchecked(&self) -> &str {
+ str::from_utf8_unchecked(self)
+ }
+}
+
+impl<'a> From<&'a str> for nsCString<'a> {
+ fn from(s: &'a str) -> nsCString<'a> {
+ s.as_bytes().into()
+ }
+}
+
+impl From<Box<str>> for nsCString<'static> {
+ fn from(s: Box<str>) -> nsCString<'static> {
+ s.into_string().into()
+ }
+}
+
+impl From<String> for nsCString<'static> {
+ fn from(s: String) -> nsCString<'static> {
+ s.into_bytes().into()
+ }
+}
+
+// Support for the write!() macro for appending to nsACStrings
+impl fmt::Write for nsACString {
+ fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+ self.append(&nsCString::from(s));
+ Ok(())
+ }
+}
+
+impl fmt::Display for nsACString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fmt::Display::fmt(&String::from_utf8_lossy(&self[..]), f)
+ }
+}
+
+impl fmt::Debug for nsACString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fmt::Debug::fmt(&String::from_utf8_lossy(&self[..]), f)
+ }
+}
+
+impl cmp::PartialEq<str> for nsACString {
+ fn eq(&self, other: &str) -> bool {
+ &self[..] == other.as_bytes()
+ }
+}
+
+#[macro_export]
+macro_rules! ns_auto_cstring {
+ ($name:ident) => {
+ let mut buf: [u8; 64] = [0; 64];
+ let mut $name = $crate::nsFixedCString::new(&mut buf);
+ }
+}
+
+///////////////////////////////////////////
+// Bindings for nsString (u16 char type) //
+///////////////////////////////////////////
+
+define_string_types! {
+ char_t = u16;
+
+ AString = nsAString;
+ String = nsString;
+ FixedString = nsFixedString;
+
+ StringRepr = nsStringRepr;
+ FixedStringRepr = nsFixedStringRepr;
+ AutoStringRepr = nsAutoStringRepr;
+}
+
+impl nsAString {
+ /// Leaves the nsAString in an unstable state with a dangling data pointer.
+ /// Should only be used in drop implementations of rust types which wrap
+ /// this type.
+ unsafe fn finalize(&mut self) {
+ Gecko_FinalizeString(self);
+ }
+
+ pub fn assign(&mut self, other: &nsAString) {
+ unsafe {
+ Gecko_AssignString(self as *mut _, other as *const _);
+ }
+ }
+
+ pub fn assign_utf8(&mut self, other: &nsACString) {
+ self.assign(&nsString::new());
+ self.append_utf8(other);
+ }
+
+ pub fn append(&mut self, other: &nsAString) {
+ unsafe {
+ Gecko_AppendString(self as *mut _, other as *const _);
+ }
+ }
+
+ pub fn append_utf8(&mut self, other: &nsACString) {
+ unsafe {
+ Gecko_AppendUTF8toString(self as *mut _, other as *const _);
+ }
+ }
+}
+
+// NOTE: The From impl for a string slice for nsString produces a <'static>
+// lifetime, as it allocates.
+impl<'a> From<&'a str> for nsString<'static> {
+ fn from(s: &'a str) -> nsString<'static> {
+ s.encode_utf16().collect::<Vec<u16>>().into()
+ }
+}
+
+// Support for the write!() macro for writing to nsStrings
+impl fmt::Write for nsAString {
+ fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+ // Directly invoke gecko's routines for appending utf8 strings to
+ // nsAString values, to avoid as much overhead as possible
+ self.append_utf8(&nsCString::from(s));
+ Ok(())
+ }
+}
+
+impl fmt::Display for nsAString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fmt::Display::fmt(&String::from_utf16_lossy(&self[..]), f)
+ }
+}
+
+impl fmt::Debug for nsAString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fmt::Debug::fmt(&String::from_utf16_lossy(&self[..]), f)
+ }
+}
+
+impl cmp::PartialEq<str> for nsAString {
+ fn eq(&self, other: &str) -> bool {
+ other.encode_utf16().eq(self.iter().cloned())
+ }
+}
+
+#[macro_export]
+macro_rules! ns_auto_string {
+ ($name:ident) => {
+ let mut buf: [u16; 64] = [0; 64];
+ let mut $name = $crate::nsFixedString::new(&mut buf);
+ }
+}
+
+// NOTE: These bindings currently only expose infallible operations. Perhaps
+// consider allowing for fallible methods?
+extern "C" {
+ // Gecko implementation in nsSubstring.cpp
+ fn Gecko_FinalizeCString(this: *mut nsACString);
+ fn Gecko_AssignCString(this: *mut nsACString, other: *const nsACString);
+ fn Gecko_AppendCString(this: *mut nsACString, other: *const nsACString);
+
+ fn Gecko_FinalizeString(this: *mut nsAString);
+ fn Gecko_AssignString(this: *mut nsAString, other: *const nsAString);
+ fn Gecko_AppendString(this: *mut nsAString, other: *const nsAString);
+
+ // Gecko implementation in nsReadableUtils.cpp
+ fn Gecko_AppendUTF16toCString(this: *mut nsACString, other: *const nsAString);
+ fn Gecko_AppendUTF8toString(this: *mut nsAString, other: *const nsACString);
+}
+
+//////////////////////////////////////
+// Repr Validation Helper Functions //
+//////////////////////////////////////
+
+pub mod test_helpers {
+ //! This module only exists to help with ensuring that the layout of the
+ //! structs inside of rust and C++ are identical.
+ //!
+ //! It is public to ensure that these testing functions are avaliable to
+ //! gtest code.
+
+ use super::{
+ nsFixedCStringRepr,
+ nsFixedStringRepr,
+ nsCStringRepr,
+ nsStringRepr,
+ F_NONE,
+ F_TERMINATED,
+ F_VOIDED,
+ F_SHARED,
+ F_OWNED,
+ F_FIXED,
+ F_LITERAL,
+ F_CLASS_FIXED,
+ };
+ use std::mem;
+
+ /// Generates an #[no_mangle] extern "C" function which returns the size and
+ /// alignment of the given type with the given name.
+ macro_rules! size_align_check {
+ ($T:ty, $fname:ident) => {
+ #[no_mangle]
+ #[allow(non_snake_case)]
+ pub extern fn $fname(size: *mut usize, align: *mut usize) {
+ unsafe {
+ *size = mem::size_of::<$T>();
+ *align = mem::align_of::<$T>();
+ }
+ }
+ }
+ }
+
+ size_align_check!(nsStringRepr, Rust_Test_ReprSizeAlign_nsString);
+ size_align_check!(nsCStringRepr, Rust_Test_ReprSizeAlign_nsCString);
+ size_align_check!(nsFixedStringRepr, Rust_Test_ReprSizeAlign_nsFixedString);
+ size_align_check!(nsFixedCStringRepr, Rust_Test_ReprSizeAlign_nsFixedCString);
+
+ /// Generates a $[no_mangle] extern "C" function which returns the size,
+ /// alignment and offset in the parent struct of a given member, with the
+ /// given name.
+ ///
+ /// This method can trigger Undefined Behavior if the accessing the member
+ /// $member on a given type would use that type's `Deref` implementation.
+ macro_rules! member_check {
+ ($T:ty, $member:ident, $method:ident) => {
+ #[no_mangle]
+ #[allow(non_snake_case)]
+ pub extern fn $method(size: *mut usize,
+ align: *mut usize,
+ offset: *mut usize) {
+ unsafe {
+ // Create a temporary value of type T to get offsets, sizes
+ // and aligns off of
+ let tmp: $T = mem::zeroed();
+ *size = mem::size_of_val(&tmp.$member);
+ *align = mem::align_of_val(&tmp.$member);
+ *offset =
+ (&tmp.$member as *const _ as usize) -
+ (&tmp as *const _ as usize);
+ mem::forget(tmp);
+ }
+ }
+ }
+ }
+
+ member_check!(nsStringRepr, data, Rust_Test_Member_nsString_mData);
+ member_check!(nsStringRepr, length, Rust_Test_Member_nsString_mLength);
+ member_check!(nsStringRepr, flags, Rust_Test_Member_nsString_mFlags);
+ member_check!(nsCStringRepr, data, Rust_Test_Member_nsCString_mData);
+ member_check!(nsCStringRepr, length, Rust_Test_Member_nsCString_mLength);
+ member_check!(nsCStringRepr, flags, Rust_Test_Member_nsCString_mFlags);
+ member_check!(nsFixedStringRepr, capacity, Rust_Test_Member_nsFixedString_mFixedCapacity);
+ member_check!(nsFixedStringRepr, buffer, Rust_Test_Member_nsFixedString_mFixedBuf);
+ member_check!(nsFixedCStringRepr, capacity, Rust_Test_Member_nsFixedCString_mFixedCapacity);
+ member_check!(nsFixedCStringRepr, buffer, Rust_Test_Member_nsFixedCString_mFixedBuf);
+
+ #[no_mangle]
+ #[allow(non_snake_case)]
+ pub extern fn Rust_Test_NsStringFlags(f_none: *mut u32,
+ f_terminated: *mut u32,
+ f_voided: *mut u32,
+ f_shared: *mut u32,
+ f_owned: *mut u32,
+ f_fixed: *mut u32,
+ f_literal: *mut u32,
+ f_class_fixed: *mut u32) {
+ unsafe {
+ *f_none = F_NONE;
+ *f_terminated = F_TERMINATED;
+ *f_voided = F_VOIDED;
+ *f_shared = F_SHARED;
+ *f_owned = F_OWNED;
+ *f_fixed = F_FIXED;
+ *f_literal = F_LITERAL;
+ *f_class_fixed = F_CLASS_FIXED;
+ }
+ }
+}
diff --git a/xpcom/string/README.html b/xpcom/string/README.html
new file mode 100644
index 000000000..4a0927c65
--- /dev/null
+++ b/xpcom/string/README.html
@@ -0,0 +1,11 @@
+<html>
+<!-- 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/. -->
+<body>
+ <h1><span class="LXRSHORTDESC">managing sequences of characters</span></h1>
+<p>
+ <span class="LXRLONGDESC"></span>
+</p>
+</body>
+</html>
diff --git a/xpcom/string/crashtests/1113005-frame.html b/xpcom/string/crashtests/1113005-frame.html
new file mode 100644
index 000000000..505fc22f1
--- /dev/null
+++ b/xpcom/string/crashtests/1113005-frame.html
@@ -0,0 +1,5 @@
+<form method=post enctype=multipart/form-data action="data:text/html,"><textarea name='file"; filename="filename.ext
+ '></textarea>
+<script>
+document.forms[0].submit();
+</script>
diff --git a/xpcom/string/crashtests/1113005.html b/xpcom/string/crashtests/1113005.html
new file mode 100644
index 000000000..e377bb637
--- /dev/null
+++ b/xpcom/string/crashtests/1113005.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src="1113005-frame.html"></iframe>
diff --git a/xpcom/string/crashtests/394275-1.html b/xpcom/string/crashtests/394275-1.html
new file mode 100644
index 000000000..4f2afd1a6
--- /dev/null
+++ b/xpcom/string/crashtests/394275-1.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<script>
+style = document.createElement("style");
+document.documentElement.appendChild(style);
+style.textContent = "tz\uDAB2 ";
+</script>
+</body>
+</html>
diff --git a/xpcom/string/crashtests/395651-1.html b/xpcom/string/crashtests/395651-1.html
new file mode 100644
index 000000000..3f89a0836
--- /dev/null
+++ b/xpcom/string/crashtests/395651-1.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+<script>
+
+function X() { dump("X\n"); }
+function Y() { dump("Y\n"); }
+
+function boom()
+{
+ dump("Start9\n");
+
+ var div = document.getElementById("v");
+
+ var textNode = document.createTextNode(String.fromCharCode(0xDAAF)); // high surrogate
+ div.appendChild(textNode);
+
+ document.addEventListener("DOMCharacterDataModified", X, true);
+ textNode.data += 'B';
+ document.removeEventListener("DOMCharacterDataModified", X, true);
+
+ document.addEventListener("DOMAttrModified", Y, true);
+ textNode.data += String.fromCharCode(0xDF53); // low surrogate
+ document.removeEventListener("DOMAttrModified", Y, true);
+}
+
+</script>
+</head>
+
+<body onload="boom();"><div id="v"></div></body>
+
+</html>
diff --git a/xpcom/string/crashtests/crashtests.list b/xpcom/string/crashtests/crashtests.list
new file mode 100644
index 000000000..8562f1ad8
--- /dev/null
+++ b/xpcom/string/crashtests/crashtests.list
@@ -0,0 +1,3 @@
+load 394275-1.html
+load 395651-1.html
+load 1113005.html
diff --git a/xpcom/string/moz.build b/xpcom/string/moz.build
new file mode 100644
index 000000000..6ad7d7cc8
--- /dev/null
+++ b/xpcom/string/moz.build
@@ -0,0 +1,61 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('**'):
+ BUG_COMPONENT = ('Core', 'String')
+
+EXPORTS += [
+ 'nsAString.h',
+ 'nsCharTraits.h',
+ 'nsDependentString.h',
+ 'nsDependentSubstring.h',
+ 'nsEmbedString.h',
+ 'nsLiteralString.h',
+ 'nsPrintfCString.h',
+ 'nsPromiseFlatString.h',
+ 'nsReadableUtils.h',
+ 'nsString.h',
+ 'nsStringBuffer.h',
+ 'nsStringFwd.h',
+ 'nsStringIterator.h',
+ 'nsSubstring.h',
+ 'nsSubstringTuple.h',
+ 'nsTDependentString.h',
+ 'nsTDependentSubstring.h',
+ 'nsTLiteralString.h',
+ 'nsTPromiseFlatString.h',
+ 'nsTString.h',
+ 'nsTSubstring.h',
+ 'nsTSubstringTuple.h',
+ 'nsUTF8Utils.h',
+ 'nsXPCOMStrings.h',
+ 'nsXPIDLString.h',
+ 'string-template-def-char.h',
+ 'string-template-def-unichar.h',
+ 'string-template-undef.h',
+]
+
+UNIFIED_SOURCES += [
+ 'nsDependentString.cpp',
+ 'nsDependentSubstring.cpp',
+ 'nsPromiseFlatString.cpp',
+ 'nsReadableUtils.cpp',
+ 'nsString.cpp',
+ 'nsStringComparator.cpp',
+ 'nsStringObsolete.cpp',
+ 'nsSubstring.cpp',
+ 'nsSubstringTuple.cpp',
+]
+
+# Are we targeting x86 or x86-64? If so, compile the SSE2 functions for
+# nsUTF8Utils.cpp and nsReadableUtils.cpp.
+if CONFIG['INTEL_ARCHITECTURE']:
+ SOURCES += ['nsUTF8UtilsSSE2.cpp']
+ SOURCES['nsUTF8UtilsSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+ SOURCES += ['nsReadableUtilsSSE2.cpp']
+ SOURCES['nsReadableUtilsSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+
+FINAL_LIBRARY = 'xul'
diff --git a/xpcom/string/nsAString.h b/xpcom/string/nsAString.h
new file mode 100644
index 000000000..0cbea0dc7
--- /dev/null
+++ b/xpcom/string/nsAString.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+#ifndef nsAString_h___
+#define nsAString_h___
+
+#include "nsStringFwd.h"
+#include "nsStringIterator.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#define kNotFound -1
+
+// declare nsAString
+#include "string-template-def-unichar.h"
+#include "nsTSubstring.h"
+#include "string-template-undef.h"
+
+// declare nsACString
+#include "string-template-def-char.h"
+#include "nsTSubstring.h"
+#include "string-template-undef.h"
+
+
+/**
+ * ASCII case-insensitive comparator. (for Unicode case-insensitive
+ * comparision, see nsUnicharUtils.h)
+ */
+class nsCaseInsensitiveCStringComparator
+ : public nsCStringComparator
+{
+public:
+ nsCaseInsensitiveCStringComparator()
+ {
+ }
+ typedef char char_type;
+
+ virtual int operator()(const char_type*, const char_type*,
+ uint32_t, uint32_t) const override;
+};
+
+class nsCaseInsensitiveCStringArrayComparator
+{
+public:
+ template<class A, class B>
+ bool Equals(const A& aStrA, const B& aStrB) const
+ {
+ return aStrA.Equals(aStrB, nsCaseInsensitiveCStringComparator());
+ }
+};
+
+// included here for backwards compatibility
+#ifndef nsSubstringTuple_h___
+#include "nsSubstringTuple.h"
+#endif
+
+#endif // !defined(nsAString_h___)
diff --git a/xpcom/string/nsCharTraits.h b/xpcom/string/nsCharTraits.h
new file mode 100644
index 000000000..d93e1f5dc
--- /dev/null
+++ b/xpcom/string/nsCharTraits.h
@@ -0,0 +1,587 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCharTraits_h___
+#define nsCharTraits_h___
+
+#include <ctype.h> // for |EOF|, |WEOF|
+#include <string.h> // for |memcpy|, et al
+
+#include "nscore.h" // for |char16_t|
+
+// This file may be used (through nsUTF8Utils.h) from non-XPCOM code, in
+// particular the standalone software updater. In that case stub out
+// the macros provided by nsDebug.h which are only usable when linking XPCOM
+
+#ifdef NS_NO_XPCOM
+#define NS_WARNING(msg)
+#define NS_ASSERTION(cond, msg)
+#define NS_ERROR(msg)
+#else
+#include "nsDebug.h" // for NS_ASSERTION
+#endif
+
+/*
+ * Some macros for converting char16_t (UTF-16) to and from Unicode scalar
+ * values.
+ *
+ * Note that UTF-16 represents all Unicode scalar values up to U+10FFFF by
+ * using "surrogate pairs". These consist of a high surrogate, i.e. a code
+ * point in the range U+D800 - U+DBFF, and a low surrogate, i.e. a code point
+ * in the range U+DC00 - U+DFFF, like this:
+ *
+ * U+D800 U+DC00 = U+10000
+ * U+D800 U+DC01 = U+10001
+ * ...
+ * U+DBFF U+DFFE = U+10FFFE
+ * U+DBFF U+DFFF = U+10FFFF
+ *
+ * These surrogate code points U+D800 - U+DFFF are not themselves valid Unicode
+ * scalar values and are not well-formed UTF-16 except as high-surrogate /
+ * low-surrogate pairs.
+ */
+
+#define PLANE1_BASE uint32_t(0x00010000)
+// High surrogates are in the range 0xD800 -- OxDBFF
+#define NS_IS_HIGH_SURROGATE(u) ((uint32_t(u) & 0xFFFFFC00) == 0xD800)
+// Low surrogates are in the range 0xDC00 -- 0xDFFF
+#define NS_IS_LOW_SURROGATE(u) ((uint32_t(u) & 0xFFFFFC00) == 0xDC00)
+// Faster than testing NS_IS_HIGH_SURROGATE || NS_IS_LOW_SURROGATE
+#define IS_SURROGATE(u) ((uint32_t(u) & 0xFFFFF800) == 0xD800)
+
+// Everything else is not a surrogate: 0x000 -- 0xD7FF, 0xE000 -- 0xFFFF
+
+// N = (H - 0xD800) * 0x400 + 0x10000 + (L - 0xDC00)
+// I wonder whether we could somehow assert that H is a high surrogate
+// and L is a low surrogate
+#define SURROGATE_TO_UCS4(h, l) (((uint32_t(h) & 0x03FF) << 10) + \
+ (uint32_t(l) & 0x03FF) + PLANE1_BASE)
+
+// Extract surrogates from a UCS4 char
+// Reference: the Unicode standard 4.0, section 3.9
+// Since (c - 0x10000) >> 10 == (c >> 10) - 0x0080 and
+// 0xD7C0 == 0xD800 - 0x0080,
+// ((c - 0x10000) >> 10) + 0xD800 can be simplified to
+#define H_SURROGATE(c) char16_t(char16_t(uint32_t(c) >> 10) + \
+ char16_t(0xD7C0))
+// where it's to be noted that 0xD7C0 is not bitwise-OR'd
+// but added.
+
+// Since 0x10000 & 0x03FF == 0,
+// (c - 0x10000) & 0x03FF == c & 0x03FF so that
+// ((c - 0x10000) & 0x03FF) | 0xDC00 is equivalent to
+#define L_SURROGATE(c) char16_t(char16_t(uint32_t(c) & uint32_t(0x03FF)) | \
+ char16_t(0xDC00))
+
+#define IS_IN_BMP(ucs) (uint32_t(ucs) < PLANE1_BASE)
+#define UCS2_REPLACEMENT_CHAR char16_t(0xFFFD)
+
+#define UCS_END uint32_t(0x00110000)
+#define IS_VALID_CHAR(c) ((uint32_t(c) < UCS_END) && !IS_SURROGATE(c))
+#define ENSURE_VALID_CHAR(c) (IS_VALID_CHAR(c) ? (c) : UCS2_REPLACEMENT_CHAR)
+
+template <class CharT>
+struct nsCharTraits
+{
+};
+
+template <>
+struct nsCharTraits<char16_t>
+{
+ typedef char16_t char_type;
+ typedef uint16_t unsigned_char_type;
+ typedef char incompatible_char_type;
+
+ static char_type* const sEmptyBuffer;
+
+ static void
+ assign(char_type& aLhs, char_type aRhs)
+ {
+ aLhs = aRhs;
+ }
+
+
+ // integer representation of characters:
+ typedef int int_type;
+
+ static char_type
+ to_char_type(int_type aChar)
+ {
+ return char_type(aChar);
+ }
+
+ static int_type
+ to_int_type(char_type aChar)
+ {
+ return int_type(static_cast<unsigned_char_type>(aChar));
+ }
+
+ static bool
+ eq_int_type(int_type aLhs, int_type aRhs)
+ {
+ return aLhs == aRhs;
+ }
+
+
+ // |char_type| comparisons:
+
+ static bool
+ eq(char_type aLhs, char_type aRhs)
+ {
+ return aLhs == aRhs;
+ }
+
+ static bool
+ lt(char_type aLhs, char_type aRhs)
+ {
+ return aLhs < aRhs;
+ }
+
+
+ // operations on s[n] arrays:
+
+ static char_type*
+ move(char_type* aStr1, const char_type* aStr2, size_t aN)
+ {
+ return static_cast<char_type*>(memmove(aStr1, aStr2,
+ aN * sizeof(char_type)));
+ }
+
+ static char_type*
+ copy(char_type* aStr1, const char_type* aStr2, size_t aN)
+ {
+ return static_cast<char_type*>(memcpy(aStr1, aStr2,
+ aN * sizeof(char_type)));
+ }
+
+ static char_type*
+ copyASCII(char_type* aStr1, const char* aStr2, size_t aN)
+ {
+ for (char_type* s = aStr1; aN--; ++s, ++aStr2) {
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ *s = static_cast<char_type>(*aStr2);
+ }
+ return aStr1;
+ }
+
+ static char_type*
+ assign(char_type* aStr, size_t aN, char_type aChar)
+ {
+ char_type* result = aStr;
+ while (aN--) {
+ assign(*aStr++, aChar);
+ }
+ return result;
+ }
+
+ static int
+ compare(const char_type* aStr1, const char_type* aStr2, size_t aN)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ if (!eq(*aStr1, *aStr2)) {
+ return to_int_type(*aStr1) - to_int_type(*aStr2);
+ }
+ }
+
+ return 0;
+ }
+
+ static int
+ compareASCII(const char_type* aStr1, const char* aStr2, size_t aN)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ if (!eq_int_type(to_int_type(*aStr1),
+ to_int_type(static_cast<char_type>(*aStr2)))) {
+ return to_int_type(*aStr1) -
+ to_int_type(static_cast<char_type>(*aStr2));
+ }
+ }
+
+ return 0;
+ }
+
+ // this version assumes that s2 is null-terminated and s1 has length n.
+ // if s1 is shorter than s2 then we return -1; if s1 is longer than s2,
+ // we return 1.
+ static int
+ compareASCIINullTerminated(const char_type* aStr1, size_t aN,
+ const char* aStr2)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ if (!*aStr2) {
+ return 1;
+ }
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ if (!eq_int_type(to_int_type(*aStr1),
+ to_int_type(static_cast<char_type>(*aStr2)))) {
+ return to_int_type(*aStr1) -
+ to_int_type(static_cast<char_type>(*aStr2));
+ }
+ }
+
+ if (*aStr2) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Convert c to its lower-case form, but only if c is in the ASCII
+ * range. Otherwise leave it alone.
+ */
+ static char_type
+ ASCIIToLower(char_type aChar)
+ {
+ if (aChar >= 'A' && aChar <= 'Z') {
+ return char_type(aChar + ('a' - 'A'));
+ }
+
+ return aChar;
+ }
+
+ static int
+ compareLowerCaseToASCII(const char_type* aStr1, const char* aStr2, size_t aN)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ NS_ASSERTION(!(*aStr2 >= 'A' && *aStr2 <= 'Z'),
+ "Unexpected uppercase character");
+ char_type lower_s1 = ASCIIToLower(*aStr1);
+ if (lower_s1 != static_cast<char_type>(*aStr2)) {
+ return to_int_type(lower_s1) -
+ to_int_type(static_cast<char_type>(*aStr2));
+ }
+ }
+
+ return 0;
+ }
+
+ // this version assumes that s2 is null-terminated and s1 has length n.
+ // if s1 is shorter than s2 then we return -1; if s1 is longer than s2,
+ // we return 1.
+ static int
+ compareLowerCaseToASCIINullTerminated(const char_type* aStr1,
+ size_t aN, const char* aStr2)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ if (!*aStr2) {
+ return 1;
+ }
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ NS_ASSERTION(!(*aStr2 >= 'A' && *aStr2 <= 'Z'),
+ "Unexpected uppercase character");
+ char_type lower_s1 = ASCIIToLower(*aStr1);
+ if (lower_s1 != static_cast<char_type>(*aStr2)) {
+ return to_int_type(lower_s1) -
+ to_int_type(static_cast<char_type>(*aStr2));
+ }
+ }
+
+ if (*aStr2) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ static size_t
+ length(const char_type* aStr)
+ {
+ size_t result = 0;
+ while (!eq(*aStr++, char_type(0))) {
+ ++result;
+ }
+ return result;
+ }
+
+ static const char_type*
+ find(const char_type* aStr, size_t aN, char_type aChar)
+ {
+ while (aN--) {
+ if (eq(*aStr, aChar)) {
+ return aStr;
+ }
+ ++aStr;
+ }
+
+ return 0;
+ }
+};
+
+template <>
+struct nsCharTraits<char>
+{
+ typedef char char_type;
+ typedef unsigned char unsigned_char_type;
+ typedef char16_t incompatible_char_type;
+
+ static char_type* const sEmptyBuffer;
+
+ static void
+ assign(char_type& aLhs, char_type aRhs)
+ {
+ aLhs = aRhs;
+ }
+
+
+ // integer representation of characters:
+
+ typedef int int_type;
+
+ static char_type
+ to_char_type(int_type aChar)
+ {
+ return char_type(aChar);
+ }
+
+ static int_type
+ to_int_type(char_type aChar)
+ {
+ return int_type(static_cast<unsigned_char_type>(aChar));
+ }
+
+ static bool
+ eq_int_type(int_type aLhs, int_type aRhs)
+ {
+ return aLhs == aRhs;
+ }
+
+
+ // |char_type| comparisons:
+
+ static bool eq(char_type aLhs, char_type aRhs)
+ {
+ return aLhs == aRhs;
+ }
+
+ static bool
+ lt(char_type aLhs, char_type aRhs)
+ {
+ return aLhs < aRhs;
+ }
+
+
+ // operations on s[n] arrays:
+
+ static char_type*
+ move(char_type* aStr1, const char_type* aStr2, size_t aN)
+ {
+ return static_cast<char_type*>(memmove(aStr1, aStr2,
+ aN * sizeof(char_type)));
+ }
+
+ static char_type*
+ copy(char_type* aStr1, const char_type* aStr2, size_t aN)
+ {
+ return static_cast<char_type*>(memcpy(aStr1, aStr2,
+ aN * sizeof(char_type)));
+ }
+
+ static char_type*
+ copyASCII(char_type* aStr1, const char* aStr2, size_t aN)
+ {
+ return copy(aStr1, aStr2, aN);
+ }
+
+ static char_type*
+ assign(char_type* aStr, size_t aN, char_type aChar)
+ {
+ return static_cast<char_type*>(memset(aStr, to_int_type(aChar), aN));
+ }
+
+ static int
+ compare(const char_type* aStr1, const char_type* aStr2, size_t aN)
+ {
+ return memcmp(aStr1, aStr2, aN);
+ }
+
+ static int
+ compareASCII(const char_type* aStr1, const char* aStr2, size_t aN)
+ {
+#ifdef DEBUG
+ for (size_t i = 0; i < aN; ++i) {
+ NS_ASSERTION(!(aStr2[i] & ~0x7F), "Unexpected non-ASCII character");
+ }
+#endif
+ return compare(aStr1, aStr2, aN);
+ }
+
+ // this version assumes that s2 is null-terminated and s1 has length n.
+ // if s1 is shorter than s2 then we return -1; if s1 is longer than s2,
+ // we return 1.
+ static int
+ compareASCIINullTerminated(const char_type* aStr1, size_t aN,
+ const char* aStr2)
+ {
+ // can't use strcmp here because we don't want to stop when aStr1
+ // contains a null
+ for (; aN--; ++aStr1, ++aStr2) {
+ if (!*aStr2) {
+ return 1;
+ }
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ if (*aStr1 != *aStr2) {
+ return to_int_type(*aStr1) - to_int_type(*aStr2);
+ }
+ }
+
+ if (*aStr2) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Convert c to its lower-case form, but only if c is ASCII.
+ */
+ static char_type
+ ASCIIToLower(char_type aChar)
+ {
+ if (aChar >= 'A' && aChar <= 'Z') {
+ return char_type(aChar + ('a' - 'A'));
+ }
+
+ return aChar;
+ }
+
+ static int
+ compareLowerCaseToASCII(const char_type* aStr1, const char* aStr2, size_t aN)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ NS_ASSERTION(!(*aStr2 >= 'A' && *aStr2 <= 'Z'),
+ "Unexpected uppercase character");
+ char_type lower_s1 = ASCIIToLower(*aStr1);
+ if (lower_s1 != *aStr2) {
+ return to_int_type(lower_s1) - to_int_type(*aStr2);
+ }
+ }
+ return 0;
+ }
+
+ // this version assumes that s2 is null-terminated and s1 has length n.
+ // if s1 is shorter than s2 then we return -1; if s1 is longer than s2,
+ // we return 1.
+ static int
+ compareLowerCaseToASCIINullTerminated(const char_type* aStr1, size_t aN,
+ const char* aStr2)
+ {
+ for (; aN--; ++aStr1, ++aStr2) {
+ if (!*aStr2) {
+ return 1;
+ }
+ NS_ASSERTION(!(*aStr2 & ~0x7F), "Unexpected non-ASCII character");
+ NS_ASSERTION(!(*aStr2 >= 'A' && *aStr2 <= 'Z'),
+ "Unexpected uppercase character");
+ char_type lower_s1 = ASCIIToLower(*aStr1);
+ if (lower_s1 != *aStr2) {
+ return to_int_type(lower_s1) - to_int_type(*aStr2);
+ }
+ }
+
+ if (*aStr2) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ static size_t
+ length(const char_type* aStr)
+ {
+ return strlen(aStr);
+ }
+
+ static const char_type*
+ find(const char_type* aStr, size_t aN, char_type aChar)
+ {
+ return reinterpret_cast<const char_type*>(memchr(aStr, to_int_type(aChar),
+ aN));
+ }
+};
+
+template <class InputIterator>
+struct nsCharSourceTraits
+{
+ typedef typename InputIterator::difference_type difference_type;
+
+ static uint32_t
+ readable_distance(const InputIterator& aFirst, const InputIterator& aLast)
+ {
+ // assumes single fragment
+ return uint32_t(aLast.get() - aFirst.get());
+ }
+
+ static const typename InputIterator::value_type*
+ read(const InputIterator& aIter)
+ {
+ return aIter.get();
+ }
+
+ static void
+ advance(InputIterator& aStr, difference_type aN)
+ {
+ aStr.advance(aN);
+ }
+};
+
+template <class CharT>
+struct nsCharSourceTraits<CharT*>
+{
+ typedef ptrdiff_t difference_type;
+
+ static uint32_t
+ readable_distance(CharT* aStr)
+ {
+ return uint32_t(nsCharTraits<CharT>::length(aStr));
+ // return numeric_limits<uint32_t>::max();
+ }
+
+ static uint32_t
+ readable_distance(CharT* aFirst, CharT* aLast)
+ {
+ return uint32_t(aLast - aFirst);
+ }
+
+ static const CharT*
+ read(CharT* aStr)
+ {
+ return aStr;
+ }
+
+ static void
+ advance(CharT*& aStr, difference_type aN)
+ {
+ aStr += aN;
+ }
+};
+
+template <class OutputIterator>
+struct nsCharSinkTraits
+{
+ static void
+ write(OutputIterator& aIter, const typename OutputIterator::value_type* aStr,
+ uint32_t aN)
+ {
+ aIter.write(aStr, aN);
+ }
+};
+
+template <class CharT>
+struct nsCharSinkTraits<CharT*>
+{
+ static void
+ write(CharT*& aIter, const CharT* aStr, uint32_t aN)
+ {
+ nsCharTraits<CharT>::move(aIter, aStr, aN);
+ aIter += aN;
+ }
+};
+
+#endif // !defined(nsCharTraits_h___)
diff --git a/xpcom/string/nsDependentString.cpp b/xpcom/string/nsDependentString.cpp
new file mode 100644
index 000000000..52240d17b
--- /dev/null
+++ b/xpcom/string/nsDependentString.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDependentString.h"
+#include "nsAlgorithm.h"
+
+// define nsDependentString
+#include "string-template-def-unichar.h"
+#include "nsTDependentString.cpp"
+#include "string-template-undef.h"
+
+// define nsDependentCString
+#include "string-template-def-char.h"
+#include "nsTDependentString.cpp"
+#include "string-template-undef.h"
diff --git a/xpcom/string/nsDependentString.h b/xpcom/string/nsDependentString.h
new file mode 100644
index 000000000..20b5997ef
--- /dev/null
+++ b/xpcom/string/nsDependentString.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDependentString_h___
+#define nsDependentString_h___
+
+#include "nsString.h"
+#include "nsDebug.h"
+
+// declare nsDependentString
+#include "string-template-def-unichar.h"
+#include "nsTDependentString.h"
+#include "string-template-undef.h"
+
+// declare nsDependentCString
+#include "string-template-def-char.h"
+#include "nsTDependentString.h"
+#include "string-template-undef.h"
+
+#endif /* !defined(nsDependentString_h___) */
diff --git a/xpcom/string/nsDependentSubstring.cpp b/xpcom/string/nsDependentSubstring.cpp
new file mode 100644
index 000000000..721cf8f6a
--- /dev/null
+++ b/xpcom/string/nsDependentSubstring.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDependentSubstring.h"
+#include "nsAlgorithm.h"
+
+// define nsDependentSubstring
+#include "string-template-def-unichar.h"
+#include "nsTDependentSubstring.cpp"
+#include "string-template-undef.h"
+
+// define nsDependentCSubstring
+#include "string-template-def-char.h"
+#include "nsTDependentSubstring.cpp"
+#include "string-template-undef.h"
diff --git a/xpcom/string/nsDependentSubstring.h b/xpcom/string/nsDependentSubstring.h
new file mode 100644
index 000000000..078b8ab54
--- /dev/null
+++ b/xpcom/string/nsDependentSubstring.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDependentSubstring_h___
+#define nsDependentSubstring_h___
+
+#include "nsSubstring.h"
+
+// declare nsDependentSubstring
+#include "string-template-def-unichar.h"
+#include "nsTDependentSubstring.h"
+#include "string-template-undef.h"
+
+// declare nsDependentCSubstring
+#include "string-template-def-char.h"
+#include "nsTDependentSubstring.h"
+#include "string-template-undef.h"
+
+#endif /* !defined(nsDependentSubstring_h___) */
diff --git a/xpcom/string/nsEmbedString.h b/xpcom/string/nsEmbedString.h
new file mode 100644
index 000000000..caedd50cd
--- /dev/null
+++ b/xpcom/string/nsEmbedString.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsEmbedString_h___
+#define nsEmbedString_h___
+
+#include "nsStringAPI.h"
+
+/**
+ * compatibility
+ */
+typedef nsString nsEmbedString;
+typedef nsCString nsEmbedCString;
+
+#endif
diff --git a/xpcom/string/nsLiteralString.h b/xpcom/string/nsLiteralString.h
new file mode 100644
index 000000000..4a0b39107
--- /dev/null
+++ b/xpcom/string/nsLiteralString.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsLiteralString_h___
+#define nsLiteralString_h___
+
+#include "nscore.h"
+#include "nsString.h"
+
+// declare nsLiteralString
+#include "string-template-def-unichar.h"
+#include "nsTLiteralString.h"
+#include "string-template-undef.h"
+
+// declare nsLiteralCString
+#include "string-template-def-char.h"
+#include "nsTLiteralString.h"
+#include "string-template-undef.h"
+
+#include "mozilla/Char16.h"
+
+#define NS_MULTILINE_LITERAL_STRING(s) static_cast<const nsLiteralString&>(nsLiteralString(s))
+#define NS_MULTILINE_LITERAL_STRING_INIT(n,s) n(s)
+#define NS_NAMED_MULTILINE_LITERAL_STRING(n,s) const nsLiteralString n(s)
+
+#define NS_LITERAL_STRING(s) static_cast<const nsLiteralString&>(nsLiteralString(u"" s))
+#define NS_LITERAL_STRING_INIT(n,s) n(u"" s)
+#define NS_NAMED_LITERAL_STRING(n,s) const nsLiteralString n(u"" s)
+
+#define NS_LITERAL_CSTRING(s) static_cast<const nsLiteralCString&>(nsLiteralCString("" s))
+#define NS_LITERAL_CSTRING_INIT(n,s) n("" s)
+#define NS_NAMED_LITERAL_CSTRING(n,s) const nsLiteralCString n("" s)
+
+#endif /* !defined(nsLiteralString_h___) */
diff --git a/xpcom/string/nsPrintfCString.h b/xpcom/string/nsPrintfCString.h
new file mode 100644
index 000000000..ce90ec497
--- /dev/null
+++ b/xpcom/string/nsPrintfCString.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPrintfCString_h___
+#define nsPrintfCString_h___
+
+#include "nsString.h"
+
+/**
+ * nsPrintfCString lets you create a nsCString using a printf-style format
+ * string. For example:
+ *
+ * NS_WARNING(nsPrintfCString("Unexpected value: %f", 13.917).get());
+ *
+ * nsPrintfCString has a small built-in auto-buffer. For larger strings, it
+ * will allocate on the heap.
+ *
+ * See also nsCString::AppendPrintf().
+ */
+class nsPrintfCString : public nsFixedCString
+{
+ typedef nsCString string_type;
+
+public:
+ explicit nsPrintfCString(const char_type* aFormat, ...)
+ : nsFixedCString(mLocalBuffer, kLocalBufferSize, 0)
+ {
+ va_list ap;
+ va_start(ap, aFormat);
+ AppendPrintf(aFormat, ap);
+ va_end(ap);
+ }
+
+private:
+ static const uint32_t kLocalBufferSize = 16;
+ char_type mLocalBuffer[kLocalBufferSize];
+};
+
+#endif // !defined(nsPrintfCString_h___)
diff --git a/xpcom/string/nsPromiseFlatString.cpp b/xpcom/string/nsPromiseFlatString.cpp
new file mode 100644
index 000000000..e66d2ef9f
--- /dev/null
+++ b/xpcom/string/nsPromiseFlatString.cpp
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsPromiseFlatString.h"
+
+// define nsPromiseFlatString
+#include "string-template-def-unichar.h"
+#include "nsTPromiseFlatString.cpp"
+#include "string-template-undef.h"
+
+// define nsPromiseFlatCString
+#include "string-template-def-char.h"
+#include "nsTPromiseFlatString.cpp"
+#include "string-template-undef.h"
diff --git a/xpcom/string/nsPromiseFlatString.h b/xpcom/string/nsPromiseFlatString.h
new file mode 100644
index 000000000..a025bfd26
--- /dev/null
+++ b/xpcom/string/nsPromiseFlatString.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPromiseFlatString_h___
+#define nsPromiseFlatString_h___
+
+#include "nsString.h"
+
+// declare nsPromiseFlatString
+#include "string-template-def-unichar.h"
+#include "nsTPromiseFlatString.h"
+#include "string-template-undef.h"
+
+// declare nsPromiseFlatCString
+#include "string-template-def-char.h"
+#include "nsTPromiseFlatString.h"
+#include "string-template-undef.h"
+
+#endif /* !defined(nsPromiseFlatString_h___) */
diff --git a/xpcom/string/nsReadableUtils.cpp b/xpcom/string/nsReadableUtils.cpp
new file mode 100644
index 000000000..524b1d7fe
--- /dev/null
+++ b/xpcom/string/nsReadableUtils.cpp
@@ -0,0 +1,1383 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsReadableUtils.h"
+#include "nsReadableUtilsImpl.h"
+
+#include <algorithm>
+
+#include "mozilla/CheckedInt.h"
+
+#include "nscore.h"
+#include "nsMemory.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsUTF8Utils.h"
+
+using mozilla::IsASCII;
+
+/**
+ * Fallback implementation for finding the first non-ASCII character in a
+ * UTF-16 string.
+ */
+static inline int32_t
+FirstNonASCIIUnvectorized(const char16_t* aBegin, const char16_t* aEnd)
+{
+ typedef mozilla::NonASCIIParameters<sizeof(size_t)> p;
+ const size_t kMask = p::mask();
+ const uintptr_t kAlignMask = p::alignMask();
+ const size_t kNumUnicharsPerWord = p::numUnicharsPerWord();
+
+ const char16_t* idx = aBegin;
+
+ // Align ourselves to a word boundary.
+ for (; idx != aEnd && ((uintptr_t(idx) & kAlignMask) != 0); idx++) {
+ if (!IsASCII(*idx)) {
+ return idx - aBegin;
+ }
+ }
+
+ // Check one word at a time.
+ const char16_t* wordWalkEnd = mozilla::aligned(aEnd, kAlignMask);
+ for (; idx != wordWalkEnd; idx += kNumUnicharsPerWord) {
+ const size_t word = *reinterpret_cast<const size_t*>(idx);
+ if (word & kMask) {
+ return idx - aBegin;
+ }
+ }
+
+ // Take care of the remainder one character at a time.
+ for (; idx != aEnd; idx++) {
+ if (!IsASCII(*idx)) {
+ return idx - aBegin;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * This function returns -1 if all characters in str are ASCII characters.
+ * Otherwise, it returns a value less than or equal to the index of the first
+ * ASCII character in str. For example, if first non-ASCII character is at
+ * position 25, it may return 25, 24, or 16. But it guarantees
+ * there are only ASCII characters before returned value.
+ */
+static inline int32_t
+FirstNonASCII(const char16_t* aBegin, const char16_t* aEnd)
+{
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ return mozilla::SSE2::FirstNonASCII(aBegin, aEnd);
+ }
+#endif
+
+ return FirstNonASCIIUnvectorized(aBegin, aEnd);
+}
+
+void
+LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest)
+{
+ aDest.Truncate();
+ LossyAppendUTF16toASCII(aSource, aDest);
+}
+
+void
+CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ aDest.Truncate();
+ AppendASCIItoUTF16(aSource, aDest);
+}
+
+void
+LossyCopyUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
+{
+ aDest.Truncate();
+ if (aSource) {
+ LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
+ }
+}
+
+void
+CopyASCIItoUTF16(const char* aSource, nsAString& aDest)
+{
+ aDest.Truncate();
+ if (aSource) {
+ AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
+ }
+}
+
+void
+CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
+{
+ if (!CopyUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
+ // Note that this may wildly underestimate the allocation that failed, as
+ // we report the length of aSource as UTF-16 instead of UTF-8.
+ aDest.AllocFailed(aDest.Length() + aSource.Length());
+ }
+}
+
+bool
+CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
+ const mozilla::fallible_t& aFallible)
+{
+ aDest.Truncate();
+ if (!AppendUTF16toUTF8(aSource, aDest, aFallible)) {
+ return false;
+ }
+ return true;
+}
+
+void
+CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ aDest.Truncate();
+ AppendUTF8toUTF16(aSource, aDest);
+}
+
+void
+CopyUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
+{
+ aDest.Truncate();
+ AppendUTF16toUTF8(aSource, aDest);
+}
+
+void
+CopyUTF8toUTF16(const char* aSource, nsAString& aDest)
+{
+ aDest.Truncate();
+ AppendUTF8toUTF16(aSource, aDest);
+}
+
+void
+LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest)
+{
+ uint32_t old_dest_length = aDest.Length();
+ aDest.SetLength(old_dest_length + aSource.Length());
+
+ nsAString::const_iterator fromBegin, fromEnd;
+
+ nsACString::iterator dest;
+ aDest.BeginWriting(dest);
+
+ dest.advance(old_dest_length);
+
+ // right now, this won't work on multi-fragment destinations
+ LossyConvertEncoding16to8 converter(dest.get());
+
+ copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ converter);
+}
+
+void
+AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ if (!AppendASCIItoUTF16(aSource, aDest, mozilla::fallible)) {
+ aDest.AllocFailed(aDest.Length() + aSource.Length());
+ }
+}
+
+bool
+AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest,
+ const mozilla::fallible_t& aFallible)
+{
+ uint32_t old_dest_length = aDest.Length();
+ if (!aDest.SetLength(old_dest_length + aSource.Length(),
+ aFallible)) {
+ return false;
+ }
+
+ nsACString::const_iterator fromBegin, fromEnd;
+
+ nsAString::iterator dest;
+ aDest.BeginWriting(dest);
+
+ dest.advance(old_dest_length);
+
+ // right now, this won't work on multi-fragment destinations
+ LossyConvertEncoding8to16 converter(dest.get());
+
+ copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ converter);
+ return true;
+}
+
+void
+LossyAppendUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
+{
+ if (aSource) {
+ LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
+ }
+}
+
+bool
+AppendASCIItoUTF16(const char* aSource, nsAString& aDest, const mozilla::fallible_t& aFallible)
+{
+ if (aSource) {
+ return AppendASCIItoUTF16(nsDependentCString(aSource), aDest, aFallible);
+ }
+
+ return true;
+}
+
+void
+AppendASCIItoUTF16(const char* aSource, nsAString& aDest)
+{
+ if (aSource) {
+ AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
+ }
+}
+
+void
+AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
+{
+ if (!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
+ // Note that this may wildly underestimate the allocation that failed, as
+ // we report the length of aSource as UTF-16 instead of UTF-8.
+ aDest.AllocFailed(aDest.Length() + aSource.Length());
+ }
+}
+
+bool
+AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
+ const mozilla::fallible_t& aFallible)
+{
+ // At 16 characters analysis showed better performance of both the all ASCII
+ // and non-ASCII cases, so we limit calling |FirstNonASCII| to strings of
+ // that length.
+ const nsAString::size_type kFastPathMinLength = 16;
+
+ int32_t firstNonASCII = 0;
+ if (aSource.Length() >= kFastPathMinLength) {
+ firstNonASCII = FirstNonASCII(aSource.BeginReading(), aSource.EndReading());
+ }
+
+ if (firstNonASCII == -1) {
+ // This is all ASCII, we can use the more efficient lossy append.
+ mozilla::CheckedInt<nsACString::size_type> new_length(aSource.Length());
+ new_length += aDest.Length();
+
+ if (!new_length.isValid() ||
+ !aDest.SetCapacity(new_length.value(), aFallible)) {
+ return false;
+ }
+
+ LossyAppendUTF16toASCII(aSource, aDest);
+ return true;
+ }
+
+ nsAString::const_iterator source_start, source_end;
+ CalculateUTF8Size calculator;
+ aSource.BeginReading(source_start);
+ aSource.EndReading(source_end);
+
+ // Skip the characters that we know are single byte.
+ source_start.advance(firstNonASCII);
+
+ copy_string(source_start,
+ source_end, calculator);
+
+ // Include the ASCII characters that were skipped in the count.
+ size_t count = calculator.Size() + firstNonASCII;
+
+ if (count) {
+ auto old_dest_length = aDest.Length();
+ // Grow the buffer if we need to.
+ mozilla::CheckedInt<nsACString::size_type> new_length(count);
+ new_length += old_dest_length;
+
+ if (!new_length.isValid() ||
+ !aDest.SetLength(new_length.value(), aFallible)) {
+ return false;
+ }
+
+ // All ready? Time to convert
+
+ nsAString::const_iterator ascii_end;
+ aSource.BeginReading(ascii_end);
+
+ if (firstNonASCII >= static_cast<int32_t>(kFastPathMinLength)) {
+ // Use the more efficient lossy converter for the ASCII portion.
+ LossyConvertEncoding16to8 lossy_converter(
+ aDest.BeginWriting() + old_dest_length);
+ nsAString::const_iterator ascii_start;
+ aSource.BeginReading(ascii_start);
+ ascii_end.advance(firstNonASCII);
+
+ copy_string(ascii_start, ascii_end, lossy_converter);
+ } else {
+ // Not using the lossy shortcut, we need to include the leading ASCII
+ // chars.
+ firstNonASCII = 0;
+ }
+
+ ConvertUTF16toUTF8 converter(
+ aDest.BeginWriting() + old_dest_length + firstNonASCII);
+ copy_string(ascii_end,
+ aSource.EndReading(source_end), converter);
+
+ NS_ASSERTION(converter.Size() == count - firstNonASCII,
+ "Unexpected disparity between CalculateUTF8Size and "
+ "ConvertUTF16toUTF8");
+ }
+
+ return true;
+}
+
+void
+AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ if (!AppendUTF8toUTF16(aSource, aDest, mozilla::fallible)) {
+ aDest.AllocFailed(aDest.Length() + aSource.Length());
+ }
+}
+
+bool
+AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest,
+ const mozilla::fallible_t& aFallible)
+{
+ nsACString::const_iterator source_start, source_end;
+ CalculateUTF8Length calculator;
+ copy_string(aSource.BeginReading(source_start),
+ aSource.EndReading(source_end), calculator);
+
+ uint32_t count = calculator.Length();
+
+ // Avoid making the string mutable if we're appending an empty string
+ if (count) {
+ uint32_t old_dest_length = aDest.Length();
+
+ // Grow the buffer if we need to.
+ if (!aDest.SetLength(old_dest_length + count, aFallible)) {
+ return false;
+ }
+
+ // All ready? Time to convert
+
+ ConvertUTF8toUTF16 converter(aDest.BeginWriting() + old_dest_length);
+ copy_string(aSource.BeginReading(source_start),
+ aSource.EndReading(source_end), converter);
+
+ NS_ASSERTION(converter.ErrorEncountered() ||
+ converter.Length() == count,
+ "CalculateUTF8Length produced the wrong length");
+
+ if (converter.ErrorEncountered()) {
+ NS_ERROR("Input wasn't UTF8 or incorrect length was calculated");
+ aDest.SetLength(old_dest_length);
+ }
+ }
+
+ return true;
+}
+
+void
+AppendUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
+{
+ if (aSource) {
+ AppendUTF16toUTF8(nsDependentString(aSource), aDest);
+ }
+}
+
+void
+AppendUTF8toUTF16(const char* aSource, nsAString& aDest)
+{
+ if (aSource) {
+ AppendUTF8toUTF16(nsDependentCString(aSource), aDest);
+ }
+}
+
+
+/**
+ * A helper function that allocates a buffer of the desired character type big enough to hold a copy of the supplied string (plus a zero terminator).
+ *
+ * @param aSource an string you will eventually be making a copy of
+ * @return a new buffer (of the type specified by the second parameter) which you must free with |free|.
+ *
+ */
+template <class FromStringT, class ToCharT>
+inline
+ToCharT*
+AllocateStringCopy(const FromStringT& aSource, ToCharT*)
+{
+ return static_cast<ToCharT*>(moz_xmalloc(
+ (aSource.Length() + 1) * sizeof(ToCharT)));
+}
+
+
+char*
+ToNewCString(const nsAString& aSource)
+{
+ char* result = AllocateStringCopy(aSource, (char*)0);
+ if (!result) {
+ return nullptr;
+ }
+
+ nsAString::const_iterator fromBegin, fromEnd;
+ LossyConvertEncoding16to8 converter(result);
+ copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ converter).write_terminator();
+ return result;
+}
+
+char*
+ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count)
+{
+ nsAString::const_iterator start, end;
+ CalculateUTF8Size calculator;
+ copy_string(aSource.BeginReading(start), aSource.EndReading(end),
+ calculator);
+
+ if (aUTF8Count) {
+ *aUTF8Count = calculator.Size();
+ }
+
+ char* result = static_cast<char*>
+ (moz_xmalloc(calculator.Size() + 1));
+ if (!result) {
+ return nullptr;
+ }
+
+ ConvertUTF16toUTF8 converter(result);
+ copy_string(aSource.BeginReading(start), aSource.EndReading(end),
+ converter).write_terminator();
+ NS_ASSERTION(calculator.Size() == converter.Size(), "length mismatch");
+
+ return result;
+}
+
+char*
+ToNewCString(const nsACString& aSource)
+{
+ // no conversion needed, just allocate a buffer of the correct length and copy into it
+
+ char* result = AllocateStringCopy(aSource, (char*)0);
+ if (!result) {
+ return nullptr;
+ }
+
+ nsACString::const_iterator fromBegin, fromEnd;
+ char* toBegin = result;
+ *copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ toBegin) = char(0);
+ return result;
+}
+
+char16_t*
+ToNewUnicode(const nsAString& aSource)
+{
+ // no conversion needed, just allocate a buffer of the correct length and copy into it
+
+ char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
+ if (!result) {
+ return nullptr;
+ }
+
+ nsAString::const_iterator fromBegin, fromEnd;
+ char16_t* toBegin = result;
+ *copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ toBegin) = char16_t(0);
+ return result;
+}
+
+char16_t*
+ToNewUnicode(const nsACString& aSource)
+{
+ char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
+ if (!result) {
+ return nullptr;
+ }
+
+ nsACString::const_iterator fromBegin, fromEnd;
+ LossyConvertEncoding8to16 converter(result);
+ copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ converter).write_terminator();
+ return result;
+}
+
+uint32_t
+CalcUTF8ToUnicodeLength(const nsACString& aSource)
+{
+ nsACString::const_iterator start, end;
+ CalculateUTF8Length calculator;
+ copy_string(aSource.BeginReading(start), aSource.EndReading(end),
+ calculator);
+ return calculator.Length();
+}
+
+char16_t*
+UTF8ToUnicodeBuffer(const nsACString& aSource, char16_t* aBuffer,
+ uint32_t* aUTF16Count)
+{
+ nsACString::const_iterator start, end;
+ ConvertUTF8toUTF16 converter(aBuffer);
+ copy_string(aSource.BeginReading(start),
+ aSource.EndReading(end),
+ converter).write_terminator();
+ if (aUTF16Count) {
+ *aUTF16Count = converter.Length();
+ }
+ return aBuffer;
+}
+
+char16_t*
+UTF8ToNewUnicode(const nsACString& aSource, uint32_t* aUTF16Count)
+{
+ const uint32_t length = CalcUTF8ToUnicodeLength(aSource);
+ const size_t buffer_size = (length + 1) * sizeof(char16_t);
+ char16_t* buffer = static_cast<char16_t*>(moz_xmalloc(buffer_size));
+ if (!buffer) {
+ return nullptr;
+ }
+
+ uint32_t copied;
+ UTF8ToUnicodeBuffer(aSource, buffer, &copied);
+ NS_ASSERTION(length == copied, "length mismatch");
+
+ if (aUTF16Count) {
+ *aUTF16Count = copied;
+ }
+ return buffer;
+}
+
+char16_t*
+CopyUnicodeTo(const nsAString& aSource, uint32_t aSrcOffset, char16_t* aDest,
+ uint32_t aLength)
+{
+ nsAString::const_iterator fromBegin, fromEnd;
+ char16_t* toBegin = aDest;
+ copy_string(aSource.BeginReading(fromBegin).advance(int32_t(aSrcOffset)),
+ aSource.BeginReading(fromEnd).advance(int32_t(aSrcOffset + aLength)),
+ toBegin);
+ return aDest;
+}
+
+void
+CopyUnicodeTo(const nsAString::const_iterator& aSrcStart,
+ const nsAString::const_iterator& aSrcEnd,
+ nsAString& aDest)
+{
+ aDest.SetLength(Distance(aSrcStart, aSrcEnd));
+
+ nsAString::char_iterator dest = aDest.BeginWriting();
+ nsAString::const_iterator fromBegin(aSrcStart);
+
+ copy_string(fromBegin, aSrcEnd, dest);
+}
+
+void
+AppendUnicodeTo(const nsAString::const_iterator& aSrcStart,
+ const nsAString::const_iterator& aSrcEnd,
+ nsAString& aDest)
+{
+ uint32_t oldLength = aDest.Length();
+ aDest.SetLength(oldLength + Distance(aSrcStart, aSrcEnd));
+
+ nsAString::char_iterator dest = aDest.BeginWriting() + oldLength;
+ nsAString::const_iterator fromBegin(aSrcStart);
+
+ copy_string(fromBegin, aSrcEnd, dest);
+}
+
+bool
+IsASCII(const nsAString& aString)
+{
+ static const char16_t NOT_ASCII = char16_t(~0x007F);
+
+
+ // Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
+
+ nsAString::const_iterator iter, done_reading;
+ aString.BeginReading(iter);
+ aString.EndReading(done_reading);
+
+ const char16_t* c = iter.get();
+ const char16_t* end = done_reading.get();
+
+ while (c < end) {
+ if (*c++ & NOT_ASCII) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+IsASCII(const nsACString& aString)
+{
+ static const char NOT_ASCII = char(~0x7F);
+
+
+ // Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
+
+ nsACString::const_iterator iter, done_reading;
+ aString.BeginReading(iter);
+ aString.EndReading(done_reading);
+
+ const char* c = iter.get();
+ const char* end = done_reading.get();
+
+ while (c < end) {
+ if (*c++ & NOT_ASCII) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+IsUTF8(const nsACString& aString, bool aRejectNonChar)
+{
+ nsReadingIterator<char> done_reading;
+ aString.EndReading(done_reading);
+
+ int32_t state = 0;
+ bool overlong = false;
+ bool surrogate = false;
+ bool nonchar = false;
+ uint16_t olupper = 0; // overlong byte upper bound.
+ uint16_t slower = 0; // surrogate byte lower bound.
+
+ nsReadingIterator<char> iter;
+ aString.BeginReading(iter);
+
+ const char* ptr = iter.get();
+ const char* end = done_reading.get();
+ while (ptr < end) {
+ uint8_t c;
+
+ if (0 == state) {
+ c = *ptr++;
+
+ if (UTF8traits::isASCII(c)) {
+ continue;
+ }
+
+ if (c <= 0xC1) { // [80-BF] where not expected, [C0-C1] for overlong.
+ return false;
+ } else if (UTF8traits::is2byte(c)) {
+ state = 1;
+ } else if (UTF8traits::is3byte(c)) {
+ state = 2;
+ if (c == 0xE0) { // to exclude E0[80-9F][80-BF]
+ overlong = true;
+ olupper = 0x9F;
+ } else if (c == 0xED) { // ED[A0-BF][80-BF] : surrogate codepoint
+ surrogate = true;
+ slower = 0xA0;
+ } else if (c == 0xEF) { // EF BF [BE-BF] : non-character
+ nonchar = true;
+ }
+ } else if (c <= 0xF4) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090)
+ state = 3;
+ nonchar = true;
+ if (c == 0xF0) { // to exclude F0[80-8F][80-BF]{2}
+ overlong = true;
+ olupper = 0x8F;
+ } else if (c == 0xF4) { // to exclude F4[90-BF][80-BF]
+ // actually not surrogates but codepoints beyond 0x10FFFF
+ surrogate = true;
+ slower = 0x90;
+ }
+ } else {
+ return false; // Not UTF-8 string
+ }
+ }
+
+ if (nonchar && !aRejectNonChar) {
+ nonchar = false;
+ }
+
+ while (ptr < end && state) {
+ c = *ptr++;
+ --state;
+
+ // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
+ if (nonchar &&
+ ((!state && c < 0xBE) ||
+ (state == 1 && c != 0xBF) ||
+ (state == 2 && 0x0F != (0x0F & c)))) {
+ nonchar = false;
+ }
+
+ if (!UTF8traits::isInSeq(c) || (overlong && c <= olupper) ||
+ (surrogate && slower <= c) || (nonchar && !state)) {
+ return false; // Not UTF-8 string
+ }
+
+ overlong = surrogate = false;
+ }
+ }
+ return !state; // state != 0 at the end indicates an invalid UTF-8 seq.
+}
+
+/**
+ * A character sink for in-place case conversion.
+ */
+class ConvertToUpperCase
+{
+public:
+ typedef char value_type;
+
+ uint32_t
+ write(const char* aSource, uint32_t aSourceLength)
+ {
+ char* cp = const_cast<char*>(aSource);
+ const char* end = aSource + aSourceLength;
+ while (cp != end) {
+ char ch = *cp;
+ if (ch >= 'a' && ch <= 'z') {
+ *cp = ch - ('a' - 'A');
+ }
+ ++cp;
+ }
+ return aSourceLength;
+ }
+};
+
+void
+ToUpperCase(nsCSubstring& aCString)
+{
+ ConvertToUpperCase converter;
+ char* start;
+ converter.write(aCString.BeginWriting(start), aCString.Length());
+}
+
+/**
+ * A character sink for copying with case conversion.
+ */
+class CopyToUpperCase
+{
+public:
+ typedef char value_type;
+
+ explicit CopyToUpperCase(nsACString::iterator& aDestIter,
+ const nsACString::iterator& aEndIter)
+ : mIter(aDestIter)
+ , mEnd(aEndIter)
+ {
+ }
+
+ uint32_t
+ write(const char* aSource, uint32_t aSourceLength)
+ {
+ uint32_t len = XPCOM_MIN(uint32_t(mEnd - mIter), aSourceLength);
+ char* cp = mIter.get();
+ const char* end = aSource + len;
+ while (aSource != end) {
+ char ch = *aSource;
+ if ((ch >= 'a') && (ch <= 'z')) {
+ *cp = ch - ('a' - 'A');
+ } else {
+ *cp = ch;
+ }
+ ++aSource;
+ ++cp;
+ }
+ mIter.advance(len);
+ return len;
+ }
+
+protected:
+ nsACString::iterator& mIter;
+ const nsACString::iterator& mEnd;
+};
+
+void
+ToUpperCase(const nsACString& aSource, nsACString& aDest)
+{
+ nsACString::const_iterator fromBegin, fromEnd;
+ nsACString::iterator toBegin, toEnd;
+ aDest.SetLength(aSource.Length());
+
+ CopyToUpperCase converter(aDest.BeginWriting(toBegin), aDest.EndWriting(toEnd));
+ copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ converter);
+}
+
+/**
+ * A character sink for case conversion.
+ */
+class ConvertToLowerCase
+{
+public:
+ typedef char value_type;
+
+ uint32_t
+ write(const char* aSource, uint32_t aSourceLength)
+ {
+ char* cp = const_cast<char*>(aSource);
+ const char* end = aSource + aSourceLength;
+ while (cp != end) {
+ char ch = *cp;
+ if ((ch >= 'A') && (ch <= 'Z')) {
+ *cp = ch + ('a' - 'A');
+ }
+ ++cp;
+ }
+ return aSourceLength;
+ }
+};
+
+void
+ToLowerCase(nsCSubstring& aCString)
+{
+ ConvertToLowerCase converter;
+ char* start;
+ converter.write(aCString.BeginWriting(start), aCString.Length());
+}
+
+/**
+ * A character sink for copying with case conversion.
+ */
+class CopyToLowerCase
+{
+public:
+ typedef char value_type;
+
+ explicit CopyToLowerCase(nsACString::iterator& aDestIter,
+ const nsACString::iterator& aEndIter)
+ : mIter(aDestIter)
+ , mEnd(aEndIter)
+ {
+ }
+
+ uint32_t
+ write(const char* aSource, uint32_t aSourceLength)
+ {
+ uint32_t len = XPCOM_MIN(uint32_t(mEnd - mIter), aSourceLength);
+ char* cp = mIter.get();
+ const char* end = aSource + len;
+ while (aSource != end) {
+ char ch = *aSource;
+ if ((ch >= 'A') && (ch <= 'Z')) {
+ *cp = ch + ('a' - 'A');
+ } else {
+ *cp = ch;
+ }
+ ++aSource;
+ ++cp;
+ }
+ mIter.advance(len);
+ return len;
+ }
+
+protected:
+ nsACString::iterator& mIter;
+ const nsACString::iterator& mEnd;
+};
+
+void
+ToLowerCase(const nsACString& aSource, nsACString& aDest)
+{
+ nsACString::const_iterator fromBegin, fromEnd;
+ nsACString::iterator toBegin, toEnd;
+ aDest.SetLength(aSource.Length());
+
+ CopyToLowerCase converter(aDest.BeginWriting(toBegin), aDest.EndWriting(toEnd));
+ copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ converter);
+}
+
+bool
+ParseString(const nsACString& aSource, char aDelimiter,
+ nsTArray<nsCString>& aArray)
+{
+ nsACString::const_iterator start, end;
+ aSource.BeginReading(start);
+ aSource.EndReading(end);
+
+ uint32_t oldLength = aArray.Length();
+
+ for (;;) {
+ nsACString::const_iterator delimiter = start;
+ FindCharInReadable(aDelimiter, delimiter, end);
+
+ if (delimiter != start) {
+ if (!aArray.AppendElement(Substring(start, delimiter))) {
+ aArray.RemoveElementsAt(oldLength, aArray.Length() - oldLength);
+ return false;
+ }
+ }
+
+ if (delimiter == end) {
+ break;
+ }
+ start = ++delimiter;
+ if (start == end) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+template <class StringT, class IteratorT, class Comparator>
+bool
+FindInReadable_Impl(const StringT& aPattern, IteratorT& aSearchStart,
+ IteratorT& aSearchEnd, const Comparator& aCompare)
+{
+ bool found_it = false;
+
+ // only bother searching at all if we're given a non-empty range to search
+ if (aSearchStart != aSearchEnd) {
+ IteratorT aPatternStart, aPatternEnd;
+ aPattern.BeginReading(aPatternStart);
+ aPattern.EndReading(aPatternEnd);
+
+ // outer loop keeps searching till we find it or run out of string to search
+ while (!found_it) {
+ // fast inner loop (that's what it's called, not what it is) looks for a potential match
+ while (aSearchStart != aSearchEnd &&
+ aCompare(aPatternStart.get(), aSearchStart.get(), 1, 1)) {
+ ++aSearchStart;
+ }
+
+ // if we broke out of the `fast' loop because we're out of string ... we're done: no match
+ if (aSearchStart == aSearchEnd) {
+ break;
+ }
+
+ // otherwise, we're at a potential match, let's see if we really hit one
+ IteratorT testPattern(aPatternStart);
+ IteratorT testSearch(aSearchStart);
+
+ // slow inner loop verifies the potential match (found by the `fast' loop) at the current position
+ for (;;) {
+ // we already compared the first character in the outer loop,
+ // so we'll advance before the next comparison
+ ++testPattern;
+ ++testSearch;
+
+ // if we verified all the way to the end of the pattern, then we found it!
+ if (testPattern == aPatternEnd) {
+ found_it = true;
+ aSearchEnd = testSearch; // return the exact found range through the parameters
+ break;
+ }
+
+ // if we got to end of the string we're searching before we hit the end of the
+ // pattern, we'll never find what we're looking for
+ if (testSearch == aSearchEnd) {
+ aSearchStart = aSearchEnd;
+ break;
+ }
+
+ // else if we mismatched ... it's time to advance to the next search position
+ // and get back into the `fast' loop
+ if (aCompare(testPattern.get(), testSearch.get(), 1, 1)) {
+ ++aSearchStart;
+ break;
+ }
+ }
+ }
+ }
+
+ return found_it;
+}
+
+/**
+ * This searches the entire string from right to left, and returns the first match found, if any.
+ */
+template <class StringT, class IteratorT, class Comparator>
+bool
+RFindInReadable_Impl(const StringT& aPattern, IteratorT& aSearchStart,
+ IteratorT& aSearchEnd, const Comparator& aCompare)
+{
+ IteratorT patternStart, patternEnd, searchEnd = aSearchEnd;
+ aPattern.BeginReading(patternStart);
+ aPattern.EndReading(patternEnd);
+
+ // Point to the last character in the pattern
+ --patternEnd;
+ // outer loop keeps searching till we run out of string to search
+ while (aSearchStart != searchEnd) {
+ // Point to the end position of the next possible match
+ --searchEnd;
+
+ // Check last character, if a match, explore further from here
+ if (aCompare(patternEnd.get(), searchEnd.get(), 1, 1) == 0) {
+ // We're at a potential match, let's see if we really hit one
+ IteratorT testPattern(patternEnd);
+ IteratorT testSearch(searchEnd);
+
+ // inner loop verifies the potential match at the current position
+ do {
+ // if we verified all the way to the end of the pattern, then we found it!
+ if (testPattern == patternStart) {
+ aSearchStart = testSearch; // point to start of match
+ aSearchEnd = ++searchEnd; // point to end of match
+ return true;
+ }
+
+ // if we got to end of the string we're searching before we hit the end of the
+ // pattern, we'll never find what we're looking for
+ if (testSearch == aSearchStart) {
+ aSearchStart = aSearchEnd;
+ return false;
+ }
+
+ // test previous character for a match
+ --testPattern;
+ --testSearch;
+ } while (aCompare(testPattern.get(), testSearch.get(), 1, 1) == 0);
+ }
+ }
+
+ aSearchStart = aSearchEnd;
+ return false;
+}
+
+bool
+FindInReadable(const nsAString& aPattern,
+ nsAString::const_iterator& aSearchStart,
+ nsAString::const_iterator& aSearchEnd,
+ const nsStringComparator& aComparator)
+{
+ return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
+}
+
+bool
+FindInReadable(const nsACString& aPattern,
+ nsACString::const_iterator& aSearchStart,
+ nsACString::const_iterator& aSearchEnd,
+ const nsCStringComparator& aComparator)
+{
+ return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
+}
+
+bool
+CaseInsensitiveFindInReadable(const nsACString& aPattern,
+ nsACString::const_iterator& aSearchStart,
+ nsACString::const_iterator& aSearchEnd)
+{
+ return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd,
+ nsCaseInsensitiveCStringComparator());
+}
+
+bool
+RFindInReadable(const nsAString& aPattern,
+ nsAString::const_iterator& aSearchStart,
+ nsAString::const_iterator& aSearchEnd,
+ const nsStringComparator& aComparator)
+{
+ return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
+}
+
+bool
+RFindInReadable(const nsACString& aPattern,
+ nsACString::const_iterator& aSearchStart,
+ nsACString::const_iterator& aSearchEnd,
+ const nsCStringComparator& aComparator)
+{
+ return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
+}
+
+bool
+FindCharInReadable(char16_t aChar, nsAString::const_iterator& aSearchStart,
+ const nsAString::const_iterator& aSearchEnd)
+{
+ int32_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
+
+ const char16_t* charFoundAt =
+ nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar);
+ if (charFoundAt) {
+ aSearchStart.advance(charFoundAt - aSearchStart.get());
+ return true;
+ }
+
+ aSearchStart.advance(fragmentLength);
+ return false;
+}
+
+bool
+FindCharInReadable(char aChar, nsACString::const_iterator& aSearchStart,
+ const nsACString::const_iterator& aSearchEnd)
+{
+ int32_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
+
+ const char* charFoundAt =
+ nsCharTraits<char>::find(aSearchStart.get(), fragmentLength, aChar);
+ if (charFoundAt) {
+ aSearchStart.advance(charFoundAt - aSearchStart.get());
+ return true;
+ }
+
+ aSearchStart.advance(fragmentLength);
+ return false;
+}
+
+uint32_t
+CountCharInReadable(const nsAString& aStr, char16_t aChar)
+{
+ uint32_t count = 0;
+ nsAString::const_iterator begin, end;
+
+ aStr.BeginReading(begin);
+ aStr.EndReading(end);
+
+ while (begin != end) {
+ if (*begin == aChar) {
+ ++count;
+ }
+ ++begin;
+ }
+
+ return count;
+}
+
+uint32_t
+CountCharInReadable(const nsACString& aStr, char aChar)
+{
+ uint32_t count = 0;
+ nsACString::const_iterator begin, end;
+
+ aStr.BeginReading(begin);
+ aStr.EndReading(end);
+
+ while (begin != end) {
+ if (*begin == aChar) {
+ ++count;
+ }
+ ++begin;
+ }
+
+ return count;
+}
+
+bool
+StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring)
+{
+ nsAString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, 0, sub_len).Equals(aSubstring);
+}
+
+bool
+StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
+ const nsStringComparator& aComparator)
+{
+ nsAString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
+}
+
+bool
+StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring)
+{
+ nsACString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, 0, sub_len).Equals(aSubstring);
+}
+
+bool
+StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
+ const nsCStringComparator& aComparator)
+{
+ nsACString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
+}
+
+bool
+StringEndsWith(const nsAString& aSource, const nsAString& aSubstring)
+{
+ nsAString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
+}
+
+bool
+StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
+ const nsStringComparator& aComparator)
+{
+ nsAString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring,
+ aComparator);
+}
+
+bool
+StringEndsWith(const nsACString& aSource, const nsACString& aSubstring)
+{
+ nsACString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
+}
+
+bool
+StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
+ const nsCStringComparator& aComparator)
+{
+ nsACString::size_type src_len = aSource.Length(),
+ sub_len = aSubstring.Length();
+ if (sub_len > src_len) {
+ return false;
+ }
+ return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring,
+ aComparator);
+}
+
+
+
+static const char16_t empty_buffer[1] = { '\0' };
+
+const nsAFlatString&
+EmptyString()
+{
+ static const nsDependentString sEmpty(empty_buffer);
+
+ return sEmpty;
+}
+
+const nsAFlatCString&
+EmptyCString()
+{
+ static const nsDependentCString sEmpty((const char*)empty_buffer);
+
+ return sEmpty;
+}
+
+const nsAFlatString&
+NullString()
+{
+ static const nsXPIDLString sNull;
+
+ return sNull;
+}
+
+const nsAFlatCString&
+NullCString()
+{
+ static const nsXPIDLCString sNull;
+
+ return sNull;
+}
+
+int32_t
+CompareUTF8toUTF16(const nsASingleFragmentCString& aUTF8String,
+ const nsASingleFragmentString& aUTF16String)
+{
+ static const uint32_t NOT_ASCII = uint32_t(~0x7F);
+
+ const char* u8;
+ const char* u8end;
+ aUTF8String.BeginReading(u8);
+ aUTF8String.EndReading(u8end);
+
+ const char16_t* u16;
+ const char16_t* u16end;
+ aUTF16String.BeginReading(u16);
+ aUTF16String.EndReading(u16end);
+
+ while (u8 != u8end && u16 != u16end) {
+ // Cast away the signedness of *u8 to prevent signextension when
+ // converting to uint32_t
+ uint32_t c8_32 = (uint8_t)*u8;
+
+ if (c8_32 & NOT_ASCII) {
+ bool err;
+ c8_32 = UTF8CharEnumerator::NextChar(&u8, u8end, &err);
+ if (err) {
+ return INT32_MIN;
+ }
+
+ uint32_t c16_32 = UTF16CharEnumerator::NextChar(&u16, u16end);
+ // The above UTF16CharEnumerator::NextChar() calls can
+ // fail, but if it does for anything other than no data to
+ // look at (which can't happen here), it returns the
+ // Unicode replacement character 0xFFFD for the invalid
+ // data they were fed. Ignore that error and treat invalid
+ // UTF16 as 0xFFFD.
+ //
+ // This matches what our UTF16 to UTF8 conversion code
+ // does, and thus a UTF8 string that came from an invalid
+ // UTF16 string will compare equal to the invalid UTF16
+ // string it came from. Same is true for any other UTF16
+ // string differs only in the invalid part of the string.
+
+ if (c8_32 != c16_32) {
+ return c8_32 < c16_32 ? -1 : 1;
+ }
+ } else {
+ if (c8_32 != *u16) {
+ return c8_32 > *u16 ? 1 : -1;
+ }
+
+ ++u8;
+ ++u16;
+ }
+ }
+
+ if (u8 != u8end) {
+ // We get to the end of the UTF16 string, but no to the end of
+ // the UTF8 string. The UTF8 string is longer than the UTF16
+ // string
+
+ return 1;
+ }
+
+ if (u16 != u16end) {
+ // We get to the end of the UTF8 string, but no to the end of
+ // the UTF16 string. The UTF16 string is longer than the UTF8
+ // string
+
+ return -1;
+ }
+
+ // The two strings match.
+
+ return 0;
+}
+
+void
+AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest)
+{
+ NS_ASSERTION(IS_VALID_CHAR(aSource), "Invalid UCS4 char");
+ if (IS_IN_BMP(aSource)) {
+ aDest.Append(char16_t(aSource));
+ } else {
+ aDest.Append(H_SURROGATE(aSource));
+ aDest.Append(L_SURROGATE(aSource));
+ }
+}
+
+extern "C" {
+
+void Gecko_AppendUTF16toCString(nsACString* aThis, const nsAString* aOther)
+{
+ AppendUTF16toUTF8(*aOther, *aThis);
+}
+
+void Gecko_AppendUTF8toString(nsAString* aThis, const nsACString* aOther)
+{
+ AppendUTF8toUTF16(*aOther, *aThis);
+}
+
+}
diff --git a/xpcom/string/nsReadableUtils.h b/xpcom/string/nsReadableUtils.h
new file mode 100644
index 000000000..24824d927
--- /dev/null
+++ b/xpcom/string/nsReadableUtils.h
@@ -0,0 +1,428 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+#ifndef nsReadableUtils_h___
+#define nsReadableUtils_h___
+
+/**
+ * I guess all the routines in this file are all mis-named.
+ * According to our conventions, they should be |NS_xxx|.
+ */
+
+#include "mozilla/Assertions.h"
+#include "nsAString.h"
+
+#include "nsTArrayForwardDeclare.h"
+
+inline size_t
+Distance(const nsReadingIterator<char16_t>& aStart,
+ const nsReadingIterator<char16_t>& aEnd)
+{
+ MOZ_ASSERT(aStart.get() <= aEnd.get());
+ return static_cast<size_t>(aEnd.get() - aStart.get());
+}
+inline size_t
+Distance(const nsReadingIterator<char>& aStart,
+ const nsReadingIterator<char>& aEnd)
+{
+ MOZ_ASSERT(aStart.get() <= aEnd.get());
+ return static_cast<size_t>(aEnd.get() - aStart.get());
+}
+
+void LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest);
+void CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest);
+
+void LossyCopyUTF16toASCII(const char16ptr_t aSource, nsACString& aDest);
+void CopyASCIItoUTF16(const char* aSource, nsAString& aDest);
+
+void CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest);
+MOZ_MUST_USE bool CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
+ const mozilla::fallible_t&);
+void CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest);
+
+void CopyUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest);
+void CopyUTF8toUTF16(const char* aSource, nsAString& aDest);
+
+void LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest);
+void AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest);
+MOZ_MUST_USE bool AppendASCIItoUTF16(const nsACString& aSource,
+ nsAString& aDest,
+ const mozilla::fallible_t&);
+
+void LossyAppendUTF16toASCII(const char16ptr_t aSource, nsACString& aDest);
+MOZ_MUST_USE bool AppendASCIItoUTF16(const char* aSource,
+ nsAString& aDest,
+ const mozilla::fallible_t&);
+void AppendASCIItoUTF16(const char* aSource, nsAString& aDest);
+
+void AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest);
+MOZ_MUST_USE bool AppendUTF16toUTF8(const nsAString& aSource,
+ nsACString& aDest,
+ const mozilla::fallible_t&);
+void AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest);
+MOZ_MUST_USE bool AppendUTF8toUTF16(const nsACString& aSource,
+ nsAString& aDest,
+ const mozilla::fallible_t&);
+
+void AppendUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest);
+void AppendUTF8toUTF16(const char* aSource, nsAString& aDest);
+
+/**
+ * Returns a new |char| buffer containing a zero-terminated copy of |aSource|.
+ *
+ * Allocates and returns a new |char| buffer which you must free with |free|.
+ * Performs a lossy encoding conversion by chopping 16-bit wide characters down to 8-bits wide while copying |aSource| to your new buffer.
+ * This conversion is not well defined; but it reproduces legacy string behavior.
+ * The new buffer is zero-terminated, but that may not help you if |aSource| contains embedded nulls.
+ *
+ * @param aSource a 16-bit wide string
+ * @return a new |char| buffer you must free with |free|.
+ */
+char* ToNewCString(const nsAString& aSource);
+
+
+/**
+ * Returns a new |char| buffer containing a zero-terminated copy of |aSource|.
+ *
+ * Allocates and returns a new |char| buffer which you must free with |free|.
+ * The new buffer is zero-terminated, but that may not help you if |aSource| contains embedded nulls.
+ *
+ * @param aSource an 8-bit wide string
+ * @return a new |char| buffer you must free with |free|.
+ */
+char* ToNewCString(const nsACString& aSource);
+
+/**
+ * Returns a new |char| buffer containing a zero-terminated copy of |aSource|.
+ *
+ * Allocates and returns a new |char| buffer which you must free with
+ * |free|.
+ * Performs an encoding conversion from a UTF-16 string to a UTF-8 string
+ * copying |aSource| to your new buffer.
+ * The new buffer is zero-terminated, but that may not help you if |aSource|
+ * contains embedded nulls.
+ *
+ * @param aSource a UTF-16 string (made of char16_t's)
+ * @param aUTF8Count the number of 8-bit units that was returned
+ * @return a new |char| buffer you must free with |free|.
+ */
+
+char* ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count = nullptr);
+
+
+/**
+ * Returns a new |char16_t| buffer containing a zero-terminated copy of
+ * |aSource|.
+ *
+ * Allocates and returns a new |char16_t| buffer which you must free with
+ * |free|.
+ * The new buffer is zero-terminated, but that may not help you if |aSource|
+ * contains embedded nulls.
+ *
+ * @param aSource a UTF-16 string
+ * @return a new |char16_t| buffer you must free with |free|.
+ */
+char16_t* ToNewUnicode(const nsAString& aSource);
+
+
+/**
+ * Returns a new |char16_t| buffer containing a zero-terminated copy of |aSource|.
+ *
+ * Allocates and returns a new |char16_t| buffer which you must free with |free|.
+ * Performs an encoding conversion by 0-padding 8-bit wide characters up to 16-bits wide while copying |aSource| to your new buffer.
+ * This conversion is not well defined; but it reproduces legacy string behavior.
+ * The new buffer is zero-terminated, but that may not help you if |aSource| contains embedded nulls.
+ *
+ * @param aSource an 8-bit wide string (a C-string, NOT UTF-8)
+ * @return a new |char16_t| buffer you must free with |free|.
+ */
+char16_t* ToNewUnicode(const nsACString& aSource);
+
+/**
+ * Returns the required length for a char16_t buffer holding
+ * a copy of aSource, using UTF-8 to UTF-16 conversion.
+ * The length does NOT include any space for zero-termination.
+ *
+ * @param aSource an 8-bit wide string, UTF-8 encoded
+ * @return length of UTF-16 encoded string copy, not zero-terminated
+ */
+uint32_t CalcUTF8ToUnicodeLength(const nsACString& aSource);
+
+/**
+ * Copies the source string into the specified buffer, converting UTF-8 to
+ * UTF-16 in the process. The conversion is well defined for valid UTF-8
+ * strings.
+ * The copied string will be zero-terminated! Any embedded nulls will be
+ * copied nonetheless. It is the caller's responsiblity to ensure the buffer
+ * is large enough to hold the string copy plus one char16_t for
+ * zero-termination!
+ *
+ * @see CalcUTF8ToUnicodeLength( const nsACString& )
+ * @see UTF8ToNewUnicode( const nsACString&, uint32_t* )
+ *
+ * @param aSource an 8-bit wide string, UTF-8 encoded
+ * @param aBuffer the buffer holding the converted string copy
+ * @param aUTF16Count receiving optionally the number of 16-bit units that
+ * were copied
+ * @return aBuffer pointer, for convenience
+ */
+char16_t* UTF8ToUnicodeBuffer(const nsACString& aSource,
+ char16_t* aBuffer,
+ uint32_t* aUTF16Count = nullptr);
+
+/**
+ * Returns a new |char16_t| buffer containing a zero-terminated copy
+ * of |aSource|.
+ *
+ * Allocates and returns a new |char| buffer which you must free with
+ * |free|. Performs an encoding conversion from UTF-8 to UTF-16
+ * while copying |aSource| to your new buffer. This conversion is well defined
+ * for a valid UTF-8 string. The new buffer is zero-terminated, but that
+ * may not help you if |aSource| contains embedded nulls.
+ *
+ * @param aSource an 8-bit wide string, UTF-8 encoded
+ * @param aUTF16Count the number of 16-bit units that was returned
+ * @return a new |char16_t| buffer you must free with |free|.
+ * (UTF-16 encoded)
+ */
+char16_t* UTF8ToNewUnicode(const nsACString& aSource,
+ uint32_t* aUTF16Count = nullptr);
+
+/**
+ * Copies |aLength| 16-bit code units from the start of |aSource| to the
+ * |char16_t| buffer |aDest|.
+ *
+ * After this operation |aDest| is not null terminated.
+ *
+ * @param aSource a UTF-16 string
+ * @param aSrcOffset start offset in the source string
+ * @param aDest a |char16_t| buffer
+ * @param aLength the number of 16-bit code units to copy
+ * @return pointer to destination buffer - identical to |aDest|
+ */
+char16_t* CopyUnicodeTo(const nsAString& aSource,
+ uint32_t aSrcOffset,
+ char16_t* aDest,
+ uint32_t aLength);
+
+
+/**
+ * Copies 16-bit characters between iterators |aSrcStart| and
+ * |aSrcEnd| to the writable string |aDest|. Similar to the
+ * |nsString::Mid| method.
+ *
+ * After this operation |aDest| is not null terminated.
+ *
+ * @param aSrcStart start source iterator
+ * @param aSrcEnd end source iterator
+ * @param aDest destination for the copy
+ */
+void CopyUnicodeTo(const nsAString::const_iterator& aSrcStart,
+ const nsAString::const_iterator& aSrcEnd,
+ nsAString& aDest);
+
+/**
+ * Appends 16-bit characters between iterators |aSrcStart| and
+ * |aSrcEnd| to the writable string |aDest|.
+ *
+ * After this operation |aDest| is not null terminated.
+ *
+ * @param aSrcStart start source iterator
+ * @param aSrcEnd end source iterator
+ * @param aDest destination for the copy
+ */
+void AppendUnicodeTo(const nsAString::const_iterator& aSrcStart,
+ const nsAString::const_iterator& aSrcEnd,
+ nsAString& aDest);
+
+/**
+ * Returns |true| if |aString| contains only ASCII characters, that is, characters in the range (0x00, 0x7F).
+ *
+ * @param aString a 16-bit wide string to scan
+ */
+bool IsASCII(const nsAString& aString);
+
+/**
+ * Returns |true| if |aString| contains only ASCII characters, that is, characters in the range (0x00, 0x7F).
+ *
+ * @param aString a 8-bit wide string to scan
+ */
+bool IsASCII(const nsACString& aString);
+
+/**
+ * Returns |true| if |aString| is a valid UTF-8 string.
+ * XXX This is not bullet-proof and nor an all-purpose UTF-8 validator.
+ * It is mainly written to replace and roughly equivalent to
+ *
+ * str.Equals(NS_ConvertUTF16toUTF8(NS_ConvertUTF8toUTF16(str)))
+ *
+ * (see bug 191541)
+ * As such, it does not check for non-UTF-8 7bit encodings such as
+ * ISO-2022-JP and HZ.
+ *
+ * It rejects sequences with the following errors:
+ *
+ * byte sequences that cannot be decoded into characters according to
+ * UTF-8's rules (including cases where the input is part of a valid
+ * UTF-8 sequence but starts or ends mid-character)
+ * overlong sequences (i.e., cases where a character was encoded
+ * non-canonically by using more bytes than necessary)
+ * surrogate codepoints (i.e., the codepoints reserved for
+ representing astral characters in UTF-16)
+ * codepoints above the unicode range (i.e., outside the first 17
+ * planes; higher than U+10FFFF), in accordance with
+ * http://tools.ietf.org/html/rfc3629
+ * when aRejectNonChar is true (the default), any codepoint whose low
+ * 16 bits are 0xFFFE or 0xFFFF
+
+ *
+ * @param aString an 8-bit wide string to scan
+ * @param aRejectNonChar a boolean to control the rejection of utf-8
+ * non characters
+ */
+bool IsUTF8(const nsACString& aString, bool aRejectNonChar = true);
+
+bool ParseString(const nsACString& aAstring, char aDelimiter,
+ nsTArray<nsCString>& aArray);
+
+/**
+ * Converts case in place in the argument string.
+ */
+void ToUpperCase(nsACString&);
+
+void ToLowerCase(nsACString&);
+
+void ToUpperCase(nsCSubstring&);
+
+void ToLowerCase(nsCSubstring&);
+
+/**
+ * Converts case from string aSource to aDest.
+ */
+void ToUpperCase(const nsACString& aSource, nsACString& aDest);
+
+void ToLowerCase(const nsACString& aSource, nsACString& aDest);
+
+/**
+ * Finds the leftmost occurrence of |aPattern|, if any in the range |aSearchStart|..|aSearchEnd|.
+ *
+ * Returns |true| if a match was found, and adjusts |aSearchStart| and |aSearchEnd| to
+ * point to the match. If no match was found, returns |false| and makes |aSearchStart == aSearchEnd|.
+ *
+ * Currently, this is equivalent to the O(m*n) implementation previously on |ns[C]String|.
+ * If we need something faster, then we can implement that later.
+ */
+
+bool FindInReadable(const nsAString& aPattern, nsAString::const_iterator&,
+ nsAString::const_iterator&,
+ const nsStringComparator& = nsDefaultStringComparator());
+bool FindInReadable(const nsACString& aPattern, nsACString::const_iterator&,
+ nsACString::const_iterator&,
+ const nsCStringComparator& = nsDefaultCStringComparator());
+
+/* sometimes we don't care about where the string was, just that we
+ * found it or not */
+inline bool
+FindInReadable(const nsAString& aPattern, const nsAString& aSource,
+ const nsStringComparator& aCompare = nsDefaultStringComparator())
+{
+ nsAString::const_iterator start, end;
+ aSource.BeginReading(start);
+ aSource.EndReading(end);
+ return FindInReadable(aPattern, start, end, aCompare);
+}
+
+inline bool
+FindInReadable(const nsACString& aPattern, const nsACString& aSource,
+ const nsCStringComparator& aCompare = nsDefaultCStringComparator())
+{
+ nsACString::const_iterator start, end;
+ aSource.BeginReading(start);
+ aSource.EndReading(end);
+ return FindInReadable(aPattern, start, end, aCompare);
+}
+
+
+bool CaseInsensitiveFindInReadable(const nsACString& aPattern,
+ nsACString::const_iterator&,
+ nsACString::const_iterator&);
+
+/**
+ * Finds the rightmost occurrence of |aPattern|
+ * Returns |true| if a match was found, and adjusts |aSearchStart| and |aSearchEnd| to
+ * point to the match. If no match was found, returns |false| and makes |aSearchStart == aSearchEnd|.
+ *
+ */
+bool RFindInReadable(const nsAString& aPattern, nsAString::const_iterator&,
+ nsAString::const_iterator&,
+ const nsStringComparator& = nsDefaultStringComparator());
+bool RFindInReadable(const nsACString& aPattern, nsACString::const_iterator&,
+ nsACString::const_iterator&,
+ const nsCStringComparator& = nsDefaultCStringComparator());
+
+/**
+* Finds the leftmost occurrence of |aChar|, if any in the range
+* |aSearchStart|..|aSearchEnd|.
+*
+* Returns |true| if a match was found, and adjusts |aSearchStart| to
+* point to the match. If no match was found, returns |false| and
+* makes |aSearchStart == aSearchEnd|.
+*/
+bool FindCharInReadable(char16_t aChar, nsAString::const_iterator& aSearchStart,
+ const nsAString::const_iterator& aSearchEnd);
+bool FindCharInReadable(char aChar, nsACString::const_iterator& aSearchStart,
+ const nsACString::const_iterator& aSearchEnd);
+
+/**
+* Finds the number of occurences of |aChar| in the string |aStr|
+*/
+uint32_t CountCharInReadable(const nsAString& aStr,
+ char16_t aChar);
+uint32_t CountCharInReadable(const nsACString& aStr,
+ char aChar);
+
+bool StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring);
+bool StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
+ const nsStringComparator& aComparator);
+bool StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring);
+bool StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
+ const nsCStringComparator& aComparator);
+bool StringEndsWith(const nsAString& aSource, const nsAString& aSubstring);
+bool StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
+ const nsStringComparator& aComparator);
+bool StringEndsWith(const nsACString& aSource, const nsACString& aSubstring);
+bool StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
+ const nsCStringComparator& aComparator);
+
+const nsAFlatString& EmptyString();
+const nsAFlatCString& EmptyCString();
+
+const nsAFlatString& NullString();
+const nsAFlatCString& NullCString();
+
+/**
+* Compare a UTF-8 string to an UTF-16 string.
+*
+* Returns 0 if the strings are equal, -1 if aUTF8String is less
+* than aUTF16Count, and 1 in the reverse case. In case of fatal
+* error (eg the strings are not valid UTF8 and UTF16 respectively),
+* this method will return INT32_MIN.
+*/
+int32_t CompareUTF8toUTF16(const nsASingleFragmentCString& aUTF8String,
+ const nsASingleFragmentString& aUTF16String);
+
+void AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest);
+
+template<class T>
+inline bool
+EnsureStringLength(T& aStr, uint32_t aLen)
+{
+ aStr.SetLength(aLen);
+ return (aStr.Length() == aLen);
+}
+
+#endif // !defined(nsReadableUtils_h___)
diff --git a/xpcom/string/nsReadableUtilsImpl.h b/xpcom/string/nsReadableUtilsImpl.h
new file mode 100644
index 000000000..ff1497b51
--- /dev/null
+++ b/xpcom/string/nsReadableUtilsImpl.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+
+namespace mozilla {
+
+inline bool IsASCII(char16_t aChar) {
+ return (aChar & 0xFF80) == 0;
+}
+
+/**
+ * Provides a pointer before or equal to |aPtr| that is is suitably aligned.
+ */
+inline const char16_t* aligned(const char16_t* aPtr, const uintptr_t aMask)
+{
+ return reinterpret_cast<const char16_t*>(
+ reinterpret_cast<const uintptr_t>(aPtr) & ~aMask);
+}
+
+/**
+ * Structures for word-sized vectorization of ASCII checking for UTF-16
+ * strings.
+ */
+template<size_t size> struct NonASCIIParameters;
+template<> struct NonASCIIParameters<4> {
+ static inline size_t mask() { return 0xff80ff80; }
+ static inline uintptr_t alignMask() { return 0x3; }
+ static inline size_t numUnicharsPerWord() { return 2; }
+};
+
+template<> struct NonASCIIParameters<8> {
+ static inline size_t mask() {
+ static const uint64_t maskAsUint64 = UINT64_C(0xff80ff80ff80ff80);
+ // We have to explicitly cast this 64-bit value to a size_t, or else
+ // compilers for 32-bit platforms will warn about it being too large to fit
+ // in the size_t return type. (Fortunately, this code isn't actually
+ // invoked on 32-bit platforms -- they'll use the <4> specialization above.
+ // So it is, in fact, OK that this value is too large for a 32-bit size_t.)
+ return (size_t)maskAsUint64;
+ }
+ static inline uintptr_t alignMask() { return 0x7; }
+ static inline size_t numUnicharsPerWord() { return 4; }
+};
+
+namespace SSE2 {
+
+int32_t FirstNonASCII(const char16_t* aBegin, const char16_t* aEnd);
+
+} // namespace SSE2
+} // namespace mozilla
diff --git a/xpcom/string/nsReadableUtilsSSE2.cpp b/xpcom/string/nsReadableUtilsSSE2.cpp
new file mode 100644
index 000000000..fe01d57af
--- /dev/null
+++ b/xpcom/string/nsReadableUtilsSSE2.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <emmintrin.h>
+
+#include "nsReadableUtilsImpl.h"
+
+namespace mozilla {
+namespace SSE2 {
+
+static inline bool
+is_zero (__m128i x)
+{
+ return
+ _mm_movemask_epi8(_mm_cmpeq_epi8(x, _mm_setzero_si128())) == 0xffff;
+}
+
+int32_t
+FirstNonASCII(const char16_t* aBegin, const char16_t* aEnd)
+{
+ const size_t kNumUnicharsPerVector = sizeof(__m128i) / sizeof(char16_t);
+ typedef NonASCIIParameters<sizeof(size_t)> p;
+ const size_t kMask = p::mask();
+ const uintptr_t kXmmAlignMask = 0xf;
+ const uint16_t kShortMask = 0xff80;
+ const size_t kNumUnicharsPerWord = p::numUnicharsPerWord();
+
+ const char16_t* idx = aBegin;
+
+ // Align ourselves to a 16-byte boundary as required by _mm_load_si128
+ for (; idx != aEnd && ((uintptr_t(idx) & kXmmAlignMask) != 0); idx++) {
+ if (!IsASCII(*idx)) {
+ return idx - aBegin;
+ }
+ }
+
+ // Check one XMM register (16 bytes) at a time.
+ const char16_t* vectWalkEnd = aligned(aEnd, kXmmAlignMask);
+ __m128i vectmask = _mm_set1_epi16(static_cast<int16_t>(kShortMask));
+ for (; idx != vectWalkEnd; idx += kNumUnicharsPerVector) {
+ const __m128i vect = *reinterpret_cast<const __m128i*>(idx);
+ if (!is_zero(_mm_and_si128(vect, vectmask))) {
+ return idx - aBegin;
+ }
+ }
+
+ // Check one word at a time.
+ const char16_t* wordWalkEnd = aligned(aEnd, p::alignMask());
+ for(; idx != wordWalkEnd; idx += kNumUnicharsPerWord) {
+ const size_t word = *reinterpret_cast<const size_t*>(idx);
+ if (word & kMask) {
+ return idx - aBegin;
+ }
+ }
+
+ // Take care of the remainder one character at a time.
+ for (; idx != aEnd; idx++) {
+ if (!IsASCII(*idx)) {
+ return idx - aBegin;
+ }
+ }
+
+ return -1;
+}
+
+} // namespace SSE2
+} // namespace mozilla
diff --git a/xpcom/string/nsString.cpp b/xpcom/string/nsString.cpp
new file mode 100644
index 000000000..2759eb4ca
--- /dev/null
+++ b/xpcom/string/nsString.cpp
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+
+// define nsString
+#include "string-template-def-unichar.h"
+#include "nsTString.cpp"
+#include "string-template-undef.h"
+
+// define nsCString
+#include "string-template-def-char.h"
+#include "nsTString.cpp"
+#include "string-template-undef.h"
diff --git a/xpcom/string/nsString.h b/xpcom/string/nsString.h
new file mode 100644
index 000000000..580e4113d
--- /dev/null
+++ b/xpcom/string/nsString.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsString_h___
+#define nsString_h___
+
+#include "mozilla/Attributes.h"
+
+#include "nsSubstring.h"
+#include "nsDependentSubstring.h"
+#include "nsReadableUtils.h"
+
+#include <new>
+
+// enable support for the obsolete string API if not explicitly disabled
+#ifndef MOZ_STRING_WITH_OBSOLETE_API
+#define MOZ_STRING_WITH_OBSOLETE_API 1
+#endif
+
+#if MOZ_STRING_WITH_OBSOLETE_API
+// radix values for ToInteger/AppendInt
+#define kRadix10 (10)
+#define kRadix16 (16)
+#define kAutoDetect (100)
+#define kRadixUnknown (kAutoDetect+1)
+#define IGNORE_CASE (true)
+#endif
+
+
+// declare nsString, et. al.
+#include "string-template-def-unichar.h"
+#include "nsTString.h"
+#include "string-template-undef.h"
+
+// declare nsCString, et. al.
+#include "string-template-def-char.h"
+#include "nsTString.h"
+#include "string-template-undef.h"
+
+static_assert(sizeof(char16_t) == 2, "size of char16_t must be 2");
+static_assert(sizeof(nsString::char_type) == 2,
+ "size of nsString::char_type must be 2");
+static_assert(nsString::char_type(-1) > nsString::char_type(0),
+ "nsString::char_type must be unsigned");
+static_assert(sizeof(nsCString::char_type) == 1,
+ "size of nsCString::char_type must be 1");
+
+
+/**
+ * A helper class that converts a UTF-16 string to ASCII in a lossy manner
+ */
+class NS_LossyConvertUTF16toASCII : public nsAutoCString
+{
+public:
+ explicit NS_LossyConvertUTF16toASCII(const char16ptr_t aString)
+ {
+ LossyAppendUTF16toASCII(aString, *this);
+ }
+
+ NS_LossyConvertUTF16toASCII(const char16ptr_t aString, uint32_t aLength)
+ {
+ LossyAppendUTF16toASCII(Substring(aString, aLength), *this);
+ }
+
+ explicit NS_LossyConvertUTF16toASCII(const nsAString& aString)
+ {
+ LossyAppendUTF16toASCII(aString, *this);
+ }
+
+private:
+ // NOT TO BE IMPLEMENTED
+ NS_LossyConvertUTF16toASCII(char) = delete;
+};
+
+
+class NS_ConvertASCIItoUTF16 : public nsAutoString
+{
+public:
+ explicit NS_ConvertASCIItoUTF16(const char* aCString)
+ {
+ AppendASCIItoUTF16(aCString, *this);
+ }
+
+ NS_ConvertASCIItoUTF16(const char* aCString, uint32_t aLength)
+ {
+ AppendASCIItoUTF16(Substring(aCString, aLength), *this);
+ }
+
+ explicit NS_ConvertASCIItoUTF16(const nsACString& aCString)
+ {
+ AppendASCIItoUTF16(aCString, *this);
+ }
+
+private:
+ // NOT TO BE IMPLEMENTED
+ NS_ConvertASCIItoUTF16(char16_t) = delete;
+};
+
+
+/**
+ * A helper class that converts a UTF-16 string to UTF-8
+ */
+class NS_ConvertUTF16toUTF8 : public nsAutoCString
+{
+public:
+ explicit NS_ConvertUTF16toUTF8(const char16ptr_t aString)
+ {
+ AppendUTF16toUTF8(aString, *this);
+ }
+
+ NS_ConvertUTF16toUTF8(const char16ptr_t aString, uint32_t aLength)
+ {
+ AppendUTF16toUTF8(Substring(aString, aLength), *this);
+ }
+
+ explicit NS_ConvertUTF16toUTF8(const nsAString& aString)
+ {
+ AppendUTF16toUTF8(aString, *this);
+ }
+
+private:
+ // NOT TO BE IMPLEMENTED
+ NS_ConvertUTF16toUTF8(char) = delete;
+};
+
+
+class NS_ConvertUTF8toUTF16 : public nsAutoString
+{
+public:
+ explicit NS_ConvertUTF8toUTF16(const char* aCString)
+ {
+ AppendUTF8toUTF16(aCString, *this);
+ }
+
+ NS_ConvertUTF8toUTF16(const char* aCString, uint32_t aLength)
+ {
+ AppendUTF8toUTF16(Substring(aCString, aLength), *this);
+ }
+
+ explicit NS_ConvertUTF8toUTF16(const nsACString& aCString)
+ {
+ AppendUTF8toUTF16(aCString, *this);
+ }
+
+private:
+ // NOT TO BE IMPLEMENTED
+ NS_ConvertUTF8toUTF16(char16_t) = delete;
+};
+
+
+#ifdef MOZ_USE_CHAR16_WRAPPER
+
+inline char16_t*
+wwc(wchar_t* aStr)
+{
+ return reinterpret_cast<char16_t*>(aStr);
+}
+
+inline wchar_t*
+wwc(char16_t* aStr)
+{
+ return reinterpret_cast<wchar_t*>(aStr);
+}
+
+inline const char16_t*
+wwc(const wchar_t* aStr)
+{
+ return reinterpret_cast<const char16_t*>(aStr);
+}
+
+inline const wchar_t*
+wwc(const char16_t* aStr)
+{
+ return reinterpret_cast<const wchar_t*>(aStr);
+}
+
+#else
+
+inline char16_t*
+wwc(char16_t* aStr)
+{
+ return aStr;
+}
+
+inline const char16_t*
+wwc(const char16_t* aStr)
+{
+ return aStr;
+}
+
+#endif
+
+// the following are included/declared for backwards compatibility
+typedef nsAutoString nsVoidableString;
+
+#include "nsDependentString.h"
+#include "nsLiteralString.h"
+#include "nsPromiseFlatString.h"
+
+// need to include these for backwards compatibility
+#include "nsMemory.h"
+#include <string.h>
+#include <stdio.h>
+#include "plhash.h"
+
+#endif // !defined(nsString_h___)
diff --git a/xpcom/string/nsStringBuffer.h b/xpcom/string/nsStringBuffer.h
new file mode 100644
index 000000000..432289bf6
--- /dev/null
+++ b/xpcom/string/nsStringBuffer.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsStringBuffer_h__
+#define nsStringBuffer_h__
+
+#include "mozilla/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+
+template<class T> struct already_AddRefed;
+
+/**
+ * This structure precedes the string buffers "we" allocate. It may be the
+ * case that nsTAString::mData does not point to one of these special
+ * buffers. The mFlags member variable distinguishes the buffer type.
+ *
+ * When this header is in use, it enables reference counting, and capacity
+ * tracking. NOTE: A string buffer can be modified only if its reference
+ * count is 1.
+ */
+class nsStringBuffer
+{
+private:
+ friend class CheckStaticAtomSizes;
+
+ mozilla::Atomic<int32_t> mRefCount;
+ uint32_t mStorageSize;
+
+public:
+
+ /**
+ * Allocates a new string buffer, with given size in bytes and a
+ * reference count of one. When the string buffer is no longer needed,
+ * it should be released via Release.
+ *
+ * It is up to the caller to set the bytes corresponding to the string
+ * buffer by calling the Data method to fetch the raw data pointer. Care
+ * must be taken to properly null terminate the character array. The
+ * storage size can be greater than the length of the actual string
+ * (i.e., it is not required that the null terminator appear in the last
+ * storage unit of the string buffer's data).
+ *
+ * @return new string buffer or null if out of memory.
+ */
+ static already_AddRefed<nsStringBuffer> Alloc(size_t aStorageSize);
+
+ /**
+ * Resizes the given string buffer to the specified storage size. This
+ * method must not be called on a readonly string buffer. Use this API
+ * carefully!!
+ *
+ * This method behaves like the ANSI-C realloc function. (i.e., If the
+ * allocation fails, null will be returned and the given string buffer
+ * will remain unmodified.)
+ *
+ * @see IsReadonly
+ */
+ static nsStringBuffer* Realloc(nsStringBuffer* aBuf, size_t aStorageSize);
+
+ /**
+ * Increment the reference count on this string buffer.
+ */
+ void NS_FASTCALL AddRef();
+
+ /**
+ * Decrement the reference count on this string buffer. The string
+ * buffer will be destroyed when its reference count reaches zero.
+ */
+ void NS_FASTCALL Release();
+
+ /**
+ * This method returns the string buffer corresponding to the given data
+ * pointer. The data pointer must have been returned previously by a
+ * call to the nsStringBuffer::Data method.
+ */
+ static nsStringBuffer* FromData(void* aData)
+ {
+ return reinterpret_cast<nsStringBuffer*>(aData) - 1;
+ }
+
+ /**
+ * This method returns the data pointer for this string buffer.
+ */
+ void* Data() const
+ {
+ return const_cast<char*>(reinterpret_cast<const char*>(this + 1));
+ }
+
+ /**
+ * This function returns the storage size of a string buffer in bytes.
+ * This value is the same value that was originally passed to Alloc (or
+ * Realloc).
+ */
+ uint32_t StorageSize() const
+ {
+ return mStorageSize;
+ }
+
+ /**
+ * If this method returns false, then the caller can be sure that their
+ * reference to the string buffer is the only reference to the string
+ * buffer, and therefore it has exclusive access to the string buffer and
+ * associated data. However, if this function returns true, then other
+ * consumers may rely on the data in this buffer being immutable and
+ * other threads may access this buffer simultaneously.
+ */
+ bool IsReadonly() const
+ {
+ return mRefCount > 1;
+ }
+
+ /**
+ * The FromString methods return a string buffer for the given string
+ * object or null if the string object does not have a string buffer.
+ * The reference count of the string buffer is NOT incremented by these
+ * methods. If the caller wishes to hold onto the returned value, then
+ * the returned string buffer must have its reference count incremented
+ * via a call to the AddRef method.
+ */
+ static nsStringBuffer* FromString(const nsAString& aStr);
+ static nsStringBuffer* FromString(const nsACString& aStr);
+
+ /**
+ * The ToString methods assign this string buffer to a given string
+ * object. If the string object does not support sharable string
+ * buffers, then its value will be set to a copy of the given string
+ * buffer. Otherwise, these methods increment the reference count of the
+ * given string buffer. It is important to specify the length (in
+ * storage units) of the string contained in the string buffer since the
+ * length of the string may be less than its storage size. The string
+ * must have a null terminator at the offset specified by |len|.
+ *
+ * NOTE: storage size is measured in bytes even for wide strings;
+ * however, string length is always measured in storage units
+ * (2-byte units for wide strings).
+ */
+ void ToString(uint32_t aLen, nsAString& aStr, bool aMoveOwnership = false);
+ void ToString(uint32_t aLen, nsACString& aStr, bool aMoveOwnership = false);
+
+ /**
+ * This measures the size only if the StringBuffer is unshared.
+ */
+ size_t SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ /**
+ * This measures the size regardless of whether the StringBuffer is
+ * unshared.
+ *
+ * WARNING: Only use this if you really know what you are doing, because
+ * it can easily lead to double-counting strings. If you do use them,
+ * please explain clearly in a comment why it's safe and won't lead to
+ * double-counting.
+ */
+ size_t SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const;
+};
+
+#endif /* !defined(nsStringBuffer_h__ */
diff --git a/xpcom/string/nsStringComparator.cpp b/xpcom/string/nsStringComparator.cpp
new file mode 100644
index 000000000..81f1629f8
--- /dev/null
+++ b/xpcom/string/nsStringComparator.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <ctype.h>
+#include "nsAString.h"
+#include "plstr.h"
+
+
+// define nsStringComparator
+#include "string-template-def-unichar.h"
+#include "nsTStringComparator.cpp"
+#include "string-template-undef.h"
+
+// define nsCStringComparator
+#include "string-template-def-char.h"
+#include "nsTStringComparator.cpp"
+#include "string-template-undef.h"
+
+
+int
+nsCaseInsensitiveCStringComparator::operator()(const char_type* aLhs,
+ const char_type* aRhs,
+ uint32_t aLhsLength,
+ uint32_t aRhsLength) const
+{
+ if (aLhsLength != aRhsLength) {
+ return (aLhsLength > aRhsLength) ? 1 : -1;
+ }
+ int32_t result = int32_t(PL_strncasecmp(aLhs, aRhs, aLhsLength));
+ //Egads. PL_strncasecmp is returning *very* negative numbers.
+ //Some folks expect -1,0,1, so let's temper its enthusiasm.
+ if (result < 0) {
+ result = -1;
+ }
+ return result;
+}
diff --git a/xpcom/string/nsStringFwd.h b/xpcom/string/nsStringFwd.h
new file mode 100644
index 000000000..a9162f384
--- /dev/null
+++ b/xpcom/string/nsStringFwd.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* nsStringFwd.h --- forward declarations for string classes */
+
+#ifndef nsStringFwd_h___
+#define nsStringFwd_h___
+
+#include "nscore.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#error Internal string headers are not available from external-linkage code.
+#endif
+
+/**
+ * double-byte (char16_t) string types
+ */
+
+class nsAString;
+class nsSubstringTuple;
+class nsString;
+class nsAutoString;
+class nsDependentString;
+class nsDependentSubstring;
+class nsPromiseFlatString;
+class nsStringComparator;
+class nsDefaultStringComparator;
+class nsXPIDLString;
+
+
+/**
+ * single-byte (char) string types
+ */
+
+class nsACString;
+class nsCSubstringTuple;
+class nsCString;
+class nsAutoCString;
+class nsDependentCString;
+class nsDependentCSubstring;
+class nsPromiseFlatCString;
+class nsCStringComparator;
+class nsDefaultCStringComparator;
+class nsXPIDLCString;
+
+
+/**
+ * typedefs for backwards compatibility
+ */
+
+typedef nsAString nsSubstring;
+typedef nsACString nsCSubstring;
+
+typedef nsString nsAFlatString;
+typedef nsSubstring nsASingleFragmentString;
+
+typedef nsCString nsAFlatCString;
+typedef nsCSubstring nsASingleFragmentCString;
+
+
+#endif /* !defined(nsStringFwd_h___) */
diff --git a/xpcom/string/nsStringIterator.h b/xpcom/string/nsStringIterator.h
new file mode 100644
index 000000000..e309a21e9
--- /dev/null
+++ b/xpcom/string/nsStringIterator.h
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsStringIterator_h___
+#define nsStringIterator_h___
+
+#include "nsCharTraits.h"
+#include "nsAlgorithm.h"
+#include "nsDebug.h"
+
+/**
+ * @see nsTAString
+ */
+
+template <class CharT>
+class nsReadingIterator
+{
+public:
+ typedef nsReadingIterator<CharT> self_type;
+ typedef ptrdiff_t difference_type;
+ typedef size_t size_type;
+ typedef CharT value_type;
+ typedef const CharT* pointer;
+ typedef const CharT& reference;
+
+private:
+ friend class nsAString;
+ friend class nsACString;
+
+ // unfortunately, the API for nsReadingIterator requires that the
+ // iterator know its start and end positions. this was needed when
+ // we supported multi-fragment strings, but now it is really just
+ // extra baggage. we should remove mStart and mEnd at some point.
+
+ const CharT* mStart;
+ const CharT* mEnd;
+ const CharT* mPosition;
+
+public:
+ nsReadingIterator()
+ {
+ }
+ // nsReadingIterator( const nsReadingIterator<CharT>& ); // auto-generated copy-constructor OK
+ // nsReadingIterator<CharT>& operator=( const nsReadingIterator<CharT>& ); // auto-generated copy-assignment operator OK
+
+ pointer get() const
+ {
+ return mPosition;
+ }
+
+ CharT operator*() const
+ {
+ return *get();
+ }
+
+ self_type& operator++()
+ {
+ ++mPosition;
+ return *this;
+ }
+
+ self_type operator++(int)
+ {
+ self_type result(*this);
+ ++mPosition;
+ return result;
+ }
+
+ self_type& operator--()
+ {
+ --mPosition;
+ return *this;
+ }
+
+ self_type operator--(int)
+ {
+ self_type result(*this);
+ --mPosition;
+ return result;
+ }
+
+ self_type& advance(difference_type aN)
+ {
+ if (aN > 0) {
+ difference_type step = XPCOM_MIN(aN, mEnd - mPosition);
+
+ NS_ASSERTION(step > 0,
+ "can't advance a reading iterator beyond the end of a string");
+
+ mPosition += step;
+ } else if (aN < 0) {
+ difference_type step = XPCOM_MAX(aN, -(mPosition - mStart));
+
+ NS_ASSERTION(step < 0,
+ "can't advance (backward) a reading iterator beyond the end of a string");
+
+ mPosition += step;
+ }
+ return *this;
+ }
+
+ // We return an unsigned type here (with corresponding assert) rather than
+ // the more usual difference_type because we want to make this class go
+ // away in favor of mozilla::RangedPtr. Since RangedPtr has the same
+ // requirement we are enforcing here, the transition ought to be much
+ // smoother.
+ size_type operator-(const self_type& aOther) const
+ {
+ MOZ_ASSERT(mPosition >= aOther.mPosition);
+ return mPosition - aOther.mPosition;
+ }
+};
+
+/**
+ * @see nsTAString
+ */
+
+template <class CharT>
+class nsWritingIterator
+{
+public:
+ typedef nsWritingIterator<CharT> self_type;
+ typedef ptrdiff_t difference_type;
+ typedef size_t size_type;
+ typedef CharT value_type;
+ typedef CharT* pointer;
+ typedef CharT& reference;
+
+private:
+ friend class nsAString;
+ friend class nsACString;
+
+ // unfortunately, the API for nsWritingIterator requires that the
+ // iterator know its start and end positions. this was needed when
+ // we supported multi-fragment strings, but now it is really just
+ // extra baggage. we should remove mStart and mEnd at some point.
+
+ CharT* mStart;
+ CharT* mEnd;
+ CharT* mPosition;
+
+public:
+ nsWritingIterator()
+ {
+ }
+ // nsWritingIterator( const nsWritingIterator<CharT>& ); // auto-generated copy-constructor OK
+ // nsWritingIterator<CharT>& operator=( const nsWritingIterator<CharT>& ); // auto-generated copy-assignment operator OK
+
+ pointer get() const
+ {
+ return mPosition;
+ }
+
+ reference operator*() const
+ {
+ return *get();
+ }
+
+ self_type& operator++()
+ {
+ ++mPosition;
+ return *this;
+ }
+
+ self_type operator++(int)
+ {
+ self_type result(*this);
+ ++mPosition;
+ return result;
+ }
+
+ self_type& operator--()
+ {
+ --mPosition;
+ return *this;
+ }
+
+ self_type operator--(int)
+ {
+ self_type result(*this);
+ --mPosition;
+ return result;
+ }
+
+ self_type& advance(difference_type aN)
+ {
+ if (aN > 0) {
+ difference_type step = XPCOM_MIN(aN, mEnd - mPosition);
+
+ NS_ASSERTION(step > 0,
+ "can't advance a writing iterator beyond the end of a string");
+
+ mPosition += step;
+ } else if (aN < 0) {
+ difference_type step = XPCOM_MAX(aN, -(mPosition - mStart));
+
+ NS_ASSERTION(step < 0,
+ "can't advance (backward) a writing iterator beyond the end of a string");
+
+ mPosition += step;
+ }
+ return *this;
+ }
+
+ // We return an unsigned type here (with corresponding assert) rather than
+ // the more usual difference_type because we want to make this class go
+ // away in favor of mozilla::RangedPtr. Since RangedPtr has the same
+ // requirement we are enforcing here, the transition ought to be much
+ // smoother.
+ size_type operator-(const self_type& aOther) const
+ {
+ MOZ_ASSERT(mPosition >= aOther.mPosition);
+ return mPosition - aOther.mPosition;
+ }
+};
+
+template <class CharT>
+struct nsCharSinkTraits<nsWritingIterator<CharT>>
+{
+ static void
+ write(nsWritingIterator<CharT>& aIter, const CharT* aStr, uint32_t aN)
+ {
+ nsCharTraits<CharT>::move(aIter.get(), aStr, aN);
+ aIter.advance(aN);
+ }
+};
+
+template <class CharT>
+inline bool
+operator==(const nsReadingIterator<CharT>& aLhs,
+ const nsReadingIterator<CharT>& aRhs)
+{
+ return aLhs.get() == aRhs.get();
+}
+
+template <class CharT>
+inline bool
+operator!=(const nsReadingIterator<CharT>& aLhs,
+ const nsReadingIterator<CharT>& aRhs)
+{
+ return aLhs.get() != aRhs.get();
+}
+
+
+//
+// |nsWritingIterator|s
+//
+
+template <class CharT>
+inline bool
+operator==(const nsWritingIterator<CharT>& aLhs,
+ const nsWritingIterator<CharT>& aRhs)
+{
+ return aLhs.get() == aRhs.get();
+}
+
+template <class CharT>
+inline bool
+operator!=(const nsWritingIterator<CharT>& aLhs,
+ const nsWritingIterator<CharT>& aRhs)
+{
+ return aLhs.get() != aRhs.get();
+}
+
+#endif /* !defined(nsStringIterator_h___) */
diff --git a/xpcom/string/nsStringObsolete.cpp b/xpcom/string/nsStringObsolete.cpp
new file mode 100644
index 000000000..bd6daacab
--- /dev/null
+++ b/xpcom/string/nsStringObsolete.cpp
@@ -0,0 +1,1053 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+
+
+/**
+ * nsTString obsolete API support
+ */
+
+#if MOZ_STRING_WITH_OBSOLETE_API
+
+#include "nsDependentString.h"
+#include "nsDependentSubstring.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsUTF8Utils.h"
+#include "prdtoa.h"
+
+/* ***** BEGIN RICKG BLOCK *****
+ *
+ * NOTE: This section of code was extracted from rickg's bufferRoutines.h file.
+ * For the most part it remains unmodified. We want to eliminate (or at
+ * least clean up) this code at some point. If you find the formatting
+ * in this section somewhat inconsistent, don't blame me! ;-)
+ */
+
+// avoid STDC's tolower since it may do weird things with non-ASCII bytes
+inline char
+ascii_tolower(char aChar)
+{
+ if (aChar >= 'A' && aChar <= 'Z')
+ return aChar + ('a' - 'A');
+ return aChar;
+}
+
+//-----------------------------------------------------------------------------
+//
+// This set of methods is used to search a buffer looking for a char.
+//
+
+
+/**
+ * This methods cans the given buffer for the given char
+ *
+ * @update gess 02/17/00
+ * @param aDest is the buffer to be searched
+ * @param aDestLength is the size (in char-units, not bytes) of the buffer
+ * @param anOffset is the start pos to begin searching
+ * @param aChar is the target character we're looking for
+ * @param aCount tells us how many characters to iterate through (which may be different than aLength); -1 means use full length.
+ * @return index of pos if found, else -1 (kNotFound)
+ */
+static int32_t
+FindChar1(const char* aDest,uint32_t aDestLength,int32_t anOffset,const char16_t aChar,int32_t aCount) {
+
+ if(anOffset < 0)
+ anOffset=0;
+
+ if(aCount < 0)
+ aCount = (int32_t)aDestLength;
+
+ if((aChar < 256) && (0 < aDestLength) && ((uint32_t)anOffset < aDestLength)) {
+
+ //We'll only search if the given aChar is within the normal ascii a range,
+ //(Since this string is definitely within the ascii range).
+
+ if(0<aCount) {
+
+ const char* left= aDest+anOffset;
+ const char* last= left+aCount;
+ const char* max = aDest+aDestLength;
+ const char* end = (last<max) ? last : max;
+
+ int32_t theMax = end-left;
+ if(0<theMax) {
+
+ unsigned char theChar = (unsigned char) aChar;
+ const char* result=(const char*)memchr(left, (int)theChar, theMax);
+
+ if(result)
+ return result-aDest;
+
+ }
+ }
+ }
+
+ return kNotFound;
+}
+
+
+/**
+ * This methods cans the given buffer for the given char
+ *
+ * @update gess 3/25/98
+ * @param aDest is the buffer to be searched
+ * @param aDestLength is the size (in char-units, not bytes) of the buffer
+ * @param anOffset is the start pos to begin searching
+ * @param aChar is the target character we're looking for
+ * @param aCount tells us how many characters to iterate through (which may be different than aLength); -1 means use full length.
+ * @return index of pos if found, else -1 (kNotFound)
+ */
+static int32_t
+FindChar2(const char16_t* aDest,uint32_t aDestLength,int32_t anOffset,const char16_t aChar,int32_t aCount) {
+
+ if(anOffset < 0)
+ anOffset=0;
+
+ if(aCount < 0)
+ aCount = (int32_t)aDestLength;
+
+ if((0<aDestLength) && ((uint32_t)anOffset < aDestLength)) {
+
+ if(0<aCount) {
+
+ const char16_t* root = aDest;
+ const char16_t* left = root+anOffset;
+ const char16_t* last = left+aCount;
+ const char16_t* max = root+aDestLength;
+ const char16_t* end = (last<max) ? last : max;
+
+ while(left<end){
+
+ if(*left==aChar)
+ return (left-root);
+
+ ++left;
+ }
+ }
+ }
+
+ return kNotFound;
+}
+
+
+/**
+ * This methods cans the given buffer (in reverse) for the given char
+ *
+ * @update gess 02/17/00
+ * @param aDest is the buffer to be searched
+ * @param aDestLength is the size (in char-units, not bytes) of the buffer
+ * @param anOffset is the start pos to begin searching
+ * @param aChar is the target character we're looking for
+ * @param aCount tells us how many characters to iterate through (which may be different than aLength); -1 means use full length.
+ * @return index of pos if found, else -1 (kNotFound)
+ */
+
+static int32_t
+RFindChar1(const char* aDest,uint32_t aDestLength,int32_t anOffset,const char16_t aChar,int32_t aCount) {
+
+ if(anOffset < 0)
+ anOffset=(int32_t)aDestLength-1;
+
+ if(aCount < 0)
+ aCount = int32_t(aDestLength);
+
+ if((aChar<256) && (0 < aDestLength) && ((uint32_t)anOffset < aDestLength)) {
+
+ //We'll only search if the given aChar is within the normal ascii a range,
+ //(Since this string is definitely within the ascii range).
+
+ if(0 < aCount) {
+
+ const char* rightmost = aDest + anOffset;
+ const char* min = rightmost - aCount + 1;
+ const char* leftmost = (min<aDest) ? aDest: min;
+
+ char theChar=(char)aChar;
+ while(leftmost <= rightmost){
+
+ if((*rightmost) == theChar)
+ return rightmost - aDest;
+
+ --rightmost;
+ }
+ }
+ }
+
+ return kNotFound;
+}
+
+
+/**
+ * This methods cans the given buffer for the given char
+ *
+ * @update gess 3/25/98
+ * @param aDest is the buffer to be searched
+ * @param aDestLength is the size (in char-units, not bytes) of the buffer
+ * @param anOffset is the start pos to begin searching
+ * @param aChar is the target character we're looking for
+ * @param aCount tells us how many characters to iterate through (which may be different than aLength); -1 means use full length.
+ * @return index of pos if found, else -1 (kNotFound)
+ */
+static int32_t
+RFindChar2(const char16_t* aDest,uint32_t aDestLength,int32_t anOffset,const char16_t aChar,int32_t aCount) {
+
+ if(anOffset < 0)
+ anOffset=(int32_t)aDestLength-1;
+
+ if(aCount < 0)
+ aCount = int32_t(aDestLength);
+
+ if((0 < aDestLength) && ((uint32_t)anOffset < aDestLength)) {
+
+ if(0 < aCount) {
+
+ const char16_t* root = aDest;
+ const char16_t* rightmost = root + anOffset;
+ const char16_t* min = rightmost - aCount + 1;
+ const char16_t* leftmost = (min<root) ? root: min;
+
+ while(leftmost <= rightmost){
+
+ if((*rightmost) == aChar)
+ return rightmost - root;
+
+ --rightmost;
+ }
+ }
+ }
+
+ return kNotFound;
+}
+
+//-----------------------------------------------------------------------------
+//
+// This set of methods is used to compare one buffer onto another. The
+// functions are differentiated by the size of source and dest character
+// sizes. WARNING: Your destination buffer MUST be big enough to hold all the
+// source bytes. We don't validate these ranges here (this should be done in
+// higher level routines).
+//
+
+
+/**
+ * This method compares the data in one buffer with another
+ * @update gess 01/04/99
+ * @param aStr1 is the first buffer to be compared
+ * @param aStr2 is the 2nd buffer to be compared
+ * @param aCount is the number of chars to compare
+ * @param aIgnoreCase tells us whether to use a case-sensitive comparison
+ * @return -1,0,1 depending on <,==,>
+ */
+static
+#ifdef __SUNPRO_CC
+inline
+#endif /* __SUNPRO_CC */
+int32_t
+Compare1To1(const char* aStr1,const char* aStr2,uint32_t aCount,bool aIgnoreCase) {
+ int32_t result=0;
+ if(aIgnoreCase)
+ result=int32_t(PL_strncasecmp(aStr1, aStr2, aCount));
+ else
+ result=nsCharTraits<char>::compare(aStr1,aStr2,aCount);
+
+ // alien comparisons may return out-of-bound answers
+ // instead of the -1, 0, 1 expected by most clients
+ if ( result < -1 )
+ result = -1;
+ else if ( result > 1 )
+ result = 1;
+ return result;
+}
+
+/**
+ * This method compares the data in one buffer with another
+ * @update gess 01/04/99
+ * @param aStr1 is the first buffer to be compared
+ * @param aStr2 is the 2nd buffer to be compared
+ * @param aCount is the number of chars to compare
+ * @param aIgnoreCase tells us whether to use a case-sensitive comparison
+ * @return -1,0,1 depending on <,==,>
+ */
+static
+#ifdef __SUNPRO_CC
+inline
+#endif /* __SUNPRO_CC */
+int32_t
+Compare2To2(const char16_t* aStr1,const char16_t* aStr2,uint32_t aCount){
+ int32_t result;
+
+ if ( aStr1 && aStr2 )
+ result = nsCharTraits<char16_t>::compare(aStr1, aStr2, aCount);
+
+ // The following cases are rare and survivable caller errors.
+ // Two null pointers are equal, but any string, even 0 length
+ // is greater than a null pointer. It might not really matter,
+ // but we pick something reasonable anyway.
+ else if ( !aStr1 && !aStr2 )
+ result = 0;
+ else if ( aStr1 )
+ result = 1;
+ else
+ result = -1;
+
+ // alien comparisons may give answers outside the -1, 0, 1 expected by callers
+ if ( result < -1 )
+ result = -1;
+ else if ( result > 1 )
+ result = 1;
+ return result;
+}
+
+
+/**
+ * This method compares the data in one buffer with another
+ * @update gess 01/04/99
+ * @param aStr1 is the first buffer to be compared
+ * @param aStr2 is the 2nd buffer to be compared
+ * @param aCount is the number of chars to compare
+ * @param aIgnoreCase tells us whether to use a case-sensitive comparison
+ * @return -1,0,1 depending on <,==,>
+ */
+static
+#ifdef __SUNPRO_CC
+inline
+#endif /* __SUNPRO_CC */
+int32_t
+Compare2To1(const char16_t* aStr1,const char* aStr2,uint32_t aCount,bool aIgnoreCase){
+ const char16_t* s1 = aStr1;
+ const char *s2 = aStr2;
+
+ if (aStr1 && aStr2) {
+ if (aCount != 0) {
+ do {
+
+ char16_t c1 = *s1++;
+ char16_t c2 = char16_t((unsigned char)*s2++);
+
+ if (c1 != c2) {
+#ifdef DEBUG
+ // we won't warn on c1>=128 (the 2-byte value) because often
+ // it is just fine to compare an constant, ascii value (i.e. "body")
+ // against some non-ascii value (i.e. a unicode string that
+ // was downloaded from a web page)
+ if (aIgnoreCase && c2>=128)
+ NS_WARNING("got a non-ASCII string, but we can't do an accurate case conversion!");
+#endif
+
+ // can't do case conversion on characters out of our range
+ if (aIgnoreCase && c1<128 && c2<128) {
+
+ c1 = ascii_tolower(char(c1));
+ c2 = ascii_tolower(char(c2));
+
+ if (c1 == c2) continue;
+ }
+
+ if (c1 < c2) return -1;
+ return 1;
+ }
+ } while (--aCount);
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * This method compares the data in one buffer with another
+ * @update gess 01/04/99
+ * @param aStr1 is the first buffer to be compared
+ * @param aStr2 is the 2nd buffer to be compared
+ * @param aCount is the number of chars to compare
+ * @param aIgnoreCase tells us whether to use a case-sensitive comparison
+ * @return -1,0,1 depending on <,==,>
+ */
+inline int32_t
+Compare1To2(const char* aStr1,const char16_t* aStr2,uint32_t aCount,bool aIgnoreCase){
+ return Compare2To1(aStr2, aStr1, aCount, aIgnoreCase) * -1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// This set of methods is used compress char sequences in a buffer...
+//
+
+
+/**
+ * This method compresses duplicate runs of a given char from the given buffer
+ *
+ * @update rickg 03.23.2000
+ * @param aString is the buffer to be manipulated
+ * @param aLength is the length of the buffer
+ * @param aSet tells us which chars to compress from given buffer
+ * @param aEliminateLeading tells us whether to strip chars from the start of the buffer
+ * @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
+ * @return the new length of the given buffer
+ */
+static int32_t
+CompressChars1(char* aString,uint32_t aLength,const char* aSet){
+
+ char* from = aString;
+ char* end = aString + aLength;
+ char* to = from;
+
+ //this code converts /n, /t, /r into normal space ' ';
+ //it also compresses runs of whitespace down to a single char...
+ if(aSet && aString && (0 < aLength)){
+ uint32_t aSetLen=strlen(aSet);
+
+ while (from < end) {
+ char theChar = *from++;
+
+ *to++=theChar; //always copy this char...
+
+ if((kNotFound!=FindChar1(aSet,aSetLen,0,theChar,aSetLen))){
+ while (from < end) {
+ theChar = *from++;
+ if(kNotFound==FindChar1(aSet,aSetLen,0,theChar,aSetLen)){
+ *to++ = theChar;
+ break;
+ }
+ } //while
+ } //if
+ } //if
+ *to = 0;
+ }
+ return to - aString;
+}
+
+
+
+/**
+ * This method compresses duplicate runs of a given char from the given buffer
+ *
+ * @update rickg 03.23.2000
+ * @param aString is the buffer to be manipulated
+ * @param aLength is the length of the buffer
+ * @param aSet tells us which chars to compress from given buffer
+ * @param aEliminateLeading tells us whether to strip chars from the start of the buffer
+ * @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
+ * @return the new length of the given buffer
+ */
+static int32_t
+CompressChars2(char16_t* aString,uint32_t aLength,const char* aSet) {
+
+ char16_t* from = aString;
+ char16_t* end = from + aLength;
+ char16_t* to = from;
+
+ //this code converts /n, /t, /r into normal space ' ';
+ //it also compresses runs of whitespace down to a single char...
+ if(aSet && aString && (0 < aLength)){
+ uint32_t aSetLen=strlen(aSet);
+
+ while (from < end) {
+ char16_t theChar = *from++;
+
+ *to++=theChar; //always copy this char...
+
+ if((theChar<256) && (kNotFound!=FindChar1(aSet,aSetLen,0,theChar,aSetLen))){
+ while (from < end) {
+ theChar = *from++;
+ if(kNotFound==FindChar1(aSet,aSetLen,0,theChar,aSetLen)){
+ *to++ = theChar;
+ break;
+ }
+ } //while
+ } //if
+ } //if
+ *to = 0;
+ }
+ return to - (char16_t*)aString;
+}
+
+/**
+ * This method strips chars in a given set from the given buffer
+ *
+ * @update gess 01/04/99
+ * @param aString is the buffer to be manipulated
+ * @param aLength is the length of the buffer
+ * @param aSet tells us which chars to compress from given buffer
+ * @param aEliminateLeading tells us whether to strip chars from the start of the buffer
+ * @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
+ * @return the new length of the given buffer
+ */
+static int32_t
+StripChars1(char* aString,uint32_t aLength,const char* aSet) {
+
+ // XXX(darin): this code should defer writing until necessary.
+
+ char* to = aString;
+ char* from = aString-1;
+ char* end = aString + aLength;
+
+ if(aSet && aString && (0 < aLength)){
+ uint32_t aSetLen=strlen(aSet);
+ while (++from < end) {
+ char theChar = *from;
+ if(kNotFound==FindChar1(aSet,aSetLen,0,theChar,aSetLen)){
+ *to++ = theChar;
+ }
+ }
+ *to = 0;
+ }
+ return to - (char*)aString;
+}
+
+
+/**
+ * This method strips chars in a given set from the given buffer
+ *
+ * @update gess 01/04/99
+ * @param aString is the buffer to be manipulated
+ * @param aLength is the length of the buffer
+ * @param aSet tells us which chars to compress from given buffer
+ * @param aEliminateLeading tells us whether to strip chars from the start of the buffer
+ * @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
+ * @return the new length of the given buffer
+ */
+static int32_t
+StripChars2(char16_t* aString,uint32_t aLength,const char* aSet) {
+
+ // XXX(darin): this code should defer writing until necessary.
+
+ char16_t* to = aString;
+ char16_t* from = aString-1;
+ char16_t* end = to + aLength;
+
+ if(aSet && aString && (0 < aLength)){
+ uint32_t aSetLen=strlen(aSet);
+ while (++from < end) {
+ char16_t theChar = *from;
+ //Note the test for ascii range below. If you have a real unicode char,
+ //and you're searching for chars in the (given) ascii string, there's no
+ //point in doing the real search since it's out of the ascii range.
+ if((255<theChar) || (kNotFound==FindChar1(aSet,aSetLen,0,theChar,aSetLen))){
+ *to++ = theChar;
+ }
+ }
+ *to = 0;
+ }
+ return to - (char16_t*)aString;
+}
+
+/* ***** END RICKG BLOCK ***** */
+
+static const char* kWhitespace="\f\t\r\n ";
+
+// This function is used to implement FindCharInSet and friends
+template <class CharT>
+#ifndef __SUNPRO_CC
+static
+#endif /* !__SUNPRO_CC */
+CharT
+GetFindInSetFilter( const CharT* set)
+{
+ CharT filter = ~CharT(0); // All bits set
+ while (*set) {
+ filter &= ~(*set);
+ ++set;
+ }
+ return filter;
+}
+
+// This template class is used by our code to access rickg's buffer routines.
+template <class CharT> struct nsBufferRoutines {};
+
+template <>
+struct nsBufferRoutines<char>
+{
+ static
+ int32_t compare( const char* a, const char* b, uint32_t max, bool ic )
+ {
+ return Compare1To1(a, b, max, ic);
+ }
+
+ static
+ int32_t compare( const char* a, const char16_t* b, uint32_t max, bool ic )
+ {
+ return Compare1To2(a, b, max, ic);
+ }
+
+ static
+ int32_t find_char( const char* s, uint32_t max, int32_t offset, const char16_t c, int32_t count )
+ {
+ return FindChar1(s, max, offset, c, count);
+ }
+
+ static
+ int32_t rfind_char( const char* s, uint32_t max, int32_t offset, const char16_t c, int32_t count )
+ {
+ return RFindChar1(s, max, offset, c, count);
+ }
+
+ static
+ char get_find_in_set_filter( const char* set )
+ {
+ return GetFindInSetFilter(set);
+ }
+
+ static
+ int32_t strip_chars( char* s, uint32_t len, const char* set )
+ {
+ return StripChars1(s, len, set);
+ }
+
+ static
+ int32_t compress_chars( char* s, uint32_t len, const char* set )
+ {
+ return CompressChars1(s, len, set);
+ }
+};
+
+template <>
+struct nsBufferRoutines<char16_t>
+{
+ static
+ int32_t compare( const char16_t* a, const char16_t* b, uint32_t max, bool ic )
+ {
+ NS_ASSERTION(!ic, "no case-insensitive compare here");
+ return Compare2To2(a, b, max);
+ }
+
+ static
+ int32_t compare( const char16_t* a, const char* b, uint32_t max, bool ic )
+ {
+ return Compare2To1(a, b, max, ic);
+ }
+
+ static
+ int32_t find_char( const char16_t* s, uint32_t max, int32_t offset, const char16_t c, int32_t count )
+ {
+ return FindChar2(s, max, offset, c, count);
+ }
+
+ static
+ int32_t rfind_char( const char16_t* s, uint32_t max, int32_t offset, const char16_t c, int32_t count )
+ {
+ return RFindChar2(s, max, offset, c, count);
+ }
+
+ static
+ char16_t get_find_in_set_filter( const char16_t* set )
+ {
+ return GetFindInSetFilter(set);
+ }
+
+ static
+ char16_t get_find_in_set_filter( const char* set )
+ {
+ return (~char16_t(0)^~char(0)) | GetFindInSetFilter(set);
+ }
+
+ static
+ int32_t strip_chars( char16_t* s, uint32_t max, const char* set )
+ {
+ return StripChars2(s, max, set);
+ }
+
+ static
+ int32_t compress_chars( char16_t* s, uint32_t len, const char* set )
+ {
+ return CompressChars2(s, len, set);
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+template <class L, class R>
+#ifndef __SUNPRO_CC
+static
+#endif /* !__SUNPRO_CC */
+int32_t
+FindSubstring( const L* big, uint32_t bigLen,
+ const R* little, uint32_t littleLen,
+ bool ignoreCase )
+{
+ if (littleLen > bigLen)
+ return kNotFound;
+
+ int32_t i, max = int32_t(bigLen - littleLen);
+ for (i=0; i<=max; ++i, ++big)
+ {
+ if (nsBufferRoutines<L>::compare(big, little, littleLen, ignoreCase) == 0)
+ return i;
+ }
+
+ return kNotFound;
+}
+
+template <class L, class R>
+#ifndef __SUNPRO_CC
+static
+#endif /* !__SUNPRO_CC */
+int32_t
+RFindSubstring( const L* big, uint32_t bigLen,
+ const R* little, uint32_t littleLen,
+ bool ignoreCase )
+{
+ if (littleLen > bigLen)
+ return kNotFound;
+
+ int32_t i, max = int32_t(bigLen - littleLen);
+
+ const L* iter = big + max;
+ for (i=max; iter >= big; --i, --iter)
+ {
+ if (nsBufferRoutines<L>::compare(iter, little, littleLen, ignoreCase) == 0)
+ return i;
+ }
+
+ return kNotFound;
+}
+
+template <class CharT, class SetCharT>
+#ifndef __SUNPRO_CC
+static
+#endif /* !__SUNPRO_CC */
+int32_t
+FindCharInSet( const CharT* data, uint32_t dataLen, const SetCharT* set )
+{
+ CharT filter = nsBufferRoutines<CharT>::get_find_in_set_filter(set);
+
+ const CharT* end = data + dataLen;
+ for (const CharT* iter = data; iter < end; ++iter)
+ {
+ CharT currentChar = *iter;
+ if (currentChar & filter)
+ continue; // char is not in filter set; go on with next char.
+
+ // test all chars
+ const SetCharT* charInSet = set;
+ CharT setChar = CharT(*charInSet);
+ while (setChar)
+ {
+ if (setChar == currentChar)
+ return iter - data; // found it! return index of the found char.
+
+ setChar = CharT(*(++charInSet));
+ }
+ }
+ return kNotFound;
+}
+
+template <class CharT, class SetCharT>
+#ifndef __SUNPRO_CC
+static
+#endif /* !__SUNPRO_CC */
+int32_t
+RFindCharInSet( const CharT* data, uint32_t dataLen, const SetCharT* set )
+{
+ CharT filter = nsBufferRoutines<CharT>::get_find_in_set_filter(set);
+
+ for (const CharT* iter = data + dataLen - 1; iter >= data; --iter)
+ {
+ CharT currentChar = *iter;
+ if (currentChar & filter)
+ continue; // char is not in filter set; go on with next char.
+
+ // test all chars
+ const CharT* charInSet = set;
+ CharT setChar = *charInSet;
+ while (setChar)
+ {
+ if (setChar == currentChar)
+ return iter - data; // found it! return index of the found char.
+
+ setChar = *(++charInSet);
+ }
+ }
+ return kNotFound;
+}
+
+/**
+ * this method changes the meaning of |offset| and |count|:
+ *
+ * upon return,
+ * |offset| specifies start of search range
+ * |count| specifies length of search range
+ */
+static void
+Find_ComputeSearchRange( uint32_t bigLen, uint32_t littleLen, int32_t& offset, int32_t& count )
+{
+ // |count| specifies how many iterations to make from |offset|
+
+ if (offset < 0)
+ {
+ offset = 0;
+ }
+ else if (uint32_t(offset) > bigLen)
+ {
+ count = 0;
+ return;
+ }
+
+ int32_t maxCount = bigLen - offset;
+ if (count < 0 || count > maxCount)
+ {
+ count = maxCount;
+ }
+ else
+ {
+ count += littleLen;
+ if (count > maxCount)
+ count = maxCount;
+ }
+}
+
+/**
+ * this method changes the meaning of |offset| and |count|:
+ *
+ * upon entry,
+ * |offset| specifies the end point from which to search backwards
+ * |count| specifies the number of iterations from |offset|
+ *
+ * upon return,
+ * |offset| specifies start of search range
+ * |count| specifies length of search range
+ *
+ *
+ * EXAMPLE
+ *
+ * + -- littleLen=4 -- +
+ * : :
+ * |____|____|____|____|____|____|____|____|____|____|____|____|
+ * : :
+ * offset=5 bigLen=12
+ *
+ * if count = 4, then we expect this function to return offset = 2 and
+ * count = 7.
+ *
+ */
+static void
+RFind_ComputeSearchRange( uint32_t bigLen, uint32_t littleLen, int32_t& offset, int32_t& count )
+{
+ if (littleLen > bigLen)
+ {
+ offset = 0;
+ count = 0;
+ return;
+ }
+
+ if (offset < 0)
+ offset = bigLen - littleLen;
+ if (count < 0)
+ count = offset + 1;
+
+ int32_t start = offset - count + 1;
+ if (start < 0)
+ start = 0;
+
+ count = offset + littleLen - start;
+ offset = start;
+}
+
+//-----------------------------------------------------------------------------
+
+// define nsString obsolete methods
+#include "string-template-def-unichar.h"
+#include "nsTStringObsolete.cpp"
+#include "string-template-undef.h"
+
+// define nsCString obsolete methods
+#include "string-template-def-char.h"
+#include "nsTStringObsolete.cpp"
+#include "string-template-undef.h"
+
+//-----------------------------------------------------------------------------
+
+// specialized methods:
+
+int32_t
+nsString::Find( const nsAFlatString& aString, int32_t aOffset, int32_t aCount ) const
+{
+ // this method changes the meaning of aOffset and aCount:
+ Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
+
+ int32_t result = FindSubstring(mData + aOffset, aCount, static_cast<const char16_t*>(aString.get()), aString.Length(), false);
+ if (result != kNotFound)
+ result += aOffset;
+ return result;
+}
+
+int32_t
+nsString::Find( const char16_t* aString, int32_t aOffset, int32_t aCount ) const
+{
+ return Find(nsDependentString(aString), aOffset, aCount);
+}
+
+int32_t
+nsString::RFind( const nsAFlatString& aString, int32_t aOffset, int32_t aCount ) const
+{
+ // this method changes the meaning of aOffset and aCount:
+ RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
+
+ int32_t result = RFindSubstring(mData + aOffset, aCount, static_cast<const char16_t*>(aString.get()), aString.Length(), false);
+ if (result != kNotFound)
+ result += aOffset;
+ return result;
+}
+
+int32_t
+nsString::RFind( const char16_t* aString, int32_t aOffset, int32_t aCount ) const
+{
+ return RFind(nsDependentString(aString), aOffset, aCount);
+}
+
+int32_t
+nsString::FindCharInSet( const char16_t* aSet, int32_t aOffset ) const
+{
+ if (aOffset < 0)
+ aOffset = 0;
+ else if (aOffset >= int32_t(mLength))
+ return kNotFound;
+
+ int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
+ if (result != kNotFound)
+ result += aOffset;
+ return result;
+}
+
+void
+nsString::ReplaceChar( const char16_t* aSet, char16_t aNewChar )
+{
+ if (!EnsureMutable()) // XXX do this lazily?
+ AllocFailed(mLength);
+
+ char16_t* data = mData;
+ uint32_t lenRemaining = mLength;
+
+ while (lenRemaining)
+ {
+ int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
+ if (i == kNotFound)
+ break;
+
+ data[i++] = aNewChar;
+ data += i;
+ lenRemaining -= i;
+ }
+}
+
+
+/**
+ * nsTString::Compare,CompareWithConversion,etc.
+ */
+
+int32_t
+nsCString::Compare( const char* aString, bool aIgnoreCase, int32_t aCount ) const
+{
+ uint32_t strLen = char_traits::length(aString);
+
+ int32_t maxCount = int32_t(XPCOM_MIN(mLength, strLen));
+
+ int32_t compareCount;
+ if (aCount < 0 || aCount > maxCount)
+ compareCount = maxCount;
+ else
+ compareCount = aCount;
+
+ int32_t result =
+ nsBufferRoutines<char>::compare(mData, aString, compareCount, aIgnoreCase);
+
+ if (result == 0 &&
+ (aCount < 0 || strLen < uint32_t(aCount) || mLength < uint32_t(aCount)))
+ {
+ // Since the caller didn't give us a length to test, or strings shorter
+ // than aCount, and compareCount characters matched, we have to assume
+ // that the longer string is greater.
+
+ if (mLength != strLen)
+ result = (mLength < strLen) ? -1 : 1;
+ }
+ return result;
+}
+
+bool
+nsString::EqualsIgnoreCase( const char* aString, int32_t aCount ) const
+{
+ uint32_t strLen = nsCharTraits<char>::length(aString);
+
+ int32_t maxCount = int32_t(XPCOM_MIN(mLength, strLen));
+
+ int32_t compareCount;
+ if (aCount < 0 || aCount > maxCount)
+ compareCount = maxCount;
+ else
+ compareCount = aCount;
+
+ int32_t result =
+ nsBufferRoutines<char16_t>::compare(mData, aString, compareCount, true);
+
+ if (result == 0 &&
+ (aCount < 0 || strLen < uint32_t(aCount) || mLength < uint32_t(aCount)))
+ {
+ // Since the caller didn't give us a length to test, or strings shorter
+ // than aCount, and compareCount characters matched, we have to assume
+ // that the longer string is greater.
+
+ if (mLength != strLen)
+ result = 1; // Arbitrarily using any number != 0
+ }
+ return result == 0;
+}
+
+
+/**
+ * nsTString::ToDouble
+ */
+
+double
+nsCString::ToDouble(nsresult* aErrorCode) const
+{
+ double res = 0.0;
+ if (mLength > 0)
+ {
+ char *conv_stopped;
+ const char *str = mData;
+ // Use PR_strtod, not strtod, since we don't want locale involved.
+ res = PR_strtod(str, &conv_stopped);
+ if (conv_stopped == str+mLength)
+ *aErrorCode = NS_OK;
+ else // Not all the string was scanned
+ *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
+ }
+ else
+ {
+ // The string was too short (0 characters)
+ *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
+ }
+ return res;
+}
+
+double
+nsString::ToDouble(nsresult* aErrorCode) const
+{
+ return NS_LossyConvertUTF16toASCII(*this).ToDouble(aErrorCode);
+}
+
+
+/**
+ * nsTString::AssignWithConversion
+ */
+
+void
+nsCString::AssignWithConversion( const nsAString& aData )
+{
+ LossyCopyUTF16toASCII(aData, *this);
+}
+
+void
+nsString::AssignWithConversion( const nsACString& aData )
+{
+ CopyASCIItoUTF16(aData, *this);
+}
+
+#endif // !MOZ_STRING_WITH_OBSOLETE_API
diff --git a/xpcom/string/nsSubstring.cpp b/xpcom/string/nsSubstring.cpp
new file mode 100644
index 000000000..5bc69f741
--- /dev/null
+++ b/xpcom/string/nsSubstring.cpp
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef DEBUG
+#define ENABLE_STRING_STATS
+#endif
+
+#include "mozilla/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+
+#ifdef ENABLE_STRING_STATS
+#include <stdio.h>
+#endif
+
+#include <stdlib.h>
+#include "nsSubstring.h"
+#include "nsString.h"
+#include "nsStringBuffer.h"
+#include "nsDependentString.h"
+#include "nsMemory.h"
+#include "prprf.h"
+#include "nsStaticAtom.h"
+#include "nsCOMPtr.h"
+
+#include "mozilla/IntegerPrintfMacros.h"
+#ifdef XP_WIN
+#include <windows.h>
+#include <process.h>
+#define getpid() _getpid()
+#define pthread_self() GetCurrentThreadId()
+#else
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+using mozilla::Atomic;
+
+// ---------------------------------------------------------------------------
+
+static const char16_t gNullChar = 0;
+
+char* const nsCharTraits<char>::sEmptyBuffer =
+ (char*)const_cast<char16_t*>(&gNullChar);
+char16_t* const nsCharTraits<char16_t>::sEmptyBuffer =
+ const_cast<char16_t*>(&gNullChar);
+
+// ---------------------------------------------------------------------------
+
+#ifdef ENABLE_STRING_STATS
+class nsStringStats
+{
+public:
+ nsStringStats()
+ : mAllocCount(0)
+ , mReallocCount(0)
+ , mFreeCount(0)
+ , mShareCount(0)
+ {
+ }
+
+ ~nsStringStats()
+ {
+ // this is a hack to suppress duplicate string stats printing
+ // in seamonkey as a result of the string code being linked
+ // into seamonkey and libxpcom! :-(
+ if (!mAllocCount && !mAdoptCount) {
+ return;
+ }
+
+ printf("nsStringStats\n");
+ printf(" => mAllocCount: % 10d\n", int(mAllocCount));
+ printf(" => mReallocCount: % 10d\n", int(mReallocCount));
+ printf(" => mFreeCount: % 10d", int(mFreeCount));
+ if (mAllocCount > mFreeCount) {
+ printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount);
+ } else {
+ printf("\n");
+ }
+ printf(" => mShareCount: % 10d\n", int(mShareCount));
+ printf(" => mAdoptCount: % 10d\n", int(mAdoptCount));
+ printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount));
+ if (mAdoptCount > mAdoptFreeCount) {
+ printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount);
+ } else {
+ printf("\n");
+ }
+ printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n",
+ uintptr_t(getpid()), uintptr_t(pthread_self()));
+ }
+
+ Atomic<int32_t> mAllocCount;
+ Atomic<int32_t> mReallocCount;
+ Atomic<int32_t> mFreeCount;
+ Atomic<int32_t> mShareCount;
+ Atomic<int32_t> mAdoptCount;
+ Atomic<int32_t> mAdoptFreeCount;
+};
+static nsStringStats gStringStats;
+#define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++
+#else
+#define STRING_STAT_INCREMENT(_s)
+#endif
+
+// ---------------------------------------------------------------------------
+
+void
+ReleaseData(void* aData, uint32_t aFlags)
+{
+ if (aFlags & nsSubstring::F_SHARED) {
+ nsStringBuffer::FromData(aData)->Release();
+ } else if (aFlags & nsSubstring::F_OWNED) {
+ free(aData);
+ STRING_STAT_INCREMENT(AdoptFree);
+ // Treat this as destruction of a "StringAdopt" object for leak
+ // tracking purposes.
+ MOZ_LOG_DTOR(aData, "StringAdopt", 1);
+ }
+ // otherwise, nothing to do.
+}
+
+// ---------------------------------------------------------------------------
+
+// XXX or we could make nsStringBuffer be a friend of nsTAString
+
+class nsAStringAccessor : public nsAString
+{
+private:
+ nsAStringAccessor(); // NOT IMPLEMENTED
+
+public:
+ char_type* data() const
+ {
+ return mData;
+ }
+ size_type length() const
+ {
+ return mLength;
+ }
+ uint32_t flags() const
+ {
+ return mFlags;
+ }
+
+ void set(char_type* aData, size_type aLen, uint32_t aFlags)
+ {
+ ReleaseData(mData, mFlags);
+ mData = aData;
+ mLength = aLen;
+ mFlags = aFlags;
+ }
+};
+
+class nsACStringAccessor : public nsACString
+{
+private:
+ nsACStringAccessor(); // NOT IMPLEMENTED
+
+public:
+ char_type* data() const
+ {
+ return mData;
+ }
+ size_type length() const
+ {
+ return mLength;
+ }
+ uint32_t flags() const
+ {
+ return mFlags;
+ }
+
+ void set(char_type* aData, size_type aLen, uint32_t aFlags)
+ {
+ ReleaseData(mData, mFlags);
+ mData = aData;
+ mLength = aLen;
+ mFlags = aFlags;
+ }
+};
+
+// ---------------------------------------------------------------------------
+
+void
+nsStringBuffer::AddRef()
+{
+ ++mRefCount;
+ STRING_STAT_INCREMENT(Share);
+ NS_LOG_ADDREF(this, mRefCount, "nsStringBuffer", sizeof(*this));
+}
+
+void
+nsStringBuffer::Release()
+{
+ int32_t count = --mRefCount;
+ NS_LOG_RELEASE(this, count, "nsStringBuffer");
+ if (count == 0) {
+ STRING_STAT_INCREMENT(Free);
+ free(this); // we were allocated with |malloc|
+ }
+}
+
+/**
+ * Alloc returns a pointer to a new string header with set capacity.
+ */
+already_AddRefed<nsStringBuffer>
+nsStringBuffer::Alloc(size_t aSize)
+{
+ NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
+ NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
+ sizeof(nsStringBuffer) + aSize > aSize,
+ "mStorageSize will truncate");
+
+ nsStringBuffer* hdr =
+ (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
+ if (hdr) {
+ STRING_STAT_INCREMENT(Alloc);
+
+ hdr->mRefCount = 1;
+ hdr->mStorageSize = aSize;
+ NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
+ }
+ return dont_AddRef(hdr);
+}
+
+nsStringBuffer*
+nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize)
+{
+ STRING_STAT_INCREMENT(Realloc);
+
+ NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
+ NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
+ sizeof(nsStringBuffer) + aSize > aSize,
+ "mStorageSize will truncate");
+
+ // no point in trying to save ourselves if we hit this assertion
+ NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
+
+ // Treat this as a release and addref for refcounting purposes, since we
+ // just asserted that the refcount is 1. If we don't do that, refcount
+ // logging will claim we've leaked all sorts of stuff.
+ NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer");
+
+ aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
+ if (aHdr) {
+ NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
+ aHdr->mStorageSize = aSize;
+ }
+
+ return aHdr;
+}
+
+nsStringBuffer*
+nsStringBuffer::FromString(const nsAString& aStr)
+{
+ const nsAStringAccessor* accessor =
+ static_cast<const nsAStringAccessor*>(&aStr);
+
+ if (!(accessor->flags() & nsSubstring::F_SHARED)) {
+ return nullptr;
+ }
+
+ return FromData(accessor->data());
+}
+
+nsStringBuffer*
+nsStringBuffer::FromString(const nsACString& aStr)
+{
+ const nsACStringAccessor* accessor =
+ static_cast<const nsACStringAccessor*>(&aStr);
+
+ if (!(accessor->flags() & nsCSubstring::F_SHARED)) {
+ return nullptr;
+ }
+
+ return FromData(accessor->data());
+}
+
+void
+nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
+ bool aMoveOwnership)
+{
+ char16_t* data = static_cast<char16_t*>(Data());
+
+ nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&aStr);
+ MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0),
+ "data should be null terminated");
+
+ // preserve class flags
+ uint32_t flags = accessor->flags();
+ flags = (flags & 0xFFFF0000) | nsSubstring::F_SHARED | nsSubstring::F_TERMINATED;
+
+ if (!aMoveOwnership) {
+ AddRef();
+ }
+ accessor->set(data, aLen, flags);
+}
+
+void
+nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
+ bool aMoveOwnership)
+{
+ char* data = static_cast<char*>(Data());
+
+ nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&aStr);
+ MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
+ "data should be null terminated");
+
+ // preserve class flags
+ uint32_t flags = accessor->flags();
+ flags = (flags & 0xFFFF0000) | nsCSubstring::F_SHARED | nsCSubstring::F_TERMINATED;
+
+ if (!aMoveOwnership) {
+ AddRef();
+ }
+ accessor->set(data, aLen, flags);
+}
+
+size_t
+nsStringBuffer::SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return IsReadonly() ? 0 : aMallocSizeOf(this);
+}
+
+size_t
+nsStringBuffer::SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this);
+}
+
+// ---------------------------------------------------------------------------
+
+
+// define nsSubstring
+#include "string-template-def-unichar.h"
+#include "nsTSubstring.cpp"
+#include "string-template-undef.h"
+
+// define nsCSubstring
+#include "string-template-def-char.h"
+#include "nsTSubstring.cpp"
+#include "string-template-undef.h"
+
+// Check that internal and external strings have the same size.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=430581
+
+#include "mozilla/Logging.h"
+#include "nsXPCOMStrings.h"
+
+static_assert(sizeof(nsStringContainer_base) == sizeof(nsSubstring),
+ "internal and external strings must have the same size");
+
+// Provide rust bindings to the nsA[C]String types
+extern "C" {
+
+void Gecko_FinalizeCString(nsACString* aThis)
+{
+ aThis->~nsACString();
+}
+
+void Gecko_AssignCString(nsACString* aThis, const nsACString* aOther)
+{
+ aThis->Assign(*aOther);
+}
+
+void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther)
+{
+ aThis->Append(*aOther);
+}
+
+void Gecko_FinalizeString(nsAString* aThis)
+{
+ aThis->~nsAString();
+}
+
+void Gecko_AssignString(nsAString* aThis, const nsAString* aOther)
+{
+ aThis->Assign(*aOther);
+}
+
+void Gecko_AppendString(nsAString* aThis, const nsAString* aOther)
+{
+ aThis->Append(*aOther);
+}
+
+} // extern "C"
diff --git a/xpcom/string/nsSubstring.h b/xpcom/string/nsSubstring.h
new file mode 100644
index 000000000..67125ba31
--- /dev/null
+++ b/xpcom/string/nsSubstring.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSubstring_h___
+#define nsSubstring_h___
+
+#include "nsAString.h"
+
+#endif // !defined(nsSubstring_h___)
diff --git a/xpcom/string/nsSubstringTuple.cpp b/xpcom/string/nsSubstringTuple.cpp
new file mode 100644
index 000000000..3de928dda
--- /dev/null
+++ b/xpcom/string/nsSubstringTuple.cpp
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSubstringTuple.h"
+
+// convert fragment to |const substring_type&|
+#define TO_SUBSTRING(_v) (*(_v))
+
+// define nsSubstringTuple
+#include "string-template-def-unichar.h"
+#include "nsTSubstringTuple.cpp"
+#include "string-template-undef.h"
+
+// define nsCSubstringTuple
+#include "string-template-def-char.h"
+#include "nsTSubstringTuple.cpp"
+#include "string-template-undef.h"
diff --git a/xpcom/string/nsSubstringTuple.h b/xpcom/string/nsSubstringTuple.h
new file mode 100644
index 000000000..5a61cd831
--- /dev/null
+++ b/xpcom/string/nsSubstringTuple.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSubstringTuple_h___
+#define nsSubstringTuple_h___
+
+#include "nsSubstring.h"
+
+// declare nsSubstringTuple
+#include "string-template-def-unichar.h"
+#include "nsTSubstringTuple.h"
+#include "string-template-undef.h"
+
+// declare nsCSubstringTuple
+#include "string-template-def-char.h"
+#include "nsTSubstringTuple.h"
+#include "string-template-undef.h"
+
+#endif // !defined(nsSubstringTuple_h___)
diff --git a/xpcom/string/nsTDependentString.cpp b/xpcom/string/nsTDependentString.cpp
new file mode 100644
index 000000000..2f3a095d1
--- /dev/null
+++ b/xpcom/string/nsTDependentString.cpp
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+void
+nsTDependentString_CharT::Rebind(const string_type& str, uint32_t startPos)
+{
+ MOZ_ASSERT(str.Flags() & F_TERMINATED, "Unterminated flat string");
+
+ // If we currently own a buffer, release it.
+ Finalize();
+
+ size_type strLength = str.Length();
+
+ if (startPos > strLength) {
+ startPos = strLength;
+ }
+
+ mData = const_cast<char_type*>(static_cast<const char_type*>(str.Data())) + startPos;
+ mLength = strLength - startPos;
+
+ SetDataFlags(str.Flags() & (F_TERMINATED | F_LITERAL));
+}
diff --git a/xpcom/string/nsTDependentString.h b/xpcom/string/nsTDependentString.h
new file mode 100644
index 000000000..44055d5ac
--- /dev/null
+++ b/xpcom/string/nsTDependentString.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/**
+ * nsTDependentString_CharT
+ *
+ * Stores a null-terminated, immutable sequence of characters.
+ *
+ * Subclass of nsTString that restricts string value to an immutable
+ * character sequence. This class does not own its data, so the creator
+ * of objects of this type must take care to ensure that a
+ * nsTDependentString continues to reference valid memory for the
+ * duration of its use.
+ */
+class nsTDependentString_CharT : public nsTString_CharT
+{
+public:
+
+ typedef nsTDependentString_CharT self_type;
+
+public:
+
+ /**
+ * constructors
+ */
+
+ nsTDependentString_CharT(const char_type* aStart, const char_type* aEnd)
+ : string_type(const_cast<char_type*>(aStart),
+ uint32_t(aEnd - aStart), F_TERMINATED)
+ {
+ AssertValidDependentString();
+ }
+
+ nsTDependentString_CharT(const char_type* aData, uint32_t aLength)
+ : string_type(const_cast<char_type*>(aData), aLength, F_TERMINATED)
+ {
+ AssertValidDependentString();
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ nsTDependentString_CharT(char16ptr_t aData, uint32_t aLength)
+ : nsTDependentString_CharT(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+#endif
+
+ explicit
+ nsTDependentString_CharT(const char_type* aData)
+ : string_type(const_cast<char_type*>(aData),
+ uint32_t(char_traits::length(aData)), F_TERMINATED)
+ {
+ AssertValidDependentString();
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ explicit
+ nsTDependentString_CharT(char16ptr_t aData)
+ : nsTDependentString_CharT(static_cast<const char16_t*>(aData))
+ {
+ }
+#endif
+
+ nsTDependentString_CharT(const string_type& aStr, uint32_t aStartPos)
+ : string_type()
+ {
+ Rebind(aStr, aStartPos);
+ }
+
+ // Create a nsTDependentSubstring to be bound later
+ nsTDependentString_CharT()
+ : string_type()
+ {
+ }
+
+ // XXX are you sure??
+ // auto-generated copy-constructor OK
+ // auto-generated copy-assignment operator OK
+ // auto-generated destructor OK
+
+
+ /**
+ * allow this class to be bound to a different string...
+ */
+
+ using nsTString_CharT::Rebind;
+ void Rebind(const char_type* aData)
+ {
+ Rebind(aData, uint32_t(char_traits::length(aData)));
+ }
+
+ void Rebind(const char_type* aStart, const char_type* aEnd)
+ {
+ Rebind(aStart, uint32_t(aEnd - aStart));
+ }
+
+ void Rebind(const string_type&, uint32_t aStartPos);
+
+private:
+
+ // NOT USED
+ nsTDependentString_CharT(const substring_tuple_type&) = delete;
+};
diff --git a/xpcom/string/nsTDependentSubstring.cpp b/xpcom/string/nsTDependentSubstring.cpp
new file mode 100644
index 000000000..b540c028d
--- /dev/null
+++ b/xpcom/string/nsTDependentSubstring.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+void
+nsTDependentSubstring_CharT::Rebind(const substring_type& str,
+ uint32_t startPos, uint32_t length)
+{
+ // If we currently own a buffer, release it.
+ Finalize();
+
+ size_type strLength = str.Length();
+
+ if (startPos > strLength) {
+ startPos = strLength;
+ }
+
+ mData = const_cast<char_type*>(static_cast<const char_type*>(str.Data())) + startPos;
+ mLength = XPCOM_MIN(length, strLength - startPos);
+
+ SetDataFlags(F_NONE);
+}
+
+void
+nsTDependentSubstring_CharT::Rebind(const char_type* data, size_type length)
+{
+ NS_ASSERTION(data, "nsTDependentSubstring must wrap a non-NULL buffer");
+
+ // If we currently own a buffer, release it.
+ Finalize();
+
+ mData = const_cast<char_type*>(static_cast<const char_type*>(data));
+ mLength = length;
+ SetDataFlags(F_NONE);
+}
diff --git a/xpcom/string/nsTDependentSubstring.h b/xpcom/string/nsTDependentSubstring.h
new file mode 100644
index 000000000..dd28f32f9
--- /dev/null
+++ b/xpcom/string/nsTDependentSubstring.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+/**
+ * nsTDependentSubstring_CharT
+ *
+ * A string class which wraps an external array of string characters. It
+ * is the client code's responsibility to ensure that the external buffer
+ * remains valid for a long as the string is alive.
+ *
+ * NAMES:
+ * nsDependentSubstring for wide characters
+ * nsDependentCSubstring for narrow characters
+ */
+class nsTDependentSubstring_CharT : public nsTSubstring_CharT
+{
+public:
+
+ typedef nsTDependentSubstring_CharT self_type;
+
+public:
+
+ void Rebind(const substring_type&, uint32_t aStartPos,
+ uint32_t aLength = size_type(-1));
+
+ void Rebind(const char_type* aData, size_type aLength);
+
+ void Rebind(const char_type* aStart, const char_type* aEnd)
+ {
+ Rebind(aStart, size_type(aEnd - aStart));
+ }
+
+ nsTDependentSubstring_CharT(const substring_type& aStr, uint32_t aStartPos,
+ uint32_t aLength = size_type(-1))
+ : substring_type()
+ {
+ Rebind(aStr, aStartPos, aLength);
+ }
+
+ nsTDependentSubstring_CharT(const char_type* aData, size_type aLength)
+ : substring_type(const_cast<char_type*>(aData), aLength, F_NONE)
+ {
+ }
+
+ nsTDependentSubstring_CharT(const char_type* aStart, const char_type* aEnd)
+ : substring_type(const_cast<char_type*>(aStart), uint32_t(aEnd - aStart),
+ F_NONE)
+ {
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ nsTDependentSubstring_CharT(char16ptr_t aData, size_type aLength)
+ : nsTDependentSubstring_CharT(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+
+ nsTDependentSubstring_CharT(char16ptr_t aStart, char16ptr_t aEnd)
+ : nsTDependentSubstring_CharT(static_cast<const char16_t*>(aStart),
+ static_cast<const char16_t*>(aEnd))
+ {
+ }
+#endif
+
+ nsTDependentSubstring_CharT(const const_iterator& aStart,
+ const const_iterator& aEnd)
+ : substring_type(const_cast<char_type*>(aStart.get()),
+ uint32_t(aEnd.get() - aStart.get()), F_NONE)
+ {
+ }
+
+ // Create a nsTDependentSubstring to be bound later
+ nsTDependentSubstring_CharT()
+ : substring_type()
+ {
+ }
+
+ // auto-generated copy-constructor OK (XXX really?? what about base class copy-ctor?)
+
+private:
+ // NOT USED
+ void operator=(const self_type&); // we're immutable, you can't assign into a substring
+};
+
+inline const nsTDependentSubstring_CharT
+Substring(const nsTSubstring_CharT& aStr, uint32_t aStartPos,
+ uint32_t aLength = uint32_t(-1))
+{
+ return nsTDependentSubstring_CharT(aStr, aStartPos, aLength);
+}
+
+inline const nsTDependentSubstring_CharT
+Substring(const nsReadingIterator<CharT>& aStart,
+ const nsReadingIterator<CharT>& aEnd)
+{
+ return nsTDependentSubstring_CharT(aStart.get(), aEnd.get());
+}
+
+inline const nsTDependentSubstring_CharT
+Substring(const CharT* aData, uint32_t aLength)
+{
+ return nsTDependentSubstring_CharT(aData, aLength);
+}
+
+inline const nsTDependentSubstring_CharT
+Substring(const CharT* aStart, const CharT* aEnd)
+{
+ return nsTDependentSubstring_CharT(aStart, aEnd);
+}
+
+inline const nsTDependentSubstring_CharT
+StringHead(const nsTSubstring_CharT& aStr, uint32_t aCount)
+{
+ return nsTDependentSubstring_CharT(aStr, 0, aCount);
+}
+
+inline const nsTDependentSubstring_CharT
+StringTail(const nsTSubstring_CharT& aStr, uint32_t aCount)
+{
+ return nsTDependentSubstring_CharT(aStr, aStr.Length() - aCount, aCount);
+}
diff --git a/xpcom/string/nsTLiteralString.h b/xpcom/string/nsTLiteralString.h
new file mode 100644
index 000000000..fa75ba829
--- /dev/null
+++ b/xpcom/string/nsTLiteralString.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/**
+ * nsTLiteralString_CharT
+ *
+ * Stores a null-terminated, immutable sequence of characters.
+ *
+ * Subclass of nsTString that restricts string value to a literal
+ * character sequence. This class does not own its data. The data is
+ * assumed to be permanent. In practice this is true because this code
+ * is only usable by and for libxul.
+ */
+class nsTLiteralString_CharT : public nsTString_CharT
+{
+public:
+
+ typedef nsTLiteralString_CharT self_type;
+
+public:
+
+ /**
+ * constructor
+ */
+
+ template<size_type N>
+ explicit nsTLiteralString_CharT(const char_type (&aStr)[N])
+ : string_type(const_cast<char_type*>(aStr), N - 1, F_TERMINATED | F_LITERAL)
+ {
+ }
+
+private:
+
+ // NOT TO BE IMPLEMENTED
+ template<size_type N>
+ nsTLiteralString_CharT(char_type (&aStr)[N]) = delete;
+};
diff --git a/xpcom/string/nsTPromiseFlatString.cpp b/xpcom/string/nsTPromiseFlatString.cpp
new file mode 100644
index 000000000..f02fc925c
--- /dev/null
+++ b/xpcom/string/nsTPromiseFlatString.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+void
+nsTPromiseFlatString_CharT::Init(const substring_type& str)
+{
+ if (str.IsTerminated()) {
+ mData = const_cast<char_type*>(static_cast<const char_type*>(str.Data()));
+ mLength = str.Length();
+ mFlags = str.mFlags & (F_TERMINATED | F_LITERAL);
+ // does not promote F_VOIDED
+ } else {
+ Assign(str);
+ }
+}
diff --git a/xpcom/string/nsTPromiseFlatString.h b/xpcom/string/nsTPromiseFlatString.h
new file mode 100644
index 000000000..d71ce148d
--- /dev/null
+++ b/xpcom/string/nsTPromiseFlatString.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/**
+ * NOTE:
+ *
+ * Try to avoid flat strings. |PromiseFlat[C]String| will help you as a last
+ * resort, and this may be necessary when dealing with legacy or OS calls,
+ * but in general, requiring a null-terminated array of characters kills many
+ * of the performance wins the string classes offer. Write your own code to
+ * use |nsA[C]String&|s for parameters. Write your string proccessing
+ * algorithms to exploit iterators. If you do this, you will benefit from
+ * being able to chain operations without copying or allocating and your code
+ * will be significantly more efficient. Remember, a function that takes an
+ * |const nsA[C]String&| can always be passed a raw character pointer by
+ * wrapping it (for free) in a |nsDependent[C]String|. But a function that
+ * takes a character pointer always has the potential to force allocation and
+ * copying.
+ *
+ *
+ * How to use it:
+ *
+ * A |nsPromiseFlat[C]String| doesn't necessarily own the characters it
+ * promises. You must never use it to promise characters out of a string
+ * with a shorter lifespan. The typical use will be something like this:
+ *
+ * SomeOSFunction( PromiseFlatCString(aCSubstring).get() ); // GOOD
+ *
+ * Here's a BAD use:
+ *
+ * const char* buffer = PromiseFlatCString(aCSubstring).get();
+ * SomeOSFunction(buffer); // BAD!! |buffer| is a dangling pointer
+ *
+ * The only way to make one is with the function |PromiseFlat[C]String|,
+ * which produce a |const| instance. ``What if I need to keep a promise
+ * around for a little while?'' you might ask. In that case, you can keep a
+ * reference, like so:
+ *
+ * const nsCString& flat = PromiseFlatString(aCSubstring);
+ * // Temporaries usually die after the full expression containing the
+ * // expression that created the temporary is evaluated. But when a
+ * // temporary is assigned to a local reference, the temporary's lifetime
+ * // is extended to the reference's lifetime (C++11 [class.temporary]p5).
+ * //
+ * // This reference holds the anonymous temporary alive. But remember: it
+ * // must _still_ have a lifetime shorter than that of |aCSubstring|, and
+ * // |aCSubstring| must not be changed while the PromiseFlatString lives.
+ *
+ * SomeOSFunction(flat.get());
+ * SomeOtherOSFunction(flat.get());
+ *
+ *
+ * How does it work?
+ *
+ * A |nsPromiseFlat[C]String| is just a wrapper for another string. If you
+ * apply it to a string that happens to be flat, your promise is just a
+ * dependent reference to the string's data. If you apply it to a non-flat
+ * string, then a temporary flat string is created for you, by allocating and
+ * copying. In the event that you end up assigning the result into a sharing
+ * string (e.g., |nsTString|), the right thing happens.
+ */
+
+class nsTPromiseFlatString_CharT : public nsTString_CharT
+{
+public:
+
+ typedef nsTPromiseFlatString_CharT self_type;
+
+private:
+
+ void Init(const substring_type&);
+
+ // NOT TO BE IMPLEMENTED
+ void operator=(const self_type&) = delete;
+
+ // NOT TO BE IMPLEMENTED
+ nsTPromiseFlatString_CharT() = delete;
+
+ // NOT TO BE IMPLEMENTED
+ nsTPromiseFlatString_CharT(const string_type& aStr) = delete;
+
+public:
+
+ explicit
+ nsTPromiseFlatString_CharT(const substring_type& aStr)
+ : string_type()
+ {
+ Init(aStr);
+ }
+
+ explicit
+ nsTPromiseFlatString_CharT(const substring_tuple_type& aTuple)
+ : string_type()
+ {
+ // nothing else to do here except assign the value of the tuple
+ // into ourselves.
+ Assign(aTuple);
+ }
+};
+
+// We template this so that the constructor is chosen based on the type of the
+// parameter. This allows us to reject attempts to promise a flat flat string.
+template<class T>
+const nsTPromiseFlatString_CharT
+TPromiseFlatString_CharT(const T& aString)
+{
+ return nsTPromiseFlatString_CharT(aString);
+}
diff --git a/xpcom/string/nsTString.cpp b/xpcom/string/nsTString.cpp
new file mode 100644
index 000000000..13dd2628e
--- /dev/null
+++ b/xpcom/string/nsTString.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+nsTAdoptingString_CharT&
+nsTAdoptingString_CharT::operator=(const self_type& str)
+{
+ // This'll violate the constness of this argument, that's just
+ // the nature of this class...
+ self_type* mutable_str = const_cast<self_type*>(&str);
+
+ if (str.mFlags & F_OWNED) {
+ // We want to do what Adopt() does, but without actually incrementing
+ // the Adopt count. Note that we can be a little more straightforward
+ // about this than Adopt() is, because we know that str.mData is
+ // non-null. Should we be able to assert that str is not void here?
+ NS_ASSERTION(str.mData, "String with null mData?");
+ Finalize();
+ mData = str.mData;
+ mLength = str.mLength;
+ SetDataFlags(F_TERMINATED | F_OWNED);
+
+ // Make str forget the buffer we just took ownership of.
+ new (mutable_str) self_type();
+ } else {
+ Assign(str);
+
+ mutable_str->Truncate();
+ }
+
+ return *this;
+}
+
+void
+nsTString_CharT::Rebind(const char_type* data, size_type length)
+{
+ // If we currently own a buffer, release it.
+ Finalize();
+
+ mData = const_cast<char_type*>(data);
+ mLength = length;
+ SetDataFlags(F_TERMINATED);
+ AssertValidDependentString();
+}
+
diff --git a/xpcom/string/nsTString.h b/xpcom/string/nsTString.h
new file mode 100644
index 000000000..6fbb9d3ad
--- /dev/null
+++ b/xpcom/string/nsTString.h
@@ -0,0 +1,883 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+/**
+ * This is the canonical null-terminated string class. All subclasses
+ * promise null-terminated storage. Instances of this class allocate
+ * strings on the heap.
+ *
+ * NAMES:
+ * nsString for wide characters
+ * nsCString for narrow characters
+ *
+ * This class is also known as nsAFlat[C]String, where "flat" is used
+ * to denote a null-terminated string.
+ */
+class nsTString_CharT : public nsTSubstring_CharT
+{
+public:
+
+ typedef nsTString_CharT self_type;
+
+public:
+
+ /**
+ * constructors
+ */
+
+ nsTString_CharT()
+ : substring_type()
+ {
+ }
+
+ explicit
+ nsTString_CharT(const char_type* aData, size_type aLength = size_type(-1))
+ : substring_type()
+ {
+ Assign(aData, aLength);
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ explicit
+ nsTString_CharT(char16ptr_t aStr, size_type aLength = size_type(-1))
+ : substring_type()
+ {
+ Assign(static_cast<const char16_t*>(aStr), aLength);
+ }
+#endif
+
+ nsTString_CharT(const self_type& aStr)
+ : substring_type()
+ {
+ Assign(aStr);
+ }
+
+ MOZ_IMPLICIT nsTString_CharT(const substring_tuple_type& aTuple)
+ : substring_type()
+ {
+ Assign(aTuple);
+ }
+
+ explicit
+ nsTString_CharT(const substring_type& aReadable)
+ : substring_type()
+ {
+ Assign(aReadable);
+ }
+
+
+ // |operator=| does not inherit, so we must define our own
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+ self_type& operator=(const char_type* aData)
+ {
+ Assign(aData);
+ return *this;
+ }
+ self_type& operator=(const self_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ self_type& operator=(const char16ptr_t aStr)
+ {
+ Assign(static_cast<const char16_t*>(aStr));
+ return *this;
+ }
+#endif
+ self_type& operator=(const substring_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple)
+ {
+ Assign(aTuple);
+ return *this;
+ }
+
+ /**
+ * returns the null-terminated string
+ */
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ char16ptr_t get() const
+#else
+ const char_type* get() const
+#endif
+ {
+ return mData;
+ }
+
+
+ /**
+ * returns character at specified index.
+ *
+ * NOTE: unlike nsTSubstring::CharAt, this function allows you to index
+ * the null terminator character.
+ */
+
+ char_type CharAt(index_type aIndex) const
+ {
+ NS_ASSERTION(aIndex <= mLength, "index exceeds allowable range");
+ return mData[aIndex];
+ }
+
+ char_type operator[](index_type aIndex) const
+ {
+ return CharAt(aIndex);
+ }
+
+
+#if MOZ_STRING_WITH_OBSOLETE_API
+
+
+ /**
+ * Search for the given substring within this string.
+ *
+ * @param aString is substring to be sought in this
+ * @param aIgnoreCase selects case sensitivity
+ * @param aOffset tells us where in this string to start searching
+ * @param aCount tells us how far from the offset we are to search. Use
+ * -1 to search the whole string.
+ * @return offset in string, or kNotFound
+ */
+
+ int32_t Find(const nsCString& aString, bool aIgnoreCase = false,
+ int32_t aOffset = 0, int32_t aCount = -1) const;
+ int32_t Find(const char* aString, bool aIgnoreCase = false,
+ int32_t aOffset = 0, int32_t aCount = -1) const;
+
+#ifdef CharT_is_PRUnichar
+ int32_t Find(const nsAFlatString& aString, int32_t aOffset = 0,
+ int32_t aCount = -1) const;
+ int32_t Find(const char16_t* aString, int32_t aOffset = 0,
+ int32_t aCount = -1) const;
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ int32_t Find(char16ptr_t aString, int32_t aOffset = 0,
+ int32_t aCount = -1) const
+ {
+ return Find(static_cast<const char16_t*>(aString), aOffset, aCount);
+ }
+#endif
+#endif
+
+
+ /**
+ * This methods scans the string backwards, looking for the given string
+ *
+ * @param aString is substring to be sought in this
+ * @param aIgnoreCase tells us whether or not to do caseless compare
+ * @param aOffset tells us where in this string to start searching.
+ * Use -1 to search from the end of the string.
+ * @param aCount tells us how many iterations to make starting at the
+ * given offset.
+ * @return offset in string, or kNotFound
+ */
+
+ int32_t RFind(const nsCString& aString, bool aIgnoreCase = false,
+ int32_t aOffset = -1, int32_t aCount = -1) const;
+ int32_t RFind(const char* aCString, bool aIgnoreCase = false,
+ int32_t aOffset = -1, int32_t aCount = -1) const;
+
+#ifdef CharT_is_PRUnichar
+ int32_t RFind(const nsAFlatString& aString, int32_t aOffset = -1,
+ int32_t aCount = -1) const;
+ int32_t RFind(const char16_t* aString, int32_t aOffset = -1,
+ int32_t aCount = -1) const;
+#endif
+
+
+ /**
+ * Search for given char within this string
+ *
+ * @param aChar is the character to search for
+ * @param aOffset tells us where in this string to start searching
+ * @param aCount tells us how far from the offset we are to search.
+ * Use -1 to search the whole string.
+ * @return offset in string, or kNotFound
+ */
+
+ // int32_t FindChar( char16_t aChar, int32_t aOffset=0, int32_t aCount=-1 ) const;
+ int32_t RFindChar(char16_t aChar, int32_t aOffset = -1,
+ int32_t aCount = -1) const;
+
+
+ /**
+ * This method searches this string for the first character found in
+ * the given string.
+ *
+ * @param aString contains set of chars to be found
+ * @param aOffset tells us where in this string to start searching
+ * (counting from left)
+ * @return offset in string, or kNotFound
+ */
+
+ int32_t FindCharInSet(const char* aString, int32_t aOffset = 0) const;
+ int32_t FindCharInSet(const self_type& aString, int32_t aOffset = 0) const
+ {
+ return FindCharInSet(aString.get(), aOffset);
+ }
+
+#ifdef CharT_is_PRUnichar
+ int32_t FindCharInSet(const char16_t* aString, int32_t aOffset = 0) const;
+#endif
+
+
+ /**
+ * This method searches this string for the last character found in
+ * the given string.
+ *
+ * @param aString contains set of chars to be found
+ * @param aOffset tells us where in this string to start searching
+ * (counting from left)
+ * @return offset in string, or kNotFound
+ */
+
+ int32_t RFindCharInSet(const char_type* aString, int32_t aOffset = -1) const;
+ int32_t RFindCharInSet(const self_type& aString, int32_t aOffset = -1) const
+ {
+ return RFindCharInSet(aString.get(), aOffset);
+ }
+
+
+ /**
+ * Compares a given string to this string.
+ *
+ * @param aString is the string to be compared
+ * @param aIgnoreCase tells us how to treat case
+ * @param aCount tells us how many chars to compare
+ * @return -1,0,1
+ */
+
+#ifdef CharT_is_char
+ int32_t Compare(const char* aString, bool aIgnoreCase = false,
+ int32_t aCount = -1) const;
+#endif
+
+
+ /**
+ * Equality check between given string and this string.
+ *
+ * @param aString is the string to check
+ * @param aIgnoreCase tells us how to treat case
+ * @param aCount tells us how many chars to compare
+ * @return boolean
+ */
+#ifdef CharT_is_char
+ bool EqualsIgnoreCase(const char* aString, int32_t aCount = -1) const
+ {
+ return Compare(aString, true, aCount) == 0;
+ }
+#else
+ bool EqualsIgnoreCase(const char* aString, int32_t aCount = -1) const;
+
+
+#endif // !CharT_is_PRUnichar
+
+ /**
+ * Perform string to double-precision float conversion.
+ *
+ * @param aErrorCode will contain error if one occurs
+ * @return double-precision float rep of string value
+ */
+ double ToDouble(nsresult* aErrorCode) const;
+
+ /**
+ * Perform string to single-precision float conversion.
+ *
+ * @param aErrorCode will contain error if one occurs
+ * @return single-precision float rep of string value
+ */
+ float ToFloat(nsresult* aErrorCode) const
+ {
+ return (float)ToDouble(aErrorCode);
+ }
+
+
+ /**
+ * Perform string to int conversion.
+ * @param aErrorCode will contain error if one occurs
+ * @param aRadix tells us which radix to assume; kAutoDetect tells us to determine the radix for you.
+ * @return int rep of string value, and possible (out) error code
+ */
+ int32_t ToInteger(nsresult* aErrorCode, uint32_t aRadix = kRadix10) const;
+
+ /**
+ * Perform string to 64-bit int conversion.
+ * @param aErrorCode will contain error if one occurs
+ * @param aRadix tells us which radix to assume; kAutoDetect tells us to determine the radix for you.
+ * @return 64-bit int rep of string value, and possible (out) error code
+ */
+ int64_t ToInteger64(nsresult* aErrorCode, uint32_t aRadix = kRadix10) const;
+
+
+ /**
+ * |Left|, |Mid|, and |Right| are annoying signatures that seem better almost
+ * any _other_ way than they are now. Consider these alternatives
+ *
+ * aWritable = aReadable.Left(17); // ...a member function that returns a |Substring|
+ * aWritable = Left(aReadable, 17); // ...a global function that returns a |Substring|
+ * Left(aReadable, 17, aWritable); // ...a global function that does the assignment
+ *
+ * as opposed to the current signature
+ *
+ * aReadable.Left(aWritable, 17); // ...a member function that does the assignment
+ *
+ * or maybe just stamping them out in favor of |Substring|, they are just duplicate functionality
+ *
+ * aWritable = Substring(aReadable, 0, 17);
+ */
+
+ size_type Mid(self_type& aResult, uint32_t aStartPos, uint32_t aCount) const;
+
+ size_type Left(self_type& aResult, size_type aCount) const
+ {
+ return Mid(aResult, 0, aCount);
+ }
+
+ size_type Right(self_type& aResult, size_type aCount) const
+ {
+ aCount = XPCOM_MIN(mLength, aCount);
+ return Mid(aResult, mLength - aCount, aCount);
+ }
+
+
+ /**
+ * Set a char inside this string at given index
+ *
+ * @param aChar is the char you want to write into this string
+ * @param anIndex is the ofs where you want to write the given char
+ * @return TRUE if successful
+ */
+
+ bool SetCharAt(char16_t aChar, uint32_t aIndex);
+
+
+ /**
+ * These methods are used to remove all occurrences of the
+ * characters found in aSet from this string.
+ *
+ * @param aSet -- characters to be cut from this
+ */
+ void StripChars(const char* aSet);
+ bool StripChars(const char* aSet, const fallible_t&);
+
+
+ /**
+ * This method strips whitespace throughout the string.
+ */
+ void StripWhitespace();
+ bool StripWhitespace(const fallible_t&);
+
+
+ /**
+ * swaps occurence of 1 string for another
+ */
+
+ void ReplaceChar(char_type aOldChar, char_type aNewChar);
+ void ReplaceChar(const char* aSet, char_type aNewChar);
+#ifdef CharT_is_PRUnichar
+ void ReplaceChar(const char16_t* aSet, char16_t aNewChar);
+#endif
+ /**
+ * Replace all occurrences of aTarget with aNewValue.
+ * The complexity of this function is O(n+m), n being the length of the string
+ * and m being the length of aNewValue.
+ */
+ void ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue);
+ void ReplaceSubstring(const char_type* aTarget, const char_type* aNewValue);
+ MOZ_MUST_USE bool ReplaceSubstring(const self_type& aTarget,
+ const self_type& aNewValue,
+ const fallible_t&);
+ MOZ_MUST_USE bool ReplaceSubstring(const char_type* aTarget,
+ const char_type* aNewValue,
+ const fallible_t&);
+
+
+ /**
+ * This method trims characters found in aTrimSet from
+ * either end of the underlying string.
+ *
+ * @param aSet -- contains chars to be trimmed from both ends
+ * @param aEliminateLeading
+ * @param aEliminateTrailing
+ * @param aIgnoreQuotes -- if true, causes surrounding quotes to be ignored
+ * @return this
+ */
+ void Trim(const char* aSet, bool aEliminateLeading = true,
+ bool aEliminateTrailing = true, bool aIgnoreQuotes = false);
+
+ /**
+ * This method strips whitespace from string.
+ * You can control whether whitespace is yanked from start and end of
+ * string as well.
+ *
+ * @param aEliminateLeading controls stripping of leading ws
+ * @param aEliminateTrailing controls stripping of trailing ws
+ */
+ void CompressWhitespace(bool aEliminateLeading = true,
+ bool aEliminateTrailing = true);
+
+
+ /**
+ * assign/append/insert with _LOSSY_ conversion
+ */
+
+ void AssignWithConversion(const nsTAString_IncompatibleCharT& aString);
+ void AssignWithConversion(const incompatible_char_type* aData,
+ int32_t aLength = -1);
+
+#endif // !MOZ_STRING_WITH_OBSOLETE_API
+
+ /**
+ * Allow this string to be bound to a character buffer
+ * until the string is rebound or mutated; the caller
+ * must ensure that the buffer outlives the string.
+ */
+ void Rebind(const char_type* aData, size_type aLength);
+
+ /**
+ * verify restrictions for dependent strings
+ */
+ void AssertValidDependentString()
+ {
+ NS_ASSERTION(mData, "nsTDependentString must wrap a non-NULL buffer");
+ NS_ASSERTION(mLength != size_type(-1), "nsTDependentString has bogus length");
+ NS_ASSERTION(mData[mLength] == 0,
+ "nsTDependentString must wrap only null-terminated strings. "
+ "You are probably looking for nsTDependentSubstring.");
+ }
+
+
+protected:
+
+ explicit
+ nsTString_CharT(uint32_t aFlags)
+ : substring_type(aFlags)
+ {
+ }
+
+ // allow subclasses to initialize fields directly
+ nsTString_CharT(char_type* aData, size_type aLength, uint32_t aFlags)
+ : substring_type(aData, aLength, aFlags)
+ {
+ }
+
+ struct Segment {
+ uint32_t mBegin, mLength;
+ Segment(uint32_t aBegin, uint32_t aLength)
+ : mBegin(aBegin)
+ , mLength(aLength)
+ {}
+ };
+};
+
+
+class nsTFixedString_CharT : public nsTString_CharT
+{
+public:
+
+ typedef nsTFixedString_CharT self_type;
+ typedef nsTFixedString_CharT fixed_string_type;
+
+public:
+
+ /**
+ * @param aData
+ * fixed-size buffer to be used by the string (the contents of
+ * this buffer may be modified by the string)
+ * @param aStorageSize
+ * the size of the fixed buffer
+ * @param aLength (optional)
+ * the length of the string already contained in the buffer
+ */
+
+ nsTFixedString_CharT(char_type* aData, size_type aStorageSize)
+ : string_type(aData, uint32_t(char_traits::length(aData)),
+ F_TERMINATED | F_FIXED | F_CLASS_FIXED)
+ , mFixedCapacity(aStorageSize - 1)
+ , mFixedBuf(aData)
+ {
+ }
+
+ nsTFixedString_CharT(char_type* aData, size_type aStorageSize,
+ size_type aLength)
+ : string_type(aData, aLength, F_TERMINATED | F_FIXED | F_CLASS_FIXED)
+ , mFixedCapacity(aStorageSize - 1)
+ , mFixedBuf(aData)
+ {
+ // null-terminate
+ mFixedBuf[aLength] = char_type(0);
+ }
+
+ // |operator=| does not inherit, so we must define our own
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+ self_type& operator=(const char_type* aData)
+ {
+ Assign(aData);
+ return *this;
+ }
+ self_type& operator=(const substring_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple)
+ {
+ Assign(aTuple);
+ return *this;
+ }
+
+protected:
+
+ friend class nsTSubstring_CharT;
+
+ size_type mFixedCapacity;
+ char_type* mFixedBuf;
+};
+
+
+/**
+ * nsTAutoString_CharT
+ *
+ * Subclass of nsTString_CharT that adds support for stack-based string
+ * allocation. It is normally not a good idea to use this class on the
+ * heap, because it will allocate space which may be wasted if the string
+ * it contains is significantly smaller or any larger than 64 characters.
+ *
+ * NAMES:
+ * nsAutoString for wide characters
+ * nsAutoCString for narrow characters
+ */
+class MOZ_NON_MEMMOVABLE nsTAutoString_CharT : public nsTFixedString_CharT
+{
+public:
+
+ typedef nsTAutoString_CharT self_type;
+
+public:
+
+ /**
+ * constructors
+ */
+
+ nsTAutoString_CharT()
+ : fixed_string_type(mStorage, kDefaultStorageSize, 0)
+ {
+ }
+
+ explicit
+ nsTAutoString_CharT(char_type aChar)
+ : fixed_string_type(mStorage, kDefaultStorageSize, 0)
+ {
+ Assign(aChar);
+ }
+
+ explicit
+ nsTAutoString_CharT(const char_type* aData, size_type aLength = size_type(-1))
+ : fixed_string_type(mStorage, kDefaultStorageSize, 0)
+ {
+ Assign(aData, aLength);
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ explicit
+ nsTAutoString_CharT(char16ptr_t aData, size_type aLength = size_type(-1))
+ : nsTAutoString_CharT(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+#endif
+
+ nsTAutoString_CharT(const self_type& aStr)
+ : fixed_string_type(mStorage, kDefaultStorageSize, 0)
+ {
+ Assign(aStr);
+ }
+
+ explicit
+ nsTAutoString_CharT(const substring_type& aStr)
+ : fixed_string_type(mStorage, kDefaultStorageSize, 0)
+ {
+ Assign(aStr);
+ }
+
+ MOZ_IMPLICIT nsTAutoString_CharT(const substring_tuple_type& aTuple)
+ : fixed_string_type(mStorage, kDefaultStorageSize, 0)
+ {
+ Assign(aTuple);
+ }
+
+ // |operator=| does not inherit, so we must define our own
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+ self_type& operator=(const char_type* aData)
+ {
+ Assign(aData);
+ return *this;
+ }
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ self_type& operator=(char16ptr_t aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+#endif
+ self_type& operator=(const self_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple)
+ {
+ Assign(aTuple);
+ return *this;
+ }
+
+ enum
+ {
+ kDefaultStorageSize = 64
+ };
+
+private:
+
+ char_type mStorage[kDefaultStorageSize];
+};
+
+
+//
+// nsAutoString stores pointers into itself which are invalidated when an
+// nsTArray is resized, so nsTArray must not be instantiated with nsAutoString
+// elements!
+//
+template<class E> class nsTArrayElementTraits;
+template<>
+class nsTArrayElementTraits<nsTAutoString_CharT>
+{
+public:
+ template<class A> struct Dont_Instantiate_nsTArray_of;
+ template<class A> struct Instead_Use_nsTArray_of;
+
+ static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT>*
+ Construct(Instead_Use_nsTArray_of<nsTString_CharT>* aE)
+ {
+ return 0;
+ }
+ template<class A>
+ static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT>*
+ Construct(Instead_Use_nsTArray_of<nsTString_CharT>* aE, const A& aArg)
+ {
+ return 0;
+ }
+ static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT>*
+ Destruct(Instead_Use_nsTArray_of<nsTString_CharT>* aE)
+ {
+ return 0;
+ }
+};
+
+/**
+ * nsTXPIDLString extends nsTString such that:
+ *
+ * (1) mData can be null
+ * (2) objects of this type can be automatically cast to |const CharT*|
+ * (3) getter_Copies method is supported to adopt data allocated with
+ * moz_xmalloc, such as "out string" parameters in XPIDL.
+ *
+ * NAMES:
+ * nsXPIDLString for wide characters
+ * nsXPIDLCString for narrow characters
+ */
+class nsTXPIDLString_CharT : public nsTString_CharT
+{
+public:
+
+ typedef nsTXPIDLString_CharT self_type;
+
+public:
+
+ nsTXPIDLString_CharT()
+ : string_type(char_traits::sEmptyBuffer, 0, F_TERMINATED | F_VOIDED)
+ {
+ }
+
+ // copy-constructor required to avoid default
+ nsTXPIDLString_CharT(const self_type& aStr)
+ : string_type(char_traits::sEmptyBuffer, 0, F_TERMINATED | F_VOIDED)
+ {
+ Assign(aStr);
+ }
+
+ // return nullptr if we are voided
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ char16ptr_t get() const
+#else
+ const char_type* get() const
+#endif
+ {
+ return (mFlags & F_VOIDED) ? nullptr : mData;
+ }
+
+ // this case operator is the reason why this class cannot just be a
+ // typedef for nsTString
+ operator const char_type*() const
+ {
+ return get();
+ }
+
+ // need this to diambiguous operator[int]
+ char_type operator[](int32_t aIndex) const
+ {
+ return CharAt(index_type(aIndex));
+ }
+
+ // |operator=| does not inherit, so we must define our own
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+ self_type& operator=(const char_type* aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const self_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple)
+ {
+ Assign(aTuple);
+ return *this;
+ }
+};
+
+
+/**
+ * getter_Copies support for use with raw string out params:
+ *
+ * NS_IMETHOD GetBlah(char**);
+ *
+ * void some_function()
+ * {
+ * nsXPIDLCString blah;
+ * GetBlah(getter_Copies(blah));
+ * // ...
+ * }
+ */
+class MOZ_STACK_CLASS nsTGetterCopies_CharT
+{
+public:
+ typedef CharT char_type;
+
+ explicit nsTGetterCopies_CharT(nsTSubstring_CharT& aStr)
+ : mString(aStr)
+ , mData(nullptr)
+ {
+ }
+
+ ~nsTGetterCopies_CharT()
+ {
+ mString.Adopt(mData); // OK if mData is null
+ }
+
+ operator char_type**()
+ {
+ return &mData;
+ }
+
+private:
+ nsTSubstring_CharT& mString;
+ char_type* mData;
+};
+
+inline nsTGetterCopies_CharT
+getter_Copies(nsTSubstring_CharT& aString)
+{
+ return nsTGetterCopies_CharT(aString);
+}
+
+
+/**
+ * nsTAdoptingString extends nsTXPIDLString such that:
+ *
+ * (1) Adopt given string on construction or assignment, i.e. take
+ * the value of what's given, and make what's given forget its
+ * value. Note that this class violates constness in a few
+ * places. Be careful!
+ */
+class nsTAdoptingString_CharT : public nsTXPIDLString_CharT
+{
+public:
+
+ typedef nsTAdoptingString_CharT self_type;
+
+public:
+
+ explicit nsTAdoptingString_CharT()
+ {
+ }
+ explicit nsTAdoptingString_CharT(char_type* aStr,
+ size_type aLength = size_type(-1))
+ {
+ Adopt(aStr, aLength);
+ }
+
+ // copy-constructor required to adopt on copy. Note that this
+ // will violate the constness of |aStr| in the operator=()
+ // call. |aStr| will be truncated as a side-effect of this
+ // constructor.
+ nsTAdoptingString_CharT(const self_type& aStr)
+ {
+ *this = aStr;
+ }
+
+ // |operator=| does not inherit, so we must define our own
+ self_type& operator=(const substring_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple)
+ {
+ Assign(aTuple);
+ return *this;
+ }
+
+ // Adopt(), if possible, when assigning to a self_type&. Note
+ // that this violates the constness of aStr, aStr is always
+ // truncated when this operator is called.
+ self_type& operator=(const self_type& aStr);
+
+private:
+ self_type& operator=(const char_type* aData) = delete;
+ self_type& operator=(char_type* aData) = delete;
+};
+
diff --git a/xpcom/string/nsTStringComparator.cpp b/xpcom/string/nsTStringComparator.cpp
new file mode 100644
index 000000000..3f383c65f
--- /dev/null
+++ b/xpcom/string/nsTStringComparator.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+int NS_FASTCALL
+Compare(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs,
+ const nsTStringComparator_CharT& comp)
+{
+ typedef nsTSubstring_CharT::size_type size_type;
+
+ if (&aLhs == &aRhs) {
+ return 0;
+ }
+
+ nsTSubstring_CharT::const_iterator leftIter, rightIter;
+ aLhs.BeginReading(leftIter);
+ aRhs.BeginReading(rightIter);
+
+ size_type lLength = aLhs.Length();
+ size_type rLength = aRhs.Length();
+ size_type lengthToCompare = XPCOM_MIN(lLength, rLength);
+
+ int result;
+ if ((result = comp(leftIter.get(), rightIter.get(),
+ lengthToCompare, lengthToCompare)) == 0) {
+ if (lLength < rLength) {
+ result = -1;
+ } else if (rLength < lLength) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ }
+
+ return result;
+}
+
+int
+nsTDefaultStringComparator_CharT::operator()(const char_type* aLhs,
+ const char_type* aRhs,
+ uint32_t aLLength,
+ uint32_t aRLength) const
+{
+ return
+ aLLength == aRLength ? nsCharTraits<CharT>::compare(aLhs, aRhs, aLLength) :
+ (aLLength > aRLength) ? 1 : -1;
+}
diff --git a/xpcom/string/nsTStringObsolete.cpp b/xpcom/string/nsTStringObsolete.cpp
new file mode 100644
index 000000000..5a47ca310
--- /dev/null
+++ b/xpcom/string/nsTStringObsolete.cpp
@@ -0,0 +1,700 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTArray.h"
+
+/**
+ * nsTString::Find
+ *
+ * aOffset specifies starting index
+ * aCount specifies number of string compares (iterations)
+ */
+
+int32_t
+nsTString_CharT::Find( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+{
+ // this method changes the meaning of aOffset and aCount:
+ Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
+
+ int32_t result = FindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
+ if (result != kNotFound)
+ result += aOffset;
+ return result;
+}
+
+int32_t
+nsTString_CharT::Find( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+{
+ return Find(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
+}
+
+
+/**
+ * nsTString::RFind
+ *
+ * aOffset specifies starting index
+ * aCount specifies number of string compares (iterations)
+ */
+
+int32_t
+nsTString_CharT::RFind( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+{
+ // this method changes the meaning of aOffset and aCount:
+ RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
+
+ int32_t result = RFindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
+ if (result != kNotFound)
+ result += aOffset;
+ return result;
+}
+
+int32_t
+nsTString_CharT::RFind( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+{
+ return RFind(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
+}
+
+
+/**
+ * nsTString::RFindChar
+ */
+
+int32_t
+nsTString_CharT::RFindChar( char16_t aChar, int32_t aOffset, int32_t aCount) const
+{
+ return nsBufferRoutines<CharT>::rfind_char(mData, mLength, aOffset, aChar, aCount);
+}
+
+
+/**
+ * nsTString::FindCharInSet
+ */
+
+int32_t
+nsTString_CharT::FindCharInSet( const char* aSet, int32_t aOffset ) const
+{
+ if (aOffset < 0)
+ aOffset = 0;
+ else if (aOffset >= int32_t(mLength))
+ return kNotFound;
+
+ int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
+ if (result != kNotFound)
+ result += aOffset;
+ return result;
+}
+
+
+/**
+ * nsTString::RFindCharInSet
+ */
+
+int32_t
+nsTString_CharT::RFindCharInSet( const CharT* aSet, int32_t aOffset ) const
+{
+ // We want to pass a "data length" to ::RFindCharInSet
+ if (aOffset < 0 || aOffset > int32_t(mLength))
+ aOffset = mLength;
+ else
+ ++aOffset;
+
+ return ::RFindCharInSet(mData, aOffset, aSet);
+}
+
+
+// it's a shame to replicate this code. it was done this way in the past
+// to help performance. this function also gets to keep the rickg style
+// indentation :-/
+int32_t
+nsTString_CharT::ToInteger( nsresult* aErrorCode, uint32_t aRadix ) const
+{
+ CharT* cp=mData;
+ int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
+ int32_t result=0;
+ bool negate=false;
+ CharT theChar=0;
+
+ //initial value, override if we find an integer
+ *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
+
+ if(cp) {
+
+ //begin by skipping over leading chars that shouldn't be part of the number...
+
+ CharT* endcp=cp+mLength;
+ bool done=false;
+
+ while((cp<endcp) && (!done)){
+ switch(*cp++) {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ theRadix=16;
+ done=true;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ done=true;
+ break;
+ case '-':
+ negate=true; //fall through...
+ break;
+ case 'X': case 'x':
+ theRadix=16;
+ break;
+ default:
+ break;
+ } //switch
+ }
+
+ if (done) {
+
+ //integer found
+ *aErrorCode = NS_OK;
+
+ if (aRadix!=kAutoDetect) theRadix = aRadix; // override
+
+ //now iterate the numeric chars and build our result
+ CharT* first=--cp; //in case we have to back up.
+ bool haveValue = false;
+
+ while(cp<endcp){
+ int32_t oldresult = result;
+
+ theChar=*cp++;
+ if(('0'<=theChar) && (theChar<='9')){
+ result = (theRadix * result) + (theChar-'0');
+ haveValue = true;
+ }
+ else if((theChar>='A') && (theChar<='F')) {
+ if(10==theRadix) {
+ if(kAutoDetect==aRadix){
+ theRadix=16;
+ cp=first; //backup
+ result=0;
+ haveValue = false;
+ }
+ else {
+ *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
+ result=0;
+ break;
+ }
+ }
+ else {
+ result = (theRadix * result) + ((theChar-'A')+10);
+ haveValue = true;
+ }
+ }
+ else if((theChar>='a') && (theChar<='f')) {
+ if(10==theRadix) {
+ if(kAutoDetect==aRadix){
+ theRadix=16;
+ cp=first; //backup
+ result=0;
+ haveValue = false;
+ }
+ else {
+ *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
+ result=0;
+ break;
+ }
+ }
+ else {
+ result = (theRadix * result) + ((theChar-'a')+10);
+ haveValue = true;
+ }
+ }
+ else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) {
+ continue;
+ }
+ else if((('#'==theChar) || ('+'==theChar)) && !haveValue) {
+ continue;
+ }
+ else {
+ //we've encountered a char that's not a legal number or sign
+ break;
+ }
+
+ if (result < oldresult) {
+ // overflow!
+ *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
+ result = 0;
+ break;
+ }
+ } //while
+ if(negate)
+ result=-result;
+ } //if
+ }
+ return result;
+}
+
+
+/**
+ * nsTString::ToInteger64
+ */
+int64_t
+nsTString_CharT::ToInteger64( nsresult* aErrorCode, uint32_t aRadix ) const
+{
+ CharT* cp=mData;
+ int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
+ int64_t result=0;
+ bool negate=false;
+ CharT theChar=0;
+
+ //initial value, override if we find an integer
+ *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
+
+ if(cp) {
+
+ //begin by skipping over leading chars that shouldn't be part of the number...
+
+ CharT* endcp=cp+mLength;
+ bool done=false;
+
+ while((cp<endcp) && (!done)){
+ switch(*cp++) {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ theRadix=16;
+ done=true;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ done=true;
+ break;
+ case '-':
+ negate=true; //fall through...
+ break;
+ case 'X': case 'x':
+ theRadix=16;
+ break;
+ default:
+ break;
+ } //switch
+ }
+
+ if (done) {
+
+ //integer found
+ *aErrorCode = NS_OK;
+
+ if (aRadix!=kAutoDetect) theRadix = aRadix; // override
+
+ //now iterate the numeric chars and build our result
+ CharT* first=--cp; //in case we have to back up.
+ bool haveValue = false;
+
+ while(cp<endcp){
+ int64_t oldresult = result;
+
+ theChar=*cp++;
+ if(('0'<=theChar) && (theChar<='9')){
+ result = (theRadix * result) + (theChar-'0');
+ haveValue = true;
+ }
+ else if((theChar>='A') && (theChar<='F')) {
+ if(10==theRadix) {
+ if(kAutoDetect==aRadix){
+ theRadix=16;
+ cp=first; //backup
+ result=0;
+ haveValue = false;
+ }
+ else {
+ *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
+ result=0;
+ break;
+ }
+ }
+ else {
+ result = (theRadix * result) + ((theChar-'A')+10);
+ haveValue = true;
+ }
+ }
+ else if((theChar>='a') && (theChar<='f')) {
+ if(10==theRadix) {
+ if(kAutoDetect==aRadix){
+ theRadix=16;
+ cp=first; //backup
+ result=0;
+ haveValue = false;
+ }
+ else {
+ *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
+ result=0;
+ break;
+ }
+ }
+ else {
+ result = (theRadix * result) + ((theChar-'a')+10);
+ haveValue = true;
+ }
+ }
+ else if((('X'==theChar) || ('x'==theChar)) && (!haveValue || result == 0)) {
+ continue;
+ }
+ else if((('#'==theChar) || ('+'==theChar)) && !haveValue) {
+ continue;
+ }
+ else {
+ //we've encountered a char that's not a legal number or sign
+ break;
+ }
+
+ if (result < oldresult) {
+ // overflow!
+ *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
+ result = 0;
+ break;
+ }
+ } //while
+ if(negate)
+ result=-result;
+ } //if
+ }
+ return result;
+}
+
+
+/**
+ * nsTString::Mid
+ */
+
+uint32_t
+nsTString_CharT::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const
+{
+ if (aStartPos == 0 && aLengthToCopy >= mLength)
+ aResult = *this;
+ else
+ aResult = Substring(*this, aStartPos, aLengthToCopy);
+
+ return aResult.mLength;
+}
+
+
+/**
+ * nsTString::SetCharAt
+ */
+
+bool
+nsTString_CharT::SetCharAt( char16_t aChar, uint32_t aIndex )
+{
+ if (aIndex >= mLength)
+ return false;
+
+ if (!EnsureMutable())
+ AllocFailed(mLength);
+
+ mData[aIndex] = CharT(aChar);
+ return true;
+}
+
+
+/**
+ * nsTString::StripChars,StripChar,StripWhitespace
+ */
+
+void
+nsTString_CharT::StripChars( const char* aSet )
+{
+ if (!EnsureMutable())
+ AllocFailed(mLength);
+
+ mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
+}
+
+bool
+nsTString_CharT::StripChars( const char* aSet, const fallible_t& )
+{
+ if (!EnsureMutable()) {
+ return false;
+ }
+
+ mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
+ return true;
+}
+
+void
+nsTString_CharT::StripWhitespace()
+{
+ StripChars(kWhitespace);
+}
+
+bool
+nsTString_CharT::StripWhitespace(const fallible_t& aFallible)
+{
+ return StripChars(kWhitespace, aFallible);
+}
+
+/**
+ * nsTString::ReplaceChar,ReplaceSubstring
+ */
+
+void
+nsTString_CharT::ReplaceChar( char_type aOldChar, char_type aNewChar )
+{
+ if (!EnsureMutable()) // XXX do this lazily?
+ AllocFailed(mLength);
+
+ for (uint32_t i=0; i<mLength; ++i)
+ {
+ if (mData[i] == aOldChar)
+ mData[i] = aNewChar;
+ }
+}
+
+void
+nsTString_CharT::ReplaceChar( const char* aSet, char_type aNewChar )
+{
+ if (!EnsureMutable()) // XXX do this lazily?
+ AllocFailed(mLength);
+
+ char_type* data = mData;
+ uint32_t lenRemaining = mLength;
+
+ while (lenRemaining)
+ {
+ int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
+ if (i == kNotFound)
+ break;
+
+ data[i++] = aNewChar;
+ data += i;
+ lenRemaining -= i;
+ }
+}
+
+void ReleaseData(void* aData, uint32_t aFlags);
+
+void
+nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
+ const char_type* aNewValue)
+{
+ ReplaceSubstring(nsTDependentString_CharT(aTarget),
+ nsTDependentString_CharT(aNewValue));
+}
+
+bool
+nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
+ const char_type* aNewValue,
+ const fallible_t& aFallible)
+{
+ return ReplaceSubstring(nsTDependentString_CharT(aTarget),
+ nsTDependentString_CharT(aNewValue),
+ aFallible);
+}
+
+void
+nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
+ const self_type& aNewValue)
+{
+ if (!ReplaceSubstring(aTarget, aNewValue, mozilla::fallible)) {
+ // Note that this may wildly underestimate the allocation that failed, as
+ // we could have been replacing multiple copies of aTarget.
+ AllocFailed(mLength + (aNewValue.Length() - aTarget.Length()));
+ }
+}
+
+bool
+nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
+ const self_type& aNewValue,
+ const fallible_t&)
+{
+ if (aTarget.Length() == 0)
+ return true;
+
+ // Remember all of the non-matching parts.
+ AutoTArray<Segment, 16> nonMatching;
+ uint32_t i = 0;
+ uint32_t newLength = 0;
+ while (true)
+ {
+ int32_t r = FindSubstring(mData + i, mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false);
+ int32_t until = (r == kNotFound) ? mLength - i : r;
+ nonMatching.AppendElement(Segment(i, until));
+ newLength += until;
+ if (r == kNotFound) {
+ break;
+ }
+
+ newLength += aNewValue.Length();
+ i += r + aTarget.Length();
+ if (i >= mLength) {
+ // Add an auxiliary entry at the end of the list to help as an edge case
+ // for the algorithms below.
+ nonMatching.AppendElement(Segment(mLength, 0));
+ break;
+ }
+ }
+
+ // If there's only one non-matching segment, then the target string was not
+ // found, and there's nothing to do.
+ if (nonMatching.Length() == 1) {
+ MOZ_ASSERT(nonMatching[0].mBegin == 0 && nonMatching[0].mLength == mLength,
+ "We should have the correct non-matching segment.");
+ return true;
+ }
+
+ // Make sure that we can mutate our buffer.
+ // Note that we always allocate at least an mLength sized buffer, because the
+ // rest of the algorithm relies on having access to all of the original
+ // string. In other words, we over-allocate in the shrinking case.
+ char_type* oldData;
+ uint32_t oldFlags;
+ if (!MutatePrep(XPCOM_MAX(mLength, newLength), &oldData, &oldFlags))
+ return false;
+ if (oldData) {
+ // Copy all of the old data to the new buffer.
+ char_traits::copy(mData, oldData, mLength);
+ ::ReleaseData(oldData, oldFlags);
+ }
+
+ if (aTarget.Length() >= aNewValue.Length()) {
+ // In the shrinking case, start filling the buffer from the beginning.
+ const uint32_t delta = (aTarget.Length() - aNewValue.Length());
+ for (i = 1; i < nonMatching.Length(); ++i) {
+ // When we move the i'th non-matching segment into position, we need to
+ // account for the characters deleted by the previous |i| replacements by
+ // subtracting |i * delta|.
+ const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin;
+ char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin - i * delta;
+ // Write the i'th replacement immediately before the new i'th non-matching
+ // segment.
+ char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
+ aNewValue.Data(), aNewValue.Length());
+ char_traits::move(destinationSegmentPtr, sourceSegmentPtr,
+ nonMatching[i].mLength);
+ }
+ } else {
+ // In the growing case, start filling the buffer from the end.
+ const uint32_t delta = (aNewValue.Length() - aTarget.Length());
+ for (i = nonMatching.Length() - 1; i > 0; --i) {
+ // When we move the i'th non-matching segment into position, we need to
+ // account for the characters added by the previous |i| replacements by
+ // adding |i * delta|.
+ const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin;
+ char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin + i * delta;
+ char_traits::move(destinationSegmentPtr, sourceSegmentPtr,
+ nonMatching[i].mLength);
+ // Write the i'th replacement immediately before the new i'th non-matching
+ // segment.
+ char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
+ aNewValue.Data(), aNewValue.Length());
+ }
+ }
+
+ // Adjust the length and make sure the string is null terminated.
+ mLength = newLength;
+ mData[mLength] = char_type(0);
+
+ return true;
+}
+
+/**
+ * nsTString::Trim
+ */
+
+void
+nsTString_CharT::Trim( const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes )
+{
+ // the old implementation worried about aSet being null :-/
+ if (!aSet)
+ return;
+
+ char_type* start = mData;
+ char_type* end = mData + mLength;
+
+ // skip over quotes if requested
+ if (aIgnoreQuotes && mLength > 2 && mData[0] == mData[mLength - 1] &&
+ (mData[0] == '\'' || mData[0] == '"'))
+ {
+ ++start;
+ --end;
+ }
+
+ uint32_t setLen = nsCharTraits<char>::length(aSet);
+
+ if (aTrimLeading)
+ {
+ uint32_t cutStart = start - mData;
+ uint32_t cutLength = 0;
+
+ // walk forward from start to end
+ for (; start != end; ++start, ++cutLength)
+ {
+ int32_t pos = FindChar1(aSet, setLen, 0, *start, setLen);
+ if (pos == kNotFound)
+ break;
+ }
+
+ if (cutLength)
+ {
+ Cut(cutStart, cutLength);
+
+ // reset iterators
+ start = mData + cutStart;
+ end = mData + mLength - cutStart;
+ }
+ }
+
+ if (aTrimTrailing)
+ {
+ uint32_t cutEnd = end - mData;
+ uint32_t cutLength = 0;
+
+ // walk backward from end to start
+ --end;
+ for (; end >= start; --end, ++cutLength)
+ {
+ int32_t pos = FindChar1(aSet, setLen, 0, *end, setLen);
+ if (pos == kNotFound)
+ break;
+ }
+
+ if (cutLength)
+ Cut(cutEnd - cutLength, cutLength);
+ }
+}
+
+
+/**
+ * nsTString::CompressWhitespace
+ */
+
+void
+nsTString_CharT::CompressWhitespace( bool aTrimLeading, bool aTrimTrailing )
+{
+ const char* set = kWhitespace;
+
+ ReplaceChar(set, ' ');
+ Trim(set, aTrimLeading, aTrimTrailing);
+
+ // this one does some questionable fu... just copying the old code!
+ mLength = nsBufferRoutines<char_type>::compress_chars(mData, mLength, set);
+}
+
+
+/**
+ * nsTString::AssignWithConversion
+ */
+
+void
+nsTString_CharT::AssignWithConversion( const incompatible_char_type* aData, int32_t aLength )
+{
+ // for compatibility with the old string implementation, we need to allow
+ // for a nullptr input buffer :-(
+ if (!aData)
+ {
+ Truncate();
+ }
+ else
+ {
+ if (aLength < 0)
+ aLength = nsCharTraits<incompatible_char_type>::length(aData);
+
+ AssignWithConversion(Substring(aData, aLength));
+ }
+}
diff --git a/xpcom/string/nsTSubstring.cpp b/xpcom/string/nsTSubstring.cpp
new file mode 100644
index 000000000..a3a830b9d
--- /dev/null
+++ b/xpcom/string/nsTSubstring.cpp
@@ -0,0 +1,1089 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/double-conversion.h"
+#include "mozilla/MemoryReporting.h"
+
+using double_conversion::DoubleToStringConverter;
+
+const nsTSubstring_CharT::size_type nsTSubstring_CharT::kMaxCapacity =
+ (nsTSubstring_CharT::size_type(-1) /
+ 2 - sizeof(nsStringBuffer)) /
+ sizeof(nsTSubstring_CharT::char_type) - 2;
+
+#ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
+nsTSubstring_CharT::nsTSubstring_CharT(char_type* aData, size_type aLength,
+ uint32_t aFlags)
+ : mData(aData),
+ mLength(aLength),
+ mFlags(aFlags)
+{
+ MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
+
+ if (aFlags & F_OWNED) {
+ STRING_STAT_INCREMENT(Adopt);
+ MOZ_LOG_CTOR(mData, "StringAdopt", 1);
+ }
+}
+#endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
+
+/**
+ * helper function for down-casting a nsTSubstring to a nsTFixedString.
+ */
+inline const nsTFixedString_CharT*
+AsFixedString(const nsTSubstring_CharT* aStr)
+{
+ return static_cast<const nsTFixedString_CharT*>(aStr);
+}
+
+
+/**
+ * this function is called to prepare mData for writing. the given capacity
+ * indicates the required minimum storage size for mData, in sizeof(char_type)
+ * increments. this function returns true if the operation succeeds. it also
+ * returns the old data and old flags members if mData is newly allocated.
+ * the old data must be released by the caller.
+ */
+bool
+nsTSubstring_CharT::MutatePrep(size_type aCapacity, char_type** aOldData,
+ uint32_t* aOldFlags)
+{
+ // initialize to no old data
+ *aOldData = nullptr;
+ *aOldFlags = 0;
+
+ size_type curCapacity = Capacity();
+
+ // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
+ // able to allocate it. Just bail out in cases like that. We don't want
+ // to be allocating 2GB+ strings anyway.
+ static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
+ "bad size for nsStringBuffer");
+ if (!CheckCapacity(aCapacity)) {
+ return false;
+ }
+
+ // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
+ // need to allocate a new buffer. We cannot use the existing buffer even
+ // though it might be large enough.
+
+ if (curCapacity != 0) {
+ if (aCapacity <= curCapacity) {
+ mFlags &= ~F_VOIDED; // mutation clears voided flag
+ return true;
+ }
+ }
+
+ if (curCapacity < aCapacity) {
+ // We increase our capacity so that the allocated buffer grows
+ // exponentially, which gives us amortized O(1) appending. Below the
+ // threshold, we use powers-of-two. Above the threshold, we grow by at
+ // least 1.125, rounding up to the nearest MiB.
+ const size_type slowGrowthThreshold = 8 * 1024 * 1024;
+
+ // nsStringBuffer allocates sizeof(nsStringBuffer) + passed size, and
+ // storageSize below wants extra 1 * sizeof(char_type).
+ const size_type neededExtraSpace =
+ sizeof(nsStringBuffer) / sizeof(char_type) + 1;
+
+ size_type temp;
+ if (aCapacity >= slowGrowthThreshold) {
+ size_type minNewCapacity = curCapacity + (curCapacity >> 3); // multiply by 1.125
+ temp = XPCOM_MAX(aCapacity, minNewCapacity) + neededExtraSpace;
+
+ // Round up to the next multiple of MiB, but ensure the expected
+ // capacity doesn't include the extra space required by nsStringBuffer
+ // and null-termination.
+ const size_t MiB = 1 << 20;
+ temp = (MiB * ((temp + MiB - 1) / MiB)) - neededExtraSpace;
+ } else {
+ // Round up to the next power of two.
+ temp =
+ mozilla::RoundUpPow2(aCapacity + neededExtraSpace) - neededExtraSpace;
+ }
+
+ MOZ_ASSERT(XPCOM_MIN(temp, kMaxCapacity) >= aCapacity,
+ "should have hit the early return at the top");
+ aCapacity = XPCOM_MIN(temp, kMaxCapacity);
+ }
+
+ //
+ // several cases:
+ //
+ // (1) we have a shared buffer (mFlags & F_SHARED)
+ // (2) we have an owned buffer (mFlags & F_OWNED)
+ // (3) we have a fixed buffer (mFlags & F_FIXED)
+ // (4) we have a readonly buffer
+ //
+ // requiring that we in some cases preserve the data before creating
+ // a new buffer complicates things just a bit ;-)
+ //
+
+ size_type storageSize = (aCapacity + 1) * sizeof(char_type);
+
+ // case #1
+ if (mFlags & F_SHARED) {
+ nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
+ if (!hdr->IsReadonly()) {
+ nsStringBuffer* newHdr = nsStringBuffer::Realloc(hdr, storageSize);
+ if (!newHdr) {
+ return false; // out-of-memory (original header left intact)
+ }
+
+ hdr = newHdr;
+ mData = (char_type*)hdr->Data();
+ mFlags &= ~F_VOIDED; // mutation clears voided flag
+ return true;
+ }
+ }
+
+ char_type* newData;
+ uint32_t newDataFlags;
+
+ // if we have a fixed buffer of sufficient size, then use it. this helps
+ // avoid heap allocations.
+ if ((mFlags & F_CLASS_FIXED) &&
+ (aCapacity < AsFixedString(this)->mFixedCapacity)) {
+ newData = AsFixedString(this)->mFixedBuf;
+ newDataFlags = F_TERMINATED | F_FIXED;
+ } else {
+ // if we reach here then, we must allocate a new buffer. we cannot
+ // make use of our F_OWNED or F_FIXED buffers because they are not
+ // large enough.
+
+ nsStringBuffer* newHdr =
+ nsStringBuffer::Alloc(storageSize).take();
+ if (!newHdr) {
+ return false; // we are still in a consistent state
+ }
+
+ newData = (char_type*)newHdr->Data();
+ newDataFlags = F_TERMINATED | F_SHARED;
+ }
+
+ // save old data and flags
+ *aOldData = mData;
+ *aOldFlags = mFlags;
+
+ mData = newData;
+ SetDataFlags(newDataFlags);
+
+ // mLength does not change
+
+ // though we are not necessarily terminated at the moment, now is probably
+ // still the best time to set F_TERMINATED.
+
+ return true;
+}
+
+void
+nsTSubstring_CharT::Finalize()
+{
+ ::ReleaseData(mData, mFlags);
+ // mData, mLength, and mFlags are purposefully left dangling
+}
+
+bool
+nsTSubstring_CharT::ReplacePrep(index_type aCutStart,
+ size_type aCutLength,
+ size_type aNewLength)
+{
+ aCutLength = XPCOM_MIN(aCutLength, mLength - aCutStart);
+
+ mozilla::CheckedInt<size_type> newTotalLen = mLength;
+ newTotalLen += aNewLength;
+ newTotalLen -= aCutLength;
+ if (!newTotalLen.isValid()) {
+ return false;
+ }
+
+ if (aCutStart == mLength && Capacity() > newTotalLen.value()) {
+ mFlags &= ~F_VOIDED;
+ mData[newTotalLen.value()] = char_type(0);
+ mLength = newTotalLen.value();
+ return true;
+ }
+
+ return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
+ newTotalLen.value());
+}
+
+bool
+nsTSubstring_CharT::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
+ size_type aFragLen, size_type aNewLen)
+{
+ char_type* oldData;
+ uint32_t oldFlags;
+ if (!MutatePrep(aNewLen, &oldData, &oldFlags)) {
+ return false; // out-of-memory
+ }
+
+ if (oldData) {
+ // determine whether or not we need to copy part of the old string
+ // over to the new string.
+
+ if (aCutStart > 0) {
+ // copy prefix from old string
+ char_traits::copy(mData, oldData, aCutStart);
+ }
+
+ if (aCutStart + aCutLen < mLength) {
+ // copy suffix from old string to new offset
+ size_type from = aCutStart + aCutLen;
+ size_type fromLen = mLength - from;
+ uint32_t to = aCutStart + aFragLen;
+ char_traits::copy(mData + to, oldData + from, fromLen);
+ }
+
+ ::ReleaseData(oldData, oldFlags);
+ } else {
+ // original data remains intact
+
+ // determine whether or not we need to move part of the existing string
+ // to make room for the requested hole.
+ if (aFragLen != aCutLen && aCutStart + aCutLen < mLength) {
+ uint32_t from = aCutStart + aCutLen;
+ uint32_t fromLen = mLength - from;
+ uint32_t to = aCutStart + aFragLen;
+ char_traits::move(mData + to, mData + from, fromLen);
+ }
+ }
+
+ // add null terminator (mutable mData always has room for the null-
+ // terminator).
+ mData[aNewLen] = char_type(0);
+ mLength = aNewLen;
+
+ return true;
+}
+
+nsTSubstring_CharT::size_type
+nsTSubstring_CharT::Capacity() const
+{
+ // return 0 to indicate an immutable or 0-sized buffer
+
+ size_type capacity;
+ if (mFlags & F_SHARED) {
+ // if the string is readonly, then we pretend that it has no capacity.
+ nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
+ if (hdr->IsReadonly()) {
+ capacity = 0;
+ } else {
+ capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
+ }
+ } else if (mFlags & F_FIXED) {
+ capacity = AsFixedString(this)->mFixedCapacity;
+ } else if (mFlags & F_OWNED) {
+ // we don't store the capacity of an adopted buffer because that would
+ // require an additional member field. the best we can do is base the
+ // capacity on our length. remains to be seen if this is the right
+ // trade-off.
+ capacity = mLength;
+ } else {
+ capacity = 0;
+ }
+
+ return capacity;
+}
+
+bool
+nsTSubstring_CharT::EnsureMutable(size_type aNewLen)
+{
+ if (aNewLen == size_type(-1) || aNewLen == mLength) {
+ if (mFlags & (F_FIXED | F_OWNED)) {
+ return true;
+ }
+ if ((mFlags & F_SHARED) &&
+ !nsStringBuffer::FromData(mData)->IsReadonly()) {
+ return true;
+ }
+
+ aNewLen = mLength;
+ }
+ return SetLength(aNewLen, mozilla::fallible);
+}
+
+// ---------------------------------------------------------------------------
+
+// This version of Assign is optimized for single-character assignment.
+void
+nsTSubstring_CharT::Assign(char_type aChar)
+{
+ if (!ReplacePrep(0, mLength, 1)) {
+ AllocFailed(mLength);
+ }
+
+ *mData = aChar;
+}
+
+bool
+nsTSubstring_CharT::Assign(char_type aChar, const fallible_t&)
+{
+ if (!ReplacePrep(0, mLength, 1)) {
+ return false;
+ }
+
+ *mData = aChar;
+ return true;
+}
+
+void
+nsTSubstring_CharT::Assign(const char_type* aData)
+{
+ if (!Assign(aData, mozilla::fallible)) {
+ AllocFailed(char_traits::length(aData));
+ }
+}
+
+bool
+nsTSubstring_CharT::Assign(const char_type* aData, const fallible_t&)
+{
+ return Assign(aData, size_type(-1), mozilla::fallible);
+}
+
+void
+nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
+{
+ if (!Assign(aData, aLength, mozilla::fallible)) {
+ AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
+ : aLength);
+ }
+}
+
+bool
+nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength,
+ const fallible_t& aFallible)
+{
+ if (!aData || aLength == 0) {
+ Truncate();
+ return true;
+ }
+
+ if (aLength == size_type(-1)) {
+ aLength = char_traits::length(aData);
+ }
+
+ if (IsDependentOn(aData, aData + aLength)) {
+ return Assign(string_type(aData, aLength), aFallible);
+ }
+
+ if (!ReplacePrep(0, mLength, aLength)) {
+ return false;
+ }
+
+ char_traits::copy(mData, aData, aLength);
+ return true;
+}
+
+void
+nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength)
+{
+ if (!AssignASCII(aData, aLength, mozilla::fallible)) {
+ AllocFailed(aLength);
+ }
+}
+
+bool
+nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength,
+ const fallible_t& aFallible)
+{
+ // A Unicode string can't depend on an ASCII string buffer,
+ // so this dependence check only applies to CStrings.
+#ifdef CharT_is_char
+ if (IsDependentOn(aData, aData + aLength)) {
+ return Assign(string_type(aData, aLength), aFallible);
+ }
+#endif
+
+ if (!ReplacePrep(0, mLength, aLength)) {
+ return false;
+ }
+
+ char_traits::copyASCII(mData, aData, aLength);
+ return true;
+}
+
+void
+nsTSubstring_CharT::AssignLiteral(const char_type* aData, size_type aLength)
+{
+ ::ReleaseData(mData, mFlags);
+ mData = const_cast<char_type*>(aData);
+ mLength = aLength;
+ SetDataFlags(F_TERMINATED | F_LITERAL);
+}
+
+void
+nsTSubstring_CharT::Assign(const self_type& aStr)
+{
+ if (!Assign(aStr, mozilla::fallible)) {
+ AllocFailed(aStr.Length());
+ }
+}
+
+bool
+nsTSubstring_CharT::Assign(const self_type& aStr, const fallible_t& aFallible)
+{
+ // |aStr| could be sharable. We need to check its flags to know how to
+ // deal with it.
+
+ if (&aStr == this) {
+ return true;
+ }
+
+ if (!aStr.mLength) {
+ Truncate();
+ mFlags |= aStr.mFlags & F_VOIDED;
+ return true;
+ }
+
+ if (aStr.mFlags & F_SHARED) {
+ // nice! we can avoid a string copy :-)
+
+ // |aStr| should be null-terminated
+ NS_ASSERTION(aStr.mFlags & F_TERMINATED, "shared, but not terminated");
+
+ ::ReleaseData(mData, mFlags);
+
+ mData = aStr.mData;
+ mLength = aStr.mLength;
+ SetDataFlags(F_TERMINATED | F_SHARED);
+
+ // get an owning reference to the mData
+ nsStringBuffer::FromData(mData)->AddRef();
+ return true;
+ } else if (aStr.mFlags & F_LITERAL) {
+ MOZ_ASSERT(aStr.mFlags & F_TERMINATED, "Unterminated literal");
+
+ AssignLiteral(aStr.mData, aStr.mLength);
+ return true;
+ }
+
+ // else, treat this like an ordinary assignment.
+ return Assign(aStr.Data(), aStr.Length(), aFallible);
+}
+
+void
+nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple)
+{
+ if (!Assign(aTuple, mozilla::fallible)) {
+ AllocFailed(aTuple.Length());
+ }
+}
+
+bool
+nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple,
+ const fallible_t& aFallible)
+{
+ if (aTuple.IsDependentOn(mData, mData + mLength)) {
+ // take advantage of sharing here...
+ return Assign(string_type(aTuple), aFallible);
+ }
+
+ size_type length = aTuple.Length();
+
+ // don't use ReplacePrep here because it changes the length
+ char_type* oldData;
+ uint32_t oldFlags;
+ if (!MutatePrep(length, &oldData, &oldFlags)) {
+ return false;
+ }
+
+ if (oldData) {
+ ::ReleaseData(oldData, oldFlags);
+ }
+
+ aTuple.WriteTo(mData, length);
+ mData[length] = 0;
+ mLength = length;
+ return true;
+}
+
+void
+nsTSubstring_CharT::Adopt(char_type* aData, size_type aLength)
+{
+ if (aData) {
+ ::ReleaseData(mData, mFlags);
+
+ if (aLength == size_type(-1)) {
+ aLength = char_traits::length(aData);
+ }
+
+ MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
+
+ mData = aData;
+ mLength = aLength;
+ SetDataFlags(F_TERMINATED | F_OWNED);
+
+ STRING_STAT_INCREMENT(Adopt);
+ // Treat this as construction of a "StringAdopt" object for leak
+ // tracking purposes.
+ MOZ_LOG_CTOR(mData, "StringAdopt", 1);
+ } else {
+ SetIsVoid(true);
+ }
+}
+
+
+// This version of Replace is optimized for single-character replacement.
+void
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar)
+{
+ aCutStart = XPCOM_MIN(aCutStart, Length());
+
+ if (ReplacePrep(aCutStart, aCutLength, 1)) {
+ mData[aCutStart] = aChar;
+ }
+}
+
+bool
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar,
+ const fallible_t&)
+{
+ aCutStart = XPCOM_MIN(aCutStart, Length());
+
+ if (!ReplacePrep(aCutStart, aCutLength, 1)) {
+ return false;
+ }
+
+ mData[aCutStart] = aChar;
+
+ return true;
+}
+
+void
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData, size_type aLength)
+{
+ if (!Replace(aCutStart, aCutLength, aData, aLength,
+ mozilla::fallible)) {
+ AllocFailed(Length() - aCutLength + 1);
+ }
+}
+
+bool
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData, size_type aLength,
+ const fallible_t& aFallible)
+{
+ // unfortunately, some callers pass null :-(
+ if (!aData) {
+ aLength = 0;
+ } else {
+ if (aLength == size_type(-1)) {
+ aLength = char_traits::length(aData);
+ }
+
+ if (IsDependentOn(aData, aData + aLength)) {
+ nsTAutoString_CharT temp(aData, aLength);
+ return Replace(aCutStart, aCutLength, temp, aFallible);
+ }
+ }
+
+ aCutStart = XPCOM_MIN(aCutStart, Length());
+
+ bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
+ if (!ok) {
+ return false;
+ }
+
+ if (aLength > 0) {
+ char_traits::copy(mData + aCutStart, aData, aLength);
+ }
+
+ return true;
+}
+
+void
+nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
+ const char* aData, size_type aLength)
+{
+ if (!ReplaceASCII(aCutStart, aCutLength, aData, aLength, mozilla::fallible)) {
+ AllocFailed(Length() - aCutLength + 1);
+ }
+}
+
+bool
+nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
+ const char* aData, size_type aLength,
+ const fallible_t& aFallible)
+{
+ if (aLength == size_type(-1)) {
+ aLength = strlen(aData);
+ }
+
+ // A Unicode string can't depend on an ASCII string buffer,
+ // so this dependence check only applies to CStrings.
+#ifdef CharT_is_char
+ if (IsDependentOn(aData, aData + aLength)) {
+ nsTAutoString_CharT temp(aData, aLength);
+ return Replace(aCutStart, aCutLength, temp, aFallible);
+ }
+#endif
+
+ aCutStart = XPCOM_MIN(aCutStart, Length());
+
+ bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
+ if (!ok) {
+ return false;
+ }
+
+ if (aLength > 0) {
+ char_traits::copyASCII(mData + aCutStart, aData, aLength);
+ }
+
+ return true;
+}
+
+void
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+ const substring_tuple_type& aTuple)
+{
+ if (aTuple.IsDependentOn(mData, mData + mLength)) {
+ nsTAutoString_CharT temp(aTuple);
+ Replace(aCutStart, aCutLength, temp);
+ return;
+ }
+
+ size_type length = aTuple.Length();
+
+ aCutStart = XPCOM_MIN(aCutStart, Length());
+
+ if (ReplacePrep(aCutStart, aCutLength, length) && length > 0) {
+ aTuple.WriteTo(mData + aCutStart, length);
+ }
+}
+
+void
+nsTSubstring_CharT::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
+ const char_type* aData, size_type aLength)
+{
+ aCutStart = XPCOM_MIN(aCutStart, Length());
+
+ if (!aCutStart && aCutLength == Length()) {
+ AssignLiteral(aData, aLength);
+ } else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
+ char_traits::copy(mData + aCutStart, aData, aLength);
+ }
+}
+
+void
+nsTSubstring_CharT::SetCapacity(size_type aCapacity)
+{
+ if (!SetCapacity(aCapacity, mozilla::fallible)) {
+ AllocFailed(aCapacity);
+ }
+}
+
+bool
+nsTSubstring_CharT::SetCapacity(size_type aCapacity, const fallible_t&)
+{
+ // capacity does not include room for the terminating null char
+
+ // if our capacity is reduced to zero, then free our buffer.
+ if (aCapacity == 0) {
+ ::ReleaseData(mData, mFlags);
+ mData = char_traits::sEmptyBuffer;
+ mLength = 0;
+ SetDataFlags(F_TERMINATED);
+ return true;
+ }
+
+ char_type* oldData;
+ uint32_t oldFlags;
+ if (!MutatePrep(aCapacity, &oldData, &oldFlags)) {
+ return false; // out-of-memory
+ }
+
+ // compute new string length
+ size_type newLen = XPCOM_MIN(mLength, aCapacity);
+
+ if (oldData) {
+ // preserve old data
+ if (mLength > 0) {
+ char_traits::copy(mData, oldData, newLen);
+ }
+
+ ::ReleaseData(oldData, oldFlags);
+ }
+
+ // adjust mLength if our buffer shrunk down in size
+ if (newLen < mLength) {
+ mLength = newLen;
+ }
+
+ // always null-terminate here, even if the buffer got longer. this is
+ // for backwards compat with the old string implementation.
+ mData[aCapacity] = char_type(0);
+
+ return true;
+}
+
+void
+nsTSubstring_CharT::SetLength(size_type aLength)
+{
+ SetCapacity(aLength);
+ mLength = aLength;
+}
+
+bool
+nsTSubstring_CharT::SetLength(size_type aLength, const fallible_t& aFallible)
+{
+ if (!SetCapacity(aLength, aFallible)) {
+ return false;
+ }
+
+ mLength = aLength;
+ return true;
+}
+
+void
+nsTSubstring_CharT::SetIsVoid(bool aVal)
+{
+ if (aVal) {
+ Truncate();
+ mFlags |= F_VOIDED;
+ } else {
+ mFlags &= ~F_VOIDED;
+ }
+}
+
+bool
+nsTSubstring_CharT::Equals(const self_type& aStr) const
+{
+ return mLength == aStr.mLength &&
+ char_traits::compare(mData, aStr.mData, mLength) == 0;
+}
+
+bool
+nsTSubstring_CharT::Equals(const self_type& aStr,
+ const comparator_type& aComp) const
+{
+ return mLength == aStr.mLength &&
+ aComp(mData, aStr.mData, mLength, aStr.mLength) == 0;
+}
+
+bool
+nsTSubstring_CharT::Equals(const char_type* aData) const
+{
+ // unfortunately, some callers pass null :-(
+ if (!aData) {
+ NS_NOTREACHED("null data pointer");
+ return mLength == 0;
+ }
+
+ // XXX avoid length calculation?
+ size_type length = char_traits::length(aData);
+ return mLength == length &&
+ char_traits::compare(mData, aData, mLength) == 0;
+}
+
+bool
+nsTSubstring_CharT::Equals(const char_type* aData,
+ const comparator_type& aComp) const
+{
+ // unfortunately, some callers pass null :-(
+ if (!aData) {
+ NS_NOTREACHED("null data pointer");
+ return mLength == 0;
+ }
+
+ // XXX avoid length calculation?
+ size_type length = char_traits::length(aData);
+ return mLength == length && aComp(mData, aData, mLength, length) == 0;
+}
+
+bool
+nsTSubstring_CharT::EqualsASCII(const char* aData, size_type aLen) const
+{
+ return mLength == aLen &&
+ char_traits::compareASCII(mData, aData, aLen) == 0;
+}
+
+bool
+nsTSubstring_CharT::EqualsASCII(const char* aData) const
+{
+ return char_traits::compareASCIINullTerminated(mData, mLength, aData) == 0;
+}
+
+bool
+nsTSubstring_CharT::LowerCaseEqualsASCII(const char* aData,
+ size_type aLen) const
+{
+ return mLength == aLen &&
+ char_traits::compareLowerCaseToASCII(mData, aData, aLen) == 0;
+}
+
+bool
+nsTSubstring_CharT::LowerCaseEqualsASCII(const char* aData) const
+{
+ return char_traits::compareLowerCaseToASCIINullTerminated(mData,
+ mLength,
+ aData) == 0;
+}
+
+nsTSubstring_CharT::size_type
+nsTSubstring_CharT::CountChar(char_type aChar) const
+{
+ const char_type* start = mData;
+ const char_type* end = mData + mLength;
+
+ return NS_COUNT(start, end, aChar);
+}
+
+int32_t
+nsTSubstring_CharT::FindChar(char_type aChar, index_type aOffset) const
+{
+ if (aOffset < mLength) {
+ const char_type* result = char_traits::find(mData + aOffset,
+ mLength - aOffset, aChar);
+ if (result) {
+ return result - mData;
+ }
+ }
+ return -1;
+}
+
+void
+nsTSubstring_CharT::StripChar(char_type aChar, int32_t aOffset)
+{
+ if (mLength == 0 || aOffset >= int32_t(mLength)) {
+ return;
+ }
+
+ if (!EnsureMutable()) { // XXX do this lazily?
+ AllocFailed(mLength);
+ }
+
+ // XXX(darin): this code should defer writing until necessary.
+
+ char_type* to = mData + aOffset;
+ char_type* from = mData + aOffset;
+ char_type* end = mData + mLength;
+
+ while (from < end) {
+ char_type theChar = *from++;
+ if (aChar != theChar) {
+ *to++ = theChar;
+ }
+ }
+ *to = char_type(0); // add the null
+ mLength = to - mData;
+}
+
+void
+nsTSubstring_CharT::StripChars(const char_type* aChars, uint32_t aOffset)
+{
+ if (aOffset >= uint32_t(mLength)) {
+ return;
+ }
+
+ if (!EnsureMutable()) { // XXX do this lazily?
+ AllocFailed(mLength);
+ }
+
+ // XXX(darin): this code should defer writing until necessary.
+
+ char_type* to = mData + aOffset;
+ char_type* from = mData + aOffset;
+ char_type* end = mData + mLength;
+
+ while (from < end) {
+ char_type theChar = *from++;
+ const char_type* test = aChars;
+
+ for (; *test && *test != theChar; ++test);
+
+ if (!*test) {
+ // Not stripped, copy this char.
+ *to++ = theChar;
+ }
+ }
+ *to = char_type(0); // add the null
+ mLength = to - mData;
+}
+
+int
+nsTSubstring_CharT::AppendFunc(void* aArg, const char* aStr, uint32_t aLen)
+{
+ self_type* self = static_cast<self_type*>(aArg);
+
+ // NSPR sends us the final null terminator even though we don't want it
+ if (aLen && aStr[aLen - 1] == '\0') {
+ --aLen;
+ }
+
+ self->AppendASCII(aStr, aLen);
+
+ return aLen;
+}
+
+void
+nsTSubstring_CharT::AppendPrintf(const char* aFormat, ...)
+{
+ va_list ap;
+ va_start(ap, aFormat);
+ uint32_t r = PR_vsxprintf(AppendFunc, this, aFormat, ap);
+ if (r == (uint32_t)-1) {
+ NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
+ }
+ va_end(ap);
+}
+
+void
+nsTSubstring_CharT::AppendPrintf(const char* aFormat, va_list aAp)
+{
+ uint32_t r = PR_vsxprintf(AppendFunc, this, aFormat, aAp);
+ if (r == (uint32_t)-1) {
+ NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
+ }
+}
+
+/* hack to make sure we define FormatWithoutTrailingZeros only once */
+#ifdef CharT_is_PRUnichar
+// Returns the length of the formatted aDouble in aBuf.
+static int
+FormatWithoutTrailingZeros(char (&aBuf)[40], double aDouble,
+ int aPrecision)
+{
+ static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
+ DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
+ "Infinity",
+ "NaN",
+ 'e',
+ -6, 21,
+ 6, 1);
+ double_conversion::StringBuilder builder(aBuf, sizeof(aBuf));
+ bool exponential_notation = false;
+ converter.ToPrecision(aDouble, aPrecision, &exponential_notation, &builder);
+ int length = builder.position();
+ char* formattedDouble = builder.Finalize();
+
+ // If we have a shorter string than aPrecision, it means we have a special
+ // value (NaN or Infinity). All other numbers will be formatted with at
+ // least aPrecision digits.
+ if (length <= aPrecision) {
+ return length;
+ }
+
+ char* end = formattedDouble + length;
+ char* decimalPoint = strchr(aBuf, '.');
+ // No trailing zeros to remove.
+ if (!decimalPoint) {
+ return length;
+ }
+
+ if (MOZ_UNLIKELY(exponential_notation)) {
+ // We need to check for cases like 1.00000e-10 (yes, this is
+ // disgusting).
+ char* exponent = end - 1;
+ for (; ; --exponent) {
+ if (*exponent == 'e') {
+ break;
+ }
+ }
+ char* zerosBeforeExponent = exponent - 1;
+ for (; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
+ if (*zerosBeforeExponent != '0') {
+ break;
+ }
+ }
+ if (zerosBeforeExponent == decimalPoint) {
+ --zerosBeforeExponent;
+ }
+ // Slide the exponent to the left over the trailing zeros. Don't
+ // worry about copying the trailing NUL character.
+ size_t exponentSize = end - exponent;
+ memmove(zerosBeforeExponent + 1, exponent, exponentSize);
+ length -= exponent - (zerosBeforeExponent + 1);
+ } else {
+ char* trailingZeros = end - 1;
+ for (; trailingZeros != decimalPoint; --trailingZeros) {
+ if (*trailingZeros != '0') {
+ break;
+ }
+ }
+ if (trailingZeros == decimalPoint) {
+ --trailingZeros;
+ }
+ length -= end - (trailingZeros + 1);
+ }
+
+ return length;
+}
+#endif /* CharT_is_PRUnichar */
+
+void
+nsTSubstring_CharT::AppendFloat(float aFloat)
+{
+ char buf[40];
+ int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
+ AppendASCII(buf, length);
+}
+
+void
+nsTSubstring_CharT::AppendFloat(double aFloat)
+{
+ char buf[40];
+ int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
+ AppendASCII(buf, length);
+}
+
+size_t
+nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
+ mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ if (mFlags & F_SHARED) {
+ return nsStringBuffer::FromData(mData)->
+ SizeOfIncludingThisIfUnshared(aMallocSizeOf);
+ }
+ if (mFlags & F_OWNED) {
+ return aMallocSizeOf(mData);
+ }
+
+ // If we reach here, exactly one of the following must be true:
+ // - F_VOIDED is set, and mData points to sEmptyBuffer;
+ // - F_FIXED is set, and mData points to a buffer within a string
+ // object (e.g. nsAutoString);
+ // - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer
+ // owned by something else.
+ //
+ // In all three cases, we don't measure it.
+ return 0;
+}
+
+size_t
+nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
+ mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ // This is identical to SizeOfExcludingThisIfUnshared except for the
+ // F_SHARED case.
+ if (mFlags & F_SHARED) {
+ return nsStringBuffer::FromData(mData)->
+ SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
+ }
+ if (mFlags & F_OWNED) {
+ return aMallocSizeOf(mData);
+ }
+ return 0;
+}
+
+size_t
+nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
+ mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
+size_t
+nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
+ mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
+}
+
diff --git a/xpcom/string/nsTSubstring.h b/xpcom/string/nsTSubstring.h
new file mode 100644
index 000000000..a08036b1f
--- /dev/null
+++ b/xpcom/string/nsTSubstring.h
@@ -0,0 +1,1186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+#include "mozilla/Casting.h"
+#include "mozilla/MemoryReporting.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#error Cannot use internal string classes without MOZILLA_INTERNAL_API defined. Use the frozen header nsStringAPI.h instead.
+#endif
+
+/**
+ * The base for string comparators
+ */
+class nsTStringComparator_CharT
+{
+public:
+ typedef CharT char_type;
+
+ nsTStringComparator_CharT()
+ {
+ }
+
+ virtual int operator()(const char_type*, const char_type*,
+ uint32_t, uint32_t) const = 0;
+};
+
+
+/**
+ * The default string comparator (case-sensitive comparision)
+ */
+class nsTDefaultStringComparator_CharT
+ : public nsTStringComparator_CharT
+{
+public:
+ typedef CharT char_type;
+
+ nsTDefaultStringComparator_CharT()
+ {
+ }
+
+ virtual int operator()(const char_type*, const char_type*,
+ uint32_t, uint32_t) const override;
+};
+
+/**
+ * nsTSubstring is the most abstract class in the string hierarchy. It
+ * represents a single contiguous array of characters, which may or may not
+ * be null-terminated. This type is not instantiated directly. A sub-class
+ * is instantiated instead. For example, see nsTString.
+ *
+ * NAMES:
+ * nsAString for wide characters
+ * nsACString for narrow characters
+ *
+ * Many of the accessors on nsTSubstring are inlined as an optimization.
+ */
+class nsTSubstring_CharT
+{
+public:
+ typedef mozilla::fallible_t fallible_t;
+
+ typedef CharT char_type;
+
+ typedef nsCharTraits<char_type> char_traits;
+ typedef char_traits::incompatible_char_type incompatible_char_type;
+
+ typedef nsTSubstring_CharT self_type;
+ typedef self_type abstract_string_type;
+ typedef self_type base_string_type;
+
+ typedef self_type substring_type;
+ typedef nsTSubstringTuple_CharT substring_tuple_type;
+ typedef nsTString_CharT string_type;
+
+ typedef nsReadingIterator<char_type> const_iterator;
+ typedef nsWritingIterator<char_type> iterator;
+
+ typedef nsTStringComparator_CharT comparator_type;
+
+ typedef char_type* char_iterator;
+ typedef const char_type* const_char_iterator;
+
+ typedef uint32_t size_type;
+ typedef uint32_t index_type;
+
+public:
+
+ // this acts like a virtual destructor
+ ~nsTSubstring_CharT()
+ {
+ Finalize();
+ }
+
+ /**
+ * reading iterators
+ */
+
+ const_char_iterator BeginReading() const
+ {
+ return mData;
+ }
+ const_char_iterator EndReading() const
+ {
+ return mData + mLength;
+ }
+
+ /**
+ * deprecated reading iterators
+ */
+
+ const_iterator& BeginReading(const_iterator& aIter) const
+ {
+ aIter.mStart = mData;
+ aIter.mEnd = mData + mLength;
+ aIter.mPosition = aIter.mStart;
+ return aIter;
+ }
+
+ const_iterator& EndReading(const_iterator& aIter) const
+ {
+ aIter.mStart = mData;
+ aIter.mEnd = mData + mLength;
+ aIter.mPosition = aIter.mEnd;
+ return aIter;
+ }
+
+ const_char_iterator& BeginReading(const_char_iterator& aIter) const
+ {
+ return aIter = mData;
+ }
+
+ const_char_iterator& EndReading(const_char_iterator& aIter) const
+ {
+ return aIter = mData + mLength;
+ }
+
+
+ /**
+ * writing iterators
+ */
+
+ char_iterator BeginWriting()
+ {
+ if (!EnsureMutable()) {
+ AllocFailed(mLength);
+ }
+
+ return mData;
+ }
+
+ char_iterator BeginWriting(const fallible_t&)
+ {
+ return EnsureMutable() ? mData : char_iterator(0);
+ }
+
+ char_iterator EndWriting()
+ {
+ if (!EnsureMutable()) {
+ AllocFailed(mLength);
+ }
+
+ return mData + mLength;
+ }
+
+ char_iterator EndWriting(const fallible_t&)
+ {
+ return EnsureMutable() ? (mData + mLength) : char_iterator(0);
+ }
+
+ char_iterator& BeginWriting(char_iterator& aIter)
+ {
+ return aIter = BeginWriting();
+ }
+
+ char_iterator& BeginWriting(char_iterator& aIter, const fallible_t& aFallible)
+ {
+ return aIter = BeginWriting(aFallible);
+ }
+
+ char_iterator& EndWriting(char_iterator& aIter)
+ {
+ return aIter = EndWriting();
+ }
+
+ char_iterator& EndWriting(char_iterator& aIter, const fallible_t& aFallible)
+ {
+ return aIter = EndWriting(aFallible);
+ }
+
+ /**
+ * deprecated writing iterators
+ */
+
+ iterator& BeginWriting(iterator& aIter)
+ {
+ char_type* data = BeginWriting();
+ aIter.mStart = data;
+ aIter.mEnd = data + mLength;
+ aIter.mPosition = aIter.mStart;
+ return aIter;
+ }
+
+ iterator& EndWriting(iterator& aIter)
+ {
+ char_type* data = BeginWriting();
+ aIter.mStart = data;
+ aIter.mEnd = data + mLength;
+ aIter.mPosition = aIter.mEnd;
+ return aIter;
+ }
+
+ /**
+ * accessors
+ */
+
+ // returns pointer to string data (not necessarily null-terminated)
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ char16ptr_t Data() const
+#else
+ const char_type* Data() const
+#endif
+ {
+ return mData;
+ }
+
+ size_type Length() const
+ {
+ return mLength;
+ }
+
+ uint32_t Flags() const
+ {
+ return mFlags;
+ }
+
+ bool IsEmpty() const
+ {
+ return mLength == 0;
+ }
+
+ bool IsLiteral() const
+ {
+ return (mFlags & F_LITERAL) != 0;
+ }
+
+ bool IsVoid() const
+ {
+ return (mFlags & F_VOIDED) != 0;
+ }
+
+ bool IsTerminated() const
+ {
+ return (mFlags & F_TERMINATED) != 0;
+ }
+
+ char_type CharAt(index_type aIndex) const
+ {
+ NS_ASSERTION(aIndex < mLength, "index exceeds allowable range");
+ return mData[aIndex];
+ }
+
+ char_type operator[](index_type aIndex) const
+ {
+ return CharAt(aIndex);
+ }
+
+ char_type First() const
+ {
+ NS_ASSERTION(mLength > 0, "|First()| called on an empty string");
+ return mData[0];
+ }
+
+ inline char_type Last() const
+ {
+ NS_ASSERTION(mLength > 0, "|Last()| called on an empty string");
+ return mData[mLength - 1];
+ }
+
+ size_type NS_FASTCALL CountChar(char_type) const;
+ int32_t NS_FASTCALL FindChar(char_type, index_type aOffset = 0) const;
+
+ inline bool Contains(char_type aChar) const
+ {
+ return FindChar(aChar) != kNotFound;
+ }
+
+ /**
+ * equality
+ */
+
+ bool NS_FASTCALL Equals(const self_type&) const;
+ bool NS_FASTCALL Equals(const self_type&, const comparator_type&) const;
+
+ bool NS_FASTCALL Equals(const char_type* aData) const;
+ bool NS_FASTCALL Equals(const char_type* aData,
+ const comparator_type& aComp) const;
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ bool NS_FASTCALL Equals(char16ptr_t aData) const
+ {
+ return Equals(static_cast<const char16_t*>(aData));
+ }
+ bool NS_FASTCALL Equals(char16ptr_t aData, const comparator_type& aComp) const
+ {
+ return Equals(static_cast<const char16_t*>(aData), aComp);
+ }
+#endif
+
+ /**
+ * An efficient comparison with ASCII that can be used even
+ * for wide strings. Call this version when you know the
+ * length of 'data'.
+ */
+ bool NS_FASTCALL EqualsASCII(const char* aData, size_type aLen) const;
+ /**
+ * An efficient comparison with ASCII that can be used even
+ * for wide strings. Call this version when 'data' is
+ * null-terminated.
+ */
+ bool NS_FASTCALL EqualsASCII(const char* aData) const;
+
+ // EqualsLiteral must ONLY be applied to an actual literal string, or
+ // a char array *constant* declared without an explicit size.
+ // Do not attempt to use it with a regular char* pointer, or with a
+ // non-constant char array variable. Use EqualsASCII for them.
+ // The template trick to acquire the array length at compile time without
+ // using a macro is due to Corey Kosak, with much thanks.
+ template<int N>
+ inline bool EqualsLiteral(const char (&aStr)[N]) const
+ {
+ return EqualsASCII(aStr, N - 1);
+ }
+
+ // The LowerCaseEquals methods compare the ASCII-lowercase version of
+ // this string (lowercasing only ASCII uppercase characters) to some
+ // ASCII/Literal string. The ASCII string is *not* lowercased for
+ // you. If you compare to an ASCII or literal string that contains an
+ // uppercase character, it is guaranteed to return false. We will
+ // throw assertions too.
+ bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData,
+ size_type aLen) const;
+ bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData) const;
+
+ // LowerCaseEqualsLiteral must ONLY be applied to an actual
+ // literal string, or a char array *constant* declared without an
+ // explicit size. Do not attempt to use it with a regular char*
+ // pointer, or with a non-constant char array variable. Use
+ // LowerCaseEqualsASCII for them.
+ template<int N>
+ inline bool LowerCaseEqualsLiteral(const char (&aStr)[N]) const
+ {
+ return LowerCaseEqualsASCII(aStr, N - 1);
+ }
+
+ /**
+ * assignment
+ */
+
+ void NS_FASTCALL Assign(char_type aChar);
+ MOZ_MUST_USE bool NS_FASTCALL Assign(char_type aChar, const fallible_t&);
+
+ void NS_FASTCALL Assign(const char_type* aData);
+ MOZ_MUST_USE bool NS_FASTCALL Assign(const char_type* aData,
+ const fallible_t&);
+
+ void NS_FASTCALL Assign(const char_type* aData, size_type aLength);
+ MOZ_MUST_USE bool NS_FASTCALL Assign(const char_type* aData,
+ size_type aLength, const fallible_t&);
+
+ void NS_FASTCALL Assign(const self_type&);
+ MOZ_MUST_USE bool NS_FASTCALL Assign(const self_type&, const fallible_t&);
+
+ void NS_FASTCALL Assign(const substring_tuple_type&);
+ MOZ_MUST_USE bool NS_FASTCALL Assign(const substring_tuple_type&,
+ const fallible_t&);
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ void Assign(char16ptr_t aData)
+ {
+ Assign(static_cast<const char16_t*>(aData));
+ }
+
+ void Assign(char16ptr_t aData, size_type aLength)
+ {
+ Assign(static_cast<const char16_t*>(aData), aLength);
+ }
+
+ MOZ_MUST_USE bool Assign(char16ptr_t aData, size_type aLength,
+ const fallible_t& aFallible)
+ {
+ return Assign(static_cast<const char16_t*>(aData), aLength,
+ aFallible);
+ }
+#endif
+
+ void NS_FASTCALL AssignASCII(const char* aData, size_type aLength);
+ MOZ_MUST_USE bool NS_FASTCALL AssignASCII(const char* aData,
+ size_type aLength,
+ const fallible_t&);
+
+ void NS_FASTCALL AssignASCII(const char* aData)
+ {
+ AssignASCII(aData, mozilla::AssertedCast<size_type, size_t>(strlen(aData)));
+ }
+ MOZ_MUST_USE bool NS_FASTCALL AssignASCII(const char* aData,
+ const fallible_t& aFallible)
+ {
+ return AssignASCII(aData,
+ mozilla::AssertedCast<size_type, size_t>(strlen(aData)),
+ aFallible);
+ }
+
+ // AssignLiteral must ONLY be applied to an actual literal string, or
+ // a char array *constant* declared without an explicit size.
+ // Do not attempt to use it with a regular char* pointer, or with a
+ // non-constant char array variable. Use AssignASCII for those.
+ // There are not fallible version of these methods because they only really
+ // apply to small allocations that we wouldn't want to check anyway.
+ template<int N>
+ void AssignLiteral(const char_type (&aStr)[N])
+ {
+ AssignLiteral(aStr, N - 1);
+ }
+#ifdef CharT_is_PRUnichar
+ template<int N>
+ void AssignLiteral(const char (&aStr)[N])
+ {
+ AssignASCII(aStr, N - 1);
+ }
+#endif
+
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+ self_type& operator=(const char_type* aData)
+ {
+ Assign(aData);
+ return *this;
+ }
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ self_type& operator=(char16ptr_t aData)
+ {
+ Assign(aData);
+ return *this;
+ }
+#endif
+ self_type& operator=(const self_type& aStr)
+ {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple)
+ {
+ Assign(aTuple);
+ return *this;
+ }
+
+ void NS_FASTCALL Adopt(char_type* aData, size_type aLength = size_type(-1));
+
+
+ /**
+ * buffer manipulation
+ */
+
+ void NS_FASTCALL Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar);
+ MOZ_MUST_USE bool NS_FASTCALL Replace(index_type aCutStart,
+ size_type aCutLength,
+ char_type aChar,
+ const fallible_t&);
+ void NS_FASTCALL Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData,
+ size_type aLength = size_type(-1));
+ MOZ_MUST_USE bool NS_FASTCALL Replace(index_type aCutStart,
+ size_type aCutLength,
+ const char_type* aData,
+ size_type aLength,
+ const fallible_t&);
+ void Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aStr)
+ {
+ Replace(aCutStart, aCutLength, aStr.Data(), aStr.Length());
+ }
+ MOZ_MUST_USE bool Replace(index_type aCutStart,
+ size_type aCutLength,
+ const self_type& aStr,
+ const fallible_t& aFallible)
+ {
+ return Replace(aCutStart, aCutLength, aStr.Data(), aStr.Length(),
+ aFallible);
+ }
+ void NS_FASTCALL Replace(index_type aCutStart, size_type aCutLength,
+ const substring_tuple_type& aTuple);
+
+ void NS_FASTCALL ReplaceASCII(index_type aCutStart, size_type aCutLength,
+ const char* aData,
+ size_type aLength = size_type(-1));
+
+ MOZ_MUST_USE bool NS_FASTCALL ReplaceASCII(index_type aCutStart, size_type aCutLength,
+ const char* aData,
+ size_type aLength,
+ const fallible_t&);
+
+ // ReplaceLiteral must ONLY be applied to an actual literal string.
+ // Do not attempt to use it with a regular char* pointer, or with a char
+ // array variable. Use Replace or ReplaceASCII for those.
+ template<int N>
+ void ReplaceLiteral(index_type aCutStart, size_type aCutLength,
+ const char_type (&aStr)[N])
+ {
+ ReplaceLiteral(aCutStart, aCutLength, aStr, N - 1);
+ }
+
+ void Append(char_type aChar)
+ {
+ Replace(mLength, 0, aChar);
+ }
+ MOZ_MUST_USE bool Append(char_type aChar, const fallible_t& aFallible)
+ {
+ return Replace(mLength, 0, aChar, aFallible);
+ }
+ void Append(const char_type* aData, size_type aLength = size_type(-1))
+ {
+ Replace(mLength, 0, aData, aLength);
+ }
+ MOZ_MUST_USE bool Append(const char_type* aData, size_type aLength,
+ const fallible_t& aFallible)
+ {
+ return Replace(mLength, 0, aData, aLength, aFallible);
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ void Append(char16ptr_t aData, size_type aLength = size_type(-1))
+ {
+ Append(static_cast<const char16_t*>(aData), aLength);
+ }
+#endif
+
+ void Append(const self_type& aStr)
+ {
+ Replace(mLength, 0, aStr);
+ }
+ MOZ_MUST_USE bool Append(const self_type& aStr, const fallible_t& aFallible)
+ {
+ return Replace(mLength, 0, aStr, aFallible);
+ }
+ void Append(const substring_tuple_type& aTuple)
+ {
+ Replace(mLength, 0, aTuple);
+ }
+
+ void AppendASCII(const char* aData, size_type aLength = size_type(-1))
+ {
+ ReplaceASCII(mLength, 0, aData, aLength);
+ }
+
+ MOZ_MUST_USE bool AppendASCII(const char* aData, const fallible_t& aFallible)
+ {
+ return ReplaceASCII(mLength, 0, aData, size_type(-1), aFallible);
+ }
+
+ MOZ_MUST_USE bool AppendASCII(const char* aData, size_type aLength, const fallible_t& aFallible)
+ {
+ return ReplaceASCII(mLength, 0, aData, aLength, aFallible);
+ }
+
+ /**
+ * Append a formatted string to the current string. Uses the format
+ * codes documented in prprf.h
+ */
+ void AppendPrintf(const char* aFormat, ...);
+ void AppendPrintf(const char* aFormat, va_list aAp);
+ void AppendInt(int32_t aInteger)
+ {
+ AppendPrintf("%d", aInteger);
+ }
+ void AppendInt(int32_t aInteger, int aRadix)
+ {
+ const char* fmt = aRadix == 10 ? "%d" : aRadix == 8 ? "%o" : "%x";
+ AppendPrintf(fmt, aInteger);
+ }
+ void AppendInt(uint32_t aInteger)
+ {
+ AppendPrintf("%u", aInteger);
+ }
+ void AppendInt(uint32_t aInteger, int aRadix)
+ {
+ const char* fmt = aRadix == 10 ? "%u" : aRadix == 8 ? "%o" : "%x";
+ AppendPrintf(fmt, aInteger);
+ }
+ void AppendInt(int64_t aInteger)
+ {
+ AppendPrintf("%lld", aInteger);
+ }
+ void AppendInt(int64_t aInteger, int aRadix)
+ {
+ const char* fmt = aRadix == 10 ? "%lld" : aRadix == 8 ? "%llo" : "%llx";
+ AppendPrintf(fmt, aInteger);
+ }
+ void AppendInt(uint64_t aInteger)
+ {
+ AppendPrintf("%llu", aInteger);
+ }
+ void AppendInt(uint64_t aInteger, int aRadix)
+ {
+ const char* fmt = aRadix == 10 ? "%llu" : aRadix == 8 ? "%llo" : "%llx";
+ AppendPrintf(fmt, aInteger);
+ }
+
+ /**
+ * Append the given float to this string
+ */
+ void NS_FASTCALL AppendFloat(float aFloat);
+ void NS_FASTCALL AppendFloat(double aFloat);
+public:
+
+ // AppendLiteral must ONLY be applied to an actual literal string.
+ // Do not attempt to use it with a regular char* pointer, or with a char
+ // array variable. Use Append or AppendASCII for those.
+ template<int N>
+ void AppendLiteral(const char_type (&aStr)[N])
+ {
+ ReplaceLiteral(mLength, 0, aStr, N - 1);
+ }
+#ifdef CharT_is_PRUnichar
+ template<int N>
+ void AppendLiteral(const char (&aStr)[N])
+ {
+ AppendASCII(aStr, N - 1);
+ }
+
+ template<int N>
+ MOZ_MUST_USE bool AppendLiteral(const char (&aStr)[N], const fallible_t& aFallible)
+ {
+ return AppendASCII(aStr, N - 1, aFallible);
+ }
+#endif
+
+ self_type& operator+=(char_type aChar)
+ {
+ Append(aChar);
+ return *this;
+ }
+ self_type& operator+=(const char_type* aData)
+ {
+ Append(aData);
+ return *this;
+ }
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ self_type& operator+=(char16ptr_t aData)
+ {
+ Append(aData);
+ return *this;
+ }
+#endif
+ self_type& operator+=(const self_type& aStr)
+ {
+ Append(aStr);
+ return *this;
+ }
+ self_type& operator+=(const substring_tuple_type& aTuple)
+ {
+ Append(aTuple);
+ return *this;
+ }
+
+ void Insert(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 0, aChar);
+ }
+ void Insert(const char_type* aData, index_type aPos,
+ size_type aLength = size_type(-1))
+ {
+ Replace(aPos, 0, aData, aLength);
+ }
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ void Insert(char16ptr_t aData, index_type aPos,
+ size_type aLength = size_type(-1))
+ {
+ Insert(static_cast<const char16_t*>(aData), aPos, aLength);
+ }
+#endif
+ void Insert(const self_type& aStr, index_type aPos)
+ {
+ Replace(aPos, 0, aStr);
+ }
+ void Insert(const substring_tuple_type& aTuple, index_type aPos)
+ {
+ Replace(aPos, 0, aTuple);
+ }
+
+ // InsertLiteral must ONLY be applied to an actual literal string.
+ // Do not attempt to use it with a regular char* pointer, or with a char
+ // array variable. Use Insert for those.
+ template<int N>
+ void InsertLiteral(const char_type (&aStr)[N], index_type aPos)
+ {
+ ReplaceLiteral(aPos, 0, aStr, N - 1);
+ }
+
+ void Cut(index_type aCutStart, size_type aCutLength)
+ {
+ Replace(aCutStart, aCutLength, char_traits::sEmptyBuffer, 0);
+ }
+
+
+ /**
+ * buffer sizing
+ */
+
+ /**
+ * Attempts to set the capacity to the given size in number of
+ * characters, without affecting the length of the string.
+ * There is no need to include room for the null terminator: it is
+ * the job of the string class.
+ * Also ensures that the buffer is mutable.
+ */
+ void NS_FASTCALL SetCapacity(size_type aNewCapacity);
+ MOZ_MUST_USE bool NS_FASTCALL SetCapacity(size_type aNewCapacity,
+ const fallible_t&);
+
+ void NS_FASTCALL SetLength(size_type aNewLength);
+ MOZ_MUST_USE bool NS_FASTCALL SetLength(size_type aNewLength,
+ const fallible_t&);
+
+ void Truncate(size_type aNewLength = 0)
+ {
+ NS_ASSERTION(aNewLength <= mLength, "Truncate cannot make string longer");
+ SetLength(aNewLength);
+ }
+
+
+ /**
+ * buffer access
+ */
+
+
+ /**
+ * Get a const pointer to the string's internal buffer. The caller
+ * MUST NOT modify the characters at the returned address.
+ *
+ * @returns The length of the buffer in characters.
+ */
+ inline size_type GetData(const char_type** aData) const
+ {
+ *aData = mData;
+ return mLength;
+ }
+
+ /**
+ * Get a pointer to the string's internal buffer, optionally resizing
+ * the buffer first. If size_type(-1) is passed for newLen, then the
+ * current length of the string is used. The caller MAY modify the
+ * characters at the returned address (up to but not exceeding the
+ * length of the string).
+ *
+ * @returns The length of the buffer in characters or 0 if unable to
+ * satisfy the request due to low-memory conditions.
+ */
+ size_type GetMutableData(char_type** aData, size_type aNewLen = size_type(-1))
+ {
+ if (!EnsureMutable(aNewLen)) {
+ AllocFailed(aNewLen == size_type(-1) ? mLength : aNewLen);
+ }
+
+ *aData = mData;
+ return mLength;
+ }
+
+ size_type GetMutableData(char_type** aData, size_type aNewLen, const fallible_t&)
+ {
+ if (!EnsureMutable(aNewLen)) {
+ *aData = nullptr;
+ return 0;
+ }
+
+ *aData = mData;
+ return mLength;
+ }
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+ size_type GetMutableData(wchar_t** aData, size_type aNewLen = size_type(-1))
+ {
+ return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen);
+ }
+
+ size_type GetMutableData(wchar_t** aData, size_type aNewLen,
+ const fallible_t& aFallible)
+ {
+ return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen,
+ aFallible);
+ }
+#endif
+
+
+ /**
+ * string data is never null, but can be marked void. if true, the
+ * string will be truncated. @see nsTSubstring::IsVoid
+ */
+
+ void NS_FASTCALL SetIsVoid(bool);
+
+ /**
+ * This method is used to remove all occurrences of aChar from this
+ * string.
+ *
+ * @param aChar -- char to be stripped
+ * @param aOffset -- where in this string to start stripping chars
+ */
+
+ void StripChar(char_type aChar, int32_t aOffset = 0);
+
+ /**
+ * This method is used to remove all occurrences of aChars from this
+ * string.
+ *
+ * @param aChars -- chars to be stripped
+ * @param aOffset -- where in this string to start stripping chars
+ */
+
+ void StripChars(const char_type* aChars, uint32_t aOffset = 0);
+
+ /**
+ * If the string uses a shared buffer, this method
+ * clears the pointer without releasing the buffer.
+ */
+ void ForgetSharedBuffer()
+ {
+ if (mFlags & nsSubstring::F_SHARED) {
+ mData = char_traits::sEmptyBuffer;
+ mLength = 0;
+ mFlags = F_TERMINATED;
+ }
+ }
+
+public:
+
+ /**
+ * this is public to support automatic conversion of tuple to string
+ * base type, which helps avoid converting to nsTAString.
+ */
+ MOZ_IMPLICIT nsTSubstring_CharT(const substring_tuple_type& aTuple)
+ : mData(nullptr)
+ , mLength(0)
+ , mFlags(F_NONE)
+ {
+ Assign(aTuple);
+ }
+
+ /**
+ * allows for direct initialization of a nsTSubstring object.
+ *
+ * NOTE: this constructor is declared public _only_ for convenience
+ * inside the string implementation.
+ */
+ // XXXbz or can I just include nscore.h and use NS_BUILD_REFCNT_LOGGING?
+#if defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)
+#define XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
+ nsTSubstring_CharT(char_type* aData, size_type aLength, uint32_t aFlags);
+#else
+#undef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
+ nsTSubstring_CharT(char_type* aData, size_type aLength, uint32_t aFlags)
+ : mData(aData)
+ , mLength(aLength)
+ , mFlags(aFlags)
+ {
+ MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
+ }
+#endif /* DEBUG || FORCE_BUILD_REFCNT_LOGGING */
+
+ size_t SizeOfExcludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf)
+ const;
+ size_t SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf)
+ const;
+
+ /**
+ * WARNING: Only use these functions if you really know what you are
+ * doing, because they can easily lead to double-counting strings. If
+ * you do use them, please explain clearly in a comment why it's safe
+ * and won't lead to double-counting.
+ */
+ size_t SizeOfExcludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf)
+ const;
+ size_t SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf)
+ const;
+
+ template<class T>
+ void NS_ABORT_OOM(T)
+ {
+ struct never {}; // a compiler-friendly way to do static_assert(false)
+ static_assert(mozilla::IsSame<T, never>::value,
+ "In string classes, use AllocFailed to account for sizeof(char_type). "
+ "Use the global ::NS_ABORT_OOM if you really have a count of bytes.");
+ }
+
+ MOZ_ALWAYS_INLINE void AllocFailed(size_t aLength)
+ {
+ ::NS_ABORT_OOM(aLength * sizeof(char_type));
+ }
+
+protected:
+
+ friend class nsTObsoleteAStringThunk_CharT;
+ friend class nsTSubstringTuple_CharT;
+
+ // XXX GCC 3.4 needs this :-(
+ friend class nsTPromiseFlatString_CharT;
+
+ char_type* mData;
+ size_type mLength;
+ uint32_t mFlags;
+
+ // default initialization
+ nsTSubstring_CharT()
+ : mData(char_traits::sEmptyBuffer)
+ , mLength(0)
+ , mFlags(F_TERMINATED)
+ {
+ }
+
+ // version of constructor that leaves mData and mLength uninitialized
+ explicit
+ nsTSubstring_CharT(uint32_t aFlags)
+ : mFlags(aFlags)
+ {
+ }
+
+ // copy-constructor, constructs as dependent on given object
+ // (NOTE: this is for internal use only)
+ nsTSubstring_CharT(const self_type& aStr)
+ : mData(aStr.mData)
+ , mLength(aStr.mLength)
+ , mFlags(aStr.mFlags & (F_TERMINATED | F_VOIDED))
+ {
+ }
+
+ /**
+ * this function releases mData and does not change the value of
+ * any of its member variables. in other words, this function acts
+ * like a destructor.
+ */
+ void NS_FASTCALL Finalize();
+
+ /**
+ * this function prepares mData to be mutated.
+ *
+ * @param aCapacity specifies the required capacity of mData
+ * @param aOldData returns null or the old value of mData
+ * @param aOldFlags returns 0 or the old value of mFlags
+ *
+ * if mData is already mutable and of sufficient capacity, then this
+ * function will return immediately. otherwise, it will either resize
+ * mData or allocate a new shared buffer. if it needs to allocate a
+ * new buffer, then it will return the old buffer and the corresponding
+ * flags. this allows the caller to decide when to free the old data.
+ *
+ * this function returns false if is unable to allocate sufficient
+ * memory.
+ *
+ * XXX we should expose a way for subclasses to free old_data.
+ */
+ bool NS_FASTCALL MutatePrep(size_type aCapacity,
+ char_type** aOldData, uint32_t* aOldFlags);
+
+ /**
+ * this function prepares a section of mData to be modified. if
+ * necessary, this function will reallocate mData and possibly move
+ * existing data to open up the specified section.
+ *
+ * @param aCutStart specifies the starting offset of the section
+ * @param aCutLength specifies the length of the section to be replaced
+ * @param aNewLength specifies the length of the new section
+ *
+ * for example, suppose mData contains the string "abcdef" then
+ *
+ * ReplacePrep(2, 3, 4);
+ *
+ * would cause mData to look like "ab____f" where the characters
+ * indicated by '_' have an unspecified value and can be freely
+ * modified. this function will null-terminate mData upon return.
+ *
+ * this function returns false if is unable to allocate sufficient
+ * memory.
+ */
+ MOZ_MUST_USE bool ReplacePrep(index_type aCutStart,
+ size_type aCutLength,
+ size_type aNewLength);
+
+ MOZ_MUST_USE bool NS_FASTCALL ReplacePrepInternal(
+ index_type aCutStart,
+ size_type aCutLength,
+ size_type aNewFragLength,
+ size_type aNewTotalLength);
+
+ /**
+ * returns the number of writable storage units starting at mData.
+ * the value does not include space for the null-terminator character.
+ *
+ * NOTE: this function returns 0 if mData is immutable (or the buffer
+ * is 0-sized).
+ */
+ size_type NS_FASTCALL Capacity() const;
+
+ /**
+ * this helper function can be called prior to directly manipulating
+ * the contents of mData. see, for example, BeginWriting.
+ */
+ MOZ_MUST_USE bool NS_FASTCALL EnsureMutable(
+ size_type aNewLen = size_type(-1));
+
+ /**
+ * returns true if this string overlaps with the given string fragment.
+ */
+ bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const
+ {
+ /**
+ * if it _isn't_ the case that one fragment starts after the other ends,
+ * or ends before the other starts, then, they conflict:
+ *
+ * !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
+ *
+ * Simplified, that gives us:
+ */
+ return (aStart < (mData + mLength) && aEnd > mData);
+ }
+
+ /**
+ * Checks if the given capacity is valid for this string type.
+ */
+ static MOZ_MUST_USE bool CheckCapacity(size_type aCapacity) {
+ if (aCapacity > kMaxCapacity) {
+ // Also assert for |aCapacity| equal to |size_type(-1)|, since we used to
+ // use that value to flag immutability.
+ NS_ASSERTION(aCapacity != size_type(-1), "Bogus capacity");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * this helper function stores the specified dataFlags in mFlags
+ */
+ void SetDataFlags(uint32_t aDataFlags)
+ {
+ NS_ASSERTION((aDataFlags & 0xFFFF0000) == 0, "bad flags");
+ mFlags = aDataFlags | (mFlags & 0xFFFF0000);
+ }
+
+ void NS_FASTCALL ReplaceLiteral(index_type aCutStart, size_type aCutLength,
+ const char_type* aData, size_type aLength);
+
+ static int AppendFunc(void* aArg, const char* aStr, uint32_t aLen);
+
+ static const size_type kMaxCapacity;
+public:
+
+ // NOTE: this method is declared public _only_ for convenience for
+ // callers who don't have access to the original nsLiteralString_CharT.
+ void NS_FASTCALL AssignLiteral(const char_type* aData, size_type aLength);
+
+ // mFlags is a bitwise combination of the following flags. the meaning
+ // and interpretation of these flags is an implementation detail.
+ //
+ // NOTE: these flags are declared public _only_ for convenience inside
+ // the string implementation.
+
+ enum
+ {
+ F_NONE = 0, // no flags
+
+ // data flags are in the lower 16-bits
+ F_TERMINATED = 1 << 0, // IsTerminated returns true
+ F_VOIDED = 1 << 1, // IsVoid returns true
+ F_SHARED = 1 << 2, // mData points to a heap-allocated, shared buffer
+ F_OWNED = 1 << 3, // mData points to a heap-allocated, raw buffer
+ F_FIXED = 1 << 4, // mData points to a fixed-size writable, dependent buffer
+ F_LITERAL = 1 << 5, // mData points to a string literal; F_TERMINATED will also be set
+
+ // class flags are in the upper 16-bits
+ F_CLASS_FIXED = 1 << 16 // indicates that |this| is of type nsTFixedString
+ };
+
+ //
+ // Some terminology:
+ //
+ // "dependent buffer" A dependent buffer is one that the string class
+ // does not own. The string class relies on some
+ // external code to ensure the lifetime of the
+ // dependent buffer.
+ //
+ // "shared buffer" A shared buffer is one that the string class
+ // allocates. When it allocates a shared string
+ // buffer, it allocates some additional space at
+ // the beginning of the buffer for additional
+ // fields, including a reference count and a
+ // buffer length. See nsStringHeader.
+ //
+ // "adopted buffer" An adopted buffer is a raw string buffer
+ // allocated on the heap (using moz_xmalloc)
+ // of which the string class subsumes ownership.
+ //
+ // Some comments about the string flags:
+ //
+ // F_SHARED, F_OWNED, and F_FIXED are all mutually exlusive. They
+ // indicate the allocation type of mData. If none of these flags
+ // are set, then the string buffer is dependent.
+ //
+ // F_SHARED, F_OWNED, or F_FIXED imply F_TERMINATED. This is because
+ // the string classes always allocate null-terminated buffers, and
+ // non-terminated substrings are always dependent.
+ //
+ // F_VOIDED implies F_TERMINATED, and moreover it implies that mData
+ // points to char_traits::sEmptyBuffer. Therefore, F_VOIDED is
+ // mutually exclusive with F_SHARED, F_OWNED, and F_FIXED.
+ //
+};
+
+int NS_FASTCALL
+Compare(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs,
+ const nsTStringComparator_CharT& = nsTDefaultStringComparator_CharT());
+
+
+inline bool
+operator!=(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs)
+{
+ return !aLhs.Equals(aRhs);
+}
+
+inline bool
+operator!=(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::char_type* aRhs)
+{
+ return !aLhs.Equals(aRhs);
+}
+
+inline bool
+operator<(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs)
+{
+ return Compare(aLhs, aRhs) < 0;
+}
+
+inline bool
+operator<=(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs)
+{
+ return Compare(aLhs, aRhs) <= 0;
+}
+
+inline bool
+operator==(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs)
+{
+ return aLhs.Equals(aRhs);
+}
+
+inline bool
+operator==(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::char_type* aRhs)
+{
+ return aLhs.Equals(aRhs);
+}
+
+
+inline bool
+operator>=(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs)
+{
+ return Compare(aLhs, aRhs) >= 0;
+}
+
+inline bool
+operator>(const nsTSubstring_CharT::base_string_type& aLhs,
+ const nsTSubstring_CharT::base_string_type& aRhs)
+{
+ return Compare(aLhs, aRhs) > 0;
+}
diff --git a/xpcom/string/nsTSubstringTuple.cpp b/xpcom/string/nsTSubstringTuple.cpp
new file mode 100644
index 000000000..2a84a9a4e
--- /dev/null
+++ b/xpcom/string/nsTSubstringTuple.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/CheckedInt.h"
+
+/**
+ * computes the aggregate string length
+ */
+
+nsTSubstringTuple_CharT::size_type
+nsTSubstringTuple_CharT::Length() const
+{
+ mozilla::CheckedInt<size_type> len;
+ if (mHead) {
+ len = mHead->Length();
+ } else {
+ len = TO_SUBSTRING(mFragA).Length();
+ }
+
+ len += TO_SUBSTRING(mFragB).Length();
+ MOZ_RELEASE_ASSERT(len.isValid(), "Substring tuple length is invalid");
+ return len.value();
+}
+
+
+/**
+ * writes the aggregate string to the given buffer. aBufLen is assumed
+ * to be equal to or greater than the value returned by the Length()
+ * method. the string written to |aBuf| is not null-terminated.
+ */
+
+void
+nsTSubstringTuple_CharT::WriteTo(char_type* aBuf, uint32_t aBufLen) const
+{
+ const substring_type& b = TO_SUBSTRING(mFragB);
+
+ MOZ_RELEASE_ASSERT(aBufLen >= b.Length(), "buffer too small");
+ uint32_t headLen = aBufLen - b.Length();
+ if (mHead) {
+ mHead->WriteTo(aBuf, headLen);
+ } else {
+ const substring_type& a = TO_SUBSTRING(mFragA);
+
+ MOZ_RELEASE_ASSERT(a.Length() == headLen, "buffer incorrectly sized");
+ char_traits::copy(aBuf, a.Data(), a.Length());
+ }
+
+ char_traits::copy(aBuf + headLen, b.Data(), b.Length());
+
+#if 0
+ // we need to write out data into |aBuf|, ending at |aBuf + aBufLen|. So our
+ // data needs to precede |aBuf + aBufLen| exactly. We trust that the buffer
+ // was properly sized!
+
+ const substring_type& b = TO_SUBSTRING(mFragB);
+
+ NS_ASSERTION(aBufLen >= b.Length(), "buffer is too small");
+ char_traits::copy(aBuf + aBufLen - b.Length(), b.Data(), b.Length());
+
+ aBufLen -= b.Length();
+
+ if (mHead) {
+ mHead->WriteTo(aBuf, aBufLen);
+ } else {
+ const substring_type& a = TO_SUBSTRING(mFragA);
+ NS_ASSERTION(aBufLen == a.Length(), "buffer is too small");
+ char_traits::copy(aBuf, a.Data(), a.Length());
+ }
+#endif
+}
+
+
+/**
+ * returns true if this tuple is dependent on (i.e., overlapping with)
+ * the given char sequence.
+ */
+
+bool
+nsTSubstringTuple_CharT::IsDependentOn(const char_type* aStart,
+ const char_type* aEnd) const
+{
+ // we aStart with the right-most fragment since it is faster to check.
+
+ if (TO_SUBSTRING(mFragB).IsDependentOn(aStart, aEnd)) {
+ return true;
+ }
+
+ if (mHead) {
+ return mHead->IsDependentOn(aStart, aEnd);
+ }
+
+ return TO_SUBSTRING(mFragA).IsDependentOn(aStart, aEnd);
+}
diff --git a/xpcom/string/nsTSubstringTuple.h b/xpcom/string/nsTSubstringTuple.h
new file mode 100644
index 000000000..5d24e2159
--- /dev/null
+++ b/xpcom/string/nsTSubstringTuple.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+/**
+ * nsTSubstringTuple_CharT
+ *
+ * Represents a tuple of string fragments. Built as a recursive binary tree.
+ * It is used to implement the concatenation of two or more string objects.
+ *
+ * NOTE: This class is a private implementation detail and should never be
+ * referenced outside the string code.
+ */
+class nsTSubstringTuple_CharT
+{
+public:
+
+ typedef CharT char_type;
+ typedef nsCharTraits<char_type> char_traits;
+
+ typedef nsTSubstringTuple_CharT self_type;
+ typedef nsTSubstring_CharT substring_type;
+ typedef nsTSubstring_CharT base_string_type;
+ typedef uint32_t size_type;
+
+public:
+
+ nsTSubstringTuple_CharT(const base_string_type* aStrA,
+ const base_string_type* aStrB)
+ : mHead(nullptr)
+ , mFragA(aStrA)
+ , mFragB(aStrB)
+ {
+ }
+
+ nsTSubstringTuple_CharT(const self_type& aHead,
+ const base_string_type* aStrB)
+ : mHead(&aHead)
+ , mFragA(nullptr) // this fragment is ignored when aHead != nullptr
+ , mFragB(aStrB)
+ {
+ }
+
+ /**
+ * computes the aggregate string length
+ */
+ size_type Length() const;
+
+ /**
+ * writes the aggregate string to the given buffer. bufLen is assumed
+ * to be equal to or greater than the value returned by the Length()
+ * method. the string written to |buf| is not null-terminated.
+ */
+ void WriteTo(char_type* aBuf, uint32_t aBufLen) const;
+
+ /**
+ * returns true if this tuple is dependent on (i.e., overlapping with)
+ * the given char sequence.
+ */
+ bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const;
+
+private:
+
+ const self_type* mHead;
+ const base_string_type* mFragA;
+ const base_string_type* mFragB;
+};
+
+inline const nsTSubstringTuple_CharT
+operator+(const nsTSubstringTuple_CharT::base_string_type& aStrA,
+ const nsTSubstringTuple_CharT::base_string_type& aStrB)
+{
+ return nsTSubstringTuple_CharT(&aStrA, &aStrB);
+}
+
+inline const nsTSubstringTuple_CharT
+operator+(const nsTSubstringTuple_CharT& aHead,
+ const nsTSubstringTuple_CharT::base_string_type& aStrB)
+{
+ return nsTSubstringTuple_CharT(aHead, &aStrB);
+}
diff --git a/xpcom/string/nsUTF8Utils.h b/xpcom/string/nsUTF8Utils.h
new file mode 100644
index 000000000..9f38fa555
--- /dev/null
+++ b/xpcom/string/nsUTF8Utils.h
@@ -0,0 +1,742 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsUTF8Utils_h_
+#define nsUTF8Utils_h_
+
+// This file may be used in two ways: if MOZILLA_INTERNAL_API is defined, this
+// file will provide signatures for the Mozilla abstract string types. It will
+// use XPCOM assertion/debugging macros, etc.
+
+#include "nscore.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/SSE.h"
+#include "mozilla/TypeTraits.h"
+
+#include "nsCharTraits.h"
+
+class UTF8traits
+{
+public:
+ static bool isASCII(char aChar)
+ {
+ return (aChar & 0x80) == 0x00;
+ }
+ static bool isInSeq(char aChar)
+ {
+ return (aChar & 0xC0) == 0x80;
+ }
+ static bool is2byte(char aChar)
+ {
+ return (aChar & 0xE0) == 0xC0;
+ }
+ static bool is3byte(char aChar)
+ {
+ return (aChar & 0xF0) == 0xE0;
+ }
+ static bool is4byte(char aChar)
+ {
+ return (aChar & 0xF8) == 0xF0;
+ }
+ static bool is5byte(char aChar)
+ {
+ return (aChar & 0xFC) == 0xF8;
+ }
+ static bool is6byte(char aChar)
+ {
+ return (aChar & 0xFE) == 0xFC;
+ }
+};
+
+/**
+ * Extract the next UCS-4 character from the buffer and return it. The
+ * pointer passed in is advanced to the start of the next character in the
+ * buffer. If non-null, the parameters err and overlong are filled in to
+ * indicate that the character was represented by an overlong sequence, or
+ * that an error occurred.
+ */
+
+class UTF8CharEnumerator
+{
+public:
+ static uint32_t NextChar(const char** aBuffer, const char* aEnd, bool* aErr)
+ {
+ NS_ASSERTION(aBuffer && *aBuffer, "null buffer!");
+
+ const char* p = *aBuffer;
+ *aErr = false;
+
+ if (p >= aEnd) {
+ *aErr = true;
+
+ return 0;
+ }
+
+ char c = *p++;
+
+ if (UTF8traits::isASCII(c)) {
+ *aBuffer = p;
+ return c;
+ }
+
+ uint32_t ucs4;
+ uint32_t minUcs4;
+ int32_t state = 0;
+
+ if (!CalcState(c, ucs4, minUcs4, state)) {
+ NS_ERROR("Not a UTF-8 string. This code should only be used for converting from known UTF-8 strings.");
+ *aErr = true;
+
+ return 0;
+ }
+
+ while (state--) {
+ if (p == aEnd) {
+ *aErr = true;
+
+ return 0;
+ }
+
+ c = *p++;
+
+ if (!AddByte(c, state, ucs4)) {
+ *aErr = true;
+
+ return 0;
+ }
+ }
+
+ if (ucs4 < minUcs4) {
+ // Overlong sequence
+ ucs4 = UCS2_REPLACEMENT_CHAR;
+ } else if (ucs4 >= 0xD800 &&
+ (ucs4 <= 0xDFFF || ucs4 >= UCS_END)) {
+ // Surrogates and code points outside the Unicode range.
+ ucs4 = UCS2_REPLACEMENT_CHAR;
+ }
+
+ *aBuffer = p;
+ return ucs4;
+ }
+
+private:
+ static bool CalcState(char aChar, uint32_t& aUcs4, uint32_t& aMinUcs4,
+ int32_t& aState)
+ {
+ if (UTF8traits::is2byte(aChar)) {
+ aUcs4 = (uint32_t(aChar) << 6) & 0x000007C0L;
+ aState = 1;
+ aMinUcs4 = 0x00000080;
+ } else if (UTF8traits::is3byte(aChar)) {
+ aUcs4 = (uint32_t(aChar) << 12) & 0x0000F000L;
+ aState = 2;
+ aMinUcs4 = 0x00000800;
+ } else if (UTF8traits::is4byte(aChar)) {
+ aUcs4 = (uint32_t(aChar) << 18) & 0x001F0000L;
+ aState = 3;
+ aMinUcs4 = 0x00010000;
+ } else if (UTF8traits::is5byte(aChar)) {
+ aUcs4 = (uint32_t(aChar) << 24) & 0x03000000L;
+ aState = 4;
+ aMinUcs4 = 0x00200000;
+ } else if (UTF8traits::is6byte(aChar)) {
+ aUcs4 = (uint32_t(aChar) << 30) & 0x40000000L;
+ aState = 5;
+ aMinUcs4 = 0x04000000;
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool AddByte(char aChar, int32_t aState, uint32_t& aUcs4)
+ {
+ if (UTF8traits::isInSeq(aChar)) {
+ int32_t shift = aState * 6;
+ aUcs4 |= (uint32_t(aChar) & 0x3F) << shift;
+ return true;
+ }
+
+ return false;
+ }
+};
+
+
+/**
+ * Extract the next UCS-4 character from the buffer and return it. The
+ * pointer passed in is advanced to the start of the next character in the
+ * buffer. If non-null, the err parameter is filled in if an error occurs.
+ *
+ * If an error occurs that causes UCS2_REPLACEMENT_CHAR to be returned, then
+ * the buffer will be updated to move only a single UCS-2 character.
+ *
+ * Any other error returns 0 and does not move the buffer position.
+ */
+
+
+class UTF16CharEnumerator
+{
+public:
+ static uint32_t NextChar(const char16_t** aBuffer, const char16_t* aEnd,
+ bool* aErr = nullptr)
+ {
+ NS_ASSERTION(aBuffer && *aBuffer, "null buffer!");
+
+ const char16_t* p = *aBuffer;
+
+ if (p >= aEnd) {
+ NS_ERROR("No input to work with");
+ if (aErr) {
+ *aErr = true;
+ }
+
+ return 0;
+ }
+
+ char16_t c = *p++;
+
+ if (!IS_SURROGATE(c)) { // U+0000 - U+D7FF,U+E000 - U+FFFF
+ if (aErr) {
+ *aErr = false;
+ }
+ *aBuffer = p;
+ return c;
+ } else if (NS_IS_HIGH_SURROGATE(c)) { // U+D800 - U+DBFF
+ if (p == aEnd) {
+ // Found a high surrogate at the end of the buffer. Flag this
+ // as an error and return the Unicode replacement
+ // character 0xFFFD.
+
+ NS_WARNING("Unexpected end of buffer after high surrogate");
+
+ if (aErr) {
+ *aErr = true;
+ }
+ *aBuffer = p;
+ return 0xFFFD;
+ }
+
+ // D800- DBFF - High Surrogate
+ char16_t h = c;
+
+ c = *p++;
+
+ if (NS_IS_LOW_SURROGATE(c)) {
+ // DC00- DFFF - Low Surrogate
+ // N = (H - D800) *400 + 10000 + (L - DC00)
+ uint32_t ucs4 = SURROGATE_TO_UCS4(h, c);
+ if (aErr) {
+ *aErr = false;
+ }
+ *aBuffer = p;
+ return ucs4;
+ } else {
+ // Found a high surrogate followed by something other than
+ // a low surrogate. Flag this as an error and return the
+ // Unicode replacement character 0xFFFD. Note that the
+ // pointer to the next character points to the second 16-bit
+ // value, not beyond it, as per Unicode 5.0.0 Chapter 3 C10,
+ // only the first code unit of an illegal sequence must be
+ // treated as an illegally terminated code unit sequence
+ // (also Chapter 3 D91, "isolated [not paired and ill-formed]
+ // UTF-16 code units in the range D800..DFFF are ill-formed").
+ NS_WARNING("got a High Surrogate but no low surrogate");
+
+ if (aErr) {
+ *aErr = true;
+ }
+ *aBuffer = p - 1;
+ return 0xFFFD;
+ }
+ } else { // U+DC00 - U+DFFF
+ // DC00- DFFF - Low Surrogate
+
+ // Found a low surrogate w/o a preceding high surrogate. Flag
+ // this as an error and return the Unicode replacement
+ // character 0xFFFD.
+
+ NS_WARNING("got a low Surrogate but no high surrogate");
+ if (aErr) {
+ *aErr = true;
+ }
+ *aBuffer = p;
+ return 0xFFFD;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Impossible UCS-2 character value.");
+ }
+};
+
+
+/**
+ * A character sink (see |copy_string| in nsAlgorithm.h) for converting
+ * UTF-8 to UTF-16
+ */
+class ConvertUTF8toUTF16
+{
+public:
+ typedef char value_type;
+ typedef char16_t buffer_type;
+
+ explicit ConvertUTF8toUTF16(buffer_type* aBuffer)
+ : mStart(aBuffer), mBuffer(aBuffer), mErrorEncountered(false)
+ {
+ }
+
+ size_t Length() const
+ {
+ return mBuffer - mStart;
+ }
+
+ bool ErrorEncountered() const
+ {
+ return mErrorEncountered;
+ }
+
+ void write(const value_type* aStart, uint32_t aN)
+ {
+ if (mErrorEncountered) {
+ return;
+ }
+
+ // algorithm assumes utf8 units won't
+ // be spread across fragments
+ const value_type* p = aStart;
+ const value_type* end = aStart + aN;
+ buffer_type* out = mBuffer;
+ for (; p != end /* && *p */;) {
+ bool err;
+ uint32_t ucs4 = UTF8CharEnumerator::NextChar(&p, end, &err);
+
+ if (err) {
+ mErrorEncountered = true;
+ mBuffer = out;
+ return;
+ }
+
+ if (ucs4 >= PLANE1_BASE) {
+ *out++ = (buffer_type)H_SURROGATE(ucs4);
+ *out++ = (buffer_type)L_SURROGATE(ucs4);
+ } else {
+ *out++ = ucs4;
+ }
+ }
+ mBuffer = out;
+ }
+
+ void write_terminator()
+ {
+ *mBuffer = buffer_type(0);
+ }
+
+private:
+ buffer_type* const mStart;
+ buffer_type* mBuffer;
+ bool mErrorEncountered;
+};
+
+/**
+ * A character sink (see |copy_string| in nsAlgorithm.h) for computing
+ * the length of the UTF-16 string equivalent to a UTF-8 string.
+ */
+class CalculateUTF8Length
+{
+public:
+ typedef char value_type;
+
+ CalculateUTF8Length()
+ : mLength(0), mErrorEncountered(false)
+ {
+ }
+
+ size_t Length() const
+ {
+ return mLength;
+ }
+
+ void write(const value_type* aStart, uint32_t aN)
+ {
+ // ignore any further requests
+ if (mErrorEncountered) {
+ return;
+ }
+
+ // algorithm assumes utf8 units won't
+ // be spread across fragments
+ const value_type* p = aStart;
+ const value_type* end = aStart + aN;
+ for (; p < end /* && *p */; ++mLength) {
+ if (UTF8traits::isASCII(*p)) {
+ p += 1;
+ } else if (UTF8traits::is2byte(*p)) {
+ p += 2;
+ } else if (UTF8traits::is3byte(*p)) {
+ p += 3;
+ } else if (UTF8traits::is4byte(*p)) {
+ // Because a UTF-8 sequence of 4 bytes represents a codepoint
+ // greater than 0xFFFF, it will become a surrogate pair in the
+ // UTF-16 string, so add 1 more to mLength.
+ // This doesn't happen with is5byte and is6byte because they
+ // are illegal UTF-8 sequences (greater than 0x10FFFF) so get
+ // converted to a single replacement character.
+
+ // However, there is one case when a 4 byte UTF-8 sequence will
+ // only generate 2 UTF-16 bytes. If we have a properly encoded
+ // sequence, but with an invalid value (too small or too big),
+ // that will result in a replacement character being written
+ // This replacement character is encoded as just 1 single
+ // UTF-16 character, which is 2 bytes.
+
+ // The below code therefore only adds 1 to mLength if the UTF8
+ // data will produce a decoded character which is greater than
+ // or equal to 0x010000 and less than 0x0110000.
+
+ // A 4byte UTF8 character is encoded as
+ // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ // Bit 1-3 on the first byte, and bit 5-6 on the second byte,
+ // map to bit 17-21 in the final result. If these bits are
+ // between 0x01 and 0x11, that means that the final result is
+ // between 0x010000 and 0x110000. The below code reads these
+ // bits out and assigns them to c, but shifted up 4 bits to
+ // avoid having to shift twice.
+
+ // It doesn't matter what to do in the case where p + 4 > end
+ // since no UTF16 characters will be written in that case by
+ // ConvertUTF8toUTF16. Likewise it doesn't matter what we do if
+ // any of the surrogate bits are wrong since no UTF16
+ // characters will be written in that case either.
+
+ if (p + 4 <= end) {
+ uint32_t c = ((uint32_t)(p[0] & 0x07)) << 6 |
+ ((uint32_t)(p[1] & 0x30));
+ if (c >= 0x010 && c < 0x110) {
+ ++mLength;
+ }
+ }
+
+ p += 4;
+ } else if (UTF8traits::is5byte(*p)) {
+ p += 5;
+ } else if (UTF8traits::is6byte(*p)) {
+ p += 6;
+ } else { // error
+ ++mLength; // to account for the decrement below
+ break;
+ }
+ }
+ if (p != end) {
+ NS_ERROR("Not a UTF-8 string. This code should only be used for converting from known UTF-8 strings.");
+ --mLength; // The last multi-byte char wasn't complete, discard it.
+ mErrorEncountered = true;
+ }
+ }
+
+private:
+ size_t mLength;
+ bool mErrorEncountered;
+};
+
+/**
+ * A character sink (see |copy_string| in nsAlgorithm.h) for
+ * converting UTF-16 to UTF-8. Treats invalid UTF-16 data as 0xFFFD
+ * (0xEFBFBD in UTF-8).
+ */
+class ConvertUTF16toUTF8
+{
+public:
+ typedef char16_t value_type;
+ typedef char buffer_type;
+
+ // The error handling here is more lenient than that in
+ // |ConvertUTF8toUTF16|, but it's that way for backwards
+ // compatibility.
+
+ explicit ConvertUTF16toUTF8(buffer_type* aBuffer)
+ : mStart(aBuffer), mBuffer(aBuffer)
+ {
+ }
+
+ size_t Size() const
+ {
+ return mBuffer - mStart;
+ }
+
+ void write(const value_type* aStart, uint32_t aN)
+ {
+ buffer_type* out = mBuffer; // gcc isn't smart enough to do this!
+
+ for (const value_type* p = aStart, *end = aStart + aN; p < end; ++p) {
+ value_type c = *p;
+ if (!(c & 0xFF80)) { // U+0000 - U+007F
+ *out++ = (char)c;
+ } else if (!(c & 0xF800)) { // U+0100 - U+07FF
+ *out++ = 0xC0 | (char)(c >> 6);
+ *out++ = 0x80 | (char)(0x003F & c);
+ } else if (!IS_SURROGATE(c)) { // U+0800 - U+D7FF,U+E000 - U+FFFF
+ *out++ = 0xE0 | (char)(c >> 12);
+ *out++ = 0x80 | (char)(0x003F & (c >> 6));
+ *out++ = 0x80 | (char)(0x003F & c);
+ } else if (NS_IS_HIGH_SURROGATE(c)) { // U+D800 - U+DBFF
+ // D800- DBFF - High Surrogate
+ value_type h = c;
+
+ ++p;
+ if (p == end) {
+ // Treat broken characters as the Unicode
+ // replacement character 0xFFFD (0xEFBFBD in
+ // UTF-8)
+ *out++ = '\xEF';
+ *out++ = '\xBF';
+ *out++ = '\xBD';
+
+ NS_WARNING("String ending in half a surrogate pair!");
+
+ break;
+ }
+ c = *p;
+
+ if (NS_IS_LOW_SURROGATE(c)) {
+ // DC00- DFFF - Low Surrogate
+ // N = (H - D800) *400 + 10000 + ( L - DC00 )
+ uint32_t ucs4 = SURROGATE_TO_UCS4(h, c);
+
+ // 0001 0000-001F FFFF
+ *out++ = 0xF0 | (char)(ucs4 >> 18);
+ *out++ = 0x80 | (char)(0x003F & (ucs4 >> 12));
+ *out++ = 0x80 | (char)(0x003F & (ucs4 >> 6));
+ *out++ = 0x80 | (char)(0x003F & ucs4);
+ } else {
+ // Treat broken characters as the Unicode
+ // replacement character 0xFFFD (0xEFBFBD in
+ // UTF-8)
+ *out++ = '\xEF';
+ *out++ = '\xBF';
+ *out++ = '\xBD';
+
+ // The pointer to the next character points to the second
+ // 16-bit value, not beyond it, as per Unicode 5.0.0
+ // Chapter 3 C10, only the first code unit of an illegal
+ // sequence must be treated as an illegally terminated
+ // code unit sequence (also Chapter 3 D91, "isolated [not
+ // paired and ill-formed] UTF-16 code units in the range
+ // D800..DFFF are ill-formed").
+ p--;
+
+ NS_WARNING("got a High Surrogate but no low surrogate");
+ }
+ } else { // U+DC00 - U+DFFF
+ // Treat broken characters as the Unicode replacement
+ // character 0xFFFD (0xEFBFBD in UTF-8)
+ *out++ = '\xEF';
+ *out++ = '\xBF';
+ *out++ = '\xBD';
+
+ // DC00- DFFF - Low Surrogate
+ NS_WARNING("got a low Surrogate but no high surrogate");
+ }
+ }
+
+ mBuffer = out;
+ }
+
+ void write_terminator()
+ {
+ *mBuffer = buffer_type(0);
+ }
+
+private:
+ buffer_type* const mStart;
+ buffer_type* mBuffer;
+};
+
+/**
+ * A character sink (see |copy_string| in nsAlgorithm.h) for computing
+ * the number of bytes a UTF-16 would occupy in UTF-8. Treats invalid
+ * UTF-16 data as 0xFFFD (0xEFBFBD in UTF-8).
+ */
+class CalculateUTF8Size
+{
+public:
+ typedef char16_t value_type;
+
+ CalculateUTF8Size()
+ : mSize(0)
+ {
+ }
+
+ size_t Size() const
+ {
+ return mSize;
+ }
+
+ void write(const value_type* aStart, uint32_t aN)
+ {
+ // Assume UCS2 surrogate pairs won't be spread across fragments.
+ for (const value_type* p = aStart, *end = aStart + aN; p < end; ++p) {
+ value_type c = *p;
+ if (!(c & 0xFF80)) { // U+0000 - U+007F
+ mSize += 1;
+ } else if (!(c & 0xF800)) { // U+0100 - U+07FF
+ mSize += 2;
+ } else if (0xD800 != (0xF800 & c)) { // U+0800 - U+D7FF,U+E000 - U+FFFF
+ mSize += 3;
+ } else if (0xD800 == (0xFC00 & c)) { // U+D800 - U+DBFF
+ ++p;
+ if (p == end) {
+ // Treat broken characters as the Unicode
+ // replacement character 0xFFFD (0xEFBFBD in
+ // UTF-8)
+ mSize += 3;
+
+ NS_WARNING("String ending in half a surrogate pair!");
+
+ break;
+ }
+ c = *p;
+
+ if (0xDC00 == (0xFC00 & c)) {
+ mSize += 4;
+ } else {
+ // Treat broken characters as the Unicode
+ // replacement character 0xFFFD (0xEFBFBD in
+ // UTF-8)
+ mSize += 3;
+
+ // The next code unit is the second 16-bit value, not
+ // the one beyond it, as per Unicode 5.0.0 Chapter 3 C10,
+ // only the first code unit of an illegal sequence must
+ // be treated as an illegally terminated code unit
+ // sequence (also Chapter 3 D91, "isolated [not paired and
+ // ill-formed] UTF-16 code units in the range D800..DFFF
+ // are ill-formed").
+ p--;
+
+ NS_WARNING("got a high Surrogate but no low surrogate");
+ }
+ } else { // U+DC00 - U+DFFF
+ // Treat broken characters as the Unicode replacement
+ // character 0xFFFD (0xEFBFBD in UTF-8)
+ mSize += 3;
+
+ NS_WARNING("got a low Surrogate but no high surrogate");
+ }
+ }
+ }
+
+private:
+ size_t mSize;
+};
+
+#ifdef MOZILLA_INTERNAL_API
+/**
+ * A character sink that performs a |reinterpret_cast|-style conversion
+ * from char to char16_t.
+ */
+class LossyConvertEncoding8to16
+{
+public:
+ typedef char value_type;
+ typedef char input_type;
+ typedef char16_t output_type;
+
+public:
+ explicit LossyConvertEncoding8to16(char16_t* aDestination) :
+ mDestination(aDestination)
+ {
+ }
+
+ void
+ write(const char* aSource, uint32_t aSourceLength)
+ {
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ write_sse2(aSource, aSourceLength);
+ return;
+ }
+#endif
+ const char* done_writing = aSource + aSourceLength;
+ while (aSource < done_writing) {
+ *mDestination++ = (char16_t)(unsigned char)(*aSource++);
+ }
+ }
+
+ void
+ write_sse2(const char* aSource, uint32_t aSourceLength);
+
+ void
+ write_terminator()
+ {
+ *mDestination = (char16_t)(0);
+ }
+
+private:
+ char16_t* mDestination;
+};
+
+/**
+ * A character sink that performs a |reinterpret_cast|-style conversion
+ * from char16_t to char.
+ */
+class LossyConvertEncoding16to8
+{
+public:
+ typedef char16_t value_type;
+ typedef char16_t input_type;
+ typedef char output_type;
+
+ explicit LossyConvertEncoding16to8(char* aDestination)
+ : mDestination(aDestination)
+ {
+ }
+
+ void
+ write(const char16_t* aSource, uint32_t aSourceLength)
+ {
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ write_sse2(aSource, aSourceLength);
+ return;
+ }
+#endif
+ const char16_t* done_writing = aSource + aSourceLength;
+ while (aSource < done_writing) {
+ *mDestination++ = (char)(*aSource++);
+ }
+ }
+
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ void
+ write_sse2(const char16_t* aSource, uint32_t aSourceLength);
+#endif
+
+ void
+ write_terminator()
+ {
+ *mDestination = '\0';
+ }
+
+private:
+ char* mDestination;
+};
+#endif // MOZILLA_INTERNAL_API
+
+
+template<typename Char, typename UnsignedT>
+inline UnsignedT
+RewindToPriorUTF8Codepoint(const Char* utf8Chars, UnsignedT index)
+{
+ static_assert(mozilla::IsSame<Char, char>::value ||
+ mozilla::IsSame<Char, unsigned char>::value ||
+ mozilla::IsSame<Char, signed char>::value,
+ "UTF-8 data must be in 8-bit units");
+ static_assert(mozilla::IsUnsigned<UnsignedT>::value, "index type must be unsigned");
+ while (index > 0 && (utf8Chars[index] & 0xC0) == 0x80)
+ --index;
+
+ return index;
+}
+
+#endif /* !defined(nsUTF8Utils_h_) */
diff --git a/xpcom/string/nsUTF8UtilsSSE2.cpp b/xpcom/string/nsUTF8UtilsSSE2.cpp
new file mode 100644
index 000000000..daf2c56b0
--- /dev/null
+++ b/xpcom/string/nsUTF8UtilsSSE2.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nscore.h"
+#include "nsAlgorithm.h"
+#include <emmintrin.h>
+#include <nsUTF8Utils.h>
+
+void
+LossyConvertEncoding16to8::write_sse2(const char16_t* aSource,
+ uint32_t aSourceLength)
+{
+ char* dest = mDestination;
+
+ // Align source to a 16-byte boundary.
+ uint32_t i = 0;
+ uint32_t alignLen =
+ XPCOM_MIN<uint32_t>(aSourceLength,
+ uint32_t(-NS_PTR_TO_INT32(aSource) & 0xf) / sizeof(char16_t));
+ for (; i < alignLen; ++i) {
+ dest[i] = static_cast<unsigned char>(aSource[i]);
+ }
+
+ // Walk 64 bytes (four XMM registers) at a time.
+ __m128i vectmask = _mm_set1_epi16(0x00ff);
+ for (; aSourceLength - i > 31; i += 32) {
+ __m128i source1 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i));
+ source1 = _mm_and_si128(source1, vectmask);
+
+ __m128i source2 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 8));
+ source2 = _mm_and_si128(source2, vectmask);
+
+ __m128i source3 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 16));
+ source3 = _mm_and_si128(source3, vectmask);
+
+ __m128i source4 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 24));
+ source4 = _mm_and_si128(source4, vectmask);
+
+
+ // Pack the source data. SSE2 views this as a saturating uint16_t to
+ // uint8_t conversion, but since we masked off the high-order byte of every
+ // uint16_t, we're really just grabbing the low-order bytes of source1 and
+ // source2.
+ __m128i packed1 = _mm_packus_epi16(source1, source2);
+ __m128i packed2 = _mm_packus_epi16(source3, source4);
+
+ // This store needs to be unaligned since there's no guarantee that the
+ // alignment we did above for the source will align the destination.
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i), packed1);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 16), packed2);
+ }
+
+ // Finish up the rest.
+ for (; i < aSourceLength; ++i) {
+ dest[i] = static_cast<unsigned char>(aSource[i]);
+ }
+
+ mDestination += i;
+}
+
+void
+LossyConvertEncoding8to16::write_sse2(const char* aSource,
+ uint32_t aSourceLength)
+{
+ char16_t* dest = mDestination;
+
+ // Align source to a 16-byte boundary. We choose to align source rather than
+ // dest because we'd rather have our loads than our stores be fast. You have
+ // to wait for a load to complete, but you can keep on moving after issuing a
+ // store.
+ uint32_t i = 0;
+ uint32_t alignLen = XPCOM_MIN(aSourceLength,
+ uint32_t(-NS_PTR_TO_INT32(aSource) & 0xf));
+ for (; i < alignLen; ++i) {
+ dest[i] = static_cast<unsigned char>(aSource[i]);
+ }
+
+ // Walk 32 bytes (two XMM registers) at a time.
+ for (; aSourceLength - i > 31; i += 32) {
+ __m128i source1 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i));
+ __m128i source2 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 16));
+
+ // Interleave 0s in with the bytes of source to create lo and hi.
+ __m128i lo1 = _mm_unpacklo_epi8(source1, _mm_setzero_si128());
+ __m128i hi1 = _mm_unpackhi_epi8(source1, _mm_setzero_si128());
+ __m128i lo2 = _mm_unpacklo_epi8(source2, _mm_setzero_si128());
+ __m128i hi2 = _mm_unpackhi_epi8(source2, _mm_setzero_si128());
+
+ // store lo and hi into dest.
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i), lo1);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 8), hi1);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 16), lo2);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 24), hi2);
+ }
+
+ // Finish up whatever's left.
+ for (; i < aSourceLength; ++i) {
+ dest[i] = static_cast<unsigned char>(aSource[i]);
+ }
+
+ mDestination += i;
+}
diff --git a/xpcom/string/nsXPCOMStrings.h b/xpcom/string/nsXPCOMStrings.h
new file mode 100644
index 000000000..493e092d6
--- /dev/null
+++ b/xpcom/string/nsXPCOMStrings.h
@@ -0,0 +1,748 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOMStrings_h__
+#define nsXPCOMStrings_h__
+
+#include <string.h>
+#include "nscore.h"
+#include <limits>
+
+/**
+ * nsXPCOMStrings.h
+ *
+ * This file describes a minimal API for working with XPCOM's abstract
+ * string classes. It divorces the consumer from having any run-time
+ * dependency on the implementation details of the abstract string types.
+ */
+
+#include "nscore.h"
+
+/* The base string types */
+class nsAString;
+class nsACString;
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * nsStringContainer
+ *
+ * This is an opaque data type that is large enough to hold the canonical
+ * implementation of nsAString. The binary structure of this class is an
+ * implementation detail.
+ *
+ * The string data stored in a string container is always single fragment
+ * and may be null-terminated depending on how it is initialized.
+ *
+ * Typically, string containers are allocated on the stack for temporary
+ * use. However, they can also be malloc'd if necessary. In either case,
+ * a string container is not useful until it has been initialized with a
+ * call to NS_StringContainerInit. The following example shows how to use
+ * a string container to call a function that takes a |nsAString &| out-param.
+ *
+ * nsresult GetBlah(nsAString &aBlah);
+ *
+ * nsresult MyCode()
+ * {
+ * nsresult rv;
+ *
+ * nsStringContainer sc;
+ * rv = NS_StringContainerInit(sc);
+ * if (NS_FAILED(rv))
+ * return rv;
+ *
+ * rv = GetBlah(sc);
+ * if (NS_SUCCEEDED(rv))
+ * {
+ * const char16_t *data;
+ * NS_StringGetData(sc, &data);
+ * //
+ * // |data| now points to the result of the GetBlah function
+ * //
+ * }
+ *
+ * NS_StringContainerFinish(sc);
+ * return rv;
+ * }
+ *
+ * The following example show how to use a string container to pass a string
+ * parameter to a function taking a |const nsAString &| in-param.
+ *
+ * nsresult SetBlah(const nsAString &aBlah);
+ *
+ * nsresult MyCode()
+ * {
+ * nsresult rv;
+ *
+ * nsStringContainer sc;
+ * rv = NS_StringContainerInit(sc);
+ * if (NS_FAILED(rv))
+ * return rv;
+ *
+ * const char16_t kData[] = {'x','y','z','\0'};
+ * rv = NS_StringSetData(sc, kData, sizeof(kData)/2 - 1);
+ * if (NS_SUCCEEDED(rv))
+ * rv = SetBlah(sc);
+ *
+ * NS_StringContainerFinish(sc);
+ * return rv;
+ * }
+ */
+class nsStringContainer;
+
+
+/**
+ * This struct is never used directly. It is designed to have the same
+ * size as nsString. It can be stack and heap allocated and the internal
+ * functions cast it to nsString.
+ * While this practice is a strict aliasing violation, it doesn't seem to
+ * cause problems since the the struct is only accessed via the casts to
+ * nsString.
+ * We use protected instead of private to avoid compiler warnings about
+ * the members being unused.
+ */
+struct nsStringContainer_base
+{
+protected:
+ void* d1;
+ uint32_t d2;
+ uint32_t d3;
+};
+
+/**
+ * Flags that may be OR'd together to pass to NS_StringContainerInit2:
+ */
+enum
+{
+ /* Data passed into NS_StringContainerInit2 is not copied; instead, the
+ * string references the passed in data pointer directly. The caller must
+ * ensure that the data is valid for the lifetime of the string container.
+ * This flag should not be combined with NS_STRING_CONTAINER_INIT_ADOPT. */
+ NS_STRING_CONTAINER_INIT_DEPEND = (1 << 1),
+
+ /* Data passed into NS_StringContainerInit2 is not copied; instead, the
+ * string takes ownership over the data pointer. The caller must have
+ * allocated the data array using the XPCOM memory allocator (nsMemory).
+ * This flag should not be combined with NS_STRING_CONTAINER_INIT_DEPEND. */
+ NS_STRING_CONTAINER_INIT_ADOPT = (1 << 2),
+
+ /* Data passed into NS_StringContainerInit2 is a substring that is not
+ * null-terminated. */
+ NS_STRING_CONTAINER_INIT_SUBSTRING = (1 << 3)
+};
+
+/**
+ * NS_StringContainerInit
+ *
+ * @param aContainer string container reference
+ * @return NS_OK if string container successfully initialized
+ *
+ * This function may allocate additional memory for aContainer. When
+ * aContainer is no longer needed, NS_StringContainerFinish should be called.
+ */
+XPCOM_API(nsresult) NS_StringContainerInit(nsStringContainer& aContainer);
+
+/**
+ * NS_StringContainerInit2
+ *
+ * @param aContainer string container reference
+ * @param aData character buffer (may be null)
+ * @param aDataLength number of characters stored at aData (may pass
+ * UINT32_MAX if aData is null-terminated)
+ * @param aFlags flags affecting how the string container is
+ * initialized. this parameter is ignored when aData
+ * is null. otherwise, if this parameter is 0, then
+ * aData is copied into the string.
+ *
+ * This function resembles NS_StringContainerInit but provides further
+ * options that permit more efficient memory usage. When aContainer is
+ * no longer needed, NS_StringContainerFinish should be called.
+ *
+ * NOTE: NS_StringContainerInit2(container, nullptr, 0, 0) is equivalent to
+ * NS_StringContainerInit(container).
+ */
+XPCOM_API(nsresult) NS_StringContainerInit2(nsStringContainer& aContainer,
+ const char16_t* aData = nullptr,
+ uint32_t aDataLength = UINT32_MAX,
+ uint32_t aFlags = 0);
+
+/**
+ * NS_StringContainerFinish
+ *
+ * @param aContainer string container reference
+ *
+ * This function frees any memory owned by aContainer.
+ */
+XPCOM_API(void) NS_StringContainerFinish(nsStringContainer& aContainer);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * NS_StringGetData
+ *
+ * This function returns a const character pointer to the string's internal
+ * buffer, the length of the string, and a boolean value indicating whether
+ * or not the buffer is null-terminated.
+ *
+ * @param aStr abstract string reference
+ * @param aData out param that will hold the address of aStr's
+ * internal buffer
+ * @param aTerminated if non-null, this out param will be set to indicate
+ * whether or not aStr's internal buffer is null-
+ * terminated
+ * @return length of aStr's internal buffer
+ */
+XPCOM_API(uint32_t) NS_StringGetData(const nsAString& aStr,
+ const char16_t** aData,
+ bool* aTerminated = nullptr);
+
+/**
+ * NS_StringGetMutableData
+ *
+ * This function provides mutable access to a string's internal buffer. It
+ * returns a pointer to an array of characters that may be modified. The
+ * returned pointer remains valid until the string object is passed to some
+ * other string function.
+ *
+ * Optionally, this function may be used to resize the string's internal
+ * buffer. The aDataLength parameter specifies the requested length of the
+ * string's internal buffer. By passing some value other than UINT32_MAX,
+ * the caller can request that the buffer be resized to the specified number of
+ * characters before returning. The caller is not responsible for writing a
+ * null-terminator.
+ *
+ * @param aStr abstract string reference
+ * @param aDataLength number of characters to resize the string's internal
+ * buffer to or UINT32_MAX if no resizing is needed
+ * @param aData out param that upon return holds the address of aStr's
+ * internal buffer or null if the function failed
+ * @return number of characters or zero if the function failed
+ *
+ * This function does not necessarily null-terminate aStr after resizing its
+ * internal buffer. The behavior depends on the implementation of the abstract
+ * string, aStr. If aStr is a reference to a nsStringContainer, then its data
+ * will be null-terminated by this function.
+ */
+XPCOM_API(uint32_t) NS_StringGetMutableData(nsAString& aStr,
+ uint32_t aDataLength,
+ char16_t** aData);
+
+/**
+ * NS_StringCloneData
+ *
+ * This function returns a null-terminated copy of the string's
+ * internal buffer.
+ *
+ * @param aStr abstract string reference
+ * @return null-terminated copy of the string's internal buffer
+ * (it must be free'd using using free)
+ */
+XPCOM_API(char16_t*) NS_StringCloneData(const nsAString& aStr);
+
+/**
+ * NS_StringSetData
+ *
+ * This function copies aData into aStr.
+ *
+ * @param aStr abstract string reference
+ * @param aData character buffer
+ * @param aDataLength number of characters to copy from source string (pass
+ * UINT32_MAX to copy until end of aData, designated by
+ * a null character)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr after copying data
+ * from aData. The behavior depends on the implementation of the abstract
+ * string, aStr. If aStr is a reference to a nsStringContainer, then its data
+ * will be null-terminated by this function.
+ */
+XPCOM_API(nsresult) NS_StringSetData(nsAString& aStr, const char16_t* aData,
+ uint32_t aDataLength = UINT32_MAX);
+
+/**
+ * NS_StringSetDataRange
+ *
+ * This function copies aData into a section of aStr. As a result it can be
+ * used to insert new characters into the string.
+ *
+ * @param aStr abstract string reference
+ * @param aCutOffset starting index where the string's existing data
+ * is to be overwritten (pass UINT32_MAX to cause
+ * aData to be appended to the end of aStr, in which
+ * case the value of aCutLength is ignored).
+ * @param aCutLength number of characters to overwrite starting at
+ * aCutOffset (pass UINT32_MAX to overwrite until the
+ * end of aStr).
+ * @param aData character buffer (pass null to cause this function
+ * to simply remove the "cut" range)
+ * @param aDataLength number of characters to copy from source string (pass
+ * UINT32_MAX to copy until end of aData, designated by
+ * a null character)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr after copying data
+ * from aData. The behavior depends on the implementation of the abstract
+ * string, aStr. If aStr is a reference to a nsStringContainer, then its data
+ * will be null-terminated by this function.
+ */
+XPCOM_API(nsresult) NS_StringSetDataRange(nsAString& aStr,
+ uint32_t aCutOffset, uint32_t aCutLength,
+ const char16_t* aData,
+ uint32_t aDataLength = UINT32_MAX);
+
+/**
+ * NS_StringCopy
+ *
+ * This function makes aDestStr have the same value as aSrcStr. It is
+ * provided as an optimization.
+ *
+ * @param aDestStr abstract string reference to be modified
+ * @param aSrcStr abstract string reference containing source string
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aDestStr after copying
+ * data from aSrcStr. The behavior depends on the implementation of the
+ * abstract string, aDestStr. If aDestStr is a reference to a
+ * nsStringContainer, then its data will be null-terminated by this function.
+ */
+XPCOM_API(nsresult) NS_StringCopy(nsAString& aDestStr,
+ const nsAString& aSrcStr);
+
+/**
+ * NS_StringAppendData
+ *
+ * This function appends data to the existing value of aStr.
+ *
+ * @param aStr abstract string reference to be modified
+ * @param aData character buffer
+ * @param aDataLength number of characters to append (pass UINT32_MAX to
+ * append until a null-character is encountered)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr upon completion.
+ * The behavior depends on the implementation of the abstract string, aStr.
+ * If aStr is a reference to a nsStringContainer, then its data will be null-
+ * terminated by this function.
+ */
+inline NS_HIDDEN_(nsresult)
+NS_StringAppendData(nsAString& aStr, const char16_t* aData,
+ uint32_t aDataLength = UINT32_MAX)
+{
+ return NS_StringSetDataRange(aStr, UINT32_MAX, 0, aData, aDataLength);
+}
+
+/**
+ * NS_StringInsertData
+ *
+ * This function inserts data into the existing value of aStr at the specified
+ * offset.
+ *
+ * @param aStr abstract string reference to be modified
+ * @param aOffset specifies where in the string to insert aData
+ * @param aData character buffer
+ * @param aDataLength number of characters to append (pass UINT32_MAX to
+ * append until a null-character is encountered)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr upon completion.
+ * The behavior depends on the implementation of the abstract string, aStr.
+ * If aStr is a reference to a nsStringContainer, then its data will be null-
+ * terminated by this function.
+ */
+inline NS_HIDDEN_(nsresult)
+NS_StringInsertData(nsAString& aStr, uint32_t aOffset, const char16_t* aData,
+ uint32_t aDataLength = UINT32_MAX)
+{
+ return NS_StringSetDataRange(aStr, aOffset, 0, aData, aDataLength);
+}
+
+/**
+ * NS_StringCutData
+ *
+ * This function shortens the existing value of aStr, by removing characters
+ * at the specified offset.
+ *
+ * @param aStr abstract string reference to be modified
+ * @param aCutOffset specifies where in the string to insert aData
+ * @param aCutLength number of characters to remove
+ * @return NS_OK if function succeeded
+ */
+inline NS_HIDDEN_(nsresult)
+NS_StringCutData(nsAString& aStr, uint32_t aCutOffset, uint32_t aCutLength)
+{
+ return NS_StringSetDataRange(aStr, aCutOffset, aCutLength, nullptr, 0);
+}
+
+/**
+ * NS_StringSetIsVoid
+ *
+ * This function marks a string as being a "void string". Any data in the
+ * string will be lost.
+ */
+XPCOM_API(void) NS_StringSetIsVoid(nsAString& aStr, const bool aIsVoid);
+
+/**
+ * NS_StringGetIsVoid
+ *
+ * This function provides a way to test if a string is a "void string", as
+ * marked by NS_StringSetIsVoid.
+ */
+XPCOM_API(bool) NS_StringGetIsVoid(const nsAString& aStr);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * nsCStringContainer
+ *
+ * This is an opaque data type that is large enough to hold the canonical
+ * implementation of nsACString. The binary structure of this class is an
+ * implementation detail.
+ *
+ * The string data stored in a string container is always single fragment
+ * and may be null-terminated depending on how it is initialized.
+ *
+ * @see nsStringContainer for use cases and further documentation.
+ */
+class nsCStringContainer;
+
+/**
+ * Flags that may be OR'd together to pass to NS_StringContainerInit2:
+ */
+enum
+{
+ /* Data passed into NS_CStringContainerInit2 is not copied; instead, the
+ * string references the passed in data pointer directly. The caller must
+ * ensure that the data is valid for the lifetime of the string container.
+ * This flag should not be combined with NS_CSTRING_CONTAINER_INIT_ADOPT. */
+ NS_CSTRING_CONTAINER_INIT_DEPEND = (1 << 1),
+
+ /* Data passed into NS_CStringContainerInit2 is not copied; instead, the
+ * string takes ownership over the data pointer. The caller must have
+ * allocated the data array using the XPCOM memory allocator (nsMemory).
+ * This flag should not be combined with NS_CSTRING_CONTAINER_INIT_DEPEND. */
+ NS_CSTRING_CONTAINER_INIT_ADOPT = (1 << 2),
+
+ /* Data passed into NS_CStringContainerInit2 is a substring that is not
+ * null-terminated. */
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING = (1 << 3)
+};
+
+/**
+ * NS_CStringContainerInit
+ *
+ * @param aContainer string container reference
+ * @return NS_OK if string container successfully initialized
+ *
+ * This function may allocate additional memory for aContainer. When
+ * aContainer is no longer needed, NS_CStringContainerFinish should be called.
+ */
+XPCOM_API(nsresult) NS_CStringContainerInit(nsCStringContainer& aContainer);
+
+/**
+ * NS_CStringContainerInit2
+ *
+ * @param aContainer string container reference
+ * @param aData character buffer (may be null)
+ * @param aDataLength number of characters stored at aData (may pass
+ * UINT32_MAX if aData is null-terminated)
+ * @param aFlags flags affecting how the string container is
+ * initialized. this parameter is ignored when aData
+ * is null. otherwise, if this parameter is 0, then
+ * aData is copied into the string.
+ *
+ * This function resembles NS_CStringContainerInit but provides further
+ * options that permit more efficient memory usage. When aContainer is
+ * no longer needed, NS_CStringContainerFinish should be called.
+ *
+ * NOTE: NS_CStringContainerInit2(container, nullptr, 0, 0) is equivalent to
+ * NS_CStringContainerInit(container).
+ */
+XPCOM_API(nsresult) NS_CStringContainerInit2(nsCStringContainer& aContainer,
+ const char* aData = nullptr,
+ uint32_t aDataLength = UINT32_MAX,
+ uint32_t aFlags = 0);
+
+/**
+ * NS_CStringContainerFinish
+ *
+ * @param aContainer string container reference
+ *
+ * This function frees any memory owned by aContainer.
+ */
+XPCOM_API(void) NS_CStringContainerFinish(nsCStringContainer& aContainer);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * NS_CStringGetData
+ *
+ * This function returns a const character pointer to the string's internal
+ * buffer, the length of the string, and a boolean value indicating whether
+ * or not the buffer is null-terminated.
+ *
+ * @param aStr abstract string reference
+ * @param aData out param that will hold the address of aStr's
+ * internal buffer
+ * @param aTerminated if non-null, this out param will be set to indicate
+ * whether or not aStr's internal buffer is null-
+ * terminated
+ * @return length of aStr's internal buffer
+ */
+XPCOM_API(uint32_t) NS_CStringGetData(const nsACString& aStr,
+ const char** aData,
+ bool* aTerminated = nullptr);
+
+/**
+ * NS_CStringGetMutableData
+ *
+ * This function provides mutable access to a string's internal buffer. It
+ * returns a pointer to an array of characters that may be modified. The
+ * returned pointer remains valid until the string object is passed to some
+ * other string function.
+ *
+ * Optionally, this function may be used to resize the string's internal
+ * buffer. The aDataLength parameter specifies the requested length of the
+ * string's internal buffer. By passing some value other than UINT32_MAX,
+ * the caller can request that the buffer be resized to the specified number of
+ * characters before returning. The caller is not responsible for writing a
+ * null-terminator.
+ *
+ * @param aStr abstract string reference
+ * @param aDataLength number of characters to resize the string's internal
+ * buffer to or UINT32_MAX if no resizing is needed
+ * @param aData out param that upon return holds the address of aStr's
+ * internal buffer or null if the function failed
+ * @return number of characters or zero if the function failed
+ *
+ * This function does not necessarily null-terminate aStr after resizing its
+ * internal buffer. The behavior depends on the implementation of the abstract
+ * string, aStr. If aStr is a reference to a nsStringContainer, then its data
+ * will be null-terminated by this function.
+ */
+XPCOM_API(uint32_t) NS_CStringGetMutableData(nsACString& aStr,
+ uint32_t aDataLength,
+ char** aData);
+
+/**
+ * NS_CStringCloneData
+ *
+ * This function returns a null-terminated copy of the string's
+ * internal buffer.
+ *
+ * @param aStr abstract string reference
+ * @return null-terminated copy of the string's internal buffer
+ * (it must be free'd using using free)
+ */
+XPCOM_API(char*) NS_CStringCloneData(const nsACString& aStr);
+
+/**
+ * NS_CStringSetData
+ *
+ * This function copies aData into aStr.
+ *
+ * @param aStr abstract string reference
+ * @param aData character buffer
+ * @param aDataLength number of characters to copy from source string (pass
+ * UINT32_MAX to copy until end of aData, designated by
+ * a null character)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr after copying data
+ * from aData. The behavior depends on the implementation of the abstract
+ * string, aStr. If aStr is a reference to a nsStringContainer, then its data
+ * will be null-terminated by this function.
+ */
+XPCOM_API(nsresult) NS_CStringSetData(nsACString& aStr, const char* aData,
+ uint32_t aDataLength = UINT32_MAX);
+
+/**
+ * NS_CStringSetDataRange
+ *
+ * This function copies aData into a section of aStr. As a result it can be
+ * used to insert new characters into the string.
+ *
+ * @param aStr abstract string reference
+ * @param aCutOffset starting index where the string's existing data
+ * is to be overwritten (pass UINT32_MAX to cause
+ * aData to be appended to the end of aStr, in which
+ * case the value of aCutLength is ignored).
+ * @param aCutLength number of characters to overwrite starting at
+ * aCutOffset (pass UINT32_MAX to overwrite until the
+ * end of aStr).
+ * @param aData character buffer (pass null to cause this function
+ * to simply remove the "cut" range)
+ * @param aDataLength number of characters to copy from source string (pass
+ * UINT32_MAX to copy until end of aData, designated by
+ * a null character)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr after copying data
+ * from aData. The behavior depends on the implementation of the abstract
+ * string, aStr. If aStr is a reference to a nsStringContainer, then its data
+ * will be null-terminated by this function.
+ */
+XPCOM_API(nsresult) NS_CStringSetDataRange(nsACString& aStr,
+ uint32_t aCutOffset,
+ uint32_t aCutLength,
+ const char* aData,
+ uint32_t aDataLength = UINT32_MAX);
+
+/**
+ * NS_CStringCopy
+ *
+ * This function makes aDestStr have the same value as aSrcStr. It is
+ * provided as an optimization.
+ *
+ * @param aDestStr abstract string reference to be modified
+ * @param aSrcStr abstract string reference containing source string
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aDestStr after copying
+ * data from aSrcStr. The behavior depends on the implementation of the
+ * abstract string, aDestStr. If aDestStr is a reference to a
+ * nsStringContainer, then its data will be null-terminated by this function.
+ */
+XPCOM_API(nsresult) NS_CStringCopy(nsACString& aDestStr,
+ const nsACString& aSrcStr);
+
+/**
+ * NS_CStringAppendData
+ *
+ * This function appends data to the existing value of aStr.
+ *
+ * @param aStr abstract string reference to be modified
+ * @param aData character buffer
+ * @param aDataLength number of characters to append (pass UINT32_MAX to
+ * append until a null-character is encountered)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr upon completion.
+ * The behavior depends on the implementation of the abstract string, aStr.
+ * If aStr is a reference to a nsStringContainer, then its data will be null-
+ * terminated by this function.
+ */
+inline NS_HIDDEN_(nsresult)
+NS_CStringAppendData(nsACString& aStr, const char* aData,
+ uint32_t aDataLength = UINT32_MAX)
+{
+ return NS_CStringSetDataRange(aStr, UINT32_MAX, 0, aData, aDataLength);
+}
+
+/**
+ * NS_CStringInsertData
+ *
+ * This function inserts data into the existing value of aStr at the specified
+ * offset.
+ *
+ * @param aStr abstract string reference to be modified
+ * @param aOffset specifies where in the string to insert aData
+ * @param aData character buffer
+ * @param aDataLength number of characters to append (pass UINT32_MAX to
+ * append until a null-character is encountered)
+ * @return NS_OK if function succeeded
+ *
+ * This function does not necessarily null-terminate aStr upon completion.
+ * The behavior depends on the implementation of the abstract string, aStr.
+ * If aStr is a reference to a nsStringContainer, then its data will be null-
+ * terminated by this function.
+ */
+inline NS_HIDDEN_(nsresult)
+NS_CStringInsertData(nsACString& aStr, uint32_t aOffset, const char* aData,
+ uint32_t aDataLength = UINT32_MAX)
+{
+ return NS_CStringSetDataRange(aStr, aOffset, 0, aData, aDataLength);
+}
+
+/**
+ * NS_CStringCutData
+ *
+ * This function shortens the existing value of aStr, by removing characters
+ * at the specified offset.
+ *
+ * @param aStr abstract string reference to be modified
+ * @param aCutOffset specifies where in the string to insert aData
+ * @param aCutLength number of characters to remove
+ * @return NS_OK if function succeeded
+ */
+inline NS_HIDDEN_(nsresult)
+NS_CStringCutData(nsACString& aStr, uint32_t aCutOffset, uint32_t aCutLength)
+{
+ return NS_CStringSetDataRange(aStr, aCutOffset, aCutLength, nullptr, 0);
+}
+
+/**
+ * NS_CStringSetIsVoid
+ *
+ * This function marks a string as being a "void string". Any data in the
+ * string will be lost.
+ */
+XPCOM_API(void) NS_CStringSetIsVoid(nsACString& aStr, const bool aIsVoid);
+
+/**
+ * NS_CStringGetIsVoid
+ *
+ * This function provides a way to test if a string is a "void string", as
+ * marked by NS_CStringSetIsVoid.
+ */
+XPCOM_API(bool) NS_CStringGetIsVoid(const nsACString& aStr);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * Encodings that can be used with the following conversion routines.
+ */
+enum nsCStringEncoding
+{
+ /* Conversion between ASCII and UTF-16 assumes that all bytes in the source
+ * string are 7-bit ASCII and can be inflated to UTF-16 by inserting null
+ * bytes. Reverse conversion is done by truncating every other byte. The
+ * conversion may result in loss and/or corruption of information if the
+ * strings do not strictly contain ASCII data. */
+ NS_CSTRING_ENCODING_ASCII = 0,
+
+ /* Conversion between UTF-8 and UTF-16 is non-lossy. */
+ NS_CSTRING_ENCODING_UTF8 = 1,
+
+ /* Conversion from UTF-16 to the native filesystem charset may result in a
+ * loss of information. No attempt is made to protect against data loss in
+ * this case. The native filesystem charset applies to strings passed to
+ * the "Native" method variants on nsIFile. */
+ NS_CSTRING_ENCODING_NATIVE_FILESYSTEM = 2
+};
+
+/**
+ * NS_CStringToUTF16
+ *
+ * This function converts the characters in a nsACString to an array of UTF-16
+ * characters, in the platform endianness. The result is stored in a nsAString
+ * object.
+ *
+ * @param aSource abstract string reference containing source string
+ * @param aSrcEncoding character encoding of the source string
+ * @param aDest abstract string reference to hold the result
+ */
+XPCOM_API(nsresult) NS_CStringToUTF16(const nsACString& aSource,
+ nsCStringEncoding aSrcEncoding,
+ nsAString& aDest);
+
+/**
+ * NS_UTF16ToCString
+ *
+ * This function converts the UTF-16 characters in a nsAString to a single-byte
+ * encoding. The result is stored in a nsACString object. In some cases this
+ * conversion may be lossy. In such cases, the conversion may succeed with a
+ * return code indicating loss of information. The exact behavior is not
+ * specified at this time.
+ *
+ * @param aSource abstract string reference containing source string
+ * @param aDestEncoding character encoding of the resulting string
+ * @param aDest abstract string reference to hold the result
+ */
+XPCOM_API(nsresult) NS_UTF16ToCString(const nsAString& aSource,
+ nsCStringEncoding aDestEncoding,
+ nsACString& aDest);
+
+#endif // nsXPCOMStrings_h__
diff --git a/xpcom/string/nsXPIDLString.h b/xpcom/string/nsXPIDLString.h
new file mode 100644
index 000000000..f5821cdb0
--- /dev/null
+++ b/xpcom/string/nsXPIDLString.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPIDLString_h___
+#define nsXPIDLString_h___
+
+#include "nsString.h"
+
+#endif /* !defined(nsXPIDLString_h___) */
diff --git a/xpcom/string/string-template-def-char.h b/xpcom/string/string-template-def-char.h
new file mode 100644
index 000000000..82f70d0fb
--- /dev/null
+++ b/xpcom/string/string-template-def-char.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+#define CharT char
+#define CharT_is_char 1
+#define nsTAString_IncompatibleCharT nsAString
+#define nsTString_CharT nsCString
+#define nsTFixedString_CharT nsFixedCString
+#define nsTAutoString_CharT nsAutoCString
+#define nsTSubstring_CharT nsACString
+#define nsTSubstringTuple_CharT nsCSubstringTuple
+#define nsTStringComparator_CharT nsCStringComparator
+#define nsTDefaultStringComparator_CharT nsDefaultCStringComparator
+#define nsTDependentString_CharT nsDependentCString
+#define nsTDependentSubstring_CharT nsDependentCSubstring
+#define nsTLiteralString_CharT nsLiteralCString
+#define nsTXPIDLString_CharT nsXPIDLCString
+#define nsTGetterCopies_CharT nsCGetterCopies
+#define nsTAdoptingString_CharT nsAdoptingCString
+#define nsTPromiseFlatString_CharT nsPromiseFlatCString
+#define TPromiseFlatString_CharT PromiseFlatCString
diff --git a/xpcom/string/string-template-def-unichar.h b/xpcom/string/string-template-def-unichar.h
new file mode 100644
index 000000000..a21e16d09
--- /dev/null
+++ b/xpcom/string/string-template-def-unichar.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+#define CharT char16_t
+#define CharT_is_PRUnichar 1
+#define nsTAString_IncompatibleCharT nsACString
+#define nsTString_CharT nsString
+#define nsTFixedString_CharT nsFixedString
+#define nsTAutoString_CharT nsAutoString
+#define nsTSubstring_CharT nsAString
+#define nsTSubstringTuple_CharT nsSubstringTuple
+#define nsTStringComparator_CharT nsStringComparator
+#define nsTDefaultStringComparator_CharT nsDefaultStringComparator
+#define nsTDependentString_CharT nsDependentString
+#define nsTDependentSubstring_CharT nsDependentSubstring
+#define nsTLiteralString_CharT nsLiteralString
+#define nsTXPIDLString_CharT nsXPIDLString
+#define nsTGetterCopies_CharT nsGetterCopies
+#define nsTAdoptingString_CharT nsAdoptingString
+#define nsTPromiseFlatString_CharT nsPromiseFlatString
+#define TPromiseFlatString_CharT PromiseFlatString
diff --git a/xpcom/string/string-template-undef.h b/xpcom/string/string-template-undef.h
new file mode 100644
index 000000000..d62cd2278
--- /dev/null
+++ b/xpcom/string/string-template-undef.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+#undef CharT
+#undef CharT_is_PRUnichar
+#undef CharT_is_char
+#undef nsTAString_IncompatibleCharT
+#undef nsTString_CharT
+#undef nsTFixedString_CharT
+#undef nsTAutoString_CharT
+#undef nsTSubstring_CharT
+#undef nsTSubstringTuple_CharT
+#undef nsTStringComparator_CharT
+#undef nsTDefaultStringComparator_CharT
+#undef nsTDependentString_CharT
+#undef nsTDependentSubstring_CharT
+#undef nsTLiteralString_CharT
+#undef nsTXPIDLString_CharT
+#undef nsTGetterCopies_CharT
+#undef nsTAdoptingString_CharT
+#undef nsTPromiseFlatString_CharT
+#undef TPromiseFlatString_CharT
diff --git a/xpcom/system/moz.build b/xpcom/system/moz.build
new file mode 100644
index 000000000..8a4f88efe
--- /dev/null
+++ b/xpcom/system/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIBlocklistService.idl',
+ 'nsIDeviceSensors.idl',
+ 'nsIGConfService.idl',
+ 'nsIGeolocationProvider.idl',
+ 'nsIGIOService.idl',
+ 'nsIGSettingsService.idl',
+ 'nsIHapticFeedback.idl',
+ 'nsIPackageKitService.idl',
+ 'nsIPlatformInfo.idl',
+ 'nsIXULAppInfo.idl',
+ 'nsIXULRuntime.idl',
+]
+
+if CONFIG['MOZ_CRASHREPORTER']:
+ XPIDL_SOURCES += [
+ 'nsICrashReporter.idl',
+ ]
+
+XPIDL_MODULE = 'xpcom_system'
diff --git a/xpcom/system/nsIBlocklistService.idl b/xpcom/system/nsIBlocklistService.idl
new file mode 100644
index 000000000..b70038431
--- /dev/null
+++ b/xpcom/system/nsIBlocklistService.idl
@@ -0,0 +1,140 @@
+/* -*- 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/. */
+
+
+#include "nsISupports.idl"
+
+interface nsIPluginTag;
+interface nsIVariant;
+
+[scriptable, uuid(a6dcc76e-9f62-4cc1-a470-b483a1a6f096)]
+interface nsIBlocklistService : nsISupports
+{
+ // Indicates that the item does not appear in the blocklist.
+ const unsigned long STATE_NOT_BLOCKED = 0;
+ // Indicates that the item is in the blocklist but the problem is not severe
+ // enough to warant forcibly blocking.
+ const unsigned long STATE_SOFTBLOCKED = 1;
+ // Indicates that the item should be blocked and never used.
+ const unsigned long STATE_BLOCKED = 2;
+ // Indicates that the item is considered outdated, and there is a known
+ // update available.
+ const unsigned long STATE_OUTDATED = 3;
+ // Indicates that the item is vulnerable and there is an update.
+ const unsigned long STATE_VULNERABLE_UPDATE_AVAILABLE = 4;
+ // Indicates that the item is vulnerable and there is no update.
+ const unsigned long STATE_VULNERABLE_NO_UPDATE = 5;
+
+ /**
+ * Determine if an item is blocklisted
+ * @param addon
+ * The addon item to be checked.
+ * @param appVersion
+ * The version of the application we are checking in the blocklist.
+ * If this parameter is null, the version of the running application
+ * is used.
+ * @param toolkitVersion
+ * The version of the toolkit we are checking in the blocklist.
+ * If this parameter is null, the version of the running toolkit
+ * is used.
+ * @returns true if the item is compatible with this version of the
+ * application or this version of the toolkit, false, otherwise.
+ */
+ boolean isAddonBlocklisted(in jsval addon,
+ [optional] in AString appVersion,
+ [optional] in AString toolkitVersion);
+
+ /**
+ * Determine the blocklist state of an add-on
+ * @param id
+ * The addon item to be checked.
+ * @param appVersion
+ * The version of the application we are checking in the blocklist.
+ * If this parameter is null, the version of the running application
+ * is used.
+ * @param toolkitVersion
+ * The version of the toolkit we are checking in the blocklist.
+ * If this parameter is null, the version of the running toolkit
+ * is used.
+ * @returns The STATE constant.
+ */
+ unsigned long getAddonBlocklistState(in jsval addon,
+ [optional] in AString appVersion,
+ [optional] in AString toolkitVersion);
+
+ /**
+ * Determine the blocklist state of a plugin
+ * @param plugin
+ * The plugin to get the state for
+ * @param appVersion
+ * The version of the application we are checking in the blocklist.
+ * If this parameter is null, the version of the running application
+ * is used.
+ * @param toolkitVersion
+ * The version of the toolkit we are checking in the blocklist.
+ * If this parameter is null, the version of the running toolkit
+ * is used.
+ * @returns The STATE constant.
+ */
+ unsigned long getPluginBlocklistState(in nsIPluginTag plugin,
+ [optional] in AString appVersion,
+ [optional] in AString toolkitVersion);
+
+ /**
+ * Determine the blocklist web page of an add-on.
+ * @param addon
+ * The addon item whose url is required.
+ * @returns The URL of the description page.
+ */
+ AString getAddonBlocklistURL(in jsval addon,
+ [optional] in AString appVersion,
+ [optional] in AString toolkitVersion);
+
+ /**
+ * Determine the blocklist web page of a plugin.
+ * @param plugin
+ * The blocked plugin that we are determining the web page for.
+ * @returns The URL of the description page.
+ */
+ AString getPluginBlocklistURL(in nsIPluginTag plugin);
+
+ /**
+ * Determine the blocklist infoURL of a plugin.
+ * @param plugin
+ * The blocked plugin that we are determining the infoURL for.
+ * @returns The preferred URL to present the user, or |null| if
+ * it is not available.
+ */
+ AString getPluginInfoURL(in nsIPluginTag plugin);
+};
+
+/**
+ * nsIBlocklistPrompt is used, if available, by the default implementation of
+ * nsIBlocklistService to display a confirmation UI to the user before blocking
+ * extensions/plugins.
+ */
+[scriptable, uuid(ba915921-b9c0-400d-8e4f-ca1b80c5699a)]
+interface nsIBlocklistPrompt : nsISupports
+{
+ /**
+ * Prompt the user about newly blocked addons. The prompt is then resposible
+ * for soft-blocking any addons that need to be afterwards
+ *
+ * @param aAddons
+ * An array of addons and plugins that are blocked. These are javascript
+ * objects with properties:
+ * name - the plugin or extension name,
+ * version - the version of the extension or plugin,
+ * icon - the plugin or extension icon,
+ * disable - can be used by the nsIBlocklistPrompt to allows users to decide
+ * whether a soft-blocked add-on should be disabled,
+ * blocked - true if the item is hard-blocked, false otherwise,
+ * item - the nsIPluginTag or Addon object
+ * @param aCount
+ * The number of addons
+ */
+ void prompt([array, size_is(aCount)] in nsIVariant aAddons,
+ [optional] in uint32_t aCount);
+};
diff --git a/xpcom/system/nsICrashReporter.idl b/xpcom/system/nsICrashReporter.idl
new file mode 100644
index 000000000..c2f590098
--- /dev/null
+++ b/xpcom/system/nsICrashReporter.idl
@@ -0,0 +1,135 @@
+/* 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"
+
+interface nsIFile;
+interface nsIURL;
+
+/**
+ * Provides access to crash reporting functionality.
+ *
+ * @status UNSTABLE - This interface is not frozen and will probably change in
+ * future releases.
+ */
+
+[scriptable, uuid(4b74c39a-cf69-4a8a-8e63-169d81ad1ecf)]
+interface nsICrashReporter : nsISupports
+{
+ /**
+ * Get the enabled status of the crash reporter.
+ */
+ readonly attribute boolean enabled;
+
+ /**
+ * Enable or disable crash reporting at runtime. Not available to script
+ * because the JS engine relies on proper exception handler chaining.
+ */
+ [noscript]
+ void setEnabled(in bool enabled);
+
+ /**
+ * Get or set the URL to which crash reports will be submitted.
+ * Only https and http URLs are allowed, as the submission is handled
+ * by OS-native networking libraries.
+ *
+ * @throw NS_ERROR_NOT_INITIALIZED if crash reporting is not initialized
+ * @throw NS_ERROR_INVALID_ARG on set if a non-http(s) URL is assigned
+ * @throw NS_ERROR_FAILURE on get if no URL is set
+ */
+ attribute nsIURL serverURL;
+
+ /**
+ * Get or set the path on the local system to which minidumps will be
+ * written when a crash happens.
+ *
+ * @throw NS_ERROR_NOT_INITIALIZED if crash reporting is not initialized
+ */
+ attribute nsIFile minidumpPath;
+
+ /**
+ * Add some extra data to be submitted with a crash report.
+ *
+ * @param key
+ * Name of the data to be added.
+ * @param data
+ * Data to be added.
+ *
+ * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
+ * @throw NS_ERROR_INVALID_ARG if key or data contain invalid characters.
+ * Invalid characters for key are '=' and
+ * '\n'. Invalid character for data is '\0'.
+ */
+ void annotateCrashReport(in AUTF8String key, in AUTF8String data);
+
+ /**
+ * Append some data to the "Notes" field, to be submitted with a crash report.
+ * Unlike annotateCrashReport, this method will append to existing data.
+ *
+ * @param data
+ * Data to be added.
+ *
+ * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
+ * @throw NS_ERROR_INVALID_ARG if data contains invalid characters.
+ * The only invalid character is '\0'.
+ */
+ void appendAppNotesToCrashReport(in ACString data);
+
+ /**
+ * Register a given memory range to be included in the crash report.
+ *
+ * @param ptr
+ * The starting address for the bytes.
+ * @param size
+ * The number of bytes to include.
+ *
+ * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
+ * @throw NS_ERROR_NOT_IMPLEMENTED if unavailable on the current OS
+ */
+ void registerAppMemory(in unsigned long long ptr, in unsigned long long size);
+
+ /**
+ * Write a minidump immediately, with the user-supplied exception
+ * information. This is implemented on Windows only, because
+ * SEH (structured exception handling) exists on Windows only.
+ *
+ * @param aExceptionInfo EXCEPTION_INFO* provided by Window's SEH
+ */
+ [noscript] void writeMinidumpForException(in voidPtr aExceptionInfo);
+
+ /**
+ * Append note containing an Obj-C exception's info.
+ *
+ * @param aException NSException object to append note for
+ */
+ [noscript] void appendObjCExceptionInfoToAppNotes(in voidPtr aException);
+
+ /**
+ * User preference for submitting crash reports.
+ */
+ attribute boolean submitReports;
+
+ /**
+ * Cause the crash reporter to re-evaluate where crash events should go.
+ *
+ * This should be called during application startup and whenever profiles
+ * change.
+ */
+ void UpdateCrashEventsDir();
+
+ /**
+ * Save an anonymized memory report file for inclusion in a future crash
+ * report in this session.
+ *
+ * @throws NS_ERROR_NOT_INITIALIZED if crash reporting is disabled.
+ */
+ void saveMemoryReport();
+
+ /**
+ * Set the telemetry session ID which is recorded in crash metadata. This is
+ * saved in the crash manager and telemetry but is not submitted as a
+ * crash-stats annotation.
+ */
+ void setTelemetrySessionId(in AUTF8String id);
+};
diff --git a/xpcom/system/nsIDeviceSensors.idl b/xpcom/system/nsIDeviceSensors.idl
new file mode 100644
index 000000000..dcb67cb92
--- /dev/null
+++ b/xpcom/system/nsIDeviceSensors.idl
@@ -0,0 +1,60 @@
+/* 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"
+
+interface nsIDOMWindow;
+
+[scriptable, uuid(0462247e-fe8c-4aa5-b675-3752547e485f)]
+interface nsIDeviceSensorData : nsISupports
+{
+ // Keep in sync with hal/HalSensor.h
+
+ // 1. TYPE_ORIENTATION: absolute device orientation, not spec-conform and
+ // deprecated on Android.
+ // 2. TYPE_ROTATION_VECTOR: absolute device orientation affected by drift.
+ // 3. TYPE_GAME_ROTATION_VECTOR: relative device orientation less affected by drift.
+ // Preferred fallback priorities on Android are [3, 2, 1] for the general case
+ // and [2, 1] if absolute orientation (compass heading) is required.
+ const unsigned long TYPE_ORIENTATION = 0;
+ const unsigned long TYPE_ACCELERATION = 1;
+ const unsigned long TYPE_PROXIMITY = 2;
+ const unsigned long TYPE_LINEAR_ACCELERATION = 3;
+ const unsigned long TYPE_GYROSCOPE = 4;
+ const unsigned long TYPE_LIGHT = 5;
+ const unsigned long TYPE_ROTATION_VECTOR = 6;
+ const unsigned long TYPE_GAME_ROTATION_VECTOR = 7;
+
+ readonly attribute unsigned long type;
+
+ readonly attribute double x;
+ readonly attribute double y;
+ readonly attribute double z;
+};
+
+[scriptable, uuid(e46e47c7-55ff-44c4-abce-21b14ba07f86)]
+interface nsIDeviceSensors : nsISupports
+{
+ /**
+ * Returns true if the given window has any listeners of the given type
+ */
+ bool hasWindowListener(in unsigned long aType, in nsIDOMWindow aWindow);
+
+ // Holds pointers, not AddRef objects -- it is up to the caller
+ // to call RemoveWindowListener before the window is deleted.
+
+ [noscript] void addWindowListener(in unsigned long aType, in nsIDOMWindow aWindow);
+ [noscript] void removeWindowListener(in unsigned long aType, in nsIDOMWindow aWindow);
+ [noscript] void removeWindowAsListener(in nsIDOMWindow aWindow);
+};
+
+%{C++
+
+#define NS_DEVICE_SENSORS_CID \
+{ 0xecba5203, 0x77da, 0x465a, \
+{ 0x86, 0x5e, 0x78, 0xb7, 0xaf, 0x10, 0xd8, 0xf7 } }
+
+#define NS_DEVICE_SENSORS_CONTRACTID "@mozilla.org/devicesensors;1"
+
+%}
diff --git a/xpcom/system/nsIGConfService.idl b/xpcom/system/nsIGConfService.idl
new file mode 100644
index 000000000..ccc02610f
--- /dev/null
+++ b/xpcom/system/nsIGConfService.idl
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIArray;
+
+[scriptable, uuid(5009acae-6973-48c3-b6d6-52c692cc5d9d)]
+interface nsIGConfService : nsISupports
+{
+ /* Basic registry access */
+ boolean getBool(in AUTF8String key);
+ AUTF8String getString(in AUTF8String key);
+ long getInt(in AUTF8String key);
+ float getFloat(in AUTF8String key);
+
+ /*
+ * Use this to return any list items in GConf, this will return
+ * an array of UTF16 nsISupportsString's.
+ */
+ nsIArray getStringList(in AUTF8String key);
+
+ void setBool(in AUTF8String key, in boolean value);
+ void setString(in AUTF8String key, in AUTF8String value);
+ void setInt(in AUTF8String key, in long value);
+ void setFloat(in AUTF8String key, in float value);
+
+ /*
+ * Look up the handler for a protocol under the
+ * /desktop/gnome/url-handlers hierarchy.
+ */
+ AUTF8String getAppForProtocol(in AUTF8String scheme, out boolean enabled);
+
+ /*
+ * Check whether the handler for a scheme requires a terminal to run.
+ */
+ boolean handlerRequiresTerminal(in AUTF8String scheme);
+
+ /*
+ * Set the handler for a protocol, marking it as enabled.
+ * This removes any GnomeVFSMimeApp association for the protocol.
+ */
+ void setAppForProtocol(in AUTF8String scheme, in AUTF8String command);
+};
+
+%{C++
+#define NS_GCONFSERVICE_CONTRACTID "@mozilla.org/gnome-gconf-service;1"
+%}
diff --git a/xpcom/system/nsIGIOService.idl b/xpcom/system/nsIGIOService.idl
new file mode 100644
index 000000000..cbbdabe7e
--- /dev/null
+++ b/xpcom/system/nsIGIOService.idl
@@ -0,0 +1,82 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIUTF8StringEnumerator;
+interface nsIURI;
+
+/* nsIGIOMimeApp holds information about an application that is looked up
+ with nsIGIOService::GetAppForMimeType. */
+// 66009894-9877-405b-9321-bf30420e34e6 prev uuid
+
+[scriptable, uuid(ca6bad0c-8a48-48ac-82c7-27bb8f510fbe)]
+interface nsIGIOMimeApp : nsISupports
+{
+ const long EXPECTS_URIS = 0;
+ const long EXPECTS_PATHS = 1;
+ const long EXPECTS_URIS_FOR_NON_FILES = 2;
+
+ readonly attribute AUTF8String id;
+ readonly attribute AUTF8String name;
+ readonly attribute AUTF8String command;
+ readonly attribute long expectsURIs; // see constants above
+ readonly attribute nsIUTF8StringEnumerator supportedURISchemes;
+
+ void launch(in AUTF8String uri);
+ void setAsDefaultForMimeType(in AUTF8String mimeType);
+ void setAsDefaultForFileExtensions(in AUTF8String extensions);
+ void setAsDefaultForURIScheme(in AUTF8String uriScheme);
+};
+
+/*
+ * The VFS service makes use of two distinct registries.
+ *
+ * The application registry holds information about applications (uniquely
+ * identified by id), such as which MIME types and URI schemes they are
+ * capable of handling, whether they run in a terminal, etc.
+ *
+ * The MIME registry holds information about MIME types, such as which
+ * extensions map to a given MIME type. The MIME registry also stores the
+ * id of the application selected to handle each MIME type.
+ */
+
+// prev id dea20bf0-4e4d-48c5-b932-dc3e116dc64b
+[scriptable, uuid(eda22a30-84e1-4e16-9ca0-cd1553c2b34a)]
+interface nsIGIOService : nsISupports
+{
+
+ /*** MIME registry methods ***/
+
+ /* Obtain the MIME type registered for an extension. The extension
+ should not include a leading dot. */
+ AUTF8String getMimeTypeFromExtension(in AUTF8String extension);
+
+ /* Obtain the preferred application for opening a given URI scheme */
+ nsIGIOMimeApp getAppForURIScheme(in AUTF8String aURIScheme);
+
+ /* Obtain the preferred application for opening a given MIME type */
+ nsIGIOMimeApp getAppForMimeType(in AUTF8String mimeType);
+
+ /* Obtain the preferred application for opening a given MIME type */
+ nsIGIOMimeApp createAppFromCommand(in AUTF8String cmd,
+ in AUTF8String appName);
+
+ /* Obtain a description for the given MIME type */
+ AUTF8String getDescriptionForMimeType(in AUTF8String mimeType);
+
+ /*** Misc. methods ***/
+
+ /* Open the given URI in the default application */
+ void showURI(in nsIURI uri);
+ [noscript] void showURIForInput(in ACString uri);
+
+ /* Open path in file manager using org.freedesktop.FileManager1 interface */
+ [noscript] void orgFreedesktopFileManager1ShowItems(in ACString path);
+};
+
+%{C++
+#define NS_GIOSERVICE_CONTRACTID "@mozilla.org/gio-service;1"
+%}
diff --git a/xpcom/system/nsIGSettingsService.idl b/xpcom/system/nsIGSettingsService.idl
new file mode 100644
index 000000000..26d86a77e
--- /dev/null
+++ b/xpcom/system/nsIGSettingsService.idl
@@ -0,0 +1,30 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIArray;
+
+[scriptable, uuid(16d5b0ed-e756-4f1b-a8ce-9132e869acd8)]
+interface nsIGSettingsCollection : nsISupports
+{
+ void setString(in AUTF8String key, in AUTF8String value);
+ void setBoolean(in AUTF8String key, in boolean value);
+ void setInt(in AUTF8String key, in long value);
+ AUTF8String getString(in AUTF8String key);
+ boolean getBoolean(in AUTF8String key);
+ long getInt(in AUTF8String key);
+ nsIArray getStringList(in AUTF8String key);
+};
+
+[scriptable, uuid(849c088b-57d1-4f24-b7b2-3dc4acb04c0a)]
+interface nsIGSettingsService : nsISupports
+{
+ nsIGSettingsCollection getCollectionForSchema(in AUTF8String schema);
+};
+
+%{C++
+#define NS_GSETTINGSSERVICE_CONTRACTID "@mozilla.org/gsettings-service;1"
+%}
diff --git a/xpcom/system/nsIGeolocationProvider.idl b/xpcom/system/nsIGeolocationProvider.idl
new file mode 100644
index 000000000..29044266d
--- /dev/null
+++ b/xpcom/system/nsIGeolocationProvider.idl
@@ -0,0 +1,83 @@
+/* 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"
+
+interface nsIURI;
+interface nsIDOMWindow;
+interface nsIDOMElement;
+interface nsIDOMGeoPosition;
+interface nsIGeolocationPrompt;
+
+/**
+
+ * Interface provides a way for a geolocation provider to
+ * notify the system that a new location is available.
+ */
+[scriptable, uuid(643dc5e9-b911-4b2c-8d44-603162696baf)]
+interface nsIGeolocationUpdate : nsISupports {
+
+ /**
+ * Notify the geolocation service that a new geolocation
+ * has been discovered.
+ * This must be called on the main thread
+ */
+ void update(in nsIDOMGeoPosition position);
+ /**
+ * Notify the geolocation service of an error.
+ * This must be called on the main thread.
+ * The parameter refers to one of the constants in the
+ * nsIDOMGeoPositionError interface.
+ * Use this to report spurious errors coming from the
+ * provider; for errors occurring inside the methods in
+ * the nsIGeolocationProvider interface, just use the return
+ * value.
+ */
+ void notifyError(in unsigned short error);
+};
+
+
+/**
+ * Interface provides location information to the nsGeolocator
+ * via the nsIDOMGeolocationCallback interface. After
+ * startup is called, any geo location change should call
+ * callback.update().
+ */
+[scriptable, uuid(AC4A133B-9F92-4F7C-B369-D40CB6B17650)]
+interface nsIGeolocationProvider : nsISupports {
+
+ /**
+ * Start up the provider. This is called before any other
+ * method. may be called multiple times.
+ */
+ void startup();
+
+ /**
+ * watch
+ * When a location change is observed, notify the callback.
+ */
+ void watch(in nsIGeolocationUpdate callback);
+
+ /**
+ * shutdown
+ * Shuts down the location device.
+ */
+ void shutdown();
+
+ /**
+ * hint to provide to use any amount of power to provide a better result
+ */
+ void setHighAccuracy(in boolean enable);
+
+};
+
+%{C++
+/*
+ This must be implemented by geolocation providers. It
+ must support nsIGeolocationProvider.
+*/
+#define NS_GEOLOCATION_PROVIDER_CONTRACTID "@mozilla.org/geolocation/provider;1"
+%}
diff --git a/xpcom/system/nsIHapticFeedback.idl b/xpcom/system/nsIHapticFeedback.idl
new file mode 100644
index 000000000..25d0d8e7b
--- /dev/null
+++ b/xpcom/system/nsIHapticFeedback.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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(91917c98-a8f3-4c98-8f10-4afb872f54c7)]
+interface nsIHapticFeedback : nsISupports {
+
+ const long ShortPress = 0;
+ const long LongPress = 1;
+
+ /**
+ * Perform haptic feedback
+ *
+ * @param isLongPress
+ * indicate whether feedback is for a long press (vs. short press)
+ */
+ void performSimpleAction(in long isLongPress);
+};
diff --git a/xpcom/system/nsIPackageKitService.idl b/xpcom/system/nsIPackageKitService.idl
new file mode 100644
index 000000000..5cd3494a3
--- /dev/null
+++ b/xpcom/system/nsIPackageKitService.idl
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIArray;
+interface nsIObserver;
+
+[scriptable, uuid(89bb04f6-ce2a-11e3-8f4f-60a44c717042)]
+interface nsIPackageKitService : nsISupports
+{
+
+ /* PackageKit installation methods */
+ /* See https://github.com/nekohayo/gnome-packagekit/blob/master/src/org.freedesktop.PackageKit.xml */
+ const unsigned long PK_INSTALL_PACKAGE_NAMES = 0;
+ const unsigned long PK_INSTALL_MIME_TYPES = 1;
+ const unsigned long PK_INSTALL_FONTCONFIG_RESOURCES = 2;
+ const unsigned long PK_INSTALL_GSTREAMER_RESOURCES = 3;
+ const unsigned long PK_INSTALL_METHOD_COUNT = 4;
+
+ /* Ask to install a list of packages via PackageKit
+ * @param packageKitMethod
+ * The PackageKit installation method
+ * @param packageArray
+ * A nonempty array of strings describing the list of packages to
+ * install.
+ * @param An object implementing nsIObserver that will be notified with
+ * a message of topic "packagekit-install". The message data
+ * contains the error returned by PackageKit if the installation
+ * fails and is null otherwise.
+ *
+ * This function may raise an NS_ERROR_INVALID_ARG, NS_ERROR_FAILURE or
+ * NS_ERROR_OUT_OF_MEMORY exception. Otherwise, the observer will be notified
+ * when the operation completes.
+ *
+ */
+ void installPackages(in unsigned long packageKitMethod,
+ in nsIArray packageArray,
+ in nsIObserver observer);
+};
+
+%{C++
+#define NS_PACKAGEKITSERVICE_CONTRACTID "@mozilla.org/packagekit-service;1"
+%}
diff --git a/xpcom/system/nsIPlatformInfo.idl b/xpcom/system/nsIPlatformInfo.idl
new file mode 100644
index 000000000..fba78b6ec
--- /dev/null
+++ b/xpcom/system/nsIPlatformInfo.idl
@@ -0,0 +1,19 @@
+/* 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(ab6650cf-0806-4aea-b8f2-40fdae74f1cc)]
+interface nsIPlatformInfo : nsISupports
+{
+ /**
+ * The version of the XULRunner platform.
+ */
+ readonly attribute ACString platformVersion;
+
+ /**
+ * The build ID/date of gecko and the XULRunner platform.
+ */
+ readonly attribute ACString platformBuildID;
+};
diff --git a/xpcom/system/nsIXULAppInfo.idl b/xpcom/system/nsIXULAppInfo.idl
new file mode 100644
index 000000000..1ea9208a3
--- /dev/null
+++ b/xpcom/system/nsIXULAppInfo.idl
@@ -0,0 +1,53 @@
+/* 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 "nsIPlatformInfo.idl"
+
+/**
+ * A scriptable interface to the nsXULAppAPI structure. See nsXULAppAPI.h for
+ * a detailed description of each attribute.
+ */
+
+[scriptable, uuid(ddea4f31-3c5e-4769-ac68-21ab4b3d7845)]
+interface nsIXULAppInfo : nsIPlatformInfo
+{
+ /**
+ * @see nsXREAppData.vendor
+ * @returns an empty string if nsXREAppData.vendor is not set.
+ */
+ readonly attribute ACString vendor;
+
+ /**
+ * @see nsXREAppData.name
+ */
+ readonly attribute ACString name;
+
+ /**
+ * @see nsXREAppData.ID
+ * @returns an empty string if nsXREAppData.ID is not set.
+ */
+ readonly attribute ACString ID;
+
+ /**
+ * The version of the XUL application. It is different than the
+ * version of the XULRunner platform. Be careful about which one you want.
+ *
+ * @see nsXREAppData.version
+ * @returns an empty string if nsXREAppData.version is not set.
+ */
+ readonly attribute ACString version;
+
+ /**
+ * The build ID/date of the application. For xulrunner applications,
+ * this will be different than the build ID of the platform. Be careful
+ * about which one you want.
+ */
+ readonly attribute ACString appBuildID;
+
+ /**
+ * @see nsXREAppData.UAName
+ * @returns an empty string if nsXREAppData.UAName is not set.
+ */
+ readonly attribute ACString UAName;
+};
diff --git a/xpcom/system/nsIXULRuntime.idl b/xpcom/system/nsIXULRuntime.idl
new file mode 100644
index 000000000..fda0e696f
--- /dev/null
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -0,0 +1,176 @@
+/* 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"
+
+%{C++
+
+namespace mozilla {
+// Simple C++ getter for nsIXULRuntime::browserTabsRemoteAutostart
+// This getter is a temporary function that checks for special
+// conditions in which e10s support is not great yet, and should
+// therefore be disabled. Bug 1065561 tracks its removal.
+bool BrowserTabsRemoteAutostart();
+}
+
+%}
+
+/**
+ * Provides information about the XUL runtime.
+ * @status UNSTABLE - This interface is not frozen and will probably change in
+ * future releases. If you need this functionality to be
+ * stable/frozen, please contact Benjamin Smedberg.
+ */
+
+[scriptable, uuid(a1b2e167-b748-42bf-ba85-996ec39062b9)]
+interface nsIXULRuntime : nsISupports
+{
+ /**
+ * Whether the application was launched in safe mode.
+ */
+ readonly attribute boolean inSafeMode;
+
+ /**
+ * Whether to write console errors to a log file. If a component
+ * encounters startup errors that might prevent the app from showing
+ * proper UI, it should set this flag to "true".
+ */
+ attribute boolean logConsoleErrors;
+
+ /**
+ * A string tag identifying the current operating system. This is taken
+ * from the OS_TARGET configure variable. It will always be available.
+ */
+ readonly attribute AUTF8String OS;
+
+ /**
+ * A string tag identifying the binary ABI of the current processor and
+ * compiler vtable. This is taken from the TARGET_XPCOM_ABI configure
+ * variable. It may not be available on all platforms, especially
+ * unusual processor or compiler combinations.
+ *
+ * The result takes the form <processor>-<compilerABI>, for example:
+ * x86-msvc
+ * ppc-gcc3
+ *
+ * This value should almost always be used in combination with "OS".
+ *
+ * @throw NS_ERROR_NOT_AVAILABLE if not available.
+ */
+ readonly attribute AUTF8String XPCOMABI;
+
+ /**
+ * A string tag identifying the target widget toolkit in use.
+ * This is taken from the MOZ_WIDGET_TOOLKIT configure variable.
+ */
+ readonly attribute AUTF8String widgetToolkit;
+
+ /**
+ * The legal values of processType.
+ */
+ const unsigned long PROCESS_TYPE_DEFAULT = 0;
+ const unsigned long PROCESS_TYPE_PLUGIN = 1;
+ const unsigned long PROCESS_TYPE_CONTENT = 2;
+ const unsigned long PROCESS_TYPE_IPDLUNITTEST = 3;
+ const unsigned long PROCESS_TYPE_GMPLUGIN = 4;
+ const unsigned long PROCESS_TYPE_GPU = 5;
+
+ /**
+ * The type of the caller's process. Returns one of the values above.
+ */
+ readonly attribute unsigned long processType;
+
+ /**
+ * The system process ID of the caller's process.
+ */
+ readonly attribute unsigned long processID;
+
+ /**
+ * A globally unique and non-recycled ID of the caller's process.
+ */
+ readonly attribute uint64_t uniqueProcessID;
+
+ /**
+ * If true, browser tabs may be opened by default in a different process
+ * from the main browser UI.
+ */
+ readonly attribute boolean browserTabsRemoteAutostart;
+
+ /**
+ * A numeric value indicating whether multiprocess might be blocked.
+ * Possible values can be found at nsAppRunner.cpp. A value of 0
+ * represents not blocking.
+ */
+ readonly attribute unsigned long multiprocessBlockPolicy;
+
+ /**
+ * If true, the accessibility service is running.
+ */
+ readonly attribute boolean accessibilityEnabled;
+
+ /**
+ * Indicates whether the current Firefox build is 64-bit.
+ */
+ readonly attribute boolean is64Bit;
+
+ /**
+ * Signal the apprunner to invalidate caches on the next restart.
+ * This will cause components to be autoregistered and all
+ * fastload data to be re-created.
+ */
+ void invalidateCachesOnRestart();
+
+ /**
+ * Starts a child process. This method is intented to pre-start a
+ * content child process so that when it is actually needed, it is
+ * ready to go.
+ *
+ * @throw NS_ERROR_NOT_AVAILABLE if not available.
+ */
+ void ensureContentProcess();
+
+ /**
+ * Modification time of the profile lock before the profile was locked on
+ * this startup. Used to know the last time the profile was used and not
+ * closed cleanly. This is set to 0 if there was no existing profile lock.
+ */
+ readonly attribute PRTime replacedLockTime;
+
+ /**
+ * Local ID of the minidump generated when the process crashed
+ * on the previous run. Can be passed directly to CrashSubmit.submit.
+ */
+ readonly attribute DOMString lastRunCrashID;
+
+ /**
+ * True if this is RELEASE_OR_BETA.
+ */
+ readonly attribute boolean isReleaseOrBeta;
+
+ /**
+ * True if this build uses official branding (MOZ_OFFICIAL_BRANDING).
+ */
+ readonly attribute boolean isOfficialBranding;
+
+ /**
+ * The default update channel (MOZ_UPDATE_CHANNEL).
+ */
+ readonly attribute AUTF8String defaultUpdateChannel;
+
+ /**
+ * The distribution ID for this build (MOZ_DISTRIBUTION_ID).
+ */
+ readonly attribute AUTF8String distributionID;
+
+ /**
+ * True if this is an official build (MOZILLA_OFFICIAL).
+ */
+ readonly attribute boolean isOfficial;
+
+ /**
+ * True if Windows DLL blocklist initialized correctly. This is
+ * primarily for automated testing purposes.
+ */
+ readonly attribute boolean windowsDLLBlocklistStatus;
+};
diff --git a/xpcom/tests/Makefile.in b/xpcom/tests/Makefile.in
new file mode 100644
index 000000000..75796b93b
--- /dev/null
+++ b/xpcom/tests/Makefile.in
@@ -0,0 +1,13 @@
+# 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/.
+
+# Make sure we have symbols in case we need to debug these.
+MOZ_DEBUG_SYMBOLS = 1
+
+include $(topsrcdir)/config/rules.mk
+
+ifneq (,$(SIMPLE_PROGRAMS))
+libs::
+ $(INSTALL) $(SIMPLE_PROGRAMS) $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit
+endif
diff --git a/xpcom/tests/NotXPCOMTest.idl b/xpcom/tests/NotXPCOMTest.idl
new file mode 100644
index 000000000..75d1e73da
--- /dev/null
+++ b/xpcom/tests/NotXPCOMTest.idl
@@ -0,0 +1,23 @@
+/* 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(93142a4f-e4cf-424a-b833-e638f87d2607)]
+interface ScriptableOK : nsISupports
+{
+ void method1();
+};
+
+[scriptable, uuid(237d01a3-771e-4c6e-adf9-c97f9aab2950)]
+interface ScriptableWithNotXPCOM : nsISupports
+{
+ [notxpcom] void method2();
+};
+
+[scriptable, uuid(4f06ec60-3bb3-4712-ab18-b2b595285558)]
+interface ScriptableWithNotXPCOMBase : ScriptableWithNotXPCOM
+{
+ void method3();
+};
diff --git a/xpcom/tests/RegFactory.cpp b/xpcom/tests/RegFactory.cpp
new file mode 100644
index 000000000..0abb7a4e8
--- /dev/null
+++ b/xpcom/tests/RegFactory.cpp
@@ -0,0 +1,130 @@
+/* -*- 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 <iostream.h>
+#include "plstr.h"
+#include "prlink.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+static bool gUnreg = false;
+
+void print_err(nsresult err)
+{
+ switch (err) {
+ case NS_ERROR_FACTORY_NOT_LOADED:
+ cerr << "Factory not loaded";
+ break;
+ case NS_NOINTERFACE:
+ cerr << "No Interface";
+ break;
+ case NS_ERROR_NULL_POINTER:
+ cerr << "Null pointer";
+ break;
+ case NS_ERROR_OUT_OF_MEMORY:
+ cerr << "Out of memory";
+ break;
+ default:
+ cerr << hex << err << dec;
+ }
+}
+
+nsresult Register(nsIComponentRegistrar* registrar, const char *path)
+{
+ nsCOMPtr<nsIFile> file;
+ nsresult rv =
+ NS_NewLocalFile(
+ NS_ConvertUTF8toUTF16(path),
+ true,
+ getter_AddRefs(file));
+ if (NS_FAILED(rv)) return rv;
+ rv = registrar->AutoRegister(file);
+ return rv;
+}
+
+nsresult Unregister(const char *path)
+{
+ /* NEEDS IMPLEMENTATION */
+#if 0
+ nsresult res = nsComponentManager::AutoUnregisterComponent(path);
+ return res;
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+int ProcessArgs(nsIComponentRegistrar* registrar, int argc, char *argv[])
+{
+ int i = 1;
+ nsresult res;
+
+ while (i < argc) {
+ if (argv[i][0] == '-') {
+ int j;
+ for (j = 1; argv[i][j] != '\0'; j++) {
+ switch (argv[i][j]) {
+ case 'u':
+ gUnreg = true;
+ break;
+ default:
+ cerr << "Unknown option '" << argv[i][j] << "'\n";
+ }
+ }
+ i++;
+ } else {
+ if (gUnreg) {
+ res = Unregister(argv[i]);
+ if (NS_SUCCEEDED(res)) {
+ cout << "Successfully unregistered: " << argv[i] << "\n";
+ } else {
+ cerr << "Unregister failed (";
+ print_err(res);
+ cerr << "): " << argv[i] << "\n";
+ }
+ } else {
+ res = Register(registrar, argv[i]);
+ if (NS_SUCCEEDED(res)) {
+ cout << "Successfully registered: " << argv[i] << "\n";
+ } else {
+ cerr << "Register failed (";
+ print_err(res);
+ cerr << "): " << argv[i] << "\n";
+ }
+ }
+ i++;
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ nsresult rv;
+ {
+ nsCOMPtr<nsIServiceManager> servMan;
+ rv = NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
+ if (NS_FAILED(rv)) return -1;
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
+ NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
+
+ /* With no arguments, RegFactory will autoregister */
+ if (argc <= 1)
+ {
+ rv = registrar->AutoRegister(nullptr);
+ ret = (NS_FAILED(rv)) ? -1 : 0;
+ }
+ else
+ ret = ProcessArgs(registrar, argc, argv);
+ } // this scopes the nsCOMPtrs
+ // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
+ rv = NS_ShutdownXPCOM(nullptr);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
+ return ret;
+}
diff --git a/xpcom/tests/SizeTest01.cpp b/xpcom/tests/SizeTest01.cpp
new file mode 100644
index 000000000..cdb7f00c0
--- /dev/null
+++ b/xpcom/tests/SizeTest01.cpp
@@ -0,0 +1,107 @@
+// Test01.cpp
+
+#include "nsIDOMNode.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+NS_DEF_PTR(nsIDOMNode);
+
+ /*
+ This test file compares the generated code size of similar functions between raw
+ COM interface pointers (|AddRef|ing and |Release|ing by hand) and |nsCOMPtr|s.
+
+ Function size results were determined by examining dissassembly of the generated code.
+ mXXX is the size of the generated code on the Macintosh. wXXX is the size on Windows.
+ For these tests, all reasonable optimizations were enabled and exceptions were
+ disabled (just as we build for release).
+
+ The tests in this file explore only the simplest functionality: assigning a pointer
+ to be reference counted into a [raw, nsCOMPtr] object; ensuring that it is
+ |AddRef|ed and |Release|d appropriately; calling through the pointer to a function
+ supplied by the underlying COM interface.
+
+ Windows:
+ raw_optimized 31 bytes
+ raw, nsCOMPtr* 34
+ nsCOMPtr_optimized* 38
+ nsCOMPtr_optimized 42
+ nsCOMPtr 46
+
+ Macintosh:
+ raw_optimized, nsCOMPtr_optimized 112 bytes (1.0000)
+ nsCOMPtr 120 (1.0714) i.e., 7.14% bigger than raw_optimized et al
+ raw 140 (1.2500)
+
+ The overall difference in size between Windows and Macintosh is caused by the
+ the PowerPC RISC architecture where every instruction is 4 bytes.
+
+ On Macintosh, nsCOMPtr generates out-of-line destructors which are
+ not referenced, and which can be stripped by the linker.
+ */
+
+void
+Test01_raw( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m140, w34
+ {
+ /*
+ This test is designed to be more like a typical large function where,
+ because you are working with several resources, you don't just return when
+ one of them is |nullptr|. Similarly: |Test01_nsCOMPtr00|, and |Test01_nsIPtr00|.
+ */
+
+ nsIDOMNode* node = aDOMNode;
+ NS_IF_ADDREF(node);
+
+ if ( node )
+ node->GetNodeName(*aResult);
+
+ NS_IF_RELEASE(node);
+ }
+
+void
+Test01_raw_optimized( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m112, w31
+ {
+ /*
+ This test simulates smaller functions where you _do_ just return
+ |nullptr| at the first sign of trouble. Similarly: |Test01_nsCOMPtr01|,
+ and |Test01_nsIPtr01|.
+ */
+
+ /*
+ This test produces smaller code that |Test01_raw| because it avoids
+ the three tests: |NS_IF_...|, and |if ( node )|.
+ */
+
+// -- the following code is assumed, but is commented out so we compare only
+// the relevent generated code
+
+// if ( !aDOMNode )
+// return;
+
+ nsIDOMNode* node = aDOMNode;
+ NS_ADDREF(node);
+ node->GetNodeName(*aResult);
+ NS_RELEASE(node);
+ }
+
+void
+Test01_nsCOMPtr( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m120, w46/34
+ {
+ nsCOMPtr<nsIDOMNode> node = aDOMNode;
+
+ if ( node )
+ node->GetNodeName(*aResult);
+ }
+
+void
+Test01_nsCOMPtr_optimized( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m112, w42/38
+ {
+// if ( !aDOMNode )
+// return;
+
+ nsCOMPtr<nsIDOMNode> node = aDOMNode;
+ node->GetNodeName(*aResult);
+ }
diff --git a/xpcom/tests/SizeTest02.cpp b/xpcom/tests/SizeTest02.cpp
new file mode 100644
index 000000000..9fcd1a0ca
--- /dev/null
+++ b/xpcom/tests/SizeTest02.cpp
@@ -0,0 +1,89 @@
+// Test02.cpp
+
+#include "nsIDOMNode.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+NS_DEF_PTR(nsIDOMNode);
+
+ /*
+ This test file compares the generated code size of similar functions between raw
+ COM interface pointers (|AddRef|ing and |Release|ing by hand) and |nsCOMPtr|s.
+
+ Function size results were determined by examining dissassembly of the generated code.
+ mXXX is the size of the generated code on the Macintosh. wXXX is the size on Windows.
+ For these tests, all reasonable optimizations were enabled and exceptions were
+ disabled (just as we build for release).
+
+ The tests in this file explore more complicated functionality: assigning a pointer
+ to be reference counted into a [raw, nsCOMPtr] object using |QueryInterface|;
+ ensuring that it is |AddRef|ed and |Release|d appropriately; calling through the pointer
+ to a function supplied by the underlying COM interface. The tests in this file expand
+ on the tests in "Test01.cpp" by adding |QueryInterface|.
+
+ Windows:
+ raw01 52
+ nsCOMPtr 63
+ raw 66
+ nsCOMPtr* 68
+
+ Macintosh:
+ nsCOMPtr 120 (1.0000)
+ Raw01 128 (1.1429) i.e., 14.29% bigger than nsCOMPtr
+ Raw00 144 (1.2000)
+ */
+
+
+void // nsresult
+Test02_Raw00( nsISupports* aDOMNode, nsString* aResult )
+ // m144, w66
+ {
+// -- the following code is assumed, but is commented out so we compare only
+// the relevent generated code
+
+// if ( !aDOMNode )
+// return NS_ERROR_NULL_POINTER;
+
+ nsIDOMNode* node = 0;
+ nsresult status = aDOMNode->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&node);
+ if ( NS_SUCCEEDED(status) )
+ {
+ node->GetNodeName(*aResult);
+ }
+
+ NS_IF_RELEASE(node);
+
+// return status;
+ }
+
+void // nsresult
+Test02_Raw01( nsISupports* aDOMNode, nsString* aResult )
+ // m128, w52
+ {
+// if ( !aDOMNode )
+// return NS_ERROR_NULL_POINTER;
+
+ nsIDOMNode* node;
+ nsresult status = aDOMNode->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&node);
+ if ( NS_SUCCEEDED(status) )
+ {
+ node->GetNodeName(*aResult);
+ NS_RELEASE(node);
+ }
+
+// return status;
+ }
+
+void // nsresult
+Test02_nsCOMPtr( nsISupports* aDOMNode, nsString* aResult )
+ // m120, w63/68
+ {
+ nsresult status;
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aDOMNode, &status);
+
+ if ( node )
+ node->GetNodeName(*aResult);
+
+// return status;
+ }
+
diff --git a/xpcom/tests/SizeTest03.cpp b/xpcom/tests/SizeTest03.cpp
new file mode 100644
index 000000000..e1593171e
--- /dev/null
+++ b/xpcom/tests/SizeTest03.cpp
@@ -0,0 +1,97 @@
+// Test03.cpp
+
+#include "nsIDOMNode.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+NS_DEF_PTR(nsIDOMNode);
+
+ /*
+ Windows:
+ nsCOMPtr_optimized* 45
+ raw_optimized 48
+ nsCOMPtr_optimized 50
+ nsCOMPtr 54
+ nsCOMPtr* 59
+ raw 62
+
+ Macintosh:
+ nsCOMPtr_optimized 112 (1.0000)
+ raw_optimized 124 bytes (1.1071) i.e., 10.71% bigger than nsCOMPtr_optimized
+ nsCOMPtr 144 (1.2857)
+ */
+
+void // nsresult
+Test03_raw( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m140, w62
+ {
+// -- the following code is assumed, but is commented out so we compare only
+// the relevent generated code
+
+// if ( !aDOMNode || !aResult )
+// return NS_ERROR_NULL_POINTER;
+
+ nsIDOMNode* parent = 0;
+ nsresult status = aDOMNode->GetParentNode(&parent);
+
+ if ( NS_SUCCEEDED(status) )
+ {
+ parent->GetNodeName(*aResult);
+ }
+
+ NS_IF_RELEASE(parent);
+
+// return status;
+ }
+
+
+void // nsresult
+Test03_raw_optimized( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m124, w48
+ {
+// if ( !aDOMNode || !aResult )
+// return NS_ERROR_NULL_POINTER;
+
+ nsIDOMNode* parent;
+ nsresult status = aDOMNode->GetParentNode(&parent);
+
+ if ( NS_SUCCEEDED(status) )
+ {
+ parent->GetNodeName(*aResult);
+ NS_RELEASE(parent);
+ }
+
+// return status;
+ }
+
+
+void // nsresult
+Test03_nsCOMPtr( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m144, w54/59
+ {
+// if ( !aDOMNode || !aResult )
+// return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsIDOMNode> parent;
+ nsresult status = aDOMNode->GetParentNode( getter_AddRefs(parent) );
+ if ( parent )
+ parent->GetNodeName(*aResult);
+
+// return status;
+ }
+
+void // nsresult
+Test03_nsCOMPtr_optimized( nsIDOMNode* aDOMNode, nsString* aResult )
+ // m112, w50/45
+ {
+// if ( !aDOMNode || !aResult )
+// return NS_ERROR_NULL_POINTER;
+
+ nsIDOMNode* temp;
+ nsresult status = aDOMNode->GetParentNode(&temp);
+ nsCOMPtr<nsIDOMNode> parent( dont_AddRef(temp) );
+ if ( parent )
+ parent->GetNodeName(*aResult);
+
+// return status;
+ }
diff --git a/xpcom/tests/SizeTest04.cpp b/xpcom/tests/SizeTest04.cpp
new file mode 100644
index 000000000..e045f29f6
--- /dev/null
+++ b/xpcom/tests/SizeTest04.cpp
@@ -0,0 +1,68 @@
+// Test04.cpp
+
+#include "nsIDOMNode.h"
+#include "nsCOMPtr.h"
+
+NS_DEF_PTR(nsIDOMNode);
+
+ /*
+ Windows:
+ nsCOMPtr 13
+ raw 36
+
+ Macintosh:
+ nsCOMPtr 36 bytes (1.0000)
+ raw 120 (3.3333) i.e., 333.33% bigger than nsCOMPtr
+ */
+
+class Test04_Raw
+ {
+ public:
+ Test04_Raw();
+ ~Test04_Raw();
+
+ void /*nsresult*/ SetNode( nsIDOMNode* newNode );
+
+ private:
+ nsIDOMNode* mNode;
+ };
+
+Test04_Raw::Test04_Raw()
+ : mNode(0)
+ {
+ // nothing else to do here
+ }
+
+Test04_Raw::~Test04_Raw()
+ {
+ NS_IF_RELEASE(mNode);
+ }
+
+void // nsresult
+Test04_Raw::SetNode( nsIDOMNode* newNode )
+ // m120, w36
+ {
+ NS_IF_ADDREF(newNode);
+ NS_IF_RELEASE(mNode);
+ mNode = newNode;
+
+// return NS_OK;
+ }
+
+
+
+class Test04_nsCOMPtr
+ {
+ public:
+ void /*nsresult*/ SetNode( nsIDOMNode* newNode );
+
+ private:
+ nsCOMPtr<nsIDOMNode> mNode;
+ };
+
+void // nsresult
+Test04_nsCOMPtr::SetNode( nsIDOMNode* newNode )
+ // m36, w13/13
+ {
+ mNode = newNode;
+ }
diff --git a/xpcom/tests/SizeTest05.cpp b/xpcom/tests/SizeTest05.cpp
new file mode 100644
index 000000000..1307b1c26
--- /dev/null
+++ b/xpcom/tests/SizeTest05.cpp
@@ -0,0 +1,74 @@
+// Test05.cpp
+
+#include "nsIDOMNode.h"
+#include "nsCOMPtr.h"
+
+NS_DEF_PTR(nsIDOMNode);
+
+ /*
+ Windows:
+ raw, nsCOMPtr 21 bytes
+
+ Macintosh:
+ Raw, nsCOMPtr 64 bytes
+ */
+
+class Test05_Raw
+ {
+ public:
+ Test05_Raw();
+ ~Test05_Raw();
+
+ void /*nsresult*/ GetNode( nsIDOMNode** aNode );
+
+ private:
+ nsIDOMNode* mNode;
+ };
+
+Test05_Raw::Test05_Raw()
+ : mNode(0)
+ {
+ // nothing else to do here
+ }
+
+Test05_Raw::~Test05_Raw()
+ {
+ NS_IF_RELEASE(mNode);
+ }
+
+void // nsresult
+Test05_Raw::GetNode( nsIDOMNode** aNode )
+ // m64, w21
+ {
+// if ( !aNode )
+// return NS_ERROR_NULL_POINTER;
+
+ *aNode = mNode;
+ NS_IF_ADDREF(*aNode);
+
+// return NS_OK;
+ }
+
+
+
+class Test05_nsCOMPtr
+ {
+ public:
+ void /*nsresult*/ GetNode( nsIDOMNode** aNode );
+
+ private:
+ nsCOMPtr<nsIDOMNode> mNode;
+ };
+
+void // nsresult
+Test05_nsCOMPtr::GetNode( nsIDOMNode** aNode )
+ // m64, w21
+ {
+// if ( !aNode )
+// return NS_ERROR_NULL_POINTER;
+
+ *aNode = mNode;
+ NS_IF_ADDREF(*aNode);
+
+// return NS_OK;
+ }
diff --git a/xpcom/tests/SizeTest06.cpp b/xpcom/tests/SizeTest06.cpp
new file mode 100644
index 000000000..67b57277d
--- /dev/null
+++ b/xpcom/tests/SizeTest06.cpp
@@ -0,0 +1,150 @@
+// Test06.cpp
+
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIBaseWindow.h"
+#include "nsCOMPtr.h"
+
+NS_DEF_PTR(nsPIDOMWindow);
+NS_DEF_PTR(nsIBaseWindow);
+
+ /*
+ Windows:
+ nsCOMPtr_optimized 176
+ nsCOMPtr_as_found 181
+ nsCOMPtr_optimized* 182
+ nsCOMPtr02* 184
+ nsCOMPtr02 187
+ nsCOMPtr02* 188
+ nsCOMPtr03 189
+ raw_optimized, nsCOMPtr00 191
+ nsCOMPtr00* 199
+ nsCOMPtr_as_found* 201
+ raw 214
+
+ Macintosh:
+ nsCOMPtr_optimized 300 (1.0000)
+ nsCOMPtr02 320 (1.0667) i.e., 6.67% bigger than nsCOMPtr_optimized
+ nsCOMPtr00 328 (1.0933)
+ raw_optimized, nsCOMPtr03 332 (1.1067)
+ nsCOMPtr_as_found 344 (1.1467)
+ raw 388 (1.2933)
+
+ */
+
+
+void // nsresult
+Test06_raw(nsIDOMWindow* aDOMWindow, nsIBaseWindow** aBaseWindow)
+ // m388, w214
+{
+// if (!aDOMWindow)
+// return NS_ERROR_NULL_POINTER;
+ nsPIDOMWindow* window = 0;
+ nsresult status = aDOMWindow->QueryInterface(NS_GET_IID(nsPIDOMWindow), (void**)&window);
+ nsIDocShell* docShell = 0;
+ if (window)
+ window->GetDocShell(&docShell);
+ nsIWebShell* rootWebShell = 0;
+ NS_IF_RELEASE(rootWebShell);
+ NS_IF_RELEASE(docShell);
+ NS_IF_RELEASE(window);
+// return status;
+}
+
+void // nsresult
+Test06_raw_optimized(nsIDOMWindow* aDOMWindow, nsIBaseWindow** aBaseWindow)
+ // m332, w191
+{
+// if (!aDOMWindow)
+// return NS_ERROR_NULL_POINTER;
+ (*aBaseWindow) = 0;
+ nsPIDOMWindow* window;
+ nsresult status = aDOMWindow->QueryInterface(NS_GET_IID(nsPIDOMWindow), (void**)&window);
+ if (NS_SUCCEEDED(status)) {
+ nsIDocShell* docShell = 0;
+ window->GetDocShell(&docShell);
+ if (docShell) {
+ NS_RELEASE(docShell);
+ }
+ NS_RELEASE(window);
+ }
+// return status;
+}
+
+void
+Test06_nsCOMPtr_as_found(nsIDOMWindow* aDOMWindow, nsCOMPtr<nsIBaseWindow>* aBaseWindow)
+ // m344, w181/201
+{
+// if (!aDOMWindow)
+// return;
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aDOMWindow);
+ nsCOMPtr<nsIDocShell> docShell;
+ if (window)
+ window->GetDocShell(getter_AddRefs(docShell));
+}
+
+void // nsresult
+Test06_nsCOMPtr00(nsIDOMWindow* aDOMWindow, nsIBaseWindow** aBaseWindow)
+ // m328, w191/199
+{
+// if (!aDOMWindow)
+// return NS_ERROR_NULL_POINTER;
+ nsresult status;
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aDOMWindow, &status);
+ nsIDocShell* temp0 = 0;
+ if (window)
+ window->GetDocShell(&temp0);
+ nsCOMPtr<nsIDocShell> docShell = dont_AddRef(temp0);
+ (*aBaseWindow) = 0;
+// return status;
+}
+
+void // nsresult
+Test06_nsCOMPtr_optimized(nsIDOMWindow* aDOMWindow, nsCOMPtr<nsIBaseWindow>* aBaseWindow)
+ // m300, w176/182
+{
+// if (!aDOMWindow)
+// return NS_ERROR_NULL_POINTER;
+ nsresult status;
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aDOMWindow, &status);
+ nsIDocShell* temp0 = 0;
+ if (window)
+ window->GetDocShell(&temp0);
+ (*aBaseWindow) = do_QueryInterface(nullptr, &status);
+// return status;
+}
+
+void // nsresult
+Test06_nsCOMPtr02(nsIDOMWindow* aDOMWindow, nsIBaseWindow** aBaseWindow)
+ // m320, w187/184
+{
+// if (!aDOMWindow)
+// return NS_ERROR_NULL_POINTER;
+ (*aBaseWindow) = 0;
+ nsresult status;
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aDOMWindow, &status);
+ if (window) {
+ nsIDocShell* temp0;
+ window->GetDocShell(&temp0);
+ }
+// return status;
+}
+
+void // nsresult
+Test06_nsCOMPtr03(nsIDOMWindow* aDOMWindow, nsCOMPtr<nsIBaseWindow>* aBaseWindow)
+ // m332, w189/188
+{
+// if (!aDOMWindow)
+// return NS_ERROR_NULL_POINTER;
+ (*aBaseWindow) = 0;
+ nsresult status;
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aDOMWindow, &status);
+ if (window) {
+ nsIDocShell* temp0;
+ window->GetDocShell(&temp0);
+ nsCOMPtr<nsIDocShell> docShell = dont_AddRef(temp0);
+ if (docShell) {
+ }
+ }
+// return status;
+}
diff --git a/xpcom/tests/TestArguments.cpp b/xpcom/tests/TestArguments.cpp
new file mode 100644
index 000000000..a9a9a258b
--- /dev/null
+++ b/xpcom/tests/TestArguments.cpp
@@ -0,0 +1,25 @@
+#include <string.h>
+
+int main(int argc, char* argv[]) {
+ if (argc != 9)
+ return -1;
+
+ if (strcmp("mozilla", argv[1]) != 0)
+ return 1;
+ if (strcmp("firefox", argv[2]) != 0)
+ return 2;
+ if (strcmp("thunderbird", argv[3]) != 0)
+ return 3;
+ if (strcmp("seamonkey", argv[4]) != 0)
+ return 4;
+ if (strcmp("foo", argv[5]) != 0)
+ return 5;
+ if (strcmp("bar", argv[6]) != 0)
+ return 6;
+ if (strcmp("argument with spaces", argv[7]) != 0)
+ return 7;
+ if (strcmp("\"argument with quotes\"", argv[8]) != 0)
+ return 8;
+
+ return 0;
+}
diff --git a/xpcom/tests/TestBlockingProcess.cpp b/xpcom/tests/TestBlockingProcess.cpp
new file mode 100644
index 000000000..9526934c1
--- /dev/null
+++ b/xpcom/tests/TestBlockingProcess.cpp
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main()
+{
+ char tmp;
+ fread(&tmp, sizeof(tmp), 1, stdin);
+ return 0;
+}
diff --git a/xpcom/tests/TestHarness.h b/xpcom/tests/TestHarness.h
new file mode 100644
index 000000000..753e0233a
--- /dev/null
+++ b/xpcom/tests/TestHarness.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Test harness for XPCOM objects, providing a scoped XPCOM initializer,
+ * nsCOMPtr, nsRefPtr, do_CreateInstance, do_GetService, ns(Auto|C|)String,
+ * and stdio.h/stdlib.h.
+ */
+
+#ifndef TestHarness_h__
+#define TestHarness_h__
+
+#include "mozilla/ArrayUtils.h"
+
+#include "prenv.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsStringGlue.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIDirectoryService.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsIObserverService.h"
+#include "nsXULAppAPI.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+static uint32_t gFailCount = 0;
+
+/**
+ * Prints the given failure message and arguments using printf, prepending
+ * "TEST-UNEXPECTED-FAIL " for the benefit of the test harness and
+ * appending "\n" to eliminate having to type it at each call site.
+ */
+void fail(const char* msg, ...)
+{
+ va_list ap;
+
+ printf("TEST-UNEXPECTED-FAIL | ");
+
+ va_start(ap, msg);
+ vprintf(msg, ap);
+ va_end(ap);
+
+ putchar('\n');
+ ++gFailCount;
+}
+
+/**
+ * Prints the given success message and arguments using printf, prepending
+ * "TEST-PASS " for the benefit of the test harness and
+ * appending "\n" to eliminate having to type it at each call site.
+ */
+void passed(const char* msg, ...)
+{
+ va_list ap;
+
+ printf("TEST-PASS | ");
+
+ va_start(ap, msg);
+ vprintf(msg, ap);
+ va_end(ap);
+
+ putchar('\n');
+}
+
+//-----------------------------------------------------------------------------
+
+class ScopedXPCOM : public nsIDirectoryServiceProvider2
+{
+ public:
+ NS_DECL_ISUPPORTS
+
+ explicit ScopedXPCOM(const char* testName,
+ nsIDirectoryServiceProvider *dirSvcProvider = nullptr)
+ : mDirSvcProvider(dirSvcProvider)
+ {
+ mTestName = testName;
+ printf("Running %s tests...\n", mTestName);
+
+ nsresult rv = NS_InitXPCOM2(&mServMgr, nullptr, this);
+ if (NS_FAILED(rv))
+ {
+ fail("NS_InitXPCOM2 returned failure code 0x%x", rv);
+ mServMgr = nullptr;
+ return;
+ }
+ }
+
+ ~ScopedXPCOM()
+ {
+ // If we created a profile directory, we need to remove it.
+ if (mProfD) {
+ nsCOMPtr<nsIObserverService> os =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+ MOZ_RELEASE_ASSERT(os);
+ MOZ_ALWAYS_SUCCEEDS(os->NotifyObservers(nullptr, "profile-change-net-teardown", nullptr));
+ MOZ_ALWAYS_SUCCEEDS(os->NotifyObservers(nullptr, "profile-change-teardown", nullptr));
+ MOZ_ALWAYS_SUCCEEDS(os->NotifyObservers(nullptr, "profile-before-change", nullptr));
+ MOZ_ALWAYS_SUCCEEDS(os->NotifyObservers(nullptr, "profile-before-change-qm", nullptr));
+ MOZ_ALWAYS_SUCCEEDS(os->NotifyObservers(nullptr, "profile-before-change-telemetry", nullptr));
+
+ if (NS_FAILED(mProfD->Remove(true))) {
+ NS_WARNING("Problem removing profile directory");
+ }
+
+ mProfD = nullptr;
+ }
+
+ if (mServMgr)
+ {
+ NS_RELEASE(mServMgr);
+ nsresult rv = NS_ShutdownXPCOM(nullptr);
+ if (NS_FAILED(rv))
+ {
+ fail("XPCOM shutdown failed with code 0x%x", rv);
+ exit(1);
+ }
+ }
+
+ printf("Finished running %s tests.\n", mTestName);
+ }
+
+ bool failed()
+ {
+ return mServMgr == nullptr;
+ }
+
+ already_AddRefed<nsIFile> GetProfileDirectory()
+ {
+ if (mProfD) {
+ nsCOMPtr<nsIFile> copy = mProfD;
+ return copy.forget();
+ }
+
+ // Create a unique temporary folder to use for this test.
+ // Note that runcppunittests.py will run tests with a temp
+ // directory as the cwd, so just put something under that.
+ nsCOMPtr<nsIFile> profD;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_PROCESS_DIR,
+ getter_AddRefs(profD));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ rv = profD->Append(NS_LITERAL_STRING("cpp-unit-profd"));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ rv = profD->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ mProfD = profD;
+ return profD.forget();
+ }
+
+ already_AddRefed<nsIFile> GetGREDirectory()
+ {
+ if (mGRED) {
+ nsCOMPtr<nsIFile> copy = mGRED;
+ return copy.forget();
+ }
+
+ char* env = PR_GetEnv("MOZ_XRE_DIR");
+ nsCOMPtr<nsIFile> greD;
+ if (env) {
+ NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false,
+ getter_AddRefs(greD));
+ }
+
+ mGRED = greD;
+ return greD.forget();
+ }
+
+ already_AddRefed<nsIFile> GetGREBinDirectory()
+ {
+ if (mGREBinD) {
+ nsCOMPtr<nsIFile> copy = mGREBinD;
+ return copy.forget();
+ }
+
+ nsCOMPtr<nsIFile> greD = GetGREDirectory();
+ if (!greD) {
+ return greD.forget();
+ }
+ greD->Clone(getter_AddRefs(mGREBinD));
+
+#ifdef XP_MACOSX
+ nsAutoCString leafName;
+ mGREBinD->GetNativeLeafName(leafName);
+ if (leafName.Equals("Resources")) {
+ mGREBinD->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
+ }
+#endif
+
+ nsCOMPtr<nsIFile> copy = mGREBinD;
+ return copy.forget();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //// nsIDirectoryServiceProvider
+
+ NS_IMETHOD GetFile(const char *aProperty, bool *_persistent,
+ nsIFile **_result) override
+ {
+ // If we were supplied a directory service provider, ask it first.
+ if (mDirSvcProvider &&
+ NS_SUCCEEDED(mDirSvcProvider->GetFile(aProperty, _persistent,
+ _result))) {
+ return NS_OK;
+ }
+
+ // Otherwise, the test harness provides some directories automatically.
+ if (0 == strcmp(aProperty, NS_APP_USER_PROFILE_50_DIR) ||
+ 0 == strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR) ||
+ 0 == strcmp(aProperty, NS_APP_PROFILE_LOCAL_DIR_STARTUP)) {
+ nsCOMPtr<nsIFile> profD = GetProfileDirectory();
+ NS_ENSURE_TRUE(profD, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIFile> clone;
+ nsresult rv = profD->Clone(getter_AddRefs(clone));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *_persistent = true;
+ clone.forget(_result);
+ return NS_OK;
+ } else if (0 == strcmp(aProperty, NS_GRE_DIR)) {
+ nsCOMPtr<nsIFile> greD = GetGREDirectory();
+ NS_ENSURE_TRUE(greD, NS_ERROR_FAILURE);
+
+ *_persistent = true;
+ greD.forget(_result);
+ return NS_OK;
+ } else if (0 == strcmp(aProperty, NS_GRE_BIN_DIR)) {
+ nsCOMPtr<nsIFile> greBinD = GetGREBinDirectory();
+ NS_ENSURE_TRUE(greBinD, NS_ERROR_FAILURE);
+
+ *_persistent = true;
+ greBinD.forget(_result);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //// nsIDirectoryServiceProvider2
+
+ NS_IMETHOD GetFiles(const char *aProperty, nsISimpleEnumerator **_enum) override
+ {
+ // If we were supplied a directory service provider, ask it first.
+ nsCOMPtr<nsIDirectoryServiceProvider2> provider =
+ do_QueryInterface(mDirSvcProvider);
+ if (provider && NS_SUCCEEDED(provider->GetFiles(aProperty, _enum))) {
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+ }
+
+ private:
+ const char* mTestName;
+ nsIServiceManager* mServMgr;
+ nsCOMPtr<nsIDirectoryServiceProvider> mDirSvcProvider;
+ nsCOMPtr<nsIFile> mProfD;
+ nsCOMPtr<nsIFile> mGRED;
+ nsCOMPtr<nsIFile> mGREBinD;
+};
+
+NS_IMPL_QUERY_INTERFACE(
+ ScopedXPCOM,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2
+)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+ScopedXPCOM::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+ScopedXPCOM::Release()
+{
+ return 1;
+}
+
+#endif // TestHarness_h__
diff --git a/xpcom/tests/TestPRIntN.cpp b/xpcom/tests/TestPRIntN.cpp
new file mode 100644
index 000000000..afc155d70
--- /dev/null
+++ b/xpcom/tests/TestPRIntN.cpp
@@ -0,0 +1,33 @@
+/* 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 <stdint.h>
+#include "prtypes.h"
+
+// This test is NOT intended to be run. It's a test to make sure
+// PRInt{N} matches int{N}_t. If they don't match, we should get a
+// compiler warning or error in main().
+
+static void
+ClearNSPRIntTypes(PRInt8 *a, PRInt16 *b, PRInt32 *c, PRInt64 *d)
+{
+ *a = 0; *b = 0; *c = 0; *d = 0;
+}
+
+static void
+ClearStdIntTypes(int8_t *w, int16_t *x, int32_t *y, int64_t *z)
+{
+ *w = 0; *x = 0; *y = 0; *z = 0;
+}
+
+int
+main()
+{
+ PRInt8 a; PRInt16 b; PRInt32 c; PRInt64 d;
+ int8_t w; int16_t x; int32_t y; int64_t z;
+
+ ClearNSPRIntTypes(&w, &x, &y, &z);
+ ClearStdIntTypes(&a, &b, &c, &d);
+ return 0;
+}
diff --git a/xpcom/tests/TestQuickReturn.cpp b/xpcom/tests/TestQuickReturn.cpp
new file mode 100644
index 000000000..d00771ba7
--- /dev/null
+++ b/xpcom/tests/TestQuickReturn.cpp
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main (int argc, char* argv[]) {
+ if (argc != 1)
+ return -1;
+
+ return 42;
+}
diff --git a/xpcom/tests/TestShutdown.cpp b/xpcom/tests/TestShutdown.cpp
new file mode 100644
index 000000000..177c83dbd
--- /dev/null
+++ b/xpcom/tests/TestShutdown.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIServiceManager.h"
+
+// Gee this seems simple! It's for testing for memory leaks with Purify.
+
+void main(int argc, char* argv[])
+{
+ nsresult rv;
+ nsIServiceManager* servMgr;
+ rv = NS_InitXPCOM2(&servMgr, nullptr, nullptr);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "NS_InitXPCOM failed");
+
+ // try loading a component and releasing it to see if it leaks
+ if (argc > 1 && argv[1] != nullptr) {
+ char* cidStr = argv[1];
+ nsISupports* obj = nullptr;
+ if (cidStr[0] == '{') {
+ nsCID cid;
+ cid.Parse(cidStr);
+ rv = CallCreateInstance(cid, &obj);
+ }
+ else {
+ // contractID case:
+ rv = CallCreateInstance(cidStr, &obj);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ printf("Successfully created %s\n", cidStr);
+ NS_RELEASE(obj);
+ }
+ else {
+ printf("Failed to create %s (%x)\n", cidStr, rv);
+ }
+ }
+
+ rv = NS_ShutdownXPCOM(servMgr);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
+}
diff --git a/xpcom/tests/TestStackCrawl.cpp b/xpcom/tests/TestStackCrawl.cpp
new file mode 100644
index 000000000..e1fe37db7
--- /dev/null
+++ b/xpcom/tests/TestStackCrawl.cpp
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+#include "nsISupportsUtils.h"
+#include "nsTraceRefcnt.h"
+
+int main(int argc, char* argv[])
+{
+ nsTraceRefcnt::WalkTheStack(stdout);
+ return 0;
+}
+
diff --git a/xpcom/tests/TestStreamUtils.cpp b/xpcom/tests/TestStreamUtils.cpp
new file mode 100644
index 000000000..72640474f
--- /dev/null
+++ b/xpcom/tests/TestStreamUtils.cpp
@@ -0,0 +1,74 @@
+/* -*- 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 <stdlib.h>
+#include <stdio.h>
+#include "nsIPipe.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+
+//----
+
+static bool test_consume_stream() {
+ const char kData[] =
+ "Get your facts first, and then you can distort them as much as you "
+ "please.";
+
+ nsCOMPtr<nsIInputStream> input;
+ nsCOMPtr<nsIOutputStream> output;
+ NS_NewPipe(getter_AddRefs(input),
+ getter_AddRefs(output),
+ 10, UINT32_MAX);
+ if (!input || !output)
+ return false;
+
+ uint32_t n = 0;
+ output->Write(kData, sizeof(kData) - 1, &n);
+ if (n != (sizeof(kData) - 1))
+ return false;
+ output = nullptr; // close output
+
+ nsCString buf;
+ if (NS_FAILED(NS_ConsumeStream(input, UINT32_MAX, buf)))
+ return false;
+
+ if (!buf.Equals(kData))
+ return false;
+
+ return true;
+}
+
+//----
+
+typedef bool (*TestFunc)();
+#define DECL_TEST(name) { #name, name }
+
+static const struct Test {
+ const char* name;
+ TestFunc func;
+} tests[] = {
+ DECL_TEST(test_consume_stream),
+ { nullptr, nullptr }
+};
+
+int main(int argc, char **argv) {
+ int count = 1;
+ if (argc > 1)
+ count = atoi(argv[1]);
+
+ if (NS_FAILED(NS_InitXPCOM2(nullptr, nullptr, nullptr)))
+ return -1;
+
+ while (count--) {
+ for (const Test* t = tests; t->name != nullptr; ++t) {
+ printf("%25s : %s\n", t->name, t->func() ? "SUCCESS" : "FAILURE");
+ }
+ }
+
+ NS_ShutdownXPCOM(nullptr);
+ return 0;
+}
diff --git a/xpcom/tests/TestUnicodeArguments.cpp b/xpcom/tests/TestUnicodeArguments.cpp
new file mode 100644
index 000000000..35d80a7f4
--- /dev/null
+++ b/xpcom/tests/TestUnicodeArguments.cpp
@@ -0,0 +1,77 @@
+/**
+ * On Windows, a Unicode argument is passed as UTF-16 using ShellExecuteExW.
+ * On other platforms, it is passed as UTF-8
+ */
+
+static const int args_length = 4;
+#if defined(XP_WIN) && defined(_MSC_VER)
+#define _UNICODE
+#include <tchar.h>
+#include <stdio.h>
+
+static const _TCHAR* expected_utf16[args_length] = {
+ // Latin-1
+ L"M\xF8z\xEEll\xE5",
+ // Cyrillic
+ L"\x41C\x43E\x437\x438\x43B\x43B\x430",
+ // Bengali
+ L"\x9AE\x9CB\x99C\x9BF\x9B2\x9BE",
+ // Cuneiform
+ L"\xD808\xDE2C\xD808\xDF63\xD808\xDDB7"
+};
+
+int wmain(int argc, _TCHAR* argv[]) {
+ printf("argc = %d\n", argc);
+
+ if (argc != args_length + 1)
+ return -1;
+
+ for (int i = 1; i < argc; ++i) {
+ printf("expected[%d]: ", i - 1);
+ for (size_t j = 0; j < _tcslen(expected_utf16[i - 1]); ++j) {
+ printf("%x ", *(expected_utf16[i - 1] + j));
+ }
+ printf("\n");
+
+ printf("argv[%d]: ", i);
+ for (size_t j = 0; j < _tcslen(argv[i]); ++j) {
+ printf("%x ", *(argv[i] + j));
+ }
+ printf("\n");
+
+ if (_tcscmp(expected_utf16[i - 1], argv[i])) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+#else
+#include <string.h>
+#include <stdio.h>
+
+static const char* expected_utf8[args_length] = {
+ // Latin-1
+ "M\xC3\xB8z\xC3\xAEll\xC3\xA5",
+ // Cyrillic
+ "\xD0\x9C\xD0\xBE\xD0\xB7\xD0\xB8\xD0\xBB\xD0\xBB\xD0\xB0",
+ // Bengali
+ "\xE0\xA6\xAE\xE0\xA7\x8B\xE0\xA6\x9C\xE0\xA6\xBF\xE0\xA6\xB2\xE0\xA6\xBE",
+ // Cuneiform
+ "\xF0\x92\x88\xAC\xF0\x92\x8D\xA3\xF0\x92\x86\xB7"
+};
+
+int main(int argc, char* argv[]) {
+ if (argc != args_length + 1)
+ return -1;
+
+ for (int i = 1; i < argc; ++i) {
+ printf("argv[%d] = %s; expected = %s\n", i, argv[i], expected_utf8[i - 1]);
+ if (strcmp(expected_utf8[i - 1], argv[i])) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+#endif
diff --git a/xpcom/tests/TestWinReg.js b/xpcom/tests/TestWinReg.js
new file mode 100644
index 000000000..5bde37900
--- /dev/null
+++ b/xpcom/tests/TestWinReg.js
@@ -0,0 +1,57 @@
+/* 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/. */
+
+/*
+ * This script is intended to be run using xpcshell
+ */
+
+const nsIWindowsRegKey = Components.interfaces.nsIWindowsRegKey;
+const BASE_PATH = "SOFTWARE\\Mozilla\\Firefox";
+
+function idump(indent, str)
+{
+ for (var j = 0; j < indent; ++j)
+ dump(" ");
+ dump(str);
+}
+
+function list_values(indent, key) {
+ idump(indent, "{\n");
+ var count = key.valueCount;
+ for (var i = 0; i < count; ++i) {
+ var vn = key.getValueName(i);
+ var val = "";
+ if (key.getValueType(vn) == nsIWindowsRegKey.TYPE_STRING) {
+ val = key.readStringValue(vn);
+ }
+ if (vn == "")
+ idump(indent + 1, "(Default): \"" + val + "\"\n");
+ else
+ idump(indent + 1, vn + ": \"" + val + "\"\n");
+ }
+ idump(indent, "}\n");
+}
+
+function list_children(indent, key) {
+ list_values(indent, key);
+
+ var count = key.childCount;
+ for (var i = 0; i < count; ++i) {
+ var cn = key.getChildName(i);
+ idump(indent, "[" + cn + "]\n");
+ list_children(indent + 1, key.openChild(cn, nsIWindowsRegKey.ACCESS_READ));
+ }
+}
+
+// enumerate everything under BASE_PATH
+var key = Components.classes["@mozilla.org/windows-registry-key;1"].
+ createInstance(nsIWindowsRegKey);
+key.open(nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, BASE_PATH,
+ nsIWindowsRegKey.ACCESS_READ);
+list_children(1, key);
+
+key.close();
+key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, BASE_PATH,
+ nsIWindowsRegKey.ACCESS_READ);
+list_children(1, key);
diff --git a/xpcom/tests/TestingAtomList.h b/xpcom/tests/TestingAtomList.h
new file mode 100644
index 000000000..ffeada605
--- /dev/null
+++ b/xpcom/tests/TestingAtomList.h
@@ -0,0 +1,6 @@
+/* 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/. */
+
+TESTING_ATOM(foo, "foo")
+TESTING_ATOM(bar, "bar")
diff --git a/xpcom/tests/bug656331_component/TestComponent.cpp b/xpcom/tests/bug656331_component/TestComponent.cpp
new file mode 100644
index 000000000..987d21451
--- /dev/null
+++ b/xpcom/tests/bug656331_component/TestComponent.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+
+// f18fb09b-28b4-4435-bc5b-8027f18df743
+#define NS_TESTING_CID \
+{ 0xf18fb09b, 0x28b4, 0x4435, \
+ { 0xbc, 0x5b, 0x80, 0x27, 0xf1, 0x8d, 0xf7, 0x43 } }
+
+NS_DEFINE_NAMED_CID(NS_TESTING_CID);
+
+static nsresult
+DummyConstructorFunc(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+static const mozilla::Module::CIDEntry kTestCIDs[] = {
+ { &kNS_TESTING_CID, false, nullptr, DummyConstructorFunc },
+ { nullptr }
+};
+
+static const mozilla::Module kTestModule = {
+ 3, /* faking mozilla::Module::kVersion with a value that will never be used */
+ kTestCIDs
+};
+
+NSMODULE_DEFN(dummy) = &kTestModule;
diff --git a/xpcom/tests/bug656331_component/bug656331.manifest b/xpcom/tests/bug656331_component/bug656331.manifest
new file mode 100644
index 000000000..fb1991a56
--- /dev/null
+++ b/xpcom/tests/bug656331_component/bug656331.manifest
@@ -0,0 +1,2 @@
+#filter substitution
+binary-component @LIBRARY_FILENAME@
diff --git a/xpcom/tests/bug656331_component/moz.build b/xpcom/tests/bug656331_component/moz.build
new file mode 100644
index 000000000..e986f3de0
--- /dev/null
+++ b/xpcom/tests/bug656331_component/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_TARGET = '_tests/xpcshell/xpcom/tests/unit'
+EXTRA_PP_COMPONENTS += [
+ 'bug656331.manifest',
+]
+
+SOURCES += [
+ 'TestComponent.cpp',
+]
+
+XPCOMBinaryComponent('test656331')
+
+DEFINES['LIBRARY_FILENAME'] = '%s%s%s' % (
+ CONFIG['DLL_PREFIX'],
+ LIBRARY_NAME,
+ CONFIG['DLL_SUFFIX']
+)
+
+# Need to link with CoreFoundation on Mac
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ OS_LIBS += CONFIG['TK_LIBS']
diff --git a/xpcom/tests/component/TestComponent.cpp b/xpcom/tests/component/TestComponent.cpp
new file mode 100644
index 000000000..85b8860ac
--- /dev/null
+++ b/xpcom/tests/component/TestComponent.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+
+#define NS_TESTING_CID \
+{ 0x335fb596, 0xe52d, 0x418f, \
+ { 0xb0, 0x1c, 0x1b, 0xf1, 0x6c, 0xe5, 0xe7, 0xe4 } }
+#define NS_NONEXISTENT_CID \
+{ 0x1e61fb15, 0xead4, 0x45cd, \
+ { 0x80, 0x13, 0x40, 0x99, 0xa7, 0x10, 0xa2, 0xfa } }
+
+NS_DEFINE_NAMED_CID(NS_TESTING_CID);
+NS_DEFINE_NAMED_CID(NS_NONEXISTENT_CID);
+
+static nsresult
+DummyConstructorFunc(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+static const mozilla::Module::CIDEntry kTestCIDs[] = {
+ { &kNS_TESTING_CID, false, nullptr, DummyConstructorFunc },
+ { &kNS_TESTING_CID, false, nullptr, DummyConstructorFunc },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kTestContractIDs[] = {
+ { "@testing/foo", &kNS_NONEXISTENT_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kTestModule = {
+ mozilla::Module::kVersion,
+ kTestCIDs,
+ kTestContractIDs
+};
+
+NSMODULE_DEFN(dummy) = &kTestModule;
+
+
diff --git a/xpcom/tests/component/moz.build b/xpcom/tests/component/moz.build
new file mode 100644
index 000000000..62e88502c
--- /dev/null
+++ b/xpcom/tests/component/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_TARGET = '_tests/xpcshell/xpcom/tests/unit'
+EXTRA_PP_COMPONENTS += [
+ 'testcomponent.manifest',
+]
+
+SOURCES += [
+ 'TestComponent.cpp',
+]
+
+XPCOMBinaryComponent('testcomponent')
+
+DEFINES['LIBRARY_FILENAME'] = '%s%s%s' % (
+ CONFIG['DLL_PREFIX'],
+ LIBRARY_NAME,
+ CONFIG['DLL_SUFFIX']
+)
+
+# Need to link with CoreFoundation on Mac
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ OS_LIBS += CONFIG['TK_LIBS']
diff --git a/xpcom/tests/component/testcomponent.manifest b/xpcom/tests/component/testcomponent.manifest
new file mode 100644
index 000000000..a570e4c18
--- /dev/null
+++ b/xpcom/tests/component/testcomponent.manifest
@@ -0,0 +1,4 @@
+#filter substitution
+binary-component @LIBRARY_FILENAME@
+binary-component @LIBRARY_FILENAME@
+binary-component @LIBRARY_FILENAME@
diff --git a/xpcom/tests/component_no_aslr/Makefile.in b/xpcom/tests/component_no_aslr/Makefile.in
new file mode 100644
index 000000000..f08d6ad8a
--- /dev/null
+++ b/xpcom/tests/component_no_aslr/Makefile.in
@@ -0,0 +1,8 @@
+#
+# 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 $(topsrcdir)/config/rules.mk
+
+LDFLAGS := $(filter-out -DYNAMICBASE,$(LDFLAGS)) -DYNAMICBASE:NO
diff --git a/xpcom/tests/component_no_aslr/TestComponent.cpp b/xpcom/tests/component_no_aslr/TestComponent.cpp
new file mode 100644
index 000000000..6fbfd316a
--- /dev/null
+++ b/xpcom/tests/component_no_aslr/TestComponent.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+
+#define NS_TESTING_CID \
+{ 0x335fb596, 0xe52d, 0x418f, \
+ { 0xb0, 0x1c, 0x1b, 0xf1, 0x6c, 0xe5, 0xe7, 0xe4 } }
+
+NS_DEFINE_NAMED_CID(NS_TESTING_CID);
+
+static nsresult
+DummyConstructorFunc(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+static const mozilla::Module::CIDEntry kTestCIDs[] = {
+ { &kNS_TESTING_CID, false, nullptr, DummyConstructorFunc },
+ { nullptr }
+};
+
+static const mozilla::Module kTestModule = {
+ mozilla::Module::kVersion,
+ kTestCIDs
+};
+
+NSMODULE_DEFN(dummy) = &kTestModule;
+
+
diff --git a/xpcom/tests/component_no_aslr/moz.build b/xpcom/tests/component_no_aslr/moz.build
new file mode 100644
index 000000000..45e69c105
--- /dev/null
+++ b/xpcom/tests/component_no_aslr/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_TARGET = '_tests/xpcshell/xpcom/tests/unit'
+EXTRA_PP_COMPONENTS += [
+ 'testcompnoaslr.manifest',
+]
+
+SOURCES += [
+ 'TestComponent.cpp',
+]
+
+XPCOMBinaryComponent('testcompnoaslr')
+
+DEFINES['LIBRARY_FILENAME'] = '%s%s%s' % (
+ CONFIG['DLL_PREFIX'],
+ LIBRARY_NAME,
+ CONFIG['DLL_SUFFIX']
+)
+
+# Need to link with CoreFoundation on Mac
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ OS_LIBS += CONFIG['TK_LIBS']
diff --git a/xpcom/tests/component_no_aslr/testcompnoaslr.manifest b/xpcom/tests/component_no_aslr/testcompnoaslr.manifest
new file mode 100644
index 000000000..fb1991a56
--- /dev/null
+++ b/xpcom/tests/component_no_aslr/testcompnoaslr.manifest
@@ -0,0 +1,2 @@
+#filter substitution
+binary-component @LIBRARY_FILENAME@
diff --git a/xpcom/tests/external/TestMinStringAPI.cpp b/xpcom/tests/external/TestMinStringAPI.cpp
new file mode 100644
index 000000000..28bc0b1c1
--- /dev/null
+++ b/xpcom/tests/external/TestMinStringAPI.cpp
@@ -0,0 +1,1009 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nsStringAPI.h"
+#include "nsXPCOM.h"
+#include "nsMemory.h"
+
+static const char kAsciiData[] = "Hello World";
+
+static const char16_t kUnicodeData[] =
+ {'H','e','l','l','o',' ','W','o','r','l','d','\0'};
+
+static bool test_basic_1()
+ {
+ nsCStringContainer s;
+ NS_CStringContainerInit(s);
+
+ const char *ptr;
+ uint32_t len;
+ char *clone;
+
+ NS_CStringGetData(s, &ptr);
+ if (ptr == nullptr || *ptr != '\0')
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+
+ NS_CStringSetData(s, kAsciiData, UINT32_MAX);
+ len = NS_CStringGetData(s, &ptr);
+ if (ptr == nullptr || strcmp(ptr, kAsciiData) != 0)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+ if (len != sizeof(kAsciiData)-1)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+
+ clone = NS_CStringCloneData(s);
+ if (ptr == nullptr || strcmp(ptr, kAsciiData) != 0)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+ free(clone);
+
+ nsCStringContainer temp;
+ NS_CStringContainerInit(temp);
+ NS_CStringCopy(temp, s);
+
+ len = NS_CStringGetData(temp, &ptr);
+ if (ptr == nullptr || strcmp(ptr, kAsciiData) != 0)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+ if (len != sizeof(kAsciiData)-1)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+
+ NS_CStringContainerFinish(temp);
+
+ NS_CStringContainerFinish(s);
+ return true;
+ }
+
+static bool test_basic_2()
+ {
+ nsStringContainer s;
+ NS_StringContainerInit(s);
+
+ const char16_t *ptr;
+ uint32_t len;
+ char16_t *clone;
+
+ NS_StringGetData(s, &ptr);
+ if (ptr == nullptr || *ptr != '\0')
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+
+ NS_StringSetData(s, kUnicodeData, UINT32_MAX);
+ len = NS_StringGetData(s, &ptr);
+ if (len != sizeof(kUnicodeData)/2 - 1)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+ if (ptr == nullptr || memcmp(ptr, kUnicodeData, sizeof(kUnicodeData)) != 0)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+
+ clone = NS_StringCloneData(s);
+ if (ptr == nullptr || memcmp(ptr, kUnicodeData, sizeof(kUnicodeData)) != 0)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+ free(clone);
+
+ nsStringContainer temp;
+ NS_StringContainerInit(temp);
+ NS_StringCopy(temp, s);
+
+ len = NS_StringGetData(temp, &ptr);
+ if (len != sizeof(kUnicodeData)/2 - 1)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+ if (ptr == nullptr || memcmp(ptr, kUnicodeData, sizeof(kUnicodeData)) != 0)
+ {
+ NS_ERROR("unexpected result");
+ return false;
+ }
+
+ NS_StringContainerFinish(temp);
+
+ NS_StringContainerFinish(s);
+
+ return true;
+ }
+
+static bool test_convert()
+ {
+ nsStringContainer s;
+ NS_StringContainerInit(s);
+ NS_StringSetData(s, kUnicodeData, sizeof(kUnicodeData)/2 - 1);
+
+ nsCStringContainer temp;
+ NS_CStringContainerInit(temp);
+
+ const char *data;
+
+ NS_UTF16ToCString(s, NS_CSTRING_ENCODING_ASCII, temp);
+ NS_CStringGetData(temp, &data);
+ if (strcmp(data, kAsciiData) != 0)
+ return false;
+
+ NS_UTF16ToCString(s, NS_CSTRING_ENCODING_UTF8, temp);
+ NS_CStringGetData(temp, &data);
+ if (strcmp(data, kAsciiData) != 0)
+ return false;
+
+ NS_CStringContainerFinish(temp);
+
+ NS_StringContainerFinish(s);
+ return true;
+ }
+
+static bool test_append()
+ {
+ nsCStringContainer s;
+ NS_CStringContainerInit(s);
+
+ NS_CStringSetData(s, "foo");
+ NS_CStringAppendData(s, "bar");
+
+ NS_CStringContainerFinish(s);
+ return true;
+ }
+
+// Replace all occurrences of |matchVal| with |newVal|
+static void ReplaceSubstring( nsACString& str,
+ const nsACString& matchVal,
+ const nsACString& newVal )
+ {
+ const char* sp;
+ const char* mp;
+ const char* np;
+ uint32_t sl = NS_CStringGetData(str, &sp);
+ uint32_t ml = NS_CStringGetData(matchVal, &mp);
+ uint32_t nl = NS_CStringGetData(newVal, &np);
+
+ for (const char* iter = sp; iter <= sp + sl - ml; ++iter)
+ {
+ if (memcmp(iter, mp, ml) == 0)
+ {
+ uint32_t offset = iter - sp;
+
+ NS_CStringSetDataRange(str, offset, ml, np, nl);
+
+ sl = NS_CStringGetData(str, &sp);
+
+ iter = sp + offset + nl - 1;
+ }
+ }
+ }
+
+static bool test_replace_driver(const char *strVal,
+ const char *matchVal,
+ const char *newVal,
+ const char *finalVal)
+ {
+ nsCStringContainer a;
+ NS_CStringContainerInit(a);
+ NS_CStringSetData(a, strVal);
+
+ nsCStringContainer b;
+ NS_CStringContainerInit(b);
+ NS_CStringSetData(b, matchVal);
+
+ nsCStringContainer c;
+ NS_CStringContainerInit(c);
+ NS_CStringSetData(c, newVal);
+
+ ReplaceSubstring(a, b, c);
+
+ const char *data;
+ NS_CStringGetData(a, &data);
+ if (strcmp(data, finalVal) != 0)
+ return false;
+
+ NS_CStringContainerFinish(c);
+ NS_CStringContainerFinish(b);
+ NS_CStringContainerFinish(a);
+ return true;
+ }
+
+static bool test_replace()
+ {
+ bool rv;
+
+ rv = test_replace_driver("hello world, hello again!",
+ "hello",
+ "goodbye",
+ "goodbye world, goodbye again!");
+ if (!rv)
+ return rv;
+
+ rv = test_replace_driver("foofoofoofoo!",
+ "foo",
+ "bar",
+ "barbarbarbar!");
+ if (!rv)
+ return rv;
+
+ rv = test_replace_driver("foo bar systems",
+ "xyz",
+ "crazy",
+ "foo bar systems");
+ if (!rv)
+ return rv;
+
+ rv = test_replace_driver("oh",
+ "xyz",
+ "crazy",
+ "oh");
+ if (!rv)
+ return rv;
+
+ return true;
+ }
+
+static const char* kWhitespace="\f\t\r\n ";
+
+static void
+CompressWhitespace(nsACString &str)
+ {
+ const char *p;
+ int32_t i, len = (int32_t) NS_CStringGetData(str, &p);
+
+ // trim leading whitespace
+
+ for (i=0; i<len; ++i)
+ {
+ if (!strchr(kWhitespace, (char) p[i]))
+ break;
+ }
+
+ if (i>0)
+ {
+ NS_CStringCutData(str, 0, i);
+ len = (int32_t) NS_CStringGetData(str, &p);
+ }
+
+ // trim trailing whitespace
+
+ for (i=len-1; i>=0; --i)
+ {
+ if (!strchr(kWhitespace, (char) p[i]))
+ break;
+ }
+
+ if (++i < len)
+ NS_CStringCutData(str, i, len - i);
+ }
+
+static bool test_compress_ws()
+ {
+ nsCStringContainer s;
+ NS_CStringContainerInit(s);
+ NS_CStringSetData(s, " \thello world\r \n");
+ CompressWhitespace(s);
+ const char *d;
+ NS_CStringGetData(s, &d);
+ bool rv = !strcmp(d, "hello world");
+ if (!rv)
+ printf("=> \"%s\"\n", d);
+ NS_CStringContainerFinish(s);
+ return rv;
+ }
+
+static bool test_depend()
+ {
+ static const char kData[] = "hello world";
+
+ nsCStringContainer s;
+ NS_ENSURE_SUCCESS(
+ NS_CStringContainerInit2(s, kData, sizeof(kData)-1,
+ NS_CSTRING_CONTAINER_INIT_DEPEND),
+ false);
+
+ const char *sd;
+ NS_CStringGetData(s, &sd);
+
+ bool rv = (sd == kData);
+ NS_CStringContainerFinish(s);
+ return rv;
+ }
+
+static bool test_depend_sub()
+ {
+ static const char kData[] = "hello world";
+
+ nsCStringContainer s;
+ NS_ENSURE_SUCCESS(
+ NS_CStringContainerInit2(s, kData, sizeof(kData)-1,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING),
+ false);
+
+ bool terminated;
+ const char *sd;
+ uint32_t len = NS_CStringGetData(s, &sd, &terminated);
+
+ bool rv = (sd == kData && len == sizeof(kData)-1 && !terminated);
+ NS_CStringContainerFinish(s);
+ return rv;
+ }
+
+static bool test_adopt()
+ {
+ static const char kData[] = "hello world";
+
+ char *data = (char *) nsMemory::Clone(kData, sizeof(kData));
+ if (!data)
+ return false;
+
+ nsCStringContainer s;
+ NS_ENSURE_SUCCESS(
+ NS_CStringContainerInit2(s, data, UINT32_MAX,
+ NS_CSTRING_CONTAINER_INIT_ADOPT),
+ false); // leaks data on failure *shrug*
+
+ const char *sd;
+ NS_CStringGetData(s, &sd);
+
+ bool rv = (sd == data);
+ NS_CStringContainerFinish(s);
+ return rv;
+ }
+
+static bool test_adopt_sub()
+ {
+ static const char kData[] = "hello world";
+
+ char *data = (char *) nsMemory::Clone(kData, sizeof(kData)-1);
+ if (!data)
+ return false;
+
+ nsCStringContainer s;
+ NS_ENSURE_SUCCESS(
+ NS_CStringContainerInit2(s, data, sizeof(kData)-1,
+ NS_CSTRING_CONTAINER_INIT_ADOPT |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING),
+ false); // leaks data on failure *shrug*
+
+ bool terminated;
+ const char *sd;
+ uint32_t len = NS_CStringGetData(s, &sd, &terminated);
+
+ bool rv = (sd == data && len == sizeof(kData)-1 && !terminated);
+ NS_CStringContainerFinish(s);
+ return rv;
+ }
+
+static bool test_mutation()
+ {
+ nsCStringContainer s;
+ NS_CStringContainerInit(s);
+
+ const char kText[] = "Every good boy does fine.";
+
+ char *buf;
+ uint32_t len = NS_CStringGetMutableData(s, sizeof(kText) - 1, &buf);
+ if (!buf || len != sizeof(kText) - 1)
+ return false;
+ memcpy(buf, kText, sizeof(kText));
+
+ const char *data;
+ NS_CStringGetData(s, &data);
+ if (strcmp(data, kText) != 0)
+ return false;
+
+ uint32_t newLen = len + 1;
+ len = NS_CStringGetMutableData(s, newLen, &buf);
+ if (!buf || len != newLen)
+ return false;
+
+ buf[len - 1] = '.';
+
+ NS_CStringGetData(s, &data);
+ if (strncmp(data, kText, len - 1) != 0 || data[len - 1] != '.')
+ return false;
+
+ NS_CStringContainerFinish(s);
+ return true;
+ }
+
+static bool test_ascii()
+{
+ nsCString testCString;
+ testCString.AppendASCII(kAsciiData);
+ if (!testCString.EqualsLiteral(kAsciiData))
+ return false;
+
+ testCString.AssignASCII(kAsciiData);
+ if (!testCString.LowerCaseEqualsLiteral("hello world"))
+ return false;
+
+ nsString testString;
+ testString.AppendASCII(kAsciiData);
+ if (!testString.EqualsLiteral(kAsciiData))
+ return false;
+
+ testString.AssignASCII(kAsciiData);
+ if (!testString.LowerCaseEqualsLiteral("hello world"))
+ return false;
+
+ return true;
+}
+
+static bool test_chars()
+{
+ nsCString testCString(kAsciiData);
+ if (testCString.First() != 'H')
+ return false;
+ if (testCString.Last() != 'd')
+ return false;
+ testCString.SetCharAt('u', 8);
+ if (!testCString.EqualsASCII("Hello Would"))
+ return false;
+
+ nsString testString(kUnicodeData);
+ if (testString.First() != 'H')
+ return false;
+ if (testString.Last() != 'd')
+ return false;
+ testString.SetCharAt('u', 8);
+ if (!testString.EqualsASCII("Hello Would"))
+ return false;
+
+ return true;
+}
+
+static bool test_stripchars()
+{
+ nsCString test(kAsciiData);
+ test.StripChars("ld");
+ if (!test.EqualsLiteral("Heo Wor"))
+ return false;
+
+ test.Assign(kAsciiData);
+ test.StripWhitespace();
+ if (!test.EqualsLiteral("HelloWorld"))
+ return false;
+
+ return true;
+}
+
+static bool test_trim()
+{
+ static const char kWS[] = "\n\t\r ";
+ static const char kTestString[] = " \n\tTesting...\n\r";
+
+ nsCString test1(kTestString);
+ nsCString test2(kTestString);
+ nsCString test3(kTestString);
+
+ test1.Trim(kWS);
+ test2.Trim(kWS, true, false);
+ test3.Trim(kWS, false, true);
+
+ if (!test1.EqualsLiteral("Testing..."))
+ return false;
+
+ if (!test2.EqualsLiteral("Testing...\n\r"))
+ return false;
+
+ if (!test3.EqualsLiteral(" \n\tTesting..."))
+ return false;
+
+ return true;
+}
+
+static bool test_find()
+{
+ nsString uni(kUnicodeData);
+
+ static const char kHello[] = "Hello";
+ static const char khello[] = "hello";
+ static const char kBye[] = "Bye!";
+
+ int32_t found;
+
+ found = uni.Find(kHello);
+ if (found != 0)
+ return false;
+
+ found = uni.Find(khello, false);
+ if (found != -1)
+ return false;
+
+ found = uni.Find(khello, true);
+ if (found != 0)
+ return false;
+
+ found = uni.Find(kBye);
+ if (found != -1)
+ return false;
+
+ found = uni.Find(NS_LITERAL_STRING("World"));
+ if (found != 6)
+ return false;
+
+ found = uni.Find(uni);
+ if (found != 0)
+ return false;
+
+ return true;
+}
+
+static bool test_compressws()
+{
+ nsString check(NS_LITERAL_STRING(" \tTesting \n\t1\n 2 3\n "));
+ CompressWhitespace(check);
+ return check.EqualsLiteral("Testing 1 2 3");
+}
+
+static bool test_comparisons()
+{
+ bool result;
+
+ // nsString
+
+ NS_NAMED_LITERAL_STRING(shortString1, "Foo");
+ NS_NAMED_LITERAL_STRING(shortString2, "Bar");
+ NS_NAMED_LITERAL_STRING(shortString3, "Bar");
+ NS_NAMED_LITERAL_STRING(shortString4, "bar");
+ NS_NAMED_LITERAL_STRING(longString, "FooBar");
+
+ // ==
+
+ result = (shortString1 == shortString2);
+ if (result)
+ return false;
+
+ result = (shortString2 == shortString3);
+ if (!result)
+ return false;
+
+ result = (shortString3 == shortString4);
+ if (result)
+ return false;
+
+ result = (shortString1 == longString);
+ if (result)
+ return false;
+
+ result = (longString == shortString1);
+ if (result)
+ return false;
+
+ // !=
+
+ result = (shortString1 != shortString2);
+ if (!result)
+ return false;
+
+ result = (shortString2 != shortString3);
+ if (result)
+ return false;
+
+ result = (shortString3 != shortString4);
+ if (!result)
+ return false;
+
+ result = (shortString1 != longString);
+ if (!result)
+ return false;
+
+ result = (longString != shortString1);
+ if (!result)
+ return false;
+
+ // <
+
+ result = (shortString1 < shortString2);
+ if (result)
+ return false;
+
+ result = (shortString2 < shortString1);
+ if (!result)
+ return false;
+
+ result = (shortString1 < longString);
+ if (!result)
+ return false;
+
+ result = (longString < shortString1);
+ if (result)
+ return false;
+
+ result = (shortString2 < shortString3);
+ if (result)
+ return false;
+
+ result = (shortString3 < shortString4);
+ if (!result)
+ return false;
+
+ result = (shortString4 < shortString3);
+ if (result)
+ return false;
+
+ // <=
+
+ result = (shortString1 <= shortString2);
+ if (result)
+ return false;
+
+ result = (shortString2 <= shortString1);
+ if (!result)
+ return false;
+
+ result = (shortString1 <= longString);
+ if (!result)
+ return false;
+
+ result = (longString <= shortString1);
+ if (result)
+ return false;
+
+ result = (shortString2 <= shortString3);
+ if (!result)
+ return false;
+
+ result = (shortString3 <= shortString4);
+ if (!result)
+ return false;
+
+ result = (shortString4 <= shortString3);
+ if (result)
+ return false;
+
+ // >
+
+ result = (shortString1 > shortString2);
+ if (!result)
+ return false;
+
+ result = (shortString2 > shortString1);
+ if (result)
+ return false;
+
+ result = (shortString1 > longString);
+ if (result)
+ return false;
+
+ result = (longString > shortString1);
+ if (!result)
+ return false;
+
+ result = (shortString2 > shortString3);
+ if (result)
+ return false;
+
+ result = (shortString3 > shortString4);
+ if (result)
+ return false;
+
+ result = (shortString4 > shortString3);
+ if (!result)
+ return false;
+
+ // >=
+
+ result = (shortString1 >= shortString2);
+ if (!result)
+ return false;
+
+ result = (shortString2 >= shortString1);
+ if (result)
+ return false;
+
+ result = (shortString1 >= longString);
+ if (result)
+ return false;
+
+ result = (longString >= shortString1);
+ if (!result)
+ return false;
+
+ result = (shortString2 >= shortString3);
+ if (!result)
+ return false;
+
+ result = (shortString3 >= shortString4);
+ if (result)
+ return false;
+
+ result = (shortString4 >= shortString3);
+ if (!result)
+ return false;
+
+ // nsCString
+
+ NS_NAMED_LITERAL_CSTRING(shortCString1, "Foo");
+ NS_NAMED_LITERAL_CSTRING(shortCString2, "Bar");
+ NS_NAMED_LITERAL_CSTRING(shortCString3, "Bar");
+ NS_NAMED_LITERAL_CSTRING(shortCString4, "bar");
+ NS_NAMED_LITERAL_CSTRING(longCString, "FooBar");
+
+ // ==
+
+ result = (shortCString1 == shortCString2);
+ if (result)
+ return false;
+
+ result = (shortCString2 == shortCString3);
+ if (!result)
+ return false;
+
+ result = (shortCString3 == shortCString4);
+ if (result)
+ return false;
+
+ result = (shortCString1 == longCString);
+ if (result)
+ return false;
+
+ result = (longCString == shortCString1);
+ if (result)
+ return false;
+
+ // !=
+
+ result = (shortCString1 != shortCString2);
+ if (!result)
+ return false;
+
+ result = (shortCString2 != shortCString3);
+ if (result)
+ return false;
+
+ result = (shortCString3 != shortCString4);
+ if (!result)
+ return false;
+
+ result = (shortCString1 != longCString);
+ if (!result)
+ return false;
+
+ result = (longCString != shortCString1);
+ if (!result)
+ return false;
+
+ // <
+
+ result = (shortCString1 < shortCString2);
+ if (result)
+ return false;
+
+ result = (shortCString2 < shortCString1);
+ if (!result)
+ return false;
+
+ result = (shortCString1 < longCString);
+ if (!result)
+ return false;
+
+ result = (longCString < shortCString1);
+ if (result)
+ return false;
+
+ result = (shortCString2 < shortCString3);
+ if (result)
+ return false;
+
+ result = (shortCString3 < shortCString4);
+ if (!result)
+ return false;
+
+ result = (shortCString4 < shortCString3);
+ if (result)
+ return false;
+
+ // <=
+
+ result = (shortCString1 <= shortCString2);
+ if (result)
+ return false;
+
+ result = (shortCString2 <= shortCString1);
+ if (!result)
+ return false;
+
+ result = (shortCString1 <= longCString);
+ if (!result)
+ return false;
+
+ result = (longCString <= shortCString1);
+ if (result)
+ return false;
+
+ result = (shortCString2 <= shortCString3);
+ if (!result)
+ return false;
+
+ result = (shortCString3 <= shortCString4);
+ if (!result)
+ return false;
+
+ result = (shortCString4 <= shortCString3);
+ if (result)
+ return false;
+
+ // >
+
+ result = (shortCString1 > shortCString2);
+ if (!result)
+ return false;
+
+ result = (shortCString2 > shortCString1);
+ if (result)
+ return false;
+
+ result = (shortCString1 > longCString);
+ if (result)
+ return false;
+
+ result = (longCString > shortCString1);
+ if (!result)
+ return false;
+
+ result = (shortCString2 > shortCString3);
+ if (result)
+ return false;
+
+ result = (shortCString3 > shortCString4);
+ if (result)
+ return false;
+
+ result = (shortCString4 > shortCString3);
+ if (!result)
+ return false;
+
+ // >=
+
+ result = (shortCString1 >= shortCString2);
+ if (!result)
+ return false;
+
+ result = (shortCString2 >= shortCString1);
+ if (result)
+ return false;
+
+ result = (shortCString1 >= longCString);
+ if (result)
+ return false;
+
+ result = (longCString >= shortCString1);
+ if (!result)
+ return false;
+
+ result = (shortCString2 >= shortCString3);
+ if (!result)
+ return false;
+
+ result = (shortCString3 >= shortCString4);
+ if (result)
+ return false;
+
+ result = (shortCString4 >= shortCString3);
+ if (!result)
+ return false;
+
+ return true;
+}
+
+static bool test_parse_string_helper(const char* str, char separator, int len,
+ const char* s1, const char* s2)
+{
+ nsCString data(str);
+ nsTArray<nsCString> results;
+ if (!ParseString(data, separator, results))
+ return false;
+ if (int(results.Length()) != len)
+ return false;
+ const char* strings[] = { s1, s2 };
+ for (int i = 0; i < len; ++i) {
+ if (!results[i].Equals(strings[i]))
+ return false;
+ }
+ return true;
+}
+
+static bool test_parse_string_helper0(const char* str, char separator)
+{
+ return test_parse_string_helper(str, separator, 0, nullptr, nullptr);
+}
+
+static bool test_parse_string_helper1(const char* str, char separator, const char* s1)
+{
+ return test_parse_string_helper(str, separator, 1, s1, nullptr);
+}
+
+static bool test_parse_string_helper2(const char* str, char separator, const char* s1, const char* s2)
+{
+ return test_parse_string_helper(str, separator, 2, s1, s2);
+}
+
+static bool test_parse_string()
+{
+ return test_parse_string_helper1("foo, bar", '_', "foo, bar") &&
+ test_parse_string_helper2("foo, bar", ',', "foo", " bar") &&
+ test_parse_string_helper2("foo, bar ", ' ', "foo,", "bar") &&
+ test_parse_string_helper2("foo,bar", 'o', "f", ",bar") &&
+ test_parse_string_helper0("", '_') &&
+ test_parse_string_helper0(" ", ' ') &&
+ test_parse_string_helper1(" foo", ' ', "foo") &&
+ test_parse_string_helper1(" foo", ' ', "foo");
+}
+
+//----
+
+typedef bool (*TestFunc)();
+
+static const struct Test
+ {
+ const char* name;
+ TestFunc func;
+ }
+tests[] =
+ {
+ { "test_basic_1", test_basic_1 },
+ { "test_basic_2", test_basic_2 },
+ { "test_convert", test_convert },
+ { "test_append", test_append },
+ { "test_replace", test_replace },
+ { "test_compress_ws", test_compress_ws },
+ { "test_depend", test_depend },
+ { "test_depend_sub", test_depend_sub },
+ { "test_adopt", test_adopt },
+ { "test_adopt_sub", test_adopt_sub },
+ { "test_mutation", test_mutation },
+ { "test_ascii", test_ascii },
+ { "test_chars", test_chars },
+ { "test_stripchars", test_stripchars },
+ { "test_trim", test_trim },
+ { "test_find", test_find },
+ { "test_compressws", test_compressws },
+ { "test_comparisons", test_comparisons },
+ { "test_parse_string", test_parse_string },
+ { nullptr, nullptr }
+ };
+
+//----
+
+int main(int argc, char **argv)
+ {
+ int count = 1;
+ if (argc > 1)
+ count = atoi(argv[1]);
+
+ while (count--)
+ {
+ for (const Test* t = tests; t->name != nullptr; ++t)
+ {
+ printf("%25s : %s\n", t->name, t->func() ? "SUCCESS" : "FAILURE");
+ }
+ }
+
+ return 0;
+ }
diff --git a/xpcom/tests/external/moz.build b/xpcom/tests/external/moz.build
new file mode 100644
index 000000000..d64560b58
--- /dev/null
+++ b/xpcom/tests/external/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+GeckoSimplePrograms([
+ 'TestMinStringAPI',
+])
diff --git a/xpcom/tests/gtest/Helpers.cpp b/xpcom/tests/gtest/Helpers.cpp
new file mode 100644
index 000000000..e06ef901b
--- /dev/null
+++ b/xpcom/tests/gtest/Helpers.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Helper routines for xpcom gtests. */
+
+#include "Helpers.h"
+
+#include <algorithm>
+#include "gtest/gtest.h"
+#include "nsIOutputStream.h"
+#include "nsStreamUtils.h"
+#include "nsTArray.h"
+
+namespace testing {
+
+// Populate an array with the given number of bytes. Data is lorem ipsum
+// random text, but deterministic across multiple calls.
+void
+CreateData(uint32_t aNumBytes, nsTArray<char>& aDataOut)
+{
+ static const char data[] =
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec egestas "
+ "purus eu condimentum iaculis. In accumsan leo eget odio porttitor, non "
+ "rhoncus nulla vestibulum. Etiam lacinia consectetur nisl nec "
+ "sollicitudin. Sed fringilla accumsan diam, pulvinar varius massa. Duis "
+ "mollis dignissim felis, eget tempus nisi tristique ut. Fusce euismod, "
+ "lectus non lacinia tempor, tellus diam suscipit quam, eget hendrerit "
+ "lacus nunc fringilla ante. Sed ultrices massa vitae risus molestie, ut "
+ "finibus quam laoreet nullam.";
+ static const uint32_t dataLength = sizeof(data) - 1;
+
+ aDataOut.SetCapacity(aNumBytes);
+
+ while (aNumBytes > 0) {
+ uint32_t amount = std::min(dataLength, aNumBytes);
+ aDataOut.AppendElements(data, amount);
+ aNumBytes -= amount;
+ }
+}
+
+// Write the given number of bytes out to the stream. Loop until expected
+// bytes count is reached or an error occurs.
+void
+Write(nsIOutputStream* aStream, const nsTArray<char>& aData, uint32_t aOffset,
+ uint32_t aNumBytes)
+{
+ uint32_t remaining =
+ std::min(aNumBytes, static_cast<uint32_t>(aData.Length() - aOffset));
+
+ while (remaining > 0) {
+ uint32_t numWritten;
+ nsresult rv = aStream->Write(aData.Elements() + aOffset, remaining,
+ &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ if (numWritten < 1) {
+ break;
+ }
+ aOffset += numWritten;
+ remaining -= numWritten;
+ }
+}
+
+// Write the given number of bytes and then close the stream.
+void
+WriteAllAndClose(nsIOutputStream* aStream, const nsTArray<char>& aData)
+{
+ Write(aStream, aData, 0, aData.Length());
+ aStream->Close();
+}
+
+// Synchronously consume the given input stream and validate the resulting data
+// against the given array of expected values.
+void
+ConsumeAndValidateStream(nsIInputStream* aStream,
+ const nsTArray<char>& aExpectedData)
+{
+ nsDependentCSubstring data(aExpectedData.Elements(), aExpectedData.Length());
+ ConsumeAndValidateStream(aStream, data);
+}
+
+// Synchronously consume the given input stream and validate the resulting data
+// against the given string of expected values.
+void
+ConsumeAndValidateStream(nsIInputStream* aStream,
+ const nsACString& aExpectedData)
+{
+ nsAutoCString outputData;
+ nsresult rv = NS_ConsumeStream(aStream, UINT32_MAX, outputData);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_EQ(aExpectedData.Length(), outputData.Length());
+ ASSERT_TRUE(aExpectedData.Equals(outputData));
+}
+
+NS_IMPL_ISUPPORTS(OutputStreamCallback, nsIOutputStreamCallback);
+
+OutputStreamCallback::OutputStreamCallback()
+ : mCalled(false)
+{
+}
+
+OutputStreamCallback::~OutputStreamCallback()
+{
+}
+
+NS_IMETHODIMP
+OutputStreamCallback::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
+{
+ mCalled = true;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(InputStreamCallback, nsIInputStreamCallback);
+
+InputStreamCallback::InputStreamCallback()
+ : mCalled(false)
+{
+}
+
+InputStreamCallback::~InputStreamCallback()
+{
+}
+
+NS_IMETHODIMP
+InputStreamCallback::OnInputStreamReady(nsIAsyncInputStream* aStream)
+{
+ mCalled = true;
+ return NS_OK;
+}
+
+} // namespace testing
diff --git a/xpcom/tests/gtest/Helpers.h b/xpcom/tests/gtest/Helpers.h
new file mode 100644
index 000000000..9cee1b825
--- /dev/null
+++ b/xpcom/tests/gtest/Helpers.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __Helpers_h
+#define __Helpers_h
+
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsString.h"
+#include "nsTArrayForwardDeclare.h"
+#include <stdint.h>
+
+class nsIInputStream;
+class nsIOutputStream;
+
+namespace testing {
+
+void
+CreateData(uint32_t aNumBytes, nsTArray<char>& aDataOut);
+
+void
+Write(nsIOutputStream* aStream, const nsTArray<char>& aData, uint32_t aOffset,
+ uint32_t aNumBytes);
+
+void
+WriteAllAndClose(nsIOutputStream* aStream, const nsTArray<char>& aData);
+
+void
+ConsumeAndValidateStream(nsIInputStream* aStream,
+ const nsTArray<char>& aExpectedData);
+
+void
+ConsumeAndValidateStream(nsIInputStream* aStream,
+ const nsACString& aExpectedData);
+
+class OutputStreamCallback final : public nsIOutputStreamCallback
+{
+public:
+ OutputStreamCallback();
+
+ bool Called() const { return mCalled; }
+
+private:
+ ~OutputStreamCallback();
+
+ bool mCalled;
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOUTPUTSTREAMCALLBACK
+};
+
+class InputStreamCallback final : public nsIInputStreamCallback
+{
+public:
+ InputStreamCallback();
+
+ bool Called() const { return mCalled; }
+
+private:
+ ~InputStreamCallback();
+
+ bool mCalled;
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+};
+
+} // namespace testing
+
+#endif // __Helpers_h
diff --git a/xpcom/tests/gtest/TestAllocReplacement.cpp b/xpcom/tests/gtest/TestAllocReplacement.cpp
new file mode 100644
index 000000000..7bcf3d4ad
--- /dev/null
+++ b/xpcom/tests/gtest/TestAllocReplacement.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Function.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/ScopeExit.h"
+#include "nsCOMPtr.h"
+#include "nsIMemoryReporter.h"
+#include "nsServiceManagerUtils.h"
+#include "gtest/gtest.h"
+
+// We want to ensure that various functions are hooked properly and that
+// allocations are getting routed through jemalloc. The strategy
+// pursued below relies on jemalloc's statistics tracking: we measure
+// the size of the jemalloc heap using nsIMemoryReporterManager,
+// allocate a chunk of memory with whatever function is supposed to be
+// hooked, and then ask for the size of the jemalloc heap again. If the
+// function has been hooked correctly, then the heap size should be
+// different between the two measurements. We can also check the
+// hooking of |free| and similar functions: once we free() the returned
+// pointer, we can measure the jemalloc heap size again, expecting it to
+// be identical to the size prior to the allocation.
+//
+// If we're not using jemalloc, then nsIMemoryReporterManager will
+// simply report an error, and we will ignore the entire test.
+//
+// This strategy is not perfect: it relies on GTests being
+// single-threaded, which they are, and no other threads doing
+// allocation during the test, which is uncertain, as XPCOM has started
+// up during gtests, and who knows what might be going on behind the
+// scenes. This latter assumption, however, does not seem to be a
+// problem in practice.
+#if defined(MOZ_MEMORY)
+#define ALLOCATION_ASSERT(b) ASSERT_TRUE((b))
+#else
+#define ALLOCATION_ASSERT(b) (void)(b)
+#endif
+
+#define ASSERT_ALLOCATION_HAPPENED(lambda) \
+ ALLOCATION_ASSERT(ValidateHookedAllocation(lambda, free));
+
+// We do run the risk of OOM'ing when we allocate something...all we can
+// do is try to allocate something so small that OOM'ing is unlikely.
+const size_t kAllocAmount = 16;
+
+// We declare this function MOZ_NEVER_INLINE to work around optimizing
+// compilers. If we permitted inlining here, then the compiler might
+// inline both this function and the calls to the function pointers we
+// pass in, giving something like:
+//
+// void* p = malloc(...);
+// ...do nothing with p except check nullptr-ness...
+// free(p);
+//
+// and the optimizer can delete the calls to malloc and free entirely,
+// which would make checking that the jemalloc heap had never changed
+// difficult.
+static MOZ_NEVER_INLINE bool
+ValidateHookedAllocation(void* (*aAllocator)(void),
+ void (*aFreeFunction)(void*))
+{
+ nsCOMPtr<nsIMemoryReporterManager> manager =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+ int64_t before = 0;
+ nsresult rv = manager->GetHeapAllocated(&before);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ {
+ void* p = aAllocator();
+
+ if (!p) {
+ return false;
+ }
+
+ int64_t after = 0;
+ rv = manager->GetHeapAllocated(&after);
+
+ // Regardless of whether that call succeeded or failed, we are done with
+ // the allocated buffer now.
+ aFreeFunction(p);
+
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ // Verify that our heap stats have changed.
+ if ((before + int64_t(kAllocAmount)) != after) {
+ return false;
+ }
+ }
+
+ // Verify that freeing the allocated pointer resets our heap to what it
+ // was before.
+ int64_t after = 0;
+ rv = manager->GetHeapAllocated(&after);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ return before == after;
+}
+
+TEST(AllocReplacement, malloc_check)
+{
+ ASSERT_ALLOCATION_HAPPENED([] {
+ return malloc(kAllocAmount);
+ });
+}
+
+TEST(AllocReplacement, calloc_check)
+{
+ ASSERT_ALLOCATION_HAPPENED([] {
+ return calloc(1, kAllocAmount);
+ });
+}
+
+TEST(AllocReplacement, realloc_check)
+{
+ ASSERT_ALLOCATION_HAPPENED([] {
+ return realloc(nullptr, kAllocAmount);
+ });
+}
+
+#if defined(HAVE_POSIX_MEMALIGN)
+TEST(AllocReplacement, posix_memalign_check)
+{
+ ASSERT_ALLOCATION_HAPPENED([] {
+ void* p = nullptr;
+ int result = posix_memalign(&p, sizeof(void*), kAllocAmount);
+ if (result != 0) {
+ return static_cast<void*>(nullptr);
+ }
+ return p;
+ });
+}
+#endif
+
+#if defined(XP_WIN)
+#include <windows.h>
+
+#undef ASSERT_ALLOCATION_HAPPENED
+#define ASSERT_ALLOCATION_HAPPENED(lambda) \
+ ALLOCATION_ASSERT(ValidateHookedAllocation(lambda, [](void* p) { \
+ HeapFree(GetProcessHeap(), 0, p); \
+ }));
+
+TEST(AllocReplacement, HeapAlloc_check)
+{
+ ASSERT_ALLOCATION_HAPPENED([] {
+ HANDLE h = GetProcessHeap();
+ return HeapAlloc(h, 0, kAllocAmount);
+ });
+}
+
+TEST(AllocReplacement, HeapReAlloc_check)
+{
+ ASSERT_ALLOCATION_HAPPENED([] {
+ HANDLE h = GetProcessHeap();
+ void *p = HeapAlloc(h, 0, kAllocAmount / 2);
+
+ if (!p) {
+ return static_cast<void*>(nullptr);
+ }
+
+ return HeapReAlloc(h, 0, p, kAllocAmount);
+ });
+}
+#endif
diff --git a/xpcom/tests/gtest/TestAtoms.cpp b/xpcom/tests/gtest/TestAtoms.cpp
new file mode 100644
index 000000000..da3cc5ea2
--- /dev/null
+++ b/xpcom/tests/gtest/TestAtoms.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsIAtom.h"
+#include "nsString.h"
+#include "UTFStrings.h"
+#include "nsIServiceManager.h"
+#include "nsStaticAtom.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+namespace TestAtoms {
+
+TEST(Atoms, Basic)
+{
+ for (unsigned int i = 0; i < ArrayLength(ValidStrings); ++i) {
+ nsDependentString str16(ValidStrings[i].m16);
+ nsDependentCString str8(ValidStrings[i].m8);
+
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(str16);
+
+ EXPECT_TRUE(atom->Equals(str16));
+
+ nsString tmp16;
+ nsCString tmp8;
+ atom->ToString(tmp16);
+ atom->ToUTF8String(tmp8);
+ EXPECT_TRUE(str16.Equals(tmp16));
+ EXPECT_TRUE(str8.Equals(tmp8));
+
+ EXPECT_TRUE(nsDependentString(atom->GetUTF16String()).Equals(str16));
+
+ EXPECT_TRUE(nsAtomString(atom).Equals(str16));
+ EXPECT_TRUE(nsDependentAtomString(atom).Equals(str16));
+ EXPECT_TRUE(nsAtomCString(atom).Equals(str8));
+ }
+}
+
+TEST(Atoms, 16vs8)
+{
+ for (unsigned int i = 0; i < ArrayLength(ValidStrings); ++i) {
+ nsCOMPtr<nsIAtom> atom16 = NS_Atomize(ValidStrings[i].m16);
+ nsCOMPtr<nsIAtom> atom8 = NS_Atomize(ValidStrings[i].m8);
+ EXPECT_EQ(atom16, atom8);
+ }
+}
+
+TEST(Atoms, BufferSharing)
+{
+ nsString unique;
+ unique.AssignLiteral("this is a unique string !@#$");
+
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(unique);
+
+ EXPECT_EQ(unique.get(), atom->GetUTF16String());
+}
+
+TEST(Atoms, Null)
+{
+ nsAutoString str(NS_LITERAL_STRING("string with a \0 char"));
+ nsDependentString strCut(str.get());
+
+ EXPECT_FALSE(str.Equals(strCut));
+
+ nsCOMPtr<nsIAtom> atomCut = NS_Atomize(strCut);
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(str);
+
+ EXPECT_EQ(atom->GetLength(), str.Length());
+ EXPECT_TRUE(atom->Equals(str));
+ EXPECT_NE(atom, atomCut);
+ EXPECT_TRUE(atomCut->Equals(strCut));
+}
+
+TEST(Atoms, Invalid)
+{
+ for (unsigned int i = 0; i < ArrayLength(Invalid16Strings); ++i) {
+ nsrefcnt count = NS_GetNumberOfAtoms();
+
+ {
+ nsCOMPtr<nsIAtom> atom16 = NS_Atomize(Invalid16Strings[i].m16);
+ EXPECT_TRUE(atom16->Equals(nsDependentString(Invalid16Strings[i].m16)));
+ }
+
+ EXPECT_EQ(count, NS_GetNumberOfAtoms());
+ }
+
+ for (unsigned int i = 0; i < ArrayLength(Invalid8Strings); ++i) {
+ nsrefcnt count = NS_GetNumberOfAtoms();
+
+ {
+ nsCOMPtr<nsIAtom> atom8 = NS_Atomize(Invalid8Strings[i].m8);
+ nsCOMPtr<nsIAtom> atom16 = NS_Atomize(Invalid8Strings[i].m16);
+ EXPECT_EQ(atom16, atom8);
+ EXPECT_TRUE(atom16->Equals(nsDependentString(Invalid8Strings[i].m16)));
+ }
+
+ EXPECT_EQ(count, NS_GetNumberOfAtoms());
+ }
+
+// Don't run this test in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ nsCOMPtr<nsIAtom> emptyAtom = NS_Atomize("");
+
+ for (unsigned int i = 0; i < ArrayLength(Malformed8Strings); ++i) {
+ nsrefcnt count = NS_GetNumberOfAtoms();
+
+ nsCOMPtr<nsIAtom> atom8 = NS_Atomize(Malformed8Strings[i]);
+ EXPECT_EQ(atom8, emptyAtom);
+ EXPECT_EQ(count, NS_GetNumberOfAtoms());
+ }
+#endif
+}
+
+#define FIRST_ATOM_STR "first static atom. Hello!"
+#define SECOND_ATOM_STR "second static atom. @World!"
+#define THIRD_ATOM_STR "third static atom?!"
+
+bool
+isStaticAtom(nsIAtom* atom)
+{
+ // Don't use logic && in order to ensure that all addrefs/releases are always
+ // run, even if one of the tests fail. This allows us to run this code on a
+ // non-static atom without affecting its refcount.
+ bool rv = (atom->AddRef() == 2);
+ rv &= (atom->AddRef() == 2);
+ rv &= (atom->AddRef() == 2);
+
+ rv &= (atom->Release() == 1);
+ rv &= (atom->Release() == 1);
+ rv &= (atom->Release() == 1);
+ return rv;
+}
+
+TEST(Atoms, Table)
+{
+ nsrefcnt count = NS_GetNumberOfAtoms();
+
+ nsCOMPtr<nsIAtom> thirdDynamic = NS_Atomize(THIRD_ATOM_STR);
+
+ EXPECT_FALSE(isStaticAtom(thirdDynamic));
+
+ EXPECT_TRUE(thirdDynamic);
+ EXPECT_EQ(NS_GetNumberOfAtoms(), count + 1);
+}
+
+}
diff --git a/xpcom/tests/gtest/TestAutoPtr.cpp b/xpcom/tests/gtest/TestAutoPtr.cpp
new file mode 100644
index 000000000..362ba55c5
--- /dev/null
+++ b/xpcom/tests/gtest/TestAutoPtr.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:cindent:ts=4:et:sw=4:
+/* 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 "nsAutoPtr.h"
+#include "gtest/gtest.h"
+
+class TestObjectBaseA {
+ public:
+ // Virtual dtor for deleting through base class pointer
+ virtual ~TestObjectBaseA() { }
+ void MemberFunction(int, int*, int&)
+ {
+ }
+ virtual void VirtualMemberFunction(int, int*, int&) { };
+ virtual void VirtualConstMemberFunction(int, int*, int&) const { };
+ int fooA;
+};
+
+class TestObjectBaseB {
+ public:
+ // Virtual dtor for deleting through base class pointer
+ virtual ~TestObjectBaseB() { }
+ int fooB;
+};
+
+class TestObject : public TestObjectBaseA, public TestObjectBaseB {
+ public:
+ TestObject()
+ {
+ }
+
+ // Virtual dtor for deleting through base class pointer
+ virtual ~TestObject()
+ {
+ destructed++;
+ }
+
+ virtual void VirtualMemberFunction(int, int*, int&) override
+ {
+ }
+ virtual void VirtualConstMemberFunction(int, int*, int&) const override
+ {
+ }
+
+ static int destructed;
+ static const void* last_ptr;
+};
+
+int TestObject::destructed = 0;
+const void* TestObject::last_ptr = nullptr;
+
+static void CreateTestObject(TestObject **aResult)
+{
+ *aResult = new TestObject();
+}
+
+static void DoSomethingWithTestObject(TestObject *aIn)
+{
+ TestObject::last_ptr = static_cast<void*>(aIn);
+}
+
+static void DoSomethingWithConstTestObject(const TestObject *aIn)
+{
+ TestObject::last_ptr = static_cast<const void*>(aIn);
+}
+
+static void DoSomethingWithTestObjectBaseB(TestObjectBaseB *aIn)
+{
+ TestObject::last_ptr = static_cast<void*>(aIn);
+}
+
+static void DoSomethingWithConstTestObjectBaseB(const TestObjectBaseB *aIn)
+{
+ TestObject::last_ptr = static_cast<const void*>(aIn);
+}
+
+TEST(AutoPtr, Assignment)
+{
+ TestObject::destructed = 0;
+
+ {
+ nsAutoPtr<TestObject> pobj( new TestObject() );
+ }
+
+ ASSERT_EQ(1, TestObject::destructed);
+
+ {
+ nsAutoPtr<TestObject> pobj( new TestObject() );
+ pobj = new TestObject();
+ ASSERT_EQ(2, TestObject::destructed);
+ }
+
+ ASSERT_EQ(3, TestObject::destructed);
+}
+
+TEST(AutoPtr, getter_Transfers)
+{
+ TestObject::destructed = 0;
+
+ {
+ nsAutoPtr<TestObject> ptr;
+ CreateTestObject(getter_Transfers(ptr));
+ }
+
+ ASSERT_EQ(1, TestObject::destructed);
+}
+
+TEST(AutoPtr, Casting)
+{
+ // This comparison is always false, as it should be. The extra parens
+ // suppress a -Wunreachable-code warning about printf being unreachable.
+ ASSERT_NE(((void*)(TestObject*)0x1000),
+ ((void*)(TestObjectBaseB*)(TestObject*)0x1000));
+
+ {
+ nsAutoPtr<TestObject> p1(new TestObject());
+ TestObjectBaseB *p2 = p1;
+ ASSERT_NE(static_cast<void*>(p1),
+ static_cast<void*>(p2));
+ ASSERT_EQ(p1, p2);
+ ASSERT_FALSE(p1 != p2);
+ ASSERT_EQ(p2, p1);
+ ASSERT_FALSE(p2 != p1);
+ }
+
+ {
+ TestObject *p1 = new TestObject();
+ nsAutoPtr<TestObjectBaseB> p2(p1);
+ ASSERT_EQ(p1, p2);
+ ASSERT_FALSE(p1 != p2);
+ ASSERT_EQ(p2, p1);
+ ASSERT_FALSE(p2 != p1);
+ }
+}
+
+TEST(AutoPtr, Forget)
+{
+ TestObject::destructed = 0;
+
+ {
+ nsAutoPtr<TestObject> pobj( new TestObject() );
+ nsAutoPtr<TestObject> pobj2( pobj.forget() );
+ ASSERT_EQ(0, TestObject::destructed);
+ }
+
+ ASSERT_EQ(1, TestObject::destructed);
+}
+
+TEST(AutoPtr, Construction)
+{
+ TestObject::destructed = 0;
+
+ {
+ nsAutoPtr<TestObject> pobj(new TestObject());
+ }
+
+ ASSERT_EQ(1, TestObject::destructed);
+}
+
+TEST(AutoPtr, ImplicitConversion)
+{
+ // This test is basically successful if it builds. We add a few assertions
+ // to make gtest happy.
+ TestObject::destructed = 0;
+
+ {
+ nsAutoPtr<TestObject> pobj(new TestObject());
+ DoSomethingWithTestObject(pobj);
+ DoSomethingWithConstTestObject(pobj);
+ }
+
+ ASSERT_EQ(1, TestObject::destructed);
+
+ {
+ nsAutoPtr<TestObject> pobj(new TestObject());
+ DoSomethingWithTestObjectBaseB(pobj);
+ DoSomethingWithConstTestObjectBaseB(pobj);
+ }
+
+ ASSERT_EQ(2, TestObject::destructed);
+
+ {
+ const nsAutoPtr<TestObject> pobj(new TestObject());
+ DoSomethingWithTestObject(pobj);
+ DoSomethingWithConstTestObject(pobj);
+ }
+
+ ASSERT_EQ(3, TestObject::destructed);
+
+ {
+ const nsAutoPtr<TestObject> pobj(new TestObject());
+ DoSomethingWithTestObjectBaseB(pobj);
+ DoSomethingWithConstTestObjectBaseB(pobj);
+ }
+
+ ASSERT_EQ(4, TestObject::destructed);
+}
+
+TEST(AutoPtr, ArrowOperator)
+{
+ // This test is basically successful if it builds. We add a few assertions
+ // to make gtest happy.
+ TestObject::destructed = 0;
+
+ {
+ int test = 1;
+ void (TestObjectBaseA::*fPtr)( int, int*, int& ) = &TestObjectBaseA::MemberFunction;
+ void (TestObjectBaseA::*fVPtr)( int, int*, int& ) = &TestObjectBaseA::VirtualMemberFunction;
+ void (TestObjectBaseA::*fVCPtr)( int, int*, int& ) const = &TestObjectBaseA::VirtualConstMemberFunction;
+ nsAutoPtr<TestObjectBaseA> pobj(new TestObject());
+ (pobj->*fPtr)(test, &test, test);
+ (pobj->*fVPtr)(test, &test, test);
+ (pobj->*fVCPtr)(test, &test, test);
+ }
+
+ ASSERT_EQ(1, TestObject::destructed);
+}
diff --git a/xpcom/tests/gtest/TestAutoRef.cpp b/xpcom/tests/gtest/TestAutoRef.cpp
new file mode 100644
index 000000000..49042e8fb
--- /dev/null
+++ b/xpcom/tests/gtest/TestAutoRef.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:cindent:ts=4:et:sw=4:
+/* 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 "nsAutoRef.h"
+#include "gtest/gtest.h"
+
+struct TestObjectA {
+public:
+ TestObjectA() : mRefCnt(0) {
+ }
+
+ ~TestObjectA() {
+ EXPECT_EQ(mRefCnt, 0);
+ }
+
+public:
+ int mRefCnt;
+};
+
+template <>
+class nsAutoRefTraits<TestObjectA> : public nsPointerRefTraits<TestObjectA>
+{
+public:
+ static int mTotalRefsCnt;
+
+ static void Release(TestObjectA *ptr) {
+ ptr->mRefCnt--;
+ if (ptr->mRefCnt == 0) {
+ delete ptr;
+ }
+ }
+
+ static void AddRef(TestObjectA *ptr) {
+ ptr->mRefCnt++;
+ }
+};
+
+int nsAutoRefTraits<TestObjectA>::mTotalRefsCnt = 0;
+
+TEST(AutoRef, Assignment)
+{
+ {
+ nsCountedRef<TestObjectA> a(new TestObjectA());
+ ASSERT_EQ(a->mRefCnt, 1);
+
+ nsCountedRef<TestObjectA> b;
+ ASSERT_EQ(b.get(), nullptr);
+
+ a.swap(b);
+ ASSERT_EQ(b->mRefCnt, 1);
+ ASSERT_EQ(a.get(), nullptr);
+ }
+}
diff --git a/xpcom/tests/gtest/TestBase64.cpp b/xpcom/tests/gtest/TestBase64.cpp
new file mode 100644
index 000000000..d8105619b
--- /dev/null
+++ b/xpcom/tests/gtest/TestBase64.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/Attributes.h"
+#include "nsIScriptableBase64Encoder.h"
+#include "nsIInputStream.h"
+#include "nsString.h"
+
+#include "gtest/gtest.h"
+
+struct Chunk {
+ Chunk(uint32_t l, const char* c)
+ : mLength(l), mData(c)
+ {}
+
+ uint32_t mLength;
+ const char* mData;
+};
+
+struct Test {
+ Test(Chunk* c, const char* r)
+ : mChunks(c), mResult(r)
+ {}
+
+ Chunk* mChunks;
+ const char* mResult;
+};
+
+static Chunk kTest1Chunks[] =
+{
+ Chunk(9, "Hello sir"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest2Chunks[] =
+{
+ Chunk(3, "Hel"),
+ Chunk(3, "lo "),
+ Chunk(3, "sir"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest3Chunks[] =
+{
+ Chunk(1, "I"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest4Chunks[] =
+{
+ Chunk(2, "Hi"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest5Chunks[] =
+{
+ Chunk(1, "B"),
+ Chunk(2, "ob"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest6Chunks[] =
+{
+ Chunk(2, "Bo"),
+ Chunk(1, "b"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest7Chunks[] =
+{
+ Chunk(1, "F"), // Carry over 1
+ Chunk(4, "iref"), // Carry over 2
+ Chunk(2, "ox"), // 1
+ Chunk(4, " is "), // 2
+ Chunk(2, "aw"), // 1
+ Chunk(4, "esom"), // 2
+ Chunk(2, "e!"),
+ Chunk(0, nullptr)
+};
+
+static Chunk kTest8Chunks[] =
+{
+ Chunk(5, "ALL T"),
+ Chunk(1, "H"),
+ Chunk(4, "ESE "),
+ Chunk(2, "WO"),
+ Chunk(21, "RLDS ARE YOURS EXCEPT"),
+ Chunk(9, " EUROPA. "),
+ Chunk(25, "ATTEMPT NO LANDING THERE."),
+ Chunk(0, nullptr)
+};
+
+static Test kTests[] =
+ {
+ // Test 1, test a simple round string in one chunk
+ Test(
+ kTest1Chunks,
+ "SGVsbG8gc2ly"
+ ),
+ // Test 2, test a simple round string split into round chunks
+ Test(
+ kTest2Chunks,
+ "SGVsbG8gc2ly"
+ ),
+ // Test 3, test a single chunk that's 2 short
+ Test(
+ kTest3Chunks,
+ "SQ=="
+ ),
+ // Test 4, test a single chunk that's 1 short
+ Test(
+ kTest4Chunks,
+ "SGk="
+ ),
+ // Test 5, test a single chunk that's 2 short, followed by a chunk of 2
+ Test(
+ kTest5Chunks,
+ "Qm9i"
+ ),
+ // Test 6, test a single chunk that's 1 short, followed by a chunk of 1
+ Test(
+ kTest6Chunks,
+ "Qm9i"
+ ),
+ // Test 7, test alternating carryovers
+ Test(
+ kTest7Chunks,
+ "RmlyZWZveCBpcyBhd2Vzb21lIQ=="
+ ),
+ // Test 8, test a longish string
+ Test(
+ kTest8Chunks,
+ "QUxMIFRIRVNFIFdPUkxEUyBBUkUgWU9VUlMgRVhDRVBUIEVVUk9QQS4gQVRURU1QVCBOTyBMQU5ESU5HIFRIRVJFLg=="
+ ),
+ // Terminator
+ Test(
+ nullptr,
+ nullptr
+ )
+ };
+
+class FakeInputStream final : public nsIInputStream
+{
+ ~FakeInputStream() {}
+
+public:
+
+ FakeInputStream()
+ : mTestNumber(0),
+ mTest(&kTests[0]),
+ mChunk(&mTest->mChunks[0]),
+ mClosed(false)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+
+ void Reset();
+ bool NextTest();
+ void CheckTest(nsACString& aResult);
+ void CheckTest(nsAString& aResult);
+private:
+ uint32_t mTestNumber;
+ const Test* mTest;
+ const Chunk* mChunk;
+ bool mClosed;
+};
+
+NS_IMPL_ISUPPORTS(FakeInputStream, nsIInputStream)
+
+NS_IMETHODIMP
+FakeInputStream::Close()
+{
+ mClosed = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FakeInputStream::Available(uint64_t* aAvailable)
+{
+ *aAvailable = 0;
+
+ if (mClosed)
+ return NS_BASE_STREAM_CLOSED;
+
+ const Chunk* chunk = mChunk;
+ while (chunk->mLength) {
+ *aAvailable += chunk->mLength;
+ chunk++;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FakeInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aOut)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FakeInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aRead)
+{
+ *aRead = 0;
+
+ if (mClosed)
+ return NS_BASE_STREAM_CLOSED;
+
+ while (mChunk->mLength) {
+ uint32_t written = 0;
+
+ nsresult rv = (*aWriter)(this, aClosure, mChunk->mData,
+ *aRead, mChunk->mLength, &written);
+
+ *aRead += written;
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mChunk++;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FakeInputStream::IsNonBlocking(bool* aIsBlocking)
+{
+ *aIsBlocking = false;
+ return NS_OK;
+}
+
+void
+FakeInputStream::Reset()
+{
+ mClosed = false;
+ mChunk = &mTest->mChunks[0];
+}
+
+bool
+FakeInputStream::NextTest()
+{
+ mTestNumber++;
+ mTest = &kTests[mTestNumber];
+ mChunk = &mTest->mChunks[0];
+ mClosed = false;
+
+ return mTest->mChunks ? true : false;
+}
+
+void
+FakeInputStream::CheckTest(nsACString& aResult)
+{
+ ASSERT_STREQ(aResult.BeginReading(), mTest->mResult);
+}
+
+void
+FakeInputStream::CheckTest(nsAString& aResult)
+{
+ ASSERT_TRUE(aResult.EqualsASCII(mTest->mResult)) <<
+ "Actual: " << aResult.BeginReading() << std::endl <<
+ "Expected: " << mTest->mResult;
+}
+
+TEST(Base64, Test)
+{
+ nsCOMPtr<nsIScriptableBase64Encoder> encoder =
+ do_CreateInstance("@mozilla.org/scriptablebase64encoder;1");
+ ASSERT_TRUE(encoder);
+
+ RefPtr<FakeInputStream> stream = new FakeInputStream();
+ do {
+ nsString wideString;
+ nsCString string;
+
+ nsresult rv;
+ rv = encoder->EncodeToString(stream, 0, wideString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ stream->Reset();
+
+ rv = encoder->EncodeToCString(stream, 0, string);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ stream->CheckTest(wideString);
+ stream->CheckTest(string);
+ } while (stream->NextTest());
+}
diff --git a/xpcom/tests/gtest/TestCOMArray.cpp b/xpcom/tests/gtest/TestCOMArray.cpp
new file mode 100644
index 000000000..6703bf9d1
--- /dev/null
+++ b/xpcom/tests/gtest/TestCOMArray.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=4:et:sw=4:
+/* 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 "nsCOMArray.h"
+#include "gtest/gtest.h"
+
+// {9e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IFOO_IID \
+ {0x9e70a320, 0xbe02, 0x11d1, \
+ {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+class IFoo : public nsISupports {
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+ NS_IMETHOD_(MozExternalRefCountType) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
+
+class Foo final : public IFoo {
+ ~Foo();
+
+public:
+
+ explicit Foo(int32_t aID);
+
+ // nsISupports implementation
+ NS_DECL_ISUPPORTS
+
+ // IFoo implementation
+ NS_IMETHOD_(MozExternalRefCountType) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return mID; }
+
+ static int32_t gCount;
+
+ int32_t mID;
+};
+
+int32_t Foo::gCount = 0;
+
+Foo::Foo(int32_t aID)
+{
+ mID = aID;
+ ++gCount;
+}
+
+Foo::~Foo()
+{
+ --gCount;
+}
+
+NS_IMPL_ISUPPORTS(Foo, IFoo)
+
+
+// {0e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IBAR_IID \
+ {0x0e70a320, 0xbe02, 0x11d1, \
+ {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+class IBar : public nsISupports {
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IBAR_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IBar, NS_IBAR_IID)
+
+class Bar final : public IBar {
+public:
+
+ explicit Bar(nsCOMArray<IBar>& aArray);
+
+ // nsISupports implementation
+ NS_DECL_ISUPPORTS
+
+ static int32_t sReleaseCalled;
+
+private:
+ ~Bar();
+
+ nsCOMArray<IBar>& mArray;
+};
+
+int32_t Bar::sReleaseCalled = 0;
+
+typedef nsCOMArray<IBar> Array2;
+
+Bar::Bar(Array2& aArray)
+ : mArray(aArray)
+{
+}
+
+Bar::~Bar()
+{
+ EXPECT_FALSE(mArray.RemoveObject(this));
+}
+
+NS_IMPL_ADDREF(Bar)
+NS_IMPL_QUERY_INTERFACE(Bar, IBar)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+Bar::Release(void)
+{
+ ++Bar::sReleaseCalled;
+ EXPECT_GT(int(mRefCnt), 0);
+ NS_ASSERT_OWNINGTHREAD(_class);
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "Bar");
+ if (mRefCnt == 0) {
+ mRefCnt = 1; /* stabilize */
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+}
+
+TEST(COMArray, Sizing)
+{
+ nsCOMArray<IFoo> arr;
+
+ for (int32_t i = 0; i < 20; ++i) {
+ nsCOMPtr<IFoo> foo = new Foo(i);
+ arr.AppendObject(foo);
+ }
+
+ ASSERT_EQ(arr.Count(), int32_t(20));
+ ASSERT_EQ(Foo::gCount, int32_t(20));
+
+ arr.TruncateLength(10);
+
+ ASSERT_EQ(arr.Count(), int32_t(10));
+ ASSERT_EQ(Foo::gCount, int32_t(10));
+
+ arr.SetCount(30);
+
+ ASSERT_EQ(arr.Count(), int32_t(30));
+ ASSERT_EQ(Foo::gCount, int32_t(10));
+
+ for (int32_t i = 0; i < 10; ++i) {
+ ASSERT_NE(arr[i], nullptr);
+ }
+
+ for (int32_t i = 10; i < 30; ++i) {
+ ASSERT_EQ(arr[i], nullptr);
+ }
+}
+
+TEST(COMArray, ObjectFunctions)
+{
+ int32_t base;
+ {
+ nsCOMArray<IBar> arr2;
+
+ IBar *thirdObject = nullptr,
+ *fourthObject = nullptr,
+ *fifthObject = nullptr,
+ *ninthObject = nullptr;
+ for (int32_t i = 0; i < 20; ++i) {
+ nsCOMPtr<IBar> bar = new Bar(arr2);
+ switch (i) {
+ case 2:
+ thirdObject = bar; break;
+ case 3:
+ fourthObject = bar; break;
+ case 4:
+ fifthObject = bar; break;
+ case 8:
+ ninthObject = bar; break;
+ }
+ arr2.AppendObject(bar);
+ }
+
+ base = Bar::sReleaseCalled;
+
+ arr2.SetCount(10);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 10);
+ ASSERT_EQ(arr2.Count(), int32_t(10));
+
+ arr2.RemoveObjectAt(9);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 11);
+ ASSERT_EQ(arr2.Count(), int32_t(9));
+
+ arr2.RemoveObject(ninthObject);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 12);
+ ASSERT_EQ(arr2.Count(), int32_t(8));
+
+ arr2.RemoveObjectsAt(2, 3);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 15);
+ ASSERT_EQ(arr2.Count(), int32_t(5));
+ for (int32_t j = 0; j < arr2.Count(); ++j) {
+ ASSERT_NE(arr2.ObjectAt(j), thirdObject);
+ ASSERT_NE(arr2.ObjectAt(j), fourthObject);
+ ASSERT_NE(arr2.ObjectAt(j), fifthObject);
+ }
+
+ arr2.RemoveObjectsAt(4, 1);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 16);
+ ASSERT_EQ(arr2.Count(), int32_t(4));
+
+ arr2.Clear();
+ ASSERT_EQ(Bar::sReleaseCalled, base + 20);
+ }
+}
+
+TEST(COMArray, ElementFunctions)
+{
+ int32_t base;
+ {
+ nsCOMArray<IBar> arr2;
+
+ IBar *thirdElement = nullptr,
+ *fourthElement = nullptr,
+ *fifthElement = nullptr,
+ *ninthElement = nullptr;
+ for (int32_t i = 0; i < 20; ++i) {
+ nsCOMPtr<IBar> bar = new Bar(arr2);
+ switch (i) {
+ case 2:
+ thirdElement = bar; break;
+ case 3:
+ fourthElement = bar; break;
+ case 4:
+ fifthElement = bar; break;
+ case 8:
+ ninthElement = bar; break;
+ }
+ arr2.AppendElement(bar);
+ }
+
+ base = Bar::sReleaseCalled;
+
+ arr2.TruncateLength(10);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 10);
+ ASSERT_EQ(arr2.Length(), uint32_t(10));
+
+ arr2.RemoveElementAt(9);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 11);
+ ASSERT_EQ(arr2.Length(), uint32_t(9));
+
+ arr2.RemoveElement(ninthElement);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 12);
+ ASSERT_EQ(arr2.Length(), uint32_t(8));
+
+ arr2.RemoveElementsAt(2, 3);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 15);
+ ASSERT_EQ(arr2.Length(), uint32_t(5));
+ for (uint32_t j = 0; j < arr2.Length(); ++j) {
+ ASSERT_NE(arr2.ElementAt(j), thirdElement);
+ ASSERT_NE(arr2.ElementAt(j), fourthElement);
+ ASSERT_NE(arr2.ElementAt(j), fifthElement);
+ }
+
+ arr2.RemoveElementsAt(4, 1);
+ ASSERT_EQ(Bar::sReleaseCalled, base + 16);
+ ASSERT_EQ(arr2.Length(), uint32_t(4));
+
+ arr2.Clear();
+ ASSERT_EQ(Bar::sReleaseCalled, base + 20);
+ }
+}
+
+TEST(COMArray, Destructor)
+{
+ int32_t base;
+ Bar::sReleaseCalled = 0;
+
+ {
+ nsCOMArray<IBar> arr2;
+
+ for (int32_t i = 0; i < 20; ++i) {
+ nsCOMPtr<IBar> bar = new Bar(arr2);
+ arr2.AppendObject(bar);
+ }
+
+ base = Bar::sReleaseCalled;
+
+ // Let arr2 be destroyed
+ }
+ ASSERT_EQ(Bar::sReleaseCalled, base + 20);
+}
diff --git a/xpcom/tests/gtest/TestCOMPtr.cpp b/xpcom/tests/gtest/TestCOMPtr.cpp
new file mode 100644
index 000000000..e17331c65
--- /dev/null
+++ b/xpcom/tests/gtest/TestCOMPtr.cpp
@@ -0,0 +1,466 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "gtest/gtest.h"
+
+#include "mozilla/Unused.h"
+
+#define NS_IFOO_IID \
+{ 0x6f7652e0, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+namespace TestCOMPtr
+{
+
+class IFoo : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+public:
+ IFoo();
+ // virtual dtor because IBar uses our Release()
+ virtual ~IFoo();
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+ NS_IMETHOD QueryInterface( const nsIID&, void** );
+
+ unsigned int refcount_;
+
+ static int total_constructions_;
+ static int total_destructions_;
+ static int total_queries_;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
+
+int IFoo::total_constructions_;
+int IFoo::total_destructions_;
+int IFoo::total_queries_;
+
+IFoo::IFoo()
+ : refcount_(0)
+{
+ ++total_constructions_;
+}
+
+IFoo::~IFoo()
+{
+ ++total_destructions_;
+}
+
+MozExternalRefCountType
+IFoo::AddRef()
+{
+ ++refcount_;
+ return refcount_;
+}
+
+MozExternalRefCountType
+IFoo::Release()
+{
+ int newcount = --refcount_;
+
+ if ( newcount == 0 )
+ {
+ delete this;
+ }
+
+ return newcount;
+}
+
+nsresult
+IFoo::QueryInterface( const nsIID& aIID, void** aResult )
+{
+ total_queries_++;
+
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(IFoo)) )
+ rawPtr = this;
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+}
+
+nsresult
+CreateIFoo( void** result )
+// a typical factory function (that calls AddRef)
+{
+ IFoo* foop = new IFoo;
+
+ foop->AddRef();
+ *result = foop;
+
+ return NS_OK;
+}
+
+void
+set_a_IFoo( nsCOMPtr<IFoo>* result )
+{
+ nsCOMPtr<IFoo> foop( do_QueryInterface(new IFoo) );
+ *result = foop;
+}
+
+nsCOMPtr<IFoo>
+return_a_IFoo()
+{
+ nsCOMPtr<IFoo> foop( do_QueryInterface(new IFoo) );
+ return foop;
+}
+
+#define NS_IBAR_IID \
+{ 0x6f7652e1, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+class IBar : public IFoo
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IBAR_IID)
+
+public:
+ IBar();
+ virtual ~IBar();
+
+ NS_IMETHOD QueryInterface( const nsIID&, void** );
+
+ static int total_destructions_;
+ static int total_queries_;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IBar, NS_IBAR_IID)
+
+int IBar::total_destructions_;
+int IBar::total_queries_;
+
+IBar::IBar()
+{
+}
+
+IBar::~IBar()
+{
+ total_destructions_++;
+}
+
+nsresult
+IBar::QueryInterface( const nsID& aIID, void** aResult )
+{
+ total_queries_++;
+
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(IBar)) )
+ rawPtr = this;
+ else if ( aIID.Equals(NS_GET_IID(IFoo)) )
+ rawPtr = static_cast<IFoo*>(this);
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+}
+
+
+
+nsresult
+CreateIBar( void** result )
+ // a typical factory function (that calls AddRef)
+{
+ IBar* barp = new IBar;
+
+ barp->AddRef();
+ *result = barp;
+
+ return NS_OK;
+}
+
+void
+AnIFooPtrPtrContext( IFoo** )
+{
+}
+
+void
+AVoidPtrPtrContext( void** )
+{
+}
+
+void
+AnISupportsPtrPtrContext( nsISupports** )
+{
+}
+
+} // namespace TestCOMPtr
+
+using namespace TestCOMPtr;
+
+TEST(COMPtr, Bloat_Raw_Unsafe)
+{
+ // ER: I'm not sure what this is testing...
+ IBar* barP = 0;
+ nsresult rv = CreateIBar(reinterpret_cast<void**>(&barP));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(barP);
+
+ IFoo* fooP = 0;
+ rv = barP->QueryInterface(NS_GET_IID(IFoo), reinterpret_cast<void**>(&fooP));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(fooP);
+
+ NS_RELEASE(fooP);
+ NS_RELEASE(barP);
+}
+
+TEST(COMPtr, Bloat_Smart)
+{
+ // ER: I'm not sure what this is testing...
+ nsCOMPtr<IBar> barP;
+ nsresult rv = CreateIBar( getter_AddRefs(barP) );
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(barP);
+
+ nsCOMPtr<IFoo> fooP( do_QueryInterface(barP, &rv) );
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(fooP);
+}
+
+TEST(COMPtr, AddRefAndRelease)
+{
+ IFoo::total_constructions_ = 0;
+ IFoo::total_destructions_ = 0;
+ IBar::total_destructions_ = 0;
+
+ {
+ nsCOMPtr<IFoo> foop( do_QueryInterface(new IFoo) );
+ ASSERT_EQ(foop->refcount_, (unsigned int)1);
+ ASSERT_EQ(IFoo::total_constructions_, 1);
+ ASSERT_EQ(IFoo::total_destructions_, 0);
+
+ foop = do_QueryInterface(new IFoo);
+ ASSERT_EQ(foop->refcount_, (unsigned int)1);
+ ASSERT_EQ(IFoo::total_constructions_, 2);
+ ASSERT_EQ(IFoo::total_destructions_, 1);
+
+ // [Shouldn't compile] Is it a compile time error to try to |AddRef| by hand?
+ //foop->AddRef();
+
+ // [Shouldn't compile] Is it a compile time error to try to |Release| be hand?
+ //foop->Release();
+
+ // [Shouldn't compile] Is it a compile time error to try to |delete| an |nsCOMPtr|?
+ //delete foop;
+
+ static_cast<IFoo*>(foop)->AddRef();
+ ASSERT_EQ(foop->refcount_, (unsigned int)2);
+ ASSERT_EQ(IFoo::total_constructions_, 2);
+ ASSERT_EQ(IFoo::total_destructions_, 1);
+
+ static_cast<IFoo*>(foop)->Release();
+ ASSERT_EQ(foop->refcount_, (unsigned int)1);
+ ASSERT_EQ(IFoo::total_constructions_, 2);
+ ASSERT_EQ(IFoo::total_destructions_, 1);
+ }
+
+ ASSERT_EQ(IFoo::total_constructions_, 2);
+ ASSERT_EQ(IFoo::total_destructions_, 2);
+
+ {
+ nsCOMPtr<IFoo> foop( do_QueryInterface(new IBar) );
+ mozilla::Unused << foop;
+ }
+
+ ASSERT_EQ(IBar::total_destructions_, 1);
+}
+
+void Comparison()
+{
+ IFoo::total_constructions_ = 0;
+ IFoo::total_destructions_ = 0;
+
+ {
+ nsCOMPtr<IFoo> foo1p( do_QueryInterface(new IFoo) );
+ nsCOMPtr<IFoo> foo2p( do_QueryInterface(new IFoo) );
+
+ ASSERT_EQ(IFoo::total_constructions_, 2);
+
+ // Test != operator
+ ASSERT_NE(foo1p, foo2p);
+ ASSERT_NE(foo1p, foo2p.get());
+
+ // Test == operator
+ foo1p = foo2p;
+
+ ASSERT_EQ(IFoo::total_destructions_, 1);
+
+ ASSERT_EQ(foo1p, foo2p);
+ ASSERT_EQ(foo2p, foo2p.get());
+ ASSERT_EQ(foo2p.get(), foo2p);
+
+ // Test () operator
+ ASSERT_TRUE(foo1p);
+
+ ASSERT_EQ(foo1p->refcount_, (unsigned int)2);
+ ASSERT_EQ(foo2p->refcount_, (unsigned int)2);
+ }
+
+ ASSERT_EQ(IFoo::total_destructions_, 2);
+}
+
+void DontAddRef()
+{
+ {
+ IFoo* raw_foo1p = new IFoo;
+ raw_foo1p->AddRef();
+
+ IFoo* raw_foo2p = new IFoo;
+ raw_foo2p->AddRef();
+
+ nsCOMPtr<IFoo> foo1p( dont_AddRef(raw_foo1p) );
+ ASSERT_EQ(raw_foo1p, foo1p);
+ ASSERT_EQ(foo1p->refcount_, (unsigned int)1);
+
+ nsCOMPtr<IFoo> foo2p;
+ foo2p = dont_AddRef(raw_foo2p);
+ ASSERT_EQ(raw_foo2p, foo2p);
+ ASSERT_EQ(foo2p->refcount_, (unsigned int)1);
+ }
+}
+
+TEST(COMPtr, AssignmentHelpers)
+{
+ IFoo::total_constructions_ = 0;
+ IFoo::total_destructions_ = 0;
+
+ {
+ nsCOMPtr<IFoo> foop;
+ ASSERT_FALSE(foop);
+ CreateIFoo( nsGetterAddRefs<IFoo>(foop) );
+ ASSERT_TRUE(foop);
+ }
+
+ ASSERT_EQ(IFoo::total_constructions_, 1);
+ ASSERT_EQ(IFoo::total_destructions_, 1);
+
+ {
+ nsCOMPtr<IFoo> foop;
+ ASSERT_FALSE(foop);
+ CreateIFoo( getter_AddRefs(foop) );
+ ASSERT_TRUE(foop);
+ }
+
+ ASSERT_EQ(IFoo::total_constructions_, 2);
+ ASSERT_EQ(IFoo::total_destructions_, 2);
+
+ {
+ nsCOMPtr<IFoo> foop;
+ ASSERT_FALSE(foop);
+ set_a_IFoo(address_of(foop));
+ ASSERT_TRUE(foop);
+
+ ASSERT_EQ(IFoo::total_constructions_, 3);
+ ASSERT_EQ(IFoo::total_destructions_, 2);
+
+ foop = return_a_IFoo();
+ ASSERT_TRUE(foop);
+
+ ASSERT_EQ(IFoo::total_constructions_, 4);
+ ASSERT_EQ(IFoo::total_destructions_, 3);
+ }
+
+ ASSERT_EQ(IFoo::total_constructions_, 4);
+ ASSERT_EQ(IFoo::total_destructions_, 4);
+
+ {
+ nsCOMPtr<IFoo> fooP( do_QueryInterface(new IFoo) );
+ ASSERT_TRUE(fooP);
+
+ ASSERT_EQ(IFoo::total_constructions_, 5);
+ ASSERT_EQ(IFoo::total_destructions_, 4);
+
+ nsCOMPtr<IFoo> fooP2( fooP.forget() );
+ ASSERT_TRUE(fooP2);
+
+ ASSERT_EQ(IFoo::total_constructions_, 5);
+ ASSERT_EQ(IFoo::total_destructions_, 4);
+ }
+
+ ASSERT_EQ(IFoo::total_constructions_, 5);
+ ASSERT_EQ(IFoo::total_destructions_, 5);
+}
+
+TEST(COMPtr, QueryInterface)
+{
+ IFoo::total_queries_ = 0;
+ IBar::total_queries_ = 0;
+
+ {
+ nsCOMPtr<IFoo> fooP;
+ ASSERT_FALSE(fooP);
+ fooP = do_QueryInterface(new IFoo);
+ ASSERT_TRUE(fooP);
+ ASSERT_EQ(IFoo::total_queries_, 1);
+
+ nsCOMPtr<IFoo> foo2P;
+
+ // Test that |QueryInterface| _not_ called when assigning a smart-pointer
+ // of the same type.);
+ foo2P = fooP;
+ ASSERT_EQ(IFoo::total_queries_, 1);
+ }
+
+ {
+ nsCOMPtr<IBar> barP( do_QueryInterface(new IBar) );
+ ASSERT_EQ(IBar::total_queries_, 1);
+
+ // Test that |QueryInterface| is called when assigning a smart-pointer of
+ // a different type.
+ nsCOMPtr<IFoo> fooP( do_QueryInterface(barP) );
+ ASSERT_EQ(IBar::total_queries_, 2);
+ ASSERT_EQ(IFoo::total_queries_, 1);
+ ASSERT_TRUE(fooP);
+ }
+}
+
+TEST(COMPtr, GetterConversions)
+{
+ // This is just a compilation test. We add a few asserts to keep gtest happy.
+ {
+ nsCOMPtr<IFoo> fooP;
+ ASSERT_FALSE(fooP);
+
+ AnIFooPtrPtrContext( getter_AddRefs(fooP) );
+ AVoidPtrPtrContext( getter_AddRefs(fooP) );
+ }
+
+
+ {
+ nsCOMPtr<nsISupports> supportsP;
+ ASSERT_FALSE(supportsP);
+
+ AVoidPtrPtrContext( getter_AddRefs(supportsP) );
+ AnISupportsPtrPtrContext( getter_AddRefs(supportsP) );
+ }
+}
diff --git a/xpcom/tests/gtest/TestCOMPtrEq.cpp b/xpcom/tests/gtest/TestCOMPtrEq.cpp
new file mode 100644
index 000000000..b7513e2ae
--- /dev/null
+++ b/xpcom/tests/gtest/TestCOMPtrEq.cpp
@@ -0,0 +1,79 @@
+/* -*- 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/. */
+
+/**
+ * This attempts to test all the possible variations of |operator==|
+ * used with |nsCOMPtr|s.
+ */
+
+#include "nsCOMPtr.h"
+#include "gtest/gtest.h"
+
+#define NS_ICOMPTREQTESTFOO_IID \
+{0x8eb5bbef, 0xd1a3, 0x4659, \
+ {0x9c, 0xf6, 0xfd, 0xf3, 0xe4, 0xd2, 0x00, 0x0e}}
+
+class nsICOMPtrEqTestFoo : public nsISupports {
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICOMPTREQTESTFOO_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsICOMPtrEqTestFoo, NS_ICOMPTREQTESTFOO_IID)
+
+TEST(COMPtrEq, NullEquality)
+{
+ nsCOMPtr<nsICOMPtrEqTestFoo> s;
+ nsICOMPtrEqTestFoo* r = nullptr;
+ const nsCOMPtr<nsICOMPtrEqTestFoo> sc;
+ const nsICOMPtrEqTestFoo* rc = nullptr;
+ nsICOMPtrEqTestFoo* const rk = nullptr;
+ const nsICOMPtrEqTestFoo* const rkc = nullptr;
+ nsICOMPtrEqTestFoo* d = s;
+
+ ASSERT_EQ(s, s);
+ ASSERT_EQ(s, r);
+ ASSERT_EQ(s, sc);
+ ASSERT_EQ(s, rc);
+ ASSERT_EQ(s, rk);
+ ASSERT_EQ(s, rkc);
+ ASSERT_EQ(s, d);
+ ASSERT_EQ(r, s);
+ ASSERT_EQ(r, sc);
+ ASSERT_EQ(r, rc);
+ ASSERT_EQ(r, rk);
+ ASSERT_EQ(r, rkc);
+ ASSERT_EQ(r, d);
+ ASSERT_EQ(sc, s);
+ ASSERT_EQ(sc, r);
+ ASSERT_EQ(sc, sc);
+ ASSERT_EQ(sc, rc);
+ ASSERT_EQ(sc, rk);
+ ASSERT_EQ(sc, rkc);
+ ASSERT_EQ(sc, d);
+ ASSERT_EQ(rc, s);
+ ASSERT_EQ(rc, r);
+ ASSERT_EQ(rc, sc);
+ ASSERT_EQ(rc, rk);
+ ASSERT_EQ(rc, rkc);
+ ASSERT_EQ(rc, d);
+ ASSERT_EQ(rk, s);
+ ASSERT_EQ(rk, r);
+ ASSERT_EQ(rk, sc);
+ ASSERT_EQ(rk, rc);
+ ASSERT_EQ(rk, rkc);
+ ASSERT_EQ(rk, d);
+ ASSERT_EQ(rkc, s);
+ ASSERT_EQ(rkc, r);
+ ASSERT_EQ(rkc, sc);
+ ASSERT_EQ(rkc, rc);
+ ASSERT_EQ(rkc, rk);
+ ASSERT_EQ(rkc, d);
+ ASSERT_EQ(d, s);
+ ASSERT_EQ(d, r);
+ ASSERT_EQ(d, sc);
+ ASSERT_EQ(d, rc);
+ ASSERT_EQ(d, rk);
+ ASSERT_EQ(d, rkc);
+}
diff --git a/xpcom/tests/gtest/TestCRT.cpp b/xpcom/tests/gtest/TestCRT.cpp
new file mode 100644
index 000000000..9fa731404
--- /dev/null
+++ b/xpcom/tests/gtest/TestCRT.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCRT.h"
+#include "nsString.h"
+#include "plstr.h"
+#include <stdlib.h>
+#include "gtest/gtest.h"
+
+namespace TestCRT {
+
+// The return from strcmp etc is only defined to be postive, zero or
+// negative. The magnitude of a non-zero return is irrelevant.
+int sign(int val) {
+ if (val == 0) {
+ return 0;
+ } else {
+ if (val > 0) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+}
+
+
+// Verify that nsCRT versions of string comparison routines get the
+// same answers as the native non-unicode versions. We only pass in
+// iso-latin-1 strings, so the comparison must be valid.
+static void Check(const char* s1, const char* s2, int n)
+{
+ int clib = PL_strcmp(s1, s2);
+
+ int clib_n = PL_strncmp(s1, s2, n);
+
+ nsAutoString t1,t2;
+ t1.AssignWithConversion(s1);
+ t2.AssignWithConversion(s2);
+ const char16_t* us1 = t1.get();
+ const char16_t* us2 = t2.get();
+
+ int u2 = nsCRT::strcmp(us1, us2);
+
+ int u2_n = nsCRT::strncmp(us1, us2, n);
+
+ EXPECT_EQ(sign(clib), sign(u2));
+ EXPECT_EQ(sign(clib), sign(u2_n));
+ EXPECT_EQ(sign(clib), sign(clib_n));
+}
+
+struct Test {
+ const char* s1;
+ const char* s2;
+ int n;
+};
+
+static Test tests[] = {
+ { "foo", "foo", 3 },
+ { "foo", "fo", 3 },
+
+ { "foo", "bar", 3 },
+ { "foo", "ba", 3 },
+
+ { "foo", "zap", 3 },
+ { "foo", "za", 3 },
+
+ { "bar", "foo", 3 },
+ { "bar", "fo", 3 },
+
+ { "bar", "foo", 3 },
+ { "bar", "fo", 3 },
+};
+#define NUM_TESTS int((sizeof(tests) / sizeof(tests[0])))
+
+TEST(CRT, main)
+{
+ TestCRT::Test* tp = tests;
+ for (int i = 0; i < NUM_TESTS; i++, tp++) {
+ Check(tp->s1, tp->s2, tp->n);
+ }
+}
+
+} // namespace TestCRT
diff --git a/xpcom/tests/gtest/TestCallTemplates.cpp b/xpcom/tests/gtest/TestCallTemplates.cpp
new file mode 100644
index 000000000..b8087e82b
--- /dev/null
+++ b/xpcom/tests/gtest/TestCallTemplates.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim:cindent:ts=8:et:sw=4:
+ *
+ * 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/. */
+
+/*
+ * This test is NOT intended to be run. It's a test to make sure
+ * a group of functions BUILD correctly.
+ */
+
+#include "nsISupportsUtils.h"
+#include "nsIWeakReference.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsWeakReference.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Attributes.h"
+
+#define NS_ITESTSERVICE_IID \
+ {0x127b5253, 0x37b1, 0x43c7, \
+ { 0x96, 0x2b, 0xab, 0xf1, 0x2d, 0x22, 0x56, 0xae }}
+
+class NS_NO_VTABLE nsITestService : public nsISupports {
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITESTSERVICE_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITestService, NS_ITESTSERVICE_IID)
+
+class nsTestService final : public nsITestService,
+ public nsSupportsWeakReference
+{
+ ~nsTestService() {}
+ public:
+ NS_DECL_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(nsTestService, nsITestService, nsISupportsWeakReference)
+
+#define NS_TEST_SERVICE_CONTRACTID "@mozilla.org/test/testservice;1"
+#define NS_TEST_SERVICE_CID \
+ {0xa00c1406, 0x283a, 0x45c9, \
+ {0xae, 0xd2, 0x1a, 0xb6, 0xdd, 0xba, 0xfe, 0x53}}
+static NS_DEFINE_CID(kTestServiceCID, NS_TEST_SERVICE_CID);
+
+void JustTestingCompilation()
+{
+ /*
+ * NOTE: This does NOT demonstrate how these functions are
+ * intended to be used. They are intended for filling in out
+ * parameters that need to be |AddRef|ed. I'm just too lazy
+ * to write lots of little getter functions for a test program
+ * when I don't need to.
+ */
+
+ NS_NOTREACHED("This test is not intended to run, only to compile!");
+
+ /* Test CallQueryInterface */
+
+ nsISupports *mySupportsPtr = reinterpret_cast<nsISupports*>(0x1000);
+
+ nsITestService *myITestService = nullptr;
+ CallQueryInterface(mySupportsPtr, &myITestService);
+
+ nsTestService *myTestService =
+ reinterpret_cast<nsTestService*>(mySupportsPtr);
+ nsISupportsWeakReference *mySupportsWeakRef;
+ CallQueryInterface(myTestService, &mySupportsWeakRef);
+
+ nsCOMPtr<nsISupports> mySupportsCOMPtr = mySupportsPtr;
+ CallQueryInterface(mySupportsCOMPtr, &myITestService);
+
+ RefPtr<nsTestService> myTestServiceRefPtr = myTestService;
+ CallQueryInterface(myTestServiceRefPtr, &mySupportsWeakRef);
+
+ /* Test CallQueryReferent */
+
+ nsIWeakReference *myWeakRef =
+ static_cast<nsIWeakReference*>(mySupportsPtr);
+ CallQueryReferent(myWeakRef, &myITestService);
+
+ /* Test CallCreateInstance */
+
+ CallCreateInstance(kTestServiceCID, mySupportsPtr, &myITestService);
+ CallCreateInstance(kTestServiceCID, &myITestService);
+ CallCreateInstance(NS_TEST_SERVICE_CONTRACTID, mySupportsPtr,
+ &myITestService);
+ CallCreateInstance(NS_TEST_SERVICE_CONTRACTID, &myITestService);
+
+ /* Test CallGetService */
+ CallGetService(kTestServiceCID, &myITestService);
+ CallGetService(NS_TEST_SERVICE_CONTRACTID, &myITestService);
+
+ /* Test CallGetInterface */
+ nsIInterfaceRequestor *myInterfaceRequestor =
+ static_cast<nsIInterfaceRequestor*>(mySupportsPtr);
+ CallGetInterface(myInterfaceRequestor, &myITestService);
+}
diff --git a/xpcom/tests/gtest/TestCloneInputStream.cpp b/xpcom/tests/gtest/TestCloneInputStream.cpp
new file mode 100644
index 000000000..de4dd5ea3
--- /dev/null
+++ b/xpcom/tests/gtest/TestCloneInputStream.cpp
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "Helpers.h"
+#include "mozilla/Unused.h"
+#include "nsICloneableInputStream.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
+#include "nsStringStream.h"
+#include "nsComponentManagerUtils.h"
+
+TEST(CloneInputStream, InvalidInput)
+{
+ nsCOMPtr<nsIInputStream> clone;
+ nsresult rv = NS_CloneInputStream(nullptr, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_FAILED(rv));
+ ASSERT_FALSE(clone);
+}
+
+TEST(CloneInputStream, CloneableInput)
+{
+ nsTArray<char> inputData;
+ testing::CreateData(4 * 1024, inputData);
+ nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> stream;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ testing::ConsumeAndValidateStream(stream, inputString);
+ testing::ConsumeAndValidateStream(clone, inputString);
+}
+
+TEST(CloneInputStream, NonCloneableInput_NoFallback)
+{
+ nsTArray<char> inputData;
+ testing::CreateData(4 * 1024, inputData);
+ nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> base;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Take advantage of nsBufferedInputStream being non-cloneable right
+ // now. If this changes in the future, then we need a different stream
+ // type in this test.
+ nsCOMPtr<nsIInputStream> stream;
+ rv = NS_NewBufferedInputStream(getter_AddRefs(stream), base, 4096);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(stream);
+ ASSERT_TRUE(cloneable == nullptr);
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_FAILED(rv));
+ ASSERT_TRUE(clone == nullptr);
+
+ testing::ConsumeAndValidateStream(stream, inputString);
+}
+
+TEST(CloneInputStream, NonCloneableInput_Fallback)
+{
+ nsTArray<char> inputData;
+ testing::CreateData(4 * 1024, inputData);
+ nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> base;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Take advantage of nsBufferedInputStream being non-cloneable right
+ // now. If this changes in the future, then we need a different stream
+ // type in this test.
+ nsCOMPtr<nsIInputStream> stream;
+ rv = NS_NewBufferedInputStream(getter_AddRefs(stream), base, 4096);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(stream);
+ ASSERT_TRUE(cloneable == nullptr);
+
+ nsCOMPtr<nsIInputStream> clone;
+ nsCOMPtr<nsIInputStream> replacement;
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone),
+ getter_AddRefs(replacement));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(clone != nullptr);
+ ASSERT_TRUE(replacement != nullptr);
+ ASSERT_TRUE(stream.get() != replacement.get());
+ ASSERT_TRUE(clone.get() != replacement.get());
+
+ stream = replacement.forget();
+
+ // The stream is being copied asynchronously on the STS event target. Spin
+ // a yield loop here until the data is available. Yes, this is a bit hacky,
+ // but AFAICT, gtest does not support async test completion.
+ uint64_t available;
+ do {
+ mozilla::Unused << PR_Sleep(PR_INTERVAL_NO_WAIT);
+ rv = stream->Available(&available);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ } while(available < inputString.Length());
+
+ testing::ConsumeAndValidateStream(stream, inputString);
+ testing::ConsumeAndValidateStream(clone, inputString);
+}
+
+TEST(CloneInputStream, CloneMultiplexStream)
+{
+ nsCOMPtr<nsIMultiplexInputStream> stream =
+ do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+ ASSERT_TRUE(stream);
+
+ nsTArray<char> inputData;
+ testing::CreateData(1024, inputData);
+ for (uint32_t i = 0; i < 2; ++i) {
+ nsCString inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> base;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = stream->AppendStream(base);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ }
+
+ // Unread stream should clone successfully.
+ nsTArray<char> doubled;
+ doubled.AppendElements(inputData);
+ doubled.AppendElements(inputData);
+
+ nsCOMPtr<nsIInputStream> clone;
+ nsresult rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ testing::ConsumeAndValidateStream(clone, doubled);
+
+ // Stream that has been read should fail.
+ char buffer[512];
+ uint32_t read;
+ rv = stream->Read(buffer, 512, &read);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> clone2;
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone2));
+ ASSERT_TRUE(NS_FAILED(rv));
+}
+
+TEST(CloneInputStream, CloneMultiplexStreamPartial)
+{
+ nsCOMPtr<nsIMultiplexInputStream> stream =
+ do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+ ASSERT_TRUE(stream);
+
+ nsTArray<char> inputData;
+ testing::CreateData(1024, inputData);
+ for (uint32_t i = 0; i < 2; ++i) {
+ nsCString inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> base;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = stream->AppendStream(base);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ }
+
+ // Fail when first stream read, but second hasn't been started.
+ char buffer[1024];
+ uint32_t read;
+ nsresult rv = stream->Read(buffer, 1024, &read);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ // Fail after beginning read of second stream.
+ rv = stream->Read(buffer, 512, &read);
+ ASSERT_TRUE(NS_SUCCEEDED(rv) && read == 512);
+
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ // Fail at the end.
+ nsAutoCString consumed;
+ rv = NS_ConsumeStream(stream, UINT32_MAX, consumed);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_FAILED(rv));
+}
diff --git a/xpcom/tests/gtest/TestDeadlockDetector.cpp b/xpcom/tests/gtest/TestDeadlockDetector.cpp
new file mode 100644
index 000000000..646ee3e1d
--- /dev/null
+++ b/xpcom/tests/gtest/TestDeadlockDetector.cpp
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * 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 "mozilla/ArrayUtils.h"
+
+#include "prthread.h"
+
+#include "nsTArray.h"
+#include "nsMemory.h"
+
+#include "mozilla/CondVar.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Mutex.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsCOMPtr.h"
+#include "nsICrashReporter.h"
+#include "nsServiceManagerUtils.h"
+#endif
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+static PRThread*
+spawn(void (*run)(void*), void* arg)
+{
+ return PR_CreateThread(PR_SYSTEM_THREAD,
+ run,
+ arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+}
+
+// This global variable is defined in toolkit/xre/nsSigHandlers.cpp.
+extern unsigned int _gdb_sleep_duration;
+
+/**
+ * Simple test fixture that makes sure the gdb sleep setup in the
+ * ah crap handler is bypassed during the death tests.
+ */
+class DeadlockDetectorTest : public ::testing::Test
+{
+protected:
+ void SetUp() final {
+ mOldSleepDuration = _gdb_sleep_duration;
+ _gdb_sleep_duration = 0;
+ }
+
+ void TearDown() final {
+ _gdb_sleep_duration = mOldSleepDuration;
+ }
+
+private:
+ unsigned int mOldSleepDuration;
+};
+
+void DisableCrashReporter()
+{
+#ifdef MOZ_CRASHREPORTER
+ nsCOMPtr<nsICrashReporter> crashreporter =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (crashreporter) {
+ crashreporter->SetEnabled(false);
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Single-threaded sanity tests
+
+// Stupidest possible deadlock.
+int
+Sanity_Child()
+{
+ DisableCrashReporter();
+
+ mozilla::Mutex m1("dd.sanity.m1");
+ m1.Lock();
+ m1.Lock();
+ return 0; // not reached
+}
+
+TEST_F(DeadlockDetectorTest, SanityDeathTest)
+{
+ const char* const regex =
+ "###!!! ERROR: Potential deadlock detected.*"
+ "=== Cyclical dependency starts at.*--- Mutex : dd.sanity.m1.*"
+ "=== Cycle completed at.*--- Mutex : dd.sanity.m1.*"
+ "###!!! Deadlock may happen NOW!.*" // better catch these easy cases...
+ "###!!! ASSERTION: Potential deadlock detected.*";
+
+ ASSERT_DEATH_IF_SUPPORTED(Sanity_Child(), regex);
+}
+
+// Slightly less stupid deadlock.
+int
+Sanity2_Child()
+{
+ DisableCrashReporter();
+
+ mozilla::Mutex m1("dd.sanity2.m1");
+ mozilla::Mutex m2("dd.sanity2.m2");
+ m1.Lock();
+ m2.Lock();
+ m1.Lock();
+ return 0; // not reached
+}
+
+TEST_F(DeadlockDetectorTest, Sanity2DeathTest)
+{
+ const char* const regex =
+ "###!!! ERROR: Potential deadlock detected.*"
+ "=== Cyclical dependency starts at.*--- Mutex : dd.sanity2.m1.*"
+ "--- Next dependency:.*--- Mutex : dd.sanity2.m2.*"
+ "=== Cycle completed at.*--- Mutex : dd.sanity2.m1.*"
+ "###!!! Deadlock may happen NOW!.*" // better catch these easy cases...
+ "###!!! ASSERTION: Potential deadlock detected.*";
+
+ ASSERT_DEATH_IF_SUPPORTED(Sanity2_Child(), regex);
+}
+
+int
+Sanity3_Child()
+{
+ DisableCrashReporter();
+
+ mozilla::Mutex m1("dd.sanity3.m1");
+ mozilla::Mutex m2("dd.sanity3.m2");
+ mozilla::Mutex m3("dd.sanity3.m3");
+ mozilla::Mutex m4("dd.sanity3.m4");
+
+ m1.Lock();
+ m2.Lock();
+ m3.Lock();
+ m4.Lock();
+ m4.Unlock();
+ m3.Unlock();
+ m2.Unlock();
+ m1.Unlock();
+
+ m4.Lock();
+ m1.Lock();
+ return 0;
+}
+
+TEST_F(DeadlockDetectorTest, Sanity3DeathTest)
+{
+ const char* const regex =
+ "###!!! ERROR: Potential deadlock detected.*"
+ "=== Cyclical dependency starts at.*--- Mutex : dd.sanity3.m1.*"
+ "--- Next dependency:.*--- Mutex : dd.sanity3.m2.*"
+ "--- Next dependency:.*--- Mutex : dd.sanity3.m3.*"
+ "--- Next dependency:.*--- Mutex : dd.sanity3.m4.*"
+ "=== Cycle completed at.*--- Mutex : dd.sanity3.m1.*"
+ "###!!! ASSERTION: Potential deadlock detected.*";
+
+ ASSERT_DEATH_IF_SUPPORTED(Sanity3_Child(), regex);
+}
+
+int
+Sanity4_Child()
+{
+ DisableCrashReporter();
+
+ mozilla::ReentrantMonitor m1("dd.sanity4.m1");
+ mozilla::Mutex m2("dd.sanity4.m2");
+ m1.Enter();
+ m2.Lock();
+ m1.Enter();
+ return 0;
+}
+
+TEST_F(DeadlockDetectorTest, Sanity4DeathTest)
+{
+ const char* const regex =
+ "Re-entering ReentrantMonitor after acquiring other resources.*"
+ "###!!! ERROR: Potential deadlock detected.*"
+ "=== Cyclical dependency starts at.*--- ReentrantMonitor : dd.sanity4.m1.*"
+ "--- Next dependency:.*--- Mutex : dd.sanity4.m2.*"
+ "=== Cycle completed at.*--- ReentrantMonitor : dd.sanity4.m1.*"
+ "###!!! ASSERTION: Potential deadlock detected.*";
+ ASSERT_DEATH_IF_SUPPORTED(Sanity4_Child(), regex);
+}
+
+//-----------------------------------------------------------------------------
+// Multithreaded tests
+
+/**
+ * Helper for passing state to threads in the multithread tests.
+ */
+struct ThreadState
+{
+ /**
+ * Locks to use during the test. This is just a reference and is owned by
+ * the main test thread.
+ */
+ const nsTArray<mozilla::Mutex*>& locks;
+
+ /**
+ * Integer argument used to identify each thread.
+ */
+ int id;
+};
+
+static void
+TwoThreads_thread(void* arg)
+{
+ ThreadState* state = static_cast<ThreadState*>(arg);
+
+ mozilla::Mutex* ttM1 = state->locks[0];
+ mozilla::Mutex* ttM2 = state->locks[1];
+
+ if (state->id) {
+ ttM1->Lock();
+ ttM2->Lock();
+ ttM2->Unlock();
+ ttM1->Unlock();
+ }
+ else {
+ ttM2->Lock();
+ ttM1->Lock();
+ ttM1->Unlock();
+ ttM2->Unlock();
+ }
+}
+
+int
+TwoThreads_Child()
+{
+ DisableCrashReporter();
+
+ nsTArray<mozilla::Mutex*> locks = {
+ new mozilla::Mutex("dd.twothreads.m1"),
+ new mozilla::Mutex("dd.twothreads.m2")
+ };
+
+ ThreadState state_1 {locks, 0};
+ PRThread* t1 = spawn(TwoThreads_thread, &state_1);
+ PR_JoinThread(t1);
+
+ ThreadState state_2 {locks, 1};
+ PRThread* t2 = spawn(TwoThreads_thread, &state_2);
+ PR_JoinThread(t2);
+
+ for (auto& lock : locks) {
+ delete lock;
+ }
+
+ return 0;
+}
+
+TEST_F(DeadlockDetectorTest, TwoThreadsDeathTest)
+{
+ const char* const regex =
+ "###!!! ERROR: Potential deadlock detected.*"
+ "=== Cyclical dependency starts at.*--- Mutex : dd.twothreads.m2.*"
+ "--- Next dependency:.*--- Mutex : dd.twothreads.m1.*"
+ "=== Cycle completed at.*--- Mutex : dd.twothreads.m2.*"
+ "###!!! ASSERTION: Potential deadlock detected.*";
+
+ ASSERT_DEATH_IF_SUPPORTED(TwoThreads_Child(), regex);
+}
+
+static void
+ContentionNoDeadlock_thread(void* arg)
+{
+ const uint32_t K = 100000;
+
+ ThreadState* state = static_cast<ThreadState*>(arg);
+ int32_t starti = static_cast<int32_t>(state->id);
+ auto& cndMs = state->locks;
+
+ for (uint32_t k = 0; k < K; ++k) {
+ for (int32_t i = starti; i < (int32_t)cndMs.Length(); ++i)
+ cndMs[i]->Lock();
+ // comment out the next two lines for deadlocking fun!
+ for (int32_t i = cndMs.Length() - 1; i >= starti; --i)
+ cndMs[i]->Unlock();
+
+ starti = (starti + 1) % 3;
+ }
+}
+
+int
+ContentionNoDeadlock_Child()
+{
+ const size_t kMutexCount = 4;
+
+ PRThread* threads[3];
+ nsTArray<mozilla::Mutex*> locks;
+ ThreadState states[] = {
+ { locks, 0 },
+ { locks, 1 },
+ { locks, 2 }
+ };
+
+ for (uint32_t i = 0; i < kMutexCount; ++i)
+ locks.AppendElement(new mozilla::Mutex("dd.cnd.ms"));
+
+ for (int32_t i = 0; i < (int32_t) ArrayLength(threads); ++i)
+ threads[i] = spawn(ContentionNoDeadlock_thread, states + i);
+
+ for (uint32_t i = 0; i < ArrayLength(threads); ++i)
+ PR_JoinThread(threads[i]);
+
+ for (uint32_t i = 0; i < locks.Length(); ++i)
+ delete locks[i];
+
+ return 0;
+}
+
+TEST_F(DeadlockDetectorTest, ContentionNoDeadlock)
+{
+ // Just check that this test runs to completion.
+ ASSERT_EQ(ContentionNoDeadlock_Child(), 0);
+}
diff --git a/xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp b/xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp
new file mode 100644
index 000000000..e25217223
--- /dev/null
+++ b/xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * 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/. */
+
+// Avoid DMD-specific parts of MOZ_DEFINE_MALLOC_SIZE_OF
+#undef MOZ_DMD
+
+#include "nsIMemoryReporter.h"
+#include "mozilla/Mutex.h"
+
+#include "gtest/gtest.h"
+
+//-----------------------------------------------------------------------------
+
+static void
+AllocLockRecurseUnlockFree(int i)
+{
+ if (0 == i)
+ return;
+
+ mozilla::Mutex* lock = new mozilla::Mutex("deadlockDetector.scalability.t1");
+ {
+ mozilla::MutexAutoLock _(*lock);
+ AllocLockRecurseUnlockFree(i - 1);
+ }
+ delete lock;
+}
+
+// This test creates a resource dependency chain N elements long, then
+// frees all the resources in the chain.
+TEST(DeadlockDetectorScalability, LengthNDepChain)
+{
+ const int N = 1 << 14; // 16K
+ AllocLockRecurseUnlockFree(N);
+ ASSERT_TRUE(true);
+}
+
+//-----------------------------------------------------------------------------
+
+// This test creates a single lock that is ordered < N resources, then
+// repeatedly exercises this order k times.
+//
+// NB: It takes a minute or two to run so it is disabled by default.
+TEST(DeadlockDetectorScalability, DISABLED_OneLockNDeps)
+{
+ // NB: Using a larger test size to stress our traversal logic.
+ const int N = 1 << 17; // 131k
+ const int K = 100;
+
+ mozilla::Mutex* lock = new mozilla::Mutex("deadlockDetector.scalability.t2.master");
+ mozilla::Mutex** locks = new mozilla::Mutex*[N];
+ if (!locks)
+ NS_RUNTIMEABORT("couldn't allocate lock array");
+
+ for (int i = 0; i < N; ++i)
+ locks[i] =
+ new mozilla::Mutex("deadlockDetector.scalability.t2.dep");
+
+ // establish orders
+ {mozilla::MutexAutoLock m(*lock);
+ for (int i = 0; i < N; ++i)
+ mozilla::MutexAutoLock s(*locks[i]);
+ }
+
+ // exercise order check
+ {mozilla::MutexAutoLock m(*lock);
+ for (int i = 0; i < K; ++i)
+ for (int j = 0; j < N; ++j)
+ mozilla::MutexAutoLock s(*locks[i]);
+ }
+
+ for (int i = 0; i < N; ++i)
+ delete locks[i];
+ delete[] locks;
+
+ ASSERT_TRUE(true);
+}
+
+//-----------------------------------------------------------------------------
+
+// This test creates N resources and adds the theoretical maximum number
+// of dependencies, O(N^2). It then repeats that sequence of
+// acquisitions k times. Finally, all resources are freed.
+//
+// It's very difficult to perform well on this test. It's put forth as a
+// challenge problem.
+
+TEST(DeadlockDetectorScalability, MaxDepsNsq)
+{
+ const int N = 1 << 10; // 1k
+ const int K = 10;
+
+ mozilla::Mutex** locks = new mozilla::Mutex*[N];
+ if (!locks)
+ NS_RUNTIMEABORT("couldn't allocate lock array");
+
+ for (int i = 0; i < N; ++i)
+ locks[i] = new mozilla::Mutex("deadlockDetector.scalability.t3");
+
+ for (int i = 0; i < N; ++i) {
+ mozilla::MutexAutoLock al1(*locks[i]);
+ for (int j = i+1; j < N; ++j)
+ mozilla::MutexAutoLock al2(*locks[j]);
+ }
+
+ for (int i = 0; i < K; ++i) {
+ for (int j = 0; j < N; ++j) {
+ mozilla::MutexAutoLock al1(*locks[j]);
+ for (int k = j+1; k < N; ++k)
+ mozilla::MutexAutoLock al2(*locks[k]);
+ }
+ }
+
+ for (int i = 0; i < N; ++i)
+ delete locks[i];
+ delete[] locks;
+
+ ASSERT_TRUE(true);
+}
+
+//-----------------------------------------------------------------------------
+
+// This test creates a single lock that is ordered < N resources. The
+// resources are allocated, exercised K times, and deallocated one at
+// a time.
+
+TEST(DeadlockDetectorScalability, OneLockNDepsUsedSeveralTimes)
+{
+ const size_t N = 1 << 17; // 131k
+ const size_t K = 3;
+
+ // Create master lock.
+ mozilla::Mutex* lock_1 = new mozilla::Mutex("deadlockDetector.scalability.t4.master");
+ for (size_t n = 0; n < N; n++) {
+ // Create child lock.
+ mozilla::Mutex* lock_2 = new mozilla::Mutex("deadlockDetector.scalability.t4.child");
+
+ // First lock the master.
+ mozilla::MutexAutoLock m(*lock_1);
+
+ // Now lock and unlock the child a few times.
+ for (size_t k = 0; k < K; k++) {
+ mozilla::MutexAutoLock c(*lock_2);
+ }
+
+ // Destroy the child lock.
+ delete lock_2;
+ }
+
+ // Cleanup the master lock.
+ delete lock_1;
+
+ ASSERT_TRUE(true);
+}
+
+//-----------------------------------------------------------------------------
+
+MOZ_DEFINE_MALLOC_SIZE_OF(DeadlockDetectorMallocSizeOf)
+
+// This is a simple test that exercises the deadlock detector memory reporting
+// functionality.
+TEST(DeadlockDetectorScalability, SizeOf)
+{
+ size_t memory_used = mozilla::BlockingResourceBase::SizeOfDeadlockDetector(
+ DeadlockDetectorMallocSizeOf);
+
+ ASSERT_GT(memory_used, size_t(0));
+}
diff --git a/xpcom/tests/gtest/TestEncoding.cpp b/xpcom/tests/gtest/TestEncoding.cpp
new file mode 100644
index 000000000..0671284ee
--- /dev/null
+++ b/xpcom/tests/gtest/TestEncoding.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include "nsString.h"
+#include "gtest/gtest.h"
+
+TEST(Encoding, GoodSurrogatePair)
+{
+ // When this string is decoded, the surrogate pair is U+10302 and the rest of
+ // the string is specified by indexes 2 onward.
+ const char16_t goodPairData[] = { 0xD800, 0xDF02, 0x65, 0x78, 0x0 };
+ nsDependentString goodPair16(goodPairData);
+
+ uint32_t byteCount = 0;
+ char* goodPair8 = ToNewUTF8String(goodPair16, &byteCount);
+ EXPECT_TRUE(!!goodPair8);
+
+ EXPECT_EQ(byteCount, 6u);
+
+ const unsigned char expected8[] =
+ { 0xF0, 0x90, 0x8C, 0x82, 0x65, 0x78, 0x0 };
+ EXPECT_EQ(0, memcmp(expected8, goodPair8, sizeof(expected8)));
+
+ // This takes a different code path from the above, so test it to make sure
+ // the UTF-16 enumeration remains in sync with the UTF-8 enumeration.
+ nsDependentCString expected((const char*)expected8);
+ EXPECT_EQ(0, CompareUTF8toUTF16(expected, goodPair16));
+
+ free(goodPair8);
+}
+
+TEST(Encoding, BackwardsSurrogatePair)
+{
+ // When this string is decoded, the two surrogates are wrongly ordered and
+ // must each be interpreted as U+FFFD.
+ const char16_t backwardsPairData[] = { 0xDDDD, 0xD863, 0x65, 0x78, 0x0 };
+ nsDependentString backwardsPair16(backwardsPairData);
+
+ uint32_t byteCount = 0;
+ char* backwardsPair8 = ToNewUTF8String(backwardsPair16, &byteCount);
+ EXPECT_TRUE(!!backwardsPair8);
+
+ EXPECT_EQ(byteCount, 8u);
+
+ const unsigned char expected8[] =
+ { 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x65, 0x78, 0x0 };
+ EXPECT_EQ(0, memcmp(expected8, backwardsPair8, sizeof(expected8)));
+
+ // This takes a different code path from the above, so test it to make sure
+ // the UTF-16 enumeration remains in sync with the UTF-8 enumeration.
+ nsDependentCString expected((const char*)expected8);
+ EXPECT_EQ(0, CompareUTF8toUTF16(expected, backwardsPair16));
+
+ free(backwardsPair8);
+}
+
+TEST(Encoding, MalformedUTF16OrphanHighSurrogate)
+{
+ // When this string is decoded, the high surrogate should be replaced and the
+ // rest of the string is specified by indexes 1 onward.
+ const char16_t highSurrogateData[] = { 0xD863, 0x74, 0x65, 0x78, 0x74, 0x0 };
+ nsDependentString highSurrogate16(highSurrogateData);
+
+ uint32_t byteCount = 0;
+ char* highSurrogate8 = ToNewUTF8String(highSurrogate16, &byteCount);
+ EXPECT_TRUE(!!highSurrogate8);
+
+ EXPECT_EQ(byteCount, 7u);
+
+ const unsigned char expected8[] =
+ { 0xEF, 0xBF, 0xBD, 0x74, 0x65, 0x78, 0x74, 0x0 };
+ EXPECT_EQ(0, memcmp(expected8, highSurrogate8, sizeof(expected8)));
+
+ // This takes a different code path from the above, so test it to make sure
+ // the UTF-16 enumeration remains in sync with the UTF-8 enumeration.
+ nsDependentCString expected((const char*)expected8);
+ EXPECT_EQ(0, CompareUTF8toUTF16(expected, highSurrogate16));
+
+ free(highSurrogate8);
+}
+
+TEST(Encoding, MalformedUTF16OrphanLowSurrogate)
+{
+ // When this string is decoded, the low surrogate should be replaced and the
+ // rest of the string is specified by indexes 1 onward.
+ const char16_t lowSurrogateData[] = { 0xDDDD, 0x74, 0x65, 0x78, 0x74, 0x0 };
+ nsDependentString lowSurrogate16(lowSurrogateData);
+
+ uint32_t byteCount = 0;
+ char* lowSurrogate8 = ToNewUTF8String(lowSurrogate16, &byteCount);
+ EXPECT_TRUE(!!lowSurrogate8);
+
+ EXPECT_EQ(byteCount, 7u);
+
+ const unsigned char expected8[] =
+ { 0xEF, 0xBF, 0xBD, 0x74, 0x65, 0x78, 0x74, 0x0 };
+ EXPECT_EQ(0, memcmp(expected8, lowSurrogate8, sizeof(expected8)));
+
+ // This takes a different code path from the above, so test it to make sure
+ // the UTF-16 enumeration remains in sync with the UTF-8 enumeration.
+ nsDependentCString expected((const char*)expected8);
+ EXPECT_EQ(0, CompareUTF8toUTF16(expected, lowSurrogate16));
+
+ free(lowSurrogate8);
+}
diff --git a/xpcom/tests/gtest/TestEscapeURL.cpp b/xpcom/tests/gtest/TestEscapeURL.cpp
new file mode 100644
index 000000000..2a8faf5f0
--- /dev/null
+++ b/xpcom/tests/gtest/TestEscapeURL.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsEscape.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+// Testing for failure here would be somewhat hard in automation. Locally you
+// could use something like ulimit to create a failure.
+
+TEST(EscapeURL, FallibleNoEscape)
+{
+ // Tests the fallible version of NS_EscapeURL works as expected when no
+ // escaping is necessary.
+ nsCString toEscape("data:,Hello%2C%20World!");
+ nsCString escaped;
+ nsresult rv = NS_EscapeURL(toEscape, esc_OnlyNonASCII, escaped, fallible);
+ EXPECT_EQ(rv, NS_OK);
+ // Nothing should have been escaped, they should be the same string.
+ EXPECT_STREQ(toEscape.BeginReading(), escaped.BeginReading());
+ // We expect them to point at the same buffer.
+ EXPECT_EQ(toEscape.BeginReading(), escaped.BeginReading());
+}
+
+TEST(EscapeURL, FallibleEscape)
+{
+ // Tests the fallible version of NS_EscapeURL works as expected when
+ // escaping is necessary.
+ nsCString toEscape("data:,Hello%2C%20World!\xC4\x9F");
+ nsCString escaped;
+ nsresult rv = NS_EscapeURL(toEscape, esc_OnlyNonASCII, escaped, fallible);
+ EXPECT_EQ(rv, NS_OK);
+ EXPECT_STRNE(toEscape.BeginReading(), escaped.BeginReading());
+ const char* const kExpected = "data:,Hello%2C%20World!%C4%9F";
+ EXPECT_STREQ(escaped.BeginReading(), kExpected);
+}
+
+TEST(EscapeURL, BadEscapeSequences)
+{
+ {
+ char bad[] = "%s\0fa";
+
+ int32_t count = nsUnescapeCount(bad);
+ EXPECT_EQ(count, 2);
+ EXPECT_STREQ(bad, "%s");
+ }
+ {
+ char bad[] = "%a";
+ int32_t count = nsUnescapeCount(bad);
+ EXPECT_EQ(count, 2);
+ EXPECT_STREQ(bad, "%a");
+ }
+ {
+ char bad[] = "%";
+ int32_t count = nsUnescapeCount(bad);
+ EXPECT_EQ(count, 1);
+ EXPECT_STREQ(bad, "%");
+ }
+ {
+ char bad[] = "%s/%s";
+ int32_t count = nsUnescapeCount(bad);
+ EXPECT_EQ(count, 5);
+ EXPECT_STREQ(bad, "%s/%s");
+ }
+}
diff --git a/xpcom/tests/gtest/TestExpirationTracker.cpp b/xpcom/tests/gtest/TestExpirationTracker.cpp
new file mode 100644
index 000000000..a6a0e146c
--- /dev/null
+++ b/xpcom/tests/gtest/TestExpirationTracker.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <prthread.h>
+#include "nsExpirationTracker.h"
+#include "nsMemory.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsXPCOM.h"
+#include "nsIFile.h"
+#include "prinrval.h"
+#include "nsThreadUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "gtest/gtest.h"
+
+namespace TestExpirationTracker {
+
+struct Object {
+ Object() : mExpired(false) { Touch(); }
+ void Touch() { mLastUsed = PR_IntervalNow(); mExpired = false; }
+
+ nsExpirationState mExpiration;
+ nsExpirationState* GetExpirationState() { return &mExpiration; }
+
+ PRIntervalTime mLastUsed;
+ bool mExpired;
+};
+
+static bool error;
+static uint32_t periodMS = 100;
+static uint32_t ops = 1000;
+static uint32_t iterations = 2;
+static bool logging = 0;
+static uint32_t sleepPeriodMS = 50;
+static uint32_t slackMS = 30; // allow this much error
+
+template <uint32_t K> class Tracker : public nsExpirationTracker<Object,K> {
+public:
+ Tracker() : nsExpirationTracker<Object,K>(periodMS, "Tracker") {
+ Object* obj = new Object();
+ mUniverse.AppendElement(obj);
+ LogAction(obj, "Created");
+ }
+
+ nsTArray<mozilla::UniquePtr<Object>> mUniverse;
+
+ void LogAction(Object* aObj, const char* aAction) {
+ if (logging) {
+ printf("%d %p(%d): %s\n", PR_IntervalNow(),
+ static_cast<void*>(aObj), aObj->mLastUsed, aAction);
+ }
+ }
+
+ void DoRandomOperation() {
+ Object* obj;
+ switch (rand() & 0x7) {
+ case 0: {
+ if (mUniverse.Length() < 50) {
+ obj = new Object();
+ mUniverse.AppendElement(obj);
+ nsExpirationTracker<Object,K>::AddObject(obj);
+ LogAction(obj, "Created and added");
+ }
+ break;
+ }
+ case 4: {
+ if (mUniverse.Length() < 50) {
+ obj = new Object();
+ mUniverse.AppendElement(obj);
+ LogAction(obj, "Created");
+ }
+ break;
+ }
+ case 1: {
+ UniquePtr<Object>& objref = mUniverse[uint32_t(rand())%mUniverse.Length()];
+ if (objref->mExpiration.IsTracked()) {
+ nsExpirationTracker<Object,K>::RemoveObject(objref.get());
+ LogAction(objref.get(), "Removed");
+ }
+ break;
+ }
+ case 2: {
+ UniquePtr<Object>& objref = mUniverse[uint32_t(rand())%mUniverse.Length()];
+ if (!objref->mExpiration.IsTracked()) {
+ objref->Touch();
+ nsExpirationTracker<Object,K>::AddObject(objref.get());
+ LogAction(objref.get(), "Added");
+ }
+ break;
+ }
+ case 3: {
+ UniquePtr<Object>& objref = mUniverse[uint32_t(rand())%mUniverse.Length()];
+ if (objref->mExpiration.IsTracked()) {
+ objref->Touch();
+ nsExpirationTracker<Object,K>::MarkUsed(objref.get());
+ LogAction(objref.get(), "Marked used");
+ }
+ break;
+ }
+ }
+ }
+
+protected:
+ void NotifyExpired(Object* aObj) {
+ LogAction(aObj, "Expired");
+ PRIntervalTime now = PR_IntervalNow();
+ uint32_t timeDiffMS = (now - aObj->mLastUsed)*1000/PR_TicksPerSecond();
+ // See the comment for NotifyExpired in nsExpirationTracker.h for these
+ // bounds
+ uint32_t lowerBoundMS = (K-1)*periodMS - slackMS;
+ uint32_t upperBoundMS = K*(periodMS + sleepPeriodMS) + slackMS;
+ if (logging) {
+ printf("Checking: %d-%d = %d [%d,%d]\n",
+ now, aObj->mLastUsed, timeDiffMS, lowerBoundMS, upperBoundMS);
+ }
+ if (timeDiffMS < lowerBoundMS || timeDiffMS > upperBoundMS) {
+ EXPECT_TRUE(timeDiffMS < periodMS && aObj->mExpired);
+ }
+ aObj->Touch();
+ aObj->mExpired = true;
+ DoRandomOperation();
+ DoRandomOperation();
+ DoRandomOperation();
+ }
+};
+
+template <uint32_t K> static bool test_random() {
+ srand(K);
+ error = false;
+
+ for (uint32_t j = 0; j < iterations; ++j) {
+ Tracker<K> tracker;
+
+ uint32_t i = 0;
+ for (i = 0; i < ops; ++i) {
+ if ((rand() & 0xF) == 0) {
+ // Simulate work that takes time
+ if (logging) {
+ printf("SLEEPING for %dms (%d)\n", sleepPeriodMS, PR_IntervalNow());
+ }
+ PR_Sleep(PR_MillisecondsToInterval(sleepPeriodMS));
+ // Process pending timer events
+ NS_ProcessPendingEvents(nullptr);
+ }
+ tracker.DoRandomOperation();
+ }
+ }
+
+ return !error;
+}
+
+static bool test_random3() { return test_random<3>(); }
+static bool test_random4() { return test_random<4>(); }
+static bool test_random8() { return test_random<8>(); }
+
+typedef bool (*TestFunc)();
+#define DECL_TEST(name) { #name, name }
+
+static const struct Test {
+ const char* name;
+ TestFunc func;
+} tests[] = {
+ DECL_TEST(test_random3),
+ DECL_TEST(test_random4),
+ DECL_TEST(test_random8),
+ { nullptr, nullptr }
+};
+
+TEST(ExpirationTracker, main)
+{
+ for (const TestExpirationTracker::Test* t = tests;
+ t->name != nullptr; ++t) {
+ EXPECT_TRUE(t->func());
+ }
+}
+
+} // namespace TestExpirationTracker
diff --git a/xpcom/tests/gtest/TestFile.cpp b/xpcom/tests/gtest/TestFile.cpp
new file mode 100644
index 000000000..7e7b4f4a1
--- /dev/null
+++ b/xpcom/tests/gtest/TestFile.cpp
@@ -0,0 +1,477 @@
+/* -*- 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 "prio.h"
+#include "prsystem.h"
+
+#include "nsIFile.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+
+#include "gtest/gtest.h"
+
+static bool VerifyResult(nsresult aRV, const char* aMsg)
+{
+ bool failed = NS_FAILED(aRV);
+ EXPECT_FALSE(failed) << aMsg << " rv=" << std::hex << (unsigned int)aRV;
+ return !failed;
+}
+
+static already_AddRefed<nsIFile> NewFile(nsIFile* aBase)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> file =
+ do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ VerifyResult(rv, "Creating nsIFile");
+ rv = file->InitWithFile(aBase);
+ VerifyResult(rv, "InitWithFile");
+ return file.forget();
+}
+
+static nsCString FixName(const char* aName)
+{
+ nsCString name;
+ for (uint32_t i = 0; aName[i]; ++i) {
+ char ch = aName[i];
+ // PR_GetPathSeparator returns the wrong value on Mac so don't use it
+#if defined(XP_WIN)
+ if (ch == '/') {
+ ch = '\\';
+ }
+#endif
+ name.Append(ch);
+ }
+ return name;
+}
+
+// Test nsIFile::AppendNative, verifying that aName is not a valid file name
+static bool TestInvalidFileName(nsIFile* aBase, const char* aName)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (NS_SUCCEEDED(rv)) {
+ EXPECT_FALSE(NS_SUCCEEDED(rv)) << "AppendNative with invalid filename " << name.get();
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::Create, verifying that the file exists and did not exist before,
+// and leaving it there for future tests
+static bool TestCreate(nsIFile* aBase, const char* aName, int32_t aType, int32_t aPerm)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (before)"))
+ return false;
+ EXPECT_FALSE(exists) << "File "<< name.get() << " already exists";
+ if (exists) {
+ return false;
+ }
+
+ rv = file->Create(aType, aPerm);
+ if (!VerifyResult(rv, "Create"))
+ return false;
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (after)"))
+ return false;
+ EXPECT_TRUE(exists) << "File " << name.get() << " was not created";
+ if (!exists) {
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::CreateUnique, verifying that the new file exists and if it existed before,
+// the new file has a different name.
+// The new file is left in place.
+static bool TestCreateUnique(nsIFile* aBase, const char* aName, int32_t aType, int32_t aPerm)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ bool existsBefore;
+ rv = file->Exists(&existsBefore);
+ if (!VerifyResult(rv, "Exists (before)"))
+ return false;
+
+ rv = file->CreateUnique(aType, aPerm);
+ if (!VerifyResult(rv, "Create"))
+ return false;
+
+ bool existsAfter;
+ rv = file->Exists(&existsAfter);
+ if (!VerifyResult(rv, "Exists (after)"))
+ return false;
+ EXPECT_TRUE(existsAfter) << "File " << name.get() << " was not created";
+ if (!existsAfter) {
+ return false;
+ }
+
+ if (existsBefore) {
+ nsAutoCString leafName;
+ rv = file->GetNativeLeafName(leafName);
+ if (!VerifyResult(rv, "GetNativeLeafName"))
+ return false;
+ EXPECT_FALSE(leafName.Equals(name)) << "File " << name.get() << " was not given a new name by CreateUnique";
+ if (leafName.Equals(name)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Test nsIFile::OpenNSPRFileDesc with DELETE_ON_CLOSE, verifying that the file exists
+// and did not exist before, and leaving it there for future tests
+static bool TestDeleteOnClose(nsIFile* aBase, const char* aName, int32_t aFlags, int32_t aPerm)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (before)"))
+ return false;
+ EXPECT_FALSE(exists) << "File " << name.get() << " already exists";
+ if (exists) {
+ return false;
+ }
+
+ PRFileDesc* fileDesc;
+ rv = file->OpenNSPRFileDesc(aFlags | nsIFile::DELETE_ON_CLOSE, aPerm, &fileDesc);
+ if (!VerifyResult(rv, "OpenNSPRFileDesc"))
+ return false;
+ PRStatus status = PR_Close(fileDesc);
+ EXPECT_EQ(status, PR_SUCCESS) << "File " << name.get() << " could not be closed";
+ if (status != PR_SUCCESS) {
+ return false;
+ }
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (after)"))
+ return false;
+ EXPECT_FALSE(exists) << "File " << name.get() << " was not removed on close";
+ if (exists) {
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::Remove, verifying that the file does not exist and did before
+static bool TestRemove(nsIFile* aBase, const char* aName, bool aRecursive)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (before)"))
+ return false;
+ EXPECT_TRUE(exists);
+ if (!exists) {
+ return false;
+ }
+
+ rv = file->Remove(aRecursive);
+ if (!VerifyResult(rv, "Remove"))
+ return false;
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (after)"))
+ return false;
+ EXPECT_FALSE(exists) << "File " << name.get() << " was not removed";
+ if (exists) {
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::MoveToNative, verifying that the file did not exist at the new location
+// before and does afterward, and that it does not exist at the old location anymore
+static bool TestMove(nsIFile* aBase, nsIFile* aDestDir, const char* aName, const char* aNewName)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (before)"))
+ return false;
+ EXPECT_TRUE(exists);
+ if (!exists) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> newFile = NewFile(file);
+ nsCString newName = FixName(aNewName);
+ rv = newFile->MoveToNative(aDestDir, newName);
+ if (!VerifyResult(rv, "MoveToNative"))
+ return false;
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (after)"))
+ return false;
+ EXPECT_FALSE(exists) << "File " << name.get() << " was not moved";
+ if (exists) {
+ return false;
+ }
+
+ file = NewFile(aDestDir);
+ if (!file)
+ return false;
+ rv = file->AppendNative(newName);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+ bool equal;
+ rv = file->Equals(newFile, &equal);
+ if (!VerifyResult(rv, "Equals"))
+ return false;
+ EXPECT_TRUE(equal) << "File object was not updated to destination";
+ if (!equal) {
+ return false;
+ }
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (new after)"))
+ return false;
+ EXPECT_TRUE(exists) << "Destination file " << newName.get() << " was not created";
+ if (!exists) {
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::CopyToNative, verifying that the file did not exist at the new location
+// before and does afterward, and that it does exist at the old location too
+static bool TestCopy(nsIFile* aBase, nsIFile* aDestDir, const char* aName, const char* aNewName)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aBase);
+ if (!file)
+ return false;
+
+ nsCString name = FixName(aName);
+ nsresult rv = file->AppendNative(name);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (before)"))
+ return false;
+ EXPECT_TRUE(exists);
+ if (!exists) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> newFile = NewFile(file);
+ nsCString newName = FixName(aNewName);
+ rv = newFile->CopyToNative(aDestDir, newName);
+ if (!VerifyResult(rv, "MoveToNative"))
+ return false;
+ bool equal;
+ rv = file->Equals(newFile, &equal);
+ if (!VerifyResult(rv, "Equals"))
+ return false;
+ EXPECT_TRUE(equal) << "File object updated unexpectedly";
+ if (!equal) {
+ return false;
+ }
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (after)"))
+ return false;
+ EXPECT_TRUE(exists) << "File " << name.get() << " was removed";
+ if (!exists) {
+ return false;
+ }
+
+ file = NewFile(aDestDir);
+ if (!file)
+ return false;
+ rv = file->AppendNative(newName);
+ if (!VerifyResult(rv, "AppendNative"))
+ return false;
+
+ rv = file->Exists(&exists);
+ if (!VerifyResult(rv, "Exists (new after)"))
+ return false;
+ EXPECT_TRUE(exists) << "Destination file " << newName.get() << " was not created";
+ if (!exists) {
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::GetParent
+static bool TestParent(nsIFile* aBase, nsIFile* aStart)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aStart);
+ if (!file)
+ return false;
+
+ nsCOMPtr<nsIFile> parent;
+ nsresult rv = file->GetParent(getter_AddRefs(parent));
+ VerifyResult(rv, "GetParent");
+
+ bool equal;
+ rv = parent->Equals(aBase, &equal);
+ VerifyResult(rv, "Equals");
+ EXPECT_TRUE(equal) << "Incorrect parent";
+ if (!equal) {
+ return false;
+ }
+
+ return true;
+}
+
+// Test nsIFile::Normalize and native path setting/getting
+static bool TestNormalizeNativePath(nsIFile* aBase, nsIFile* aStart)
+{
+ nsCOMPtr<nsIFile> file = NewFile(aStart);
+ if (!file)
+ return false;
+
+ nsAutoCString path;
+ nsresult rv = file->GetNativePath(path);
+ VerifyResult(rv, "GetNativePath");
+ path.Append(FixName("/./.."));
+ rv = file->InitWithNativePath(path);
+ VerifyResult(rv, "InitWithNativePath");
+ rv = file->Normalize();
+ VerifyResult(rv, "Normalize");
+ rv = file->GetNativePath(path);
+ VerifyResult(rv, "GetNativePath (after normalization)");
+
+ nsAutoCString basePath;
+ rv = aBase->GetNativePath(basePath);
+ VerifyResult(rv, "GetNativePath (base)");
+
+ EXPECT_TRUE(path.Equals(basePath)) << "Incorrect normalization: " << path.get() << " - " << basePath.get();
+ if (!path.Equals(basePath)) {
+ return false;
+ }
+
+ return true;
+}
+
+TEST(TestFile, Tests)
+{
+ nsCOMPtr<nsIFile> base;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(base));
+ ASSERT_TRUE(VerifyResult(rv, "Getting temp directory"));
+
+ rv = base->AppendNative(nsDependentCString("mozfiletests"));
+ ASSERT_TRUE(VerifyResult(rv, "Appending mozfiletests to temp directory name"));
+
+ // Remove the directory in case tests failed and left it behind.
+ // don't check result since it might not be there
+ base->Remove(true);
+
+ // Now create the working directory we're going to use
+ rv = base->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ ASSERT_TRUE(VerifyResult(rv, "Creating temp directory"));
+
+ // Now we can safely normalize the path
+ rv = base->Normalize();
+ ASSERT_TRUE(VerifyResult(rv, "Normalizing temp directory name"));
+
+ // Initialize subdir object for later use
+ nsCOMPtr<nsIFile> subdir = NewFile(base);
+ ASSERT_TRUE(subdir);
+
+ rv = subdir->AppendNative(nsDependentCString("subdir"));
+ ASSERT_TRUE(VerifyResult(rv, "Appending 'subdir' to test dir name"));
+
+ // ---------------
+ // End setup code.
+ // ---------------
+
+ // Test path parsing
+ ASSERT_TRUE(TestInvalidFileName(base, "a/b"));
+ ASSERT_TRUE(TestParent(base, subdir));
+
+ // Test file creation
+ ASSERT_TRUE(TestCreate(base, "file.txt", nsIFile::NORMAL_FILE_TYPE, 0600));
+ ASSERT_TRUE(TestRemove(base, "file.txt", false));
+
+ // Test directory creation
+ ASSERT_TRUE(TestCreate(base, "subdir", nsIFile::DIRECTORY_TYPE, 0700));
+
+ // Test move and copy in the base directory
+ ASSERT_TRUE(TestCreate(base, "file.txt", nsIFile::NORMAL_FILE_TYPE, 0600));
+ ASSERT_TRUE(TestMove(base, base, "file.txt", "file2.txt"));
+ ASSERT_TRUE(TestCopy(base, base, "file2.txt", "file3.txt"));
+
+ // Test moving across directories
+ ASSERT_TRUE(TestMove(base, subdir, "file2.txt", "file2.txt"));
+
+ // Test moving across directories and renaming at the same time
+ ASSERT_TRUE(TestMove(subdir, base, "file2.txt", "file4.txt"));
+
+ // Test copying across directoreis
+ ASSERT_TRUE(TestCopy(base, subdir, "file4.txt", "file5.txt"));
+
+ // Run normalization tests while the directory exists
+ ASSERT_TRUE(TestNormalizeNativePath(base, subdir));
+
+ // Test recursive directory removal
+ ASSERT_TRUE(TestRemove(base, "subdir", true));
+
+ ASSERT_TRUE(TestCreateUnique(base, "foo", nsIFile::NORMAL_FILE_TYPE, 0600));
+ ASSERT_TRUE(TestCreateUnique(base, "foo", nsIFile::NORMAL_FILE_TYPE, 0600));
+ ASSERT_TRUE(TestCreateUnique(base, "bar.xx", nsIFile::DIRECTORY_TYPE, 0700));
+ ASSERT_TRUE(TestCreateUnique(base, "bar.xx", nsIFile::DIRECTORY_TYPE, 0700));
+
+ ASSERT_TRUE(TestDeleteOnClose(base, "file7.txt", PR_RDWR | PR_CREATE_FILE, 0600));
+
+ // Clean up temporary stuff
+ rv = base->Remove(true);
+ VerifyResult(rv, "Cleaning up temp directory");
+}
diff --git a/xpcom/tests/gtest/TestHashtables.cpp b/xpcom/tests/gtest/TestHashtables.cpp
new file mode 100644
index 000000000..394812631
--- /dev/null
+++ b/xpcom/tests/gtest/TestHashtables.cpp
@@ -0,0 +1,435 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTHashtable.h"
+#include "nsBaseHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsInterfaceHashtable.h"
+#include "nsClassHashtable.h"
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsCOMArray.h"
+#include "mozilla/Attributes.h"
+
+#include "gtest/gtest.h"
+
+namespace TestHashtables {
+
+class TestUniChar // for nsClassHashtable
+{
+public:
+ explicit TestUniChar(uint32_t aWord)
+ {
+ mWord = aWord;
+ }
+
+ ~TestUniChar()
+ {
+ }
+
+ uint32_t GetChar() const { return mWord; }
+
+private:
+ uint32_t mWord;
+};
+
+struct EntityNode {
+ const char* mStr; // never owns buffer
+ uint32_t mUnicode;
+};
+
+EntityNode gEntities[] = {
+ {"nbsp",160},
+ {"iexcl",161},
+ {"cent",162},
+ {"pound",163},
+ {"curren",164},
+ {"yen",165},
+ {"brvbar",166},
+ {"sect",167},
+ {"uml",168},
+ {"copy",169},
+ {"ordf",170},
+ {"laquo",171},
+ {"not",172},
+ {"shy",173},
+ {"reg",174},
+ {"macr",175}
+};
+
+#define ENTITY_COUNT (unsigned(sizeof(gEntities)/sizeof(EntityNode)))
+
+class EntityToUnicodeEntry : public PLDHashEntryHdr
+{
+public:
+ typedef const char* KeyType;
+ typedef const char* KeyTypePointer;
+
+ explicit EntityToUnicodeEntry(const char* aKey) { mNode = nullptr; }
+ EntityToUnicodeEntry(const EntityToUnicodeEntry& aEntry) { mNode = aEntry.mNode; }
+ ~EntityToUnicodeEntry() { }
+
+ bool KeyEquals(const char* aEntity) const { return !strcmp(mNode->mStr, aEntity); }
+ static const char* KeyToPointer(const char* aEntity) { return aEntity; }
+ static PLDHashNumber HashKey(const char* aEntity) { return mozilla::HashString(aEntity); }
+ enum { ALLOW_MEMMOVE = true };
+
+ const EntityNode* mNode;
+};
+
+static uint32_t
+nsTIterPrint(nsTHashtable<EntityToUnicodeEntry>& hash)
+{
+ uint32_t n = 0;
+ for (auto iter = hash.Iter(); !iter.Done(); iter.Next()) {
+ n++;
+ }
+ return n;
+}
+
+static uint32_t
+nsTIterPrintRemove(nsTHashtable<EntityToUnicodeEntry>& hash)
+{
+ uint32_t n = 0;
+ for (auto iter = hash.Iter(); !iter.Done(); iter.Next()) {
+ iter.Remove();
+ n++;
+ }
+ return n;
+}
+
+void
+testTHashtable(nsTHashtable<EntityToUnicodeEntry>& hash, uint32_t numEntries) {
+ uint32_t i;
+ for (i = 0; i < numEntries; ++i) {
+ EntityToUnicodeEntry* entry =
+ hash.PutEntry(gEntities[i].mStr);
+
+ EXPECT_TRUE(entry);
+
+ EXPECT_FALSE(entry->mNode);
+ entry->mNode = &gEntities[i];
+ }
+
+ for (i = 0; i < numEntries; ++i) {
+ EntityToUnicodeEntry* entry =
+ hash.GetEntry(gEntities[i].mStr);
+
+ EXPECT_TRUE(entry);
+ }
+
+ EntityToUnicodeEntry* entry =
+ hash.GetEntry("xxxy");
+
+ EXPECT_FALSE(entry);
+
+ uint32_t count = nsTIterPrint(hash);
+ EXPECT_EQ(count, numEntries);
+}
+
+//
+// all this nsIFoo stuff was copied wholesale from TestCOMPtr.cpp
+//
+
+#define NS_IFOO_IID \
+{ 0x6f7652e0, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+class IFoo final : public nsISupports
+ {
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+ IFoo();
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+ NS_IMETHOD QueryInterface( const nsIID&, void** );
+
+ NS_IMETHOD SetString(const nsACString& /*in*/ aString);
+ NS_IMETHOD GetString(nsACString& /*out*/ aString);
+
+ static void print_totals();
+
+ private:
+ ~IFoo();
+
+ unsigned int refcount_;
+
+ static unsigned int total_constructions_;
+ static unsigned int total_destructions_;
+ nsCString mString;
+ };
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
+
+unsigned int IFoo::total_constructions_;
+unsigned int IFoo::total_destructions_;
+
+void
+IFoo::print_totals()
+ {
+ }
+
+IFoo::IFoo()
+ : refcount_(0)
+ {
+ ++total_constructions_;
+ }
+
+IFoo::~IFoo()
+ {
+ ++total_destructions_;
+ }
+
+MozExternalRefCountType
+IFoo::AddRef()
+ {
+ ++refcount_;
+ return refcount_;
+ }
+
+MozExternalRefCountType
+IFoo::Release()
+ {
+ int newcount = --refcount_;
+ if ( newcount == 0 )
+ {
+ delete this;
+ }
+
+ return newcount;
+ }
+
+nsresult
+IFoo::QueryInterface( const nsIID& aIID, void** aResult )
+ {
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(IFoo)) )
+ rawPtr = this;
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+ }
+
+nsresult
+IFoo::SetString(const nsACString& aString)
+{
+ mString = aString;
+ return NS_OK;
+}
+
+nsresult
+IFoo::GetString(nsACString& aString)
+{
+ aString = mString;
+ return NS_OK;
+}
+
+nsresult
+CreateIFoo( IFoo** result )
+ // a typical factory function (that calls AddRef)
+ {
+ IFoo* foop = new IFoo();
+
+ foop->AddRef();
+ *result = foop;
+
+ return NS_OK;
+ }
+
+} // namespace TestHashtables
+
+using namespace TestHashtables;
+
+TEST(Hashtable, THashtable)
+{
+ // check an nsTHashtable
+ nsTHashtable<EntityToUnicodeEntry> EntityToUnicode(ENTITY_COUNT);
+
+ testTHashtable(EntityToUnicode, 5);
+
+ uint32_t count = nsTIterPrintRemove(EntityToUnicode);
+ ASSERT_EQ(count, uint32_t(5));
+
+ count = nsTIterPrint(EntityToUnicode);
+ ASSERT_EQ(count, uint32_t(0));
+
+ testTHashtable(EntityToUnicode, ENTITY_COUNT);
+
+ EntityToUnicode.Clear();
+
+ count = nsTIterPrint(EntityToUnicode);
+ ASSERT_EQ(count, uint32_t(0));
+}
+
+TEST(Hashtables, DataHashtable)
+{
+ // check a data-hashtable
+ nsDataHashtable<nsUint32HashKey,const char*> UniToEntity(ENTITY_COUNT);
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ UniToEntity.Put(gEntities[i].mUnicode, gEntities[i].mStr);
+ }
+
+ const char* str;
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ ASSERT_TRUE(UniToEntity.Get(gEntities[i].mUnicode, &str));
+ }
+
+ ASSERT_FALSE(UniToEntity.Get(99446, &str));
+
+ uint32_t count = 0;
+ for (auto iter = UniToEntity.Iter(); !iter.Done(); iter.Next()) {
+ count++;
+ }
+ ASSERT_EQ(count, ENTITY_COUNT);
+
+ UniToEntity.Clear();
+
+ count = 0;
+ for (auto iter = UniToEntity.Iter(); !iter.Done(); iter.Next()) {
+ printf(" enumerated %u = \"%s\"\n", iter.Key(), iter.Data());
+ count++;
+ }
+ ASSERT_EQ(count, uint32_t(0));
+}
+
+TEST(Hashtables, ClassHashtable)
+{
+ // check a class-hashtable
+ nsClassHashtable<nsCStringHashKey,TestUniChar> EntToUniClass(ENTITY_COUNT);
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ TestUniChar* temp = new TestUniChar(gEntities[i].mUnicode);
+ EntToUniClass.Put(nsDependentCString(gEntities[i].mStr), temp);
+ }
+
+ TestUniChar* myChar;
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ ASSERT_TRUE(EntToUniClass.Get(nsDependentCString(gEntities[i].mStr), &myChar));
+ }
+
+ ASSERT_FALSE(EntToUniClass.Get(NS_LITERAL_CSTRING("xxxx"), &myChar));
+
+ uint32_t count = 0;
+ for (auto iter = EntToUniClass.Iter(); !iter.Done(); iter.Next()) {
+ count++;
+ }
+ ASSERT_EQ(count, ENTITY_COUNT);
+
+ EntToUniClass.Clear();
+
+ count = 0;
+ for (auto iter = EntToUniClass.Iter(); !iter.Done(); iter.Next()) {
+ count++;
+ }
+ ASSERT_EQ(count, uint32_t(0));
+}
+
+TEST(Hashtables, DataHashtableWithInterfaceKey)
+{
+ // check a data-hashtable with an interface key
+ nsDataHashtable<nsISupportsHashKey,uint32_t> EntToUniClass2(ENTITY_COUNT);
+
+ nsCOMArray<IFoo> fooArray;
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ nsCOMPtr<IFoo> foo;
+ CreateIFoo(getter_AddRefs(foo));
+ foo->SetString(nsDependentCString(gEntities[i].mStr));
+
+ fooArray.InsertObjectAt(foo, i);
+
+ EntToUniClass2.Put(foo, gEntities[i].mUnicode);
+ }
+
+ uint32_t myChar2;
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ ASSERT_TRUE(EntToUniClass2.Get(fooArray[i], &myChar2));
+ }
+
+ ASSERT_FALSE(EntToUniClass2.Get((nsISupports*) 0x55443316, &myChar2));
+
+ uint32_t count = 0;
+ for (auto iter = EntToUniClass2.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoCString s;
+ nsCOMPtr<IFoo> foo = do_QueryInterface(iter.Key());
+ foo->GetString(s);
+ count++;
+ }
+ ASSERT_EQ(count, ENTITY_COUNT);
+
+ EntToUniClass2.Clear();
+
+ count = 0;
+ for (auto iter = EntToUniClass2.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoCString s;
+ nsCOMPtr<IFoo> foo = do_QueryInterface(iter.Key());
+ foo->GetString(s);
+ count++;
+ }
+ ASSERT_EQ(count, uint32_t(0));
+}
+
+TEST(Hashtables, InterfaceHashtable)
+{
+ // check an interface-hashtable with an uint32_t key
+ nsInterfaceHashtable<nsUint32HashKey,IFoo> UniToEntClass2(ENTITY_COUNT);
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ nsCOMPtr<IFoo> foo;
+ CreateIFoo(getter_AddRefs(foo));
+ foo->SetString(nsDependentCString(gEntities[i].mStr));
+
+ UniToEntClass2.Put(gEntities[i].mUnicode, foo);
+ }
+
+ for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
+ nsCOMPtr<IFoo> myEnt;
+ ASSERT_TRUE(UniToEntClass2.Get(gEntities[i].mUnicode, getter_AddRefs(myEnt)));
+
+ nsAutoCString myEntStr;
+ myEnt->GetString(myEntStr);
+ }
+
+ nsCOMPtr<IFoo> myEnt;
+ ASSERT_FALSE(UniToEntClass2.Get(9462, getter_AddRefs(myEnt)));
+
+ uint32_t count = 0;
+ for (auto iter = UniToEntClass2.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoCString s;
+ iter.UserData()->GetString(s);
+ count++;
+ }
+ ASSERT_EQ(count, ENTITY_COUNT);
+
+ UniToEntClass2.Clear();
+
+ count = 0;
+ for (auto iter = UniToEntClass2.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoCString s;
+ iter.Data()->GetString(s);
+ count++;
+ }
+ ASSERT_EQ(count, uint32_t(0));
+}
diff --git a/xpcom/tests/gtest/TestID.cpp b/xpcom/tests/gtest/TestID.cpp
new file mode 100644
index 000000000..cb56554dc
--- /dev/null
+++ b/xpcom/tests/gtest/TestID.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsID.h"
+
+#include "gtest/gtest.h"
+
+static const char* const ids[] = {
+ "5C347B10-D55C-11D1-89B7-006008911B81",
+ "{5C347B10-D55C-11D1-89B7-006008911B81}",
+ "5c347b10-d55c-11d1-89b7-006008911b81",
+ "{5c347b10-d55c-11d1-89b7-006008911b81}",
+
+ "FC347B10-D55C-F1D1-F9B7-006008911B81",
+ "{FC347B10-D55C-F1D1-F9B7-006008911B81}",
+ "fc347b10-d55c-f1d1-f9b7-006008911b81",
+ "{fc347b10-d55c-f1d1-f9b7-006008911b81}",
+};
+#define NUM_IDS ((int) (sizeof(ids) / sizeof(ids[0])))
+
+TEST(nsID, StringConversion)
+{
+ nsID id;
+ for (int i = 0; i < NUM_IDS; i++) {
+ const char* idstr = ids[i];
+ ASSERT_TRUE(id.Parse(idstr));
+
+ char* cp = id.ToString();
+ ASSERT_NE(cp, nullptr);
+ ASSERT_STREQ(cp, ids[4*(i/4) + 3]);
+
+ free(cp);
+ }
+}
diff --git a/xpcom/tests/gtest/TestNSPRLogModulesParser.cpp b/xpcom/tests/gtest/TestNSPRLogModulesParser.cpp
new file mode 100644
index 000000000..4fd924175
--- /dev/null
+++ b/xpcom/tests/gtest/TestNSPRLogModulesParser.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NSPRLogModulesParser.h"
+#include "mozilla/ArrayUtils.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+TEST(NSPRLogModulesParser, Empty)
+{
+ bool callbackInvoked = false;
+ auto callback = [&](const char*, mozilla::LogLevel, int32_t) mutable { callbackInvoked = true; };
+
+ mozilla::NSPRLogModulesParser(nullptr, callback);
+ EXPECT_FALSE(callbackInvoked);
+
+ mozilla::NSPRLogModulesParser("", callback);
+ EXPECT_FALSE(callbackInvoked);
+}
+
+TEST(NSPRLogModulesParser, DefaultLevel)
+{
+ bool callbackInvoked = false;
+ auto callback =
+ [&](const char* aName, mozilla::LogLevel aLevel, int32_t) {
+ EXPECT_STREQ("Foo", aName);
+ EXPECT_EQ(mozilla::LogLevel::Error, aLevel);
+ callbackInvoked = true;
+ };
+
+ mozilla::NSPRLogModulesParser("Foo", callback);
+ EXPECT_TRUE(callbackInvoked);
+
+ callbackInvoked = false;
+ mozilla::NSPRLogModulesParser("Foo:", callback);
+ EXPECT_TRUE(callbackInvoked);
+}
+
+TEST(NSPRLogModulesParser, LevelSpecified)
+{
+ std::pair<const char*, mozilla::LogLevel> expected[] = {
+ { "Foo:0", mozilla::LogLevel::Disabled },
+ { "Foo:1", mozilla::LogLevel::Error },
+ { "Foo:2", mozilla::LogLevel::Warning },
+ { "Foo:3", mozilla::LogLevel::Info },
+ { "Foo:4", mozilla::LogLevel::Debug },
+ { "Foo:5", mozilla::LogLevel::Verbose },
+ { "Foo:25", mozilla::LogLevel::Verbose }, // too high
+ { "Foo:-12", mozilla::LogLevel::Disabled } // too low
+ };
+
+ auto* currTest = expected;
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(expected); i++) {
+ bool callbackInvoked = false;
+ mozilla::NSPRLogModulesParser(currTest->first,
+ [&](const char* aName, mozilla::LogLevel aLevel, int32_t) {
+ EXPECT_STREQ("Foo", aName);
+ EXPECT_EQ(currTest->second, aLevel);
+ callbackInvoked = true;
+ });
+ EXPECT_TRUE(callbackInvoked);
+ currTest++;
+ }
+}
+
+TEST(NSPRLogModulesParser, Multiple)
+{
+ std::pair<const char*, mozilla::LogLevel> expected[] = {
+ { "timestamp", mozilla::LogLevel::Error },
+ { "Foo", mozilla::LogLevel::Info },
+ { "Bar", mozilla::LogLevel::Error },
+ { "Baz", mozilla::LogLevel::Warning },
+ { "Qux", mozilla::LogLevel::Verbose },
+ };
+
+ const size_t kExpectedCount = MOZ_ARRAY_LENGTH(expected);
+
+ auto* currTest = expected;
+
+ size_t count = 0;
+ mozilla::NSPRLogModulesParser("timestamp,Foo:3, Bar,Baz:2, Qux:5",
+ [&](const char* aName, mozilla::LogLevel aLevel, int32_t) mutable {
+ ASSERT_LT(count, kExpectedCount);
+ EXPECT_STREQ(currTest->first, aName);
+ EXPECT_EQ(currTest->second, aLevel);
+ currTest++;
+ count++;
+ });
+
+ EXPECT_EQ(kExpectedCount, count);
+}
+
+TEST(NSPRLogModulesParser, RawArg)
+{
+ bool callbackInvoked = false;
+ auto callback =
+ [&](const char* aName, mozilla::LogLevel aLevel, int32_t aRawValue) {
+ EXPECT_STREQ("Foo", aName);
+ EXPECT_EQ(mozilla::LogLevel::Verbose, aLevel);
+ EXPECT_EQ(1000, aRawValue);
+ callbackInvoked = true;
+ };
+
+ mozilla::NSPRLogModulesParser("Foo:1000", callback);
+ EXPECT_TRUE(callbackInvoked);
+}
diff --git a/xpcom/tests/gtest/TestNsRefPtr.cpp b/xpcom/tests/gtest/TestNsRefPtr.cpp
new file mode 100644
index 000000000..a085c2966
--- /dev/null
+++ b/xpcom/tests/gtest/TestNsRefPtr.cpp
@@ -0,0 +1,479 @@
+/* -*- 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 "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsQueryObject.h"
+#include "mozilla/Unused.h"
+
+#include "gtest/gtest.h"
+
+namespace TestNsRefPtr
+{
+
+#define NS_FOO_IID \
+{ 0x6f7652e0, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+class Foo : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_FOO_IID)
+
+public:
+ Foo();
+ // virtual dtor because Bar uses our Release()
+ virtual ~Foo();
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+ NS_IMETHOD QueryInterface( const nsIID&, void** );
+ void MemberFunction( int, int*, int& );
+ virtual void VirtualMemberFunction( int, int*, int& );
+ virtual void VirtualConstMemberFunction( int, int*, int& ) const;
+
+ void NonconstMethod() {}
+ void ConstMethod() const {}
+
+ int refcount_;
+
+ static int total_constructions_;
+ static int total_destructions_;
+ static int total_addrefs_;
+ static int total_queries_;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(Foo, NS_FOO_IID)
+
+int Foo::total_constructions_;
+int Foo::total_destructions_;
+int Foo::total_addrefs_;
+int Foo::total_queries_;
+
+Foo::Foo()
+ : refcount_(0)
+{
+ ++total_constructions_;
+}
+
+Foo::~Foo()
+{
+ ++total_destructions_;
+}
+
+MozExternalRefCountType
+Foo::AddRef()
+{
+ ++refcount_;
+ ++total_addrefs_;
+ return refcount_;
+}
+
+MozExternalRefCountType
+Foo::Release()
+{
+ int newcount = --refcount_;
+ if ( newcount == 0 )
+ {
+ delete this;
+ }
+
+ return newcount;
+}
+
+nsresult
+Foo::QueryInterface( const nsIID& aIID, void** aResult )
+{
+ ++total_queries_;
+
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(Foo)) )
+ rawPtr = this;
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+}
+
+void
+Foo::MemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
+{
+}
+
+void
+Foo::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
+{
+}
+
+void
+Foo::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
+{
+}
+
+nsresult
+CreateFoo( void** result )
+ // a typical factory function (that calls AddRef)
+{
+ Foo* foop = new Foo;
+
+ foop->AddRef();
+ *result = foop;
+
+ return NS_OK;
+}
+
+void
+set_a_Foo( RefPtr<Foo>* result )
+{
+ assert(result);
+
+ RefPtr<Foo> foop( do_QueryObject(new Foo) );
+ *result = foop;
+}
+
+RefPtr<Foo>
+return_a_Foo()
+{
+ RefPtr<Foo> foop( do_QueryObject(new Foo) );
+ return foop;
+}
+
+#define NS_BAR_IID \
+{ 0x6f7652e1, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+class Bar : public Foo
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_BAR_IID)
+
+public:
+ Bar();
+ virtual ~Bar();
+
+ NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
+
+ virtual void VirtualMemberFunction( int, int*, int& ) override;
+ virtual void VirtualConstMemberFunction( int, int*, int& ) const override;
+
+ static int total_constructions_;
+ static int total_destructions_;
+ static int total_queries_;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(Bar, NS_BAR_IID)
+
+int Bar::total_constructions_;
+int Bar::total_destructions_;
+int Bar::total_queries_;
+
+Bar::Bar()
+{
+ ++total_constructions_;
+}
+
+Bar::~Bar()
+{
+ ++total_destructions_;
+}
+
+nsresult
+Bar::QueryInterface( const nsID& aIID, void** aResult )
+{
+ ++total_queries_;
+
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(Bar)) )
+ rawPtr = this;
+ else if ( aIID.Equals(NS_GET_IID(Foo)) )
+ rawPtr = static_cast<Foo*>(this);
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+}
+
+void
+Bar::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
+{
+}
+void
+Bar::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
+{
+}
+
+} // namespace TestNsRefPtr
+
+using namespace TestNsRefPtr;
+
+TEST(nsRefPtr, AddRefAndRelease)
+{
+ Foo::total_constructions_ = 0;
+ Foo::total_destructions_ = 0;
+
+ {
+ RefPtr<Foo> foop( do_QueryObject(new Foo) );
+ ASSERT_EQ(Foo::total_constructions_, 1);
+ ASSERT_EQ(Foo::total_destructions_, 0);
+ ASSERT_EQ(foop->refcount_, 1);
+
+ foop = do_QueryObject(new Foo);
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 1);
+
+ // [Shouldn't compile] Is it a compile time error to try to |AddRef| by hand?
+ //foop->AddRef();
+
+ // [Shouldn't compile] Is it a compile time error to try to |Release| be hand?
+ //foop->Release();
+
+ // [Shouldn't compile] Is it a compile time error to try to |delete| an |nsCOMPtr|?
+ //delete foop;
+
+ static_cast<Foo*>(foop)->AddRef();
+ ASSERT_EQ(foop->refcount_, 2);
+
+ static_cast<Foo*>(foop)->Release();
+ ASSERT_EQ(foop->refcount_, 1);
+ }
+
+ ASSERT_EQ(Foo::total_destructions_, 2);
+
+ {
+ RefPtr<Foo> fooP( do_QueryObject(new Foo) );
+ ASSERT_EQ(Foo::total_constructions_, 3);
+ ASSERT_EQ(Foo::total_destructions_, 2);
+ ASSERT_EQ(fooP->refcount_, 1);
+
+ Foo::total_addrefs_ = 0;
+ RefPtr<Foo> fooP2( fooP.forget() );
+ ASSERT_EQ(Foo::total_addrefs_, 0);
+ }
+}
+
+TEST(nsRefPtr, VirtualDestructor)
+{
+ Bar::total_destructions_ = 0;
+
+ {
+ RefPtr<Foo> foop( do_QueryObject(new Bar) );
+ mozilla::Unused << foop;
+ }
+
+ ASSERT_EQ(Bar::total_destructions_, 1);
+}
+
+TEST(nsRefPtr, Equality)
+{
+ Foo::total_constructions_ = 0;
+ Foo::total_destructions_ = 0;
+
+ {
+ RefPtr<Foo> foo1p( do_QueryObject(new Foo) );
+ RefPtr<Foo> foo2p( do_QueryObject(new Foo) );
+
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 0);
+
+ ASSERT_NE(foo1p, foo2p);
+
+ ASSERT_NE(foo1p, nullptr);
+ ASSERT_NE(nullptr, foo1p);
+ ASSERT_FALSE(foo1p == nullptr);
+ ASSERT_FALSE(nullptr == foo1p);
+
+ ASSERT_NE(foo1p, foo2p.get());
+
+ foo1p = foo2p;
+
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 1);
+ ASSERT_EQ(foo1p, foo2p);
+
+ ASSERT_EQ(foo2p, foo2p.get());
+
+ ASSERT_EQ(RefPtr<Foo>(foo2p.get()), foo2p);
+
+ ASSERT_TRUE(foo1p);
+ }
+
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 2);
+}
+
+TEST(nsRefPtr, AddRefHelpers)
+{
+ Foo::total_addrefs_ = 0;
+
+ {
+ Foo* raw_foo1p = new Foo;
+ raw_foo1p->AddRef();
+
+ Foo* raw_foo2p = new Foo;
+ raw_foo2p->AddRef();
+
+ ASSERT_EQ(Foo::total_addrefs_, 2);
+
+ RefPtr<Foo> foo1p( dont_AddRef(raw_foo1p) );
+
+ ASSERT_EQ(Foo::total_addrefs_, 2);
+
+ RefPtr<Foo> foo2p;
+ foo2p = dont_AddRef(raw_foo2p);
+
+ ASSERT_EQ(Foo::total_addrefs_, 2);
+ }
+
+ {
+ // Test that various assignment helpers compile.
+ RefPtr<Foo> foop;
+ CreateFoo( RefPtrGetterAddRefs<Foo>(foop) );
+ CreateFoo( getter_AddRefs(foop) );
+ set_a_Foo(address_of(foop));
+ foop = return_a_Foo();
+ }
+}
+
+TEST(nsRefPtr, QueryInterface)
+{
+ Foo::total_queries_ = 0;
+ Bar::total_queries_ = 0;
+
+ {
+ RefPtr<Foo> fooP;
+ fooP = do_QueryObject(new Foo);
+ ASSERT_EQ(Foo::total_queries_, 1);
+ }
+
+ {
+ RefPtr<Foo> fooP;
+ fooP = do_QueryObject(new Foo);
+ ASSERT_EQ(Foo::total_queries_, 2);
+
+ RefPtr<Foo> foo2P;
+ foo2P = fooP;
+ ASSERT_EQ(Foo::total_queries_, 2);
+ }
+
+ {
+ RefPtr<Bar> barP( do_QueryObject(new Bar) );
+ ASSERT_EQ(Bar::total_queries_, 1);
+
+ RefPtr<Foo> fooP( do_QueryObject(barP) );
+ ASSERT_TRUE(fooP);
+ ASSERT_EQ(Foo::total_queries_, 2);
+ ASSERT_EQ(Bar::total_queries_, 2);
+ }
+}
+
+// -------------------------------------------------------------------------
+// TODO(ER): The following tests should be moved to MFBT.
+
+#define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class) \
+public: \
+NS_METHOD_(MozExternalRefCountType) AddRef(void) const { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_RELEASE_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ return (nsrefcnt) count; \
+} \
+NS_METHOD_(MozExternalRefCountType) Release(void) const { \
+ MOZ_RELEASE_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ if (count == 0) { \
+ delete (this); \
+ return 0; \
+ } \
+ return count; \
+} \
+protected: \
+mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+public:
+
+class ObjectForConstPtr
+{
+ private:
+ // Reference-counted classes cannot have public destructors.
+ ~ObjectForConstPtr()
+ {
+ }
+ public:
+ NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(ObjectForConstPtr)
+ void ConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
+ {
+ }
+};
+#undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
+
+namespace TestNsRefPtr
+{
+void AnFooPtrPtrContext(Foo**) { }
+void AVoidPtrPtrContext(void**) { }
+} // namespace TestNsRefPtr
+
+TEST(nsRefPtr, RefPtrCompilationTests)
+{
+
+ {
+ RefPtr<Foo> fooP;
+
+ AnFooPtrPtrContext( getter_AddRefs(fooP) );
+ AVoidPtrPtrContext( getter_AddRefs(fooP) );
+ }
+
+ {
+ RefPtr<Foo> fooP(new Foo);
+ RefPtr<const Foo> constFooP = fooP;
+ constFooP->ConstMethod();
+
+ // [Shouldn't compile] Is it a compile time error to call a non-const method on an |RefPtr<const T>|?
+ //constFooP->NonconstMethod();
+
+ // [Shouldn't compile] Is it a compile time error to construct an |RefPtr<T> from an |RefPtr<const T>|?
+ //RefPtr<Foo> otherFooP(constFooP);
+ }
+
+ {
+ RefPtr<Foo> foop = new Foo;
+ RefPtr<Foo> foop2 = new Bar;
+ RefPtr<const ObjectForConstPtr> foop3 = new ObjectForConstPtr;
+ int test = 1;
+ void (Foo::*fPtr)( int, int*, int& ) = &Foo::MemberFunction;
+ void (Foo::*fVPtr)( int, int*, int& ) = &Foo::VirtualMemberFunction;
+ void (Foo::*fVCPtr)( int, int*, int& ) const = &Foo::VirtualConstMemberFunction;
+ void (ObjectForConstPtr::*fCPtr2)( int, int*, int& ) const = &ObjectForConstPtr::ConstMemberFunction;
+
+ (foop->*fPtr)(test, &test, test);
+ (foop2->*fVPtr)(test, &test, test);
+ (foop2->*fVCPtr)(test, &test, test);
+ (foop3->*fCPtr2)(test, &test, test);
+ }
+
+ // Looks like everything ran.
+ ASSERT_TRUE(true);
+}
diff --git a/xpcom/tests/gtest/TestObserverArray.cpp b/xpcom/tests/gtest/TestObserverArray.cpp
new file mode 100644
index 000000000..d907619be
--- /dev/null
+++ b/xpcom/tests/gtest/TestObserverArray.cpp
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=4:et:sw=4:
+/* 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 "nsTObserverArray.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+typedef nsTObserverArray<int> IntArray;
+
+#define DO_TEST(_type, _exp, _code) \
+ do { \
+ ++testNum; \
+ count = 0; \
+ IntArray::_type iter(arr); \
+ while (iter.HasMore() && count != ArrayLength(_exp)) { \
+ _code \
+ int next = iter.GetNext(); \
+ int expected = _exp[count++]; \
+ ASSERT_EQ(next, expected) << "During test " << testNum << " at position " << count - 1; \
+ } \
+ ASSERT_FALSE(iter.HasMore()) << "During test " << testNum << ", iterator ran over"; \
+ ASSERT_EQ(count, ArrayLength(_exp)) << "During test " << testNum << ", iterator finished too early"; \
+ } while (0)
+
+TEST(ObserverArray, Tests)
+{
+ IntArray arr;
+ arr.AppendElement(3);
+ arr.AppendElement(4);
+
+ size_t count;
+ int testNum = 0;
+
+ // Basic sanity
+ static int test1Expected[] = { 3, 4 };
+ DO_TEST(ForwardIterator, test1Expected, { /* nothing */ });
+
+ // Appends
+ static int test2Expected[] = { 3, 4, 2 };
+ DO_TEST(ForwardIterator, test2Expected,
+ if (count == 1) arr.AppendElement(2);
+ );
+ DO_TEST(ForwardIterator, test2Expected, { /* nothing */ });
+
+ DO_TEST(EndLimitedIterator, test2Expected,
+ if (count == 1) arr.AppendElement(5);
+ );
+
+ static int test5Expected[] = { 3, 4, 2, 5 };
+ DO_TEST(ForwardIterator, test5Expected, { /* nothing */ });
+
+ // Removals
+ DO_TEST(ForwardIterator, test5Expected,
+ if (count == 1) arr.RemoveElementAt(0);
+ );
+
+ static int test7Expected[] = { 4, 2, 5 };
+ DO_TEST(ForwardIterator, test7Expected, { /* nothing */ });
+
+ static int test8Expected[] = { 4, 5 };
+ DO_TEST(ForwardIterator, test8Expected,
+ if (count == 1) arr.RemoveElementAt(1);
+ );
+ DO_TEST(ForwardIterator, test8Expected, { /* nothing */ });
+
+ arr.AppendElement(2);
+ arr.AppendElementUnlessExists(6);
+ static int test10Expected[] = { 4, 5, 2, 6 };
+ DO_TEST(ForwardIterator, test10Expected, { /* nothing */ });
+
+ arr.AppendElementUnlessExists(5);
+ DO_TEST(ForwardIterator, test10Expected, { /* nothing */ });
+
+ static int test12Expected[] = { 4, 5, 6 };
+ DO_TEST(ForwardIterator, test12Expected,
+ if (count == 1) arr.RemoveElementAt(2);
+ );
+ DO_TEST(ForwardIterator, test12Expected, { /* nothing */ });
+
+ // Removals + Appends
+ static int test14Expected[] = { 4, 6, 7 };
+ DO_TEST(ForwardIterator, test14Expected,
+ if (count == 1) {
+ arr.RemoveElementAt(1);
+ arr.AppendElement(7);
+ }
+ );
+ DO_TEST(ForwardIterator, test14Expected, { /* nothing */ });
+
+ arr.AppendElement(2);
+ static int test16Expected[] = { 4, 6, 7, 2 };
+ DO_TEST(ForwardIterator, test16Expected, { /* nothing */ });
+
+ static int test17Expected[] = { 4, 7, 2 };
+ DO_TEST(EndLimitedIterator, test17Expected,
+ if (count == 1) {
+ arr.RemoveElementAt(1);
+ arr.AppendElement(8);
+ }
+ );
+
+ static int test18Expected[] = { 4, 7, 2, 8 };
+ DO_TEST(ForwardIterator, test18Expected, { /* nothing */ });
+
+ // Prepends
+ arr.PrependElementUnlessExists(3);
+ static int test19Expected[] = { 3, 4, 7, 2, 8 };
+ DO_TEST(ForwardIterator, test19Expected, { /* nothing */ });
+
+ arr.PrependElementUnlessExists(7);
+ DO_TEST(ForwardIterator, test19Expected, { /* nothing */ });
+
+ DO_TEST(ForwardIterator, test19Expected,
+ if (count == 1) {
+ arr.PrependElementUnlessExists(9);
+ }
+ );
+
+ static int test22Expected[] = { 9, 3, 4, 7, 2, 8 };
+ DO_TEST(ForwardIterator, test22Expected, { });
+
+ // BackwardIterator
+ static int test23Expected[] = { 8, 2, 7, 4, 3, 9 };
+ DO_TEST(BackwardIterator, test23Expected, );
+
+ // Removals
+ static int test24Expected[] = { 8, 2, 7, 4, 9 };
+ DO_TEST(BackwardIterator, test24Expected,
+ if (count == 1) arr.RemoveElementAt(1);
+ );
+
+ // Appends
+ DO_TEST(BackwardIterator, test24Expected,
+ if (count == 1) arr.AppendElement(1);
+ );
+
+ static int test26Expected[] = { 1, 8, 2, 7, 4, 9 };
+ DO_TEST(BackwardIterator, test26Expected, );
+
+ // Prepends
+ static int test27Expected[] = { 1, 8, 2, 7, 4, 9, 3 };
+ DO_TEST(BackwardIterator, test27Expected,
+ if (count == 1) arr.PrependElementUnlessExists(3);
+ );
+
+ // Removal using Iterator
+ DO_TEST(BackwardIterator, test27Expected,
+ // when this code runs, |GetNext()| has only been called once, so
+ // this actually removes the very first element
+ if (count == 1) iter.Remove();
+ );
+
+ static int test28Expected[] = { 8, 2, 7, 4, 9, 3 };
+ DO_TEST(BackwardIterator, test28Expected, );
+
+ /**
+ * Note: _code is executed before the call to GetNext(), it can therefore not
+ * test the case of prepending when the BackwardIterator already returned the
+ * first element.
+ * In that case BackwardIterator does not traverse the newly prepended Element
+ */
+
+}
diff --git a/xpcom/tests/gtest/TestObserverService.cpp b/xpcom/tests/gtest/TestObserverService.cpp
new file mode 100644
index 000000000..d4eb51a7e
--- /dev/null
+++ b/xpcom/tests/gtest/TestObserverService.cpp
@@ -0,0 +1,288 @@
+/* -*- 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 "nsISupports.h"
+#include "nsIComponentManager.h"
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "nsISimpleEnumerator.h"
+#include "nsComponentManagerUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+#include "mozilla/RefPtr.h"
+
+#include "gtest/gtest.h"
+
+static void testResult( nsresult rv ) {
+ EXPECT_TRUE(NS_SUCCEEDED(rv)) << "0x" << std::hex << (int)rv;
+}
+
+class TestObserver final : public nsIObserver,
+ public nsSupportsWeakReference
+{
+public:
+ explicit TestObserver( const nsAString &name )
+ : mName( name )
+ , mObservations( 0 ) {
+ }
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ nsString mName;
+ int mObservations;
+ static int sTotalObservations;
+
+ nsString mExpectedData;
+
+private:
+ ~TestObserver() {}
+};
+
+NS_IMPL_ISUPPORTS( TestObserver, nsIObserver, nsISupportsWeakReference )
+
+int TestObserver::sTotalObservations;
+
+NS_IMETHODIMP
+TestObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *someData ) {
+ mObservations++;
+ sTotalObservations++;
+
+ if (!mExpectedData.IsEmpty()) {
+ EXPECT_TRUE(mExpectedData.Equals(someData));
+ }
+
+ return NS_OK;
+}
+
+static nsISupports* ToSupports(TestObserver* aObs)
+{
+ return static_cast<nsIObserver*>(aObs);
+}
+
+static void TestExpectedCount(
+ nsIObserverService* svc,
+ const char* topic,
+ size_t expected)
+{
+ nsCOMPtr<nsISimpleEnumerator> e;
+ nsresult rv = svc->EnumerateObservers(topic, getter_AddRefs(e));
+ testResult(rv);
+ EXPECT_TRUE(e);
+
+ bool hasMore = false;
+ rv = e->HasMoreElements(&hasMore);
+ testResult(rv);
+
+ if (expected == 0) {
+ EXPECT_FALSE(hasMore);
+ return;
+ }
+
+ size_t count = 0;
+ while (hasMore) {
+ count++;
+
+ // Grab the element.
+ nsCOMPtr<nsISupports> supports;
+ e->GetNext(getter_AddRefs(supports));
+ ASSERT_TRUE(supports);
+
+ // Move on.
+ rv = e->HasMoreElements(&hasMore);
+ testResult(rv);
+ }
+
+ EXPECT_EQ(count, expected);
+}
+
+TEST(ObserverService, Creation)
+{
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1", &rv);
+
+ ASSERT_EQ(rv, NS_OK);
+ ASSERT_TRUE(svc);
+}
+
+TEST(ObserverService, AddObserver)
+{
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1");
+
+ // Add a strong ref.
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ nsresult rv = svc->AddObserver(a, "Foo", false);
+ testResult(rv);
+
+ // Add a few weak ref.
+ RefPtr<TestObserver> b = new TestObserver(NS_LITERAL_STRING("B"));
+ rv = svc->AddObserver(b, "Bar", true);
+ testResult(rv);
+}
+
+TEST(ObserverService, RemoveObserver)
+{
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1");
+
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ RefPtr<TestObserver> b = new TestObserver(NS_LITERAL_STRING("B"));
+ RefPtr<TestObserver> c = new TestObserver(NS_LITERAL_STRING("C"));
+
+ svc->AddObserver(a, "Foo", false);
+ svc->AddObserver(b, "Foo", true);
+
+ // Remove from non-existent topic.
+ nsresult rv = svc->RemoveObserver(a, "Bar");
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ // Remove a.
+ testResult(svc->RemoveObserver(a, "Foo"));
+
+ // Remove b.
+ testResult(svc->RemoveObserver(b, "Foo"));
+
+ // Attempt to remove c.
+ rv = svc->RemoveObserver(c, "Foo");
+ ASSERT_TRUE(NS_FAILED(rv));
+}
+
+TEST(ObserverService, EnumerateEmpty)
+{
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1");
+
+ // Try with no observers.
+ TestExpectedCount(svc, "A", 0);
+
+ // Now add an observer and enumerate an unobserved topic.
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ testResult(svc->AddObserver(a, "Foo", false));
+
+ TestExpectedCount(svc, "A", 0);
+}
+
+TEST(ObserverService, Enumerate)
+{
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1");
+
+ const size_t kFooCount = 10;
+ for (size_t i = 0; i < kFooCount; i++) {
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ testResult(svc->AddObserver(a, "Foo", false));
+ }
+
+ const size_t kBarCount = kFooCount / 2;
+ for (size_t i = 0; i < kBarCount; i++) {
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ testResult(svc->AddObserver(a, "Bar", false));
+ }
+
+ // Enumerate "Foo".
+ TestExpectedCount(svc, "Foo", kFooCount);
+
+ // Enumerate "Bar".
+ TestExpectedCount(svc, "Bar", kBarCount);
+}
+
+TEST(ObserverService, EnumerateWeakRefs)
+{
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1");
+
+ const size_t kFooCount = 10;
+ for (size_t i = 0; i < kFooCount; i++) {
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ testResult(svc->AddObserver(a, "Foo", true));
+ }
+
+ // All refs are out of scope, expect enumeration to be empty.
+ TestExpectedCount(svc, "Foo", 0);
+
+ // Now test a mixture.
+ for (size_t i = 0; i < kFooCount; i++) {
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ RefPtr<TestObserver> b = new TestObserver(NS_LITERAL_STRING("B"));
+
+ // Register a as weak for "Foo".
+ testResult(svc->AddObserver(a, "Foo", true));
+
+ // Register b as strong for "Foo".
+ testResult(svc->AddObserver(b, "Foo", false));
+ }
+
+ // Expect the b instances to stick around.
+ TestExpectedCount(svc, "Foo", kFooCount);
+
+ // Now add a couple weak refs, but don't go out of scope.
+ RefPtr<TestObserver> a = new TestObserver(NS_LITERAL_STRING("A"));
+ testResult(svc->AddObserver(a, "Foo", true));
+ RefPtr<TestObserver> b = new TestObserver(NS_LITERAL_STRING("B"));
+ testResult(svc->AddObserver(b, "Foo", true));
+
+ // Expect all the observers from before and the two new ones.
+ TestExpectedCount(svc, "Foo", kFooCount + 2);
+}
+
+TEST(ObserverService, TestNotify)
+{
+ nsCString topicA; topicA.Assign( "topic-A" );
+ nsCString topicB; topicB.Assign( "topic-B" );
+
+ nsCOMPtr<nsIObserverService> svc =
+ do_CreateInstance("@mozilla.org/observer-service;1");
+
+ RefPtr<TestObserver> aObserver = new TestObserver(NS_LITERAL_STRING("Observer-A"));
+ RefPtr<TestObserver> bObserver = new TestObserver(NS_LITERAL_STRING("Observer-B"));
+
+ // Add two observers for topicA.
+ testResult(svc->AddObserver(aObserver, topicA.get(), false));
+ testResult(svc->AddObserver(bObserver, topicA.get(), false));
+
+ // Add one observer for topicB.
+ testResult(svc->AddObserver(bObserver, topicB.get(), false));
+
+ // Notify topicA.
+ NS_NAMED_LITERAL_STRING(dataA, "Testing Notify(observer-A, topic-A)");
+ aObserver->mExpectedData = dataA;
+ bObserver->mExpectedData = dataA;
+ nsresult rv =
+ svc->NotifyObservers(ToSupports(aObserver), topicA.get(), dataA.get());
+ testResult(rv);
+ ASSERT_EQ(aObserver->mObservations, 1);
+ ASSERT_EQ(bObserver->mObservations, 1);
+
+ // Notify topicB.
+ NS_NAMED_LITERAL_STRING(dataB, "Testing Notify(observer-B, topic-B)");
+ bObserver->mExpectedData = dataB;
+ rv = svc->NotifyObservers(ToSupports(bObserver), topicB.get(), dataB.get());
+ testResult(rv);
+ ASSERT_EQ(aObserver->mObservations, 1);
+ ASSERT_EQ(bObserver->mObservations, 2);
+
+ // Remove one of the topicA observers, make sure it's not notified.
+ testResult(svc->RemoveObserver(aObserver, topicA.get()));
+
+ // Notify topicA, only bObserver is expected to be notified.
+ bObserver->mExpectedData = dataA;
+ rv = svc->NotifyObservers(ToSupports(aObserver), topicA.get(), dataA.get());
+ testResult(rv);
+ ASSERT_EQ(aObserver->mObservations, 1);
+ ASSERT_EQ(bObserver->mObservations, 3);
+
+ // Remove the other topicA observer, make sure none are notified.
+ testResult(svc->RemoveObserver(bObserver, topicA.get()));
+ rv = svc->NotifyObservers(ToSupports(aObserver), topicA.get(), dataA.get());
+ testResult(rv);
+ ASSERT_EQ(aObserver->mObservations, 1);
+ ASSERT_EQ(bObserver->mObservations, 3);
+}
diff --git a/xpcom/tests/gtest/TestPLDHash.cpp b/xpcom/tests/gtest/TestPLDHash.cpp
new file mode 100644
index 000000000..e7a73ae1b
--- /dev/null
+++ b/xpcom/tests/gtest/TestPLDHash.cpp
@@ -0,0 +1,368 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PLDHashTable.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "gtest/gtest.h"
+
+// This test mostly focuses on edge cases. But more coverage of normal
+// operations wouldn't be a bad thing.
+
+#ifdef XP_UNIX
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+// This global variable is defined in toolkit/xre/nsSigHandlers.cpp.
+extern unsigned int _gdb_sleep_duration;
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsICrashReporter.h"
+#endif
+
+// We can test that certain operations cause expected aborts by forking
+// and then checking that the child aborted in the expected way (i.e. via
+// MOZ_CRASH). We skip this for the following configurations.
+// - On Windows, because it doesn't have fork().
+// - On non-DEBUG builds, because the crashes cause the crash reporter to pop
+// up when running this test locally, which is surprising and annoying.
+// - On ASAN builds, because ASAN alters the way a MOZ_CRASHing process
+// terminates, which makes it harder to test if the right thing has occurred.
+void
+TestCrashyOperation(void (*aCrashyOperation)())
+{
+#if defined(XP_UNIX) && defined(DEBUG) && !defined(MOZ_ASAN)
+ // We're about to trigger a crash. When it happens don't pause to allow GDB
+ // to be attached.
+ unsigned int old_gdb_sleep_duration = _gdb_sleep_duration;
+ _gdb_sleep_duration = 0;
+
+ int pid = fork();
+ ASSERT_NE(pid, -1);
+
+ if (pid == 0) {
+ // Disable the crashreporter -- writing a crash dump in the child will
+ // prevent the parent from writing a subsequent dump. Crashes here are
+ // expected, so we don't want their stacks to show up in the log anyway.
+#ifdef MOZ_CRASHREPORTER
+ nsCOMPtr<nsICrashReporter> crashreporter =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (crashreporter) {
+ crashreporter->SetEnabled(false);
+ }
+#endif
+
+ // Child: perform the crashy operation.
+ fprintf(stderr, "TestCrashyOperation: The following crash is expected. Do not panic.\n");
+ aCrashyOperation();
+ fprintf(stderr, "TestCrashyOperation: didn't crash?!\n");
+ ASSERT_TRUE(false); // shouldn't reach here
+ }
+
+ // Parent: check that child crashed as expected.
+ int status;
+ ASSERT_NE(waitpid(pid, &status, 0), -1);
+
+ // The path taken here depends on the platform and configuration.
+ ASSERT_TRUE(WIFEXITED(status) || WTERMSIG(status));
+ if (WIFEXITED(status)) {
+ // This occurs if the ah_crap_handler() is run, i.e. we caught the crash.
+ // It returns the number of the caught signal.
+ int signum = WEXITSTATUS(status);
+ if (signum != SIGSEGV && signum != SIGBUS) {
+ fprintf(stderr, "TestCrashyOperation 'exited' failure: %d\n", signum);
+ ASSERT_TRUE(false);
+ }
+ } else if (WIFSIGNALED(status)) {
+ // This one occurs if we didn't catch the crash. The exit code is the
+ // number of the terminating signal.
+ int signum = WTERMSIG(status);
+ if (signum != SIGSEGV && signum != SIGBUS) {
+ fprintf(stderr, "TestCrashyOperation 'signaled' failure: %d\n", signum);
+ ASSERT_TRUE(false);
+ }
+ }
+
+ _gdb_sleep_duration = old_gdb_sleep_duration;
+#endif
+}
+
+void
+InitCapacityOk_InitialLengthTooBig()
+{
+ PLDHashTable t(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub),
+ PLDHashTable::kMaxInitialLength + 1);
+}
+
+void
+InitCapacityOk_InitialEntryStoreTooBig()
+{
+ // Try the smallest disallowed power-of-two entry store size, which is 2^32
+ // bytes (which overflows to 0). (Note that the 2^23 *length* gets converted
+ // to a 2^24 *capacity*.)
+ PLDHashTable t(PLDHashTable::StubOps(), (uint32_t)1 << 23, (uint32_t)1 << 8);
+}
+
+TEST(PLDHashTableTest, InitCapacityOk)
+{
+ // Try the largest allowed capacity. With kMaxCapacity==1<<26, this
+ // would allocate (if we added an element) 0.5GB of entry store on 32-bit
+ // platforms and 1GB on 64-bit platforms.
+ PLDHashTable t1(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub),
+ PLDHashTable::kMaxInitialLength);
+
+ // Try the largest allowed power-of-two entry store size, which is 2^31 bytes
+ // (Note that the 2^23 *length* gets converted to a 2^24 *capacity*.)
+ PLDHashTable t2(PLDHashTable::StubOps(), (uint32_t)1 << 23, (uint32_t)1 << 7);
+
+ // Try a too-large capacity (which aborts).
+ TestCrashyOperation(InitCapacityOk_InitialLengthTooBig);
+
+ // Try a large capacity combined with a large entry size that when multiplied
+ // overflow (causing abort).
+ TestCrashyOperation(InitCapacityOk_InitialEntryStoreTooBig);
+
+ // Ideally we'd also try a large-but-ok capacity that almost but doesn't
+ // quite overflow, but that would result in allocating slightly less than 4
+ // GiB of entry storage. That would be very likely to fail on 32-bit
+ // platforms, so such a test wouldn't be reliable.
+}
+
+TEST(PLDHashTableTest, LazyStorage)
+{
+ PLDHashTable t(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub));
+
+ // PLDHashTable allocates entry storage lazily. Check that all the non-add
+ // operations work appropriately when the table is empty and the storage
+ // hasn't yet been allocated.
+
+ ASSERT_EQ(t.Capacity(), 0u);
+ ASSERT_EQ(t.EntrySize(), sizeof(PLDHashEntryStub));
+ ASSERT_EQ(t.EntryCount(), 0u);
+ ASSERT_EQ(t.Generation(), 0u);
+
+ ASSERT_TRUE(!t.Search((const void*)1));
+
+ // No result to check here, but call it to make sure it doesn't crash.
+ t.Remove((const void*)2);
+
+ for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
+ ASSERT_TRUE(false); // shouldn't hit this on an empty table
+ }
+
+ ASSERT_EQ(t.ShallowSizeOfExcludingThis(moz_malloc_size_of), 0u);
+}
+
+// A trivial hash function is good enough here. It's also super-fast for the
+// GrowToMaxCapacity test because we insert the integers 0.., which means it's
+// collision-free.
+static PLDHashNumber
+TrivialHash(const void *key)
+{
+ return (PLDHashNumber)(size_t)key;
+}
+
+static void
+TrivialInitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ auto entry = static_cast<PLDHashEntryStub*>(aEntry);
+ entry->key = aKey;
+}
+
+static const PLDHashTableOps trivialOps = {
+ TrivialHash,
+ PLDHashTable::MatchEntryStub,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ TrivialInitEntry
+};
+
+TEST(PLDHashTableTest, MoveSemantics)
+{
+ PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
+ t1.Add((const void*)88);
+ PLDHashTable t2(&trivialOps, sizeof(PLDHashEntryStub));
+ t2.Add((const void*)99);
+
+ t1 = mozilla::Move(t1); // self-move
+
+ t1 = mozilla::Move(t2); // empty overwritten with empty
+
+ PLDHashTable t3(&trivialOps, sizeof(PLDHashEntryStub));
+ PLDHashTable t4(&trivialOps, sizeof(PLDHashEntryStub));
+ t3.Add((const void*)88);
+
+ t3 = mozilla::Move(t4); // non-empty overwritten with empty
+
+ PLDHashTable t5(&trivialOps, sizeof(PLDHashEntryStub));
+ PLDHashTable t6(&trivialOps, sizeof(PLDHashEntryStub));
+ t6.Add((const void*)88);
+
+ t5 = mozilla::Move(t6); // empty overwritten with non-empty
+
+ PLDHashTable t7(&trivialOps, sizeof(PLDHashEntryStub));
+ PLDHashTable t8(mozilla::Move(t7)); // new table constructed with uninited
+
+ PLDHashTable t9(&trivialOps, sizeof(PLDHashEntryStub));
+ t9.Add((const void*)88);
+ PLDHashTable t10(mozilla::Move(t9)); // new table constructed with inited
+}
+
+TEST(PLDHashTableTest, Clear)
+{
+ PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
+
+ t1.Clear();
+ ASSERT_EQ(t1.EntryCount(), 0u);
+
+ t1.ClearAndPrepareForLength(100);
+ ASSERT_EQ(t1.EntryCount(), 0u);
+
+ t1.Add((const void*)77);
+ t1.Add((const void*)88);
+ t1.Add((const void*)99);
+ ASSERT_EQ(t1.EntryCount(), 3u);
+
+ t1.Clear();
+ ASSERT_EQ(t1.EntryCount(), 0u);
+
+ t1.Add((const void*)55);
+ t1.Add((const void*)66);
+ t1.Add((const void*)77);
+ t1.Add((const void*)88);
+ t1.Add((const void*)99);
+ ASSERT_EQ(t1.EntryCount(), 5u);
+
+ t1.ClearAndPrepareForLength(8192);
+ ASSERT_EQ(t1.EntryCount(), 0u);
+}
+
+TEST(PLDHashTableTest, Iterator)
+{
+ PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
+
+ // Explicitly test the move constructor. We do this because, due to copy
+ // elision, compilers might optimize away move constructor calls for normal
+ // iterator use.
+ {
+ PLDHashTable::Iterator iter1(&t);
+ PLDHashTable::Iterator iter2(mozilla::Move(iter1));
+ }
+
+ // Iterate through the empty table.
+ for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
+ (void) iter.Get();
+ ASSERT_TRUE(false); // shouldn't hit this
+ }
+
+ // Add three entries.
+ t.Add((const void*)77);
+ t.Add((const void*)88);
+ t.Add((const void*)99);
+
+ // Check the iterator goes through each entry once.
+ bool saw77 = false, saw88 = false, saw99 = false;
+ int n = 0;
+ for (auto iter(t.Iter()); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
+ if (entry->key == (const void*)77) {
+ saw77 = true;
+ }
+ if (entry->key == (const void*)88) {
+ saw88 = true;
+ }
+ if (entry->key == (const void*)99) {
+ saw99 = true;
+ }
+ n++;
+ }
+ ASSERT_TRUE(saw77 && saw88 && saw99 && n == 3);
+
+ t.Clear();
+
+ // First, we insert 64 items, which results in a capacity of 128, and a load
+ // factor of 50%.
+ for (intptr_t i = 0; i < 64; i++) {
+ t.Add((const void*)i);
+ }
+ ASSERT_EQ(t.EntryCount(), 64u);
+ ASSERT_EQ(t.Capacity(), 128u);
+
+ // The first removing iterator does no removing; capacity and entry count are
+ // unchanged.
+ for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
+ (void) iter.Get();
+ }
+ ASSERT_EQ(t.EntryCount(), 64u);
+ ASSERT_EQ(t.Capacity(), 128u);
+
+ // The second removing iterator removes 16 items. This reduces the load
+ // factor to 37.5% (48 / 128), which isn't low enough to shrink the table.
+ for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
+ if ((intptr_t)(entry->key) % 4 == 0) {
+ iter.Remove();
+ }
+ }
+ ASSERT_EQ(t.EntryCount(), 48u);
+ ASSERT_EQ(t.Capacity(), 128u);
+
+ // The third removing iterator removes another 16 items. This reduces
+ // the load factor to 25% (32 / 128), so the table is shrunk.
+ for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
+ auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
+ if ((intptr_t)(entry->key) % 2 == 0) {
+ iter.Remove();
+ }
+ }
+ ASSERT_EQ(t.EntryCount(), 32u);
+ ASSERT_EQ(t.Capacity(), 64u);
+
+ // The fourth removing iterator removes all remaining items. This reduces
+ // the capacity to the minimum.
+ for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
+ iter.Remove();
+ }
+ ASSERT_EQ(t.EntryCount(), 0u);
+ ASSERT_EQ(t.Capacity(), unsigned(PLDHashTable::kMinCapacity));
+}
+
+// This test involves resizing a table repeatedly up to 512 MiB in size. On
+// 32-bit platforms (Win32, Android) it sometimes OOMs, causing the test to
+// fail. (See bug 931062 and bug 1267227.) Therefore, we only run it on 64-bit
+// platforms where OOM is much less likely.
+//
+// Also, it's slow, and so should always be last.
+#ifdef HAVE_64BIT_BUILD
+TEST(PLDHashTableTest, GrowToMaxCapacity)
+{
+ // This is infallible.
+ PLDHashTable* t =
+ new PLDHashTable(&trivialOps, sizeof(PLDHashEntryStub), 128);
+
+ // Keep inserting elements until failure occurs because the table is full.
+ size_t numInserted = 0;
+ while (true) {
+ if (!t->Add((const void*)numInserted, mozilla::fallible)) {
+ break;
+ }
+ numInserted++;
+ }
+
+ // We stop when the element count is 96.875% of PLDHashTable::kMaxCapacity
+ // (see MaxLoadOnGrowthFailure()).
+ if (numInserted !=
+ PLDHashTable::kMaxCapacity - (PLDHashTable::kMaxCapacity >> 5)) {
+ delete t;
+ ASSERT_TRUE(false);
+ }
+
+ delete t;
+}
+#endif
+
diff --git a/xpcom/tests/gtest/TestPipes.cpp b/xpcom/tests/gtest/TestPipes.cpp
new file mode 100644
index 000000000..87b923008
--- /dev/null
+++ b/xpcom/tests/gtest/TestPipes.cpp
@@ -0,0 +1,1097 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+#include "gtest/gtest.h"
+#include "Helpers.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIBufferedStreams.h"
+#include "nsIClassInfo.h"
+#include "nsICloneableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIPipe.h"
+#include "nsISeekableStream.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "prprf.h"
+#include "prinrval.h"
+
+using namespace mozilla;
+
+#define ITERATIONS 33333
+char kTestPattern[] = "My hovercraft is full of eels.\n";
+
+bool gTrace = false;
+
+static nsresult
+WriteAll(nsIOutputStream *os, const char *buf, uint32_t bufLen, uint32_t *lenWritten)
+{
+ const char *p = buf;
+ *lenWritten = 0;
+ while (bufLen) {
+ uint32_t n;
+ nsresult rv = os->Write(p, bufLen, &n);
+ if (NS_FAILED(rv)) return rv;
+ p += n;
+ bufLen -= n;
+ *lenWritten += n;
+ }
+ return NS_OK;
+}
+
+class nsReceiver final : public nsIRunnable {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ nsresult rv;
+ char buf[101];
+ uint32_t count;
+ PRIntervalTime start = PR_IntervalNow();
+ while (true) {
+ rv = mIn->Read(buf, 100, &count);
+ if (NS_FAILED(rv)) {
+ printf("read failed\n");
+ break;
+ }
+ if (count == 0) {
+// printf("EOF count = %d\n", mCount);
+ break;
+ }
+
+ if (gTrace) {
+ buf[count] = '\0';
+ printf("read: %s\n", buf);
+ }
+ mCount += count;
+ }
+ PRIntervalTime end = PR_IntervalNow();
+ printf("read %d bytes, time = %dms\n", mCount,
+ PR_IntervalToMilliseconds(end - start));
+ return rv;
+ }
+
+ explicit nsReceiver(nsIInputStream* in) : mIn(in), mCount(0) {
+ }
+
+ uint32_t GetBytesRead() { return mCount; }
+
+private:
+ ~nsReceiver() {}
+
+protected:
+ nsCOMPtr<nsIInputStream> mIn;
+ uint32_t mCount;
+};
+
+NS_IMPL_ISUPPORTS(nsReceiver, nsIRunnable)
+
+nsresult
+TestPipe(nsIInputStream* in, nsIOutputStream* out)
+{
+ RefPtr<nsReceiver> receiver = new nsReceiver(in);
+ if (!receiver)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_NewThread(getter_AddRefs(thread), receiver);
+ if (NS_FAILED(rv)) return rv;
+
+ uint32_t total = 0;
+ PRIntervalTime start = PR_IntervalNow();
+ for (uint32_t i = 0; i < ITERATIONS; i++) {
+ uint32_t writeCount;
+ char *buf = PR_smprintf("%d %s", i, kTestPattern);
+ uint32_t len = strlen(buf);
+ rv = WriteAll(out, buf, len, &writeCount);
+ if (gTrace) {
+ printf("wrote: ");
+ for (uint32_t j = 0; j < writeCount; j++) {
+ putc(buf[j], stdout);
+ }
+ printf("\n");
+ }
+ PR_smprintf_free(buf);
+ if (NS_FAILED(rv)) return rv;
+ total += writeCount;
+ }
+ rv = out->Close();
+ if (NS_FAILED(rv)) return rv;
+
+ PRIntervalTime end = PR_IntervalNow();
+
+ thread->Shutdown();
+
+ printf("wrote %d bytes, time = %dms\n", total,
+ PR_IntervalToMilliseconds(end - start));
+ EXPECT_EQ(receiver->GetBytesRead(), total);
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class nsShortReader final : public nsIRunnable {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ nsresult rv;
+ char buf[101];
+ uint32_t count;
+ uint32_t total = 0;
+ while (true) {
+ //if (gTrace)
+ // printf("calling Read\n");
+ rv = mIn->Read(buf, 100, &count);
+ if (NS_FAILED(rv)) {
+ printf("read failed\n");
+ break;
+ }
+ if (count == 0) {
+ break;
+ }
+
+ if (gTrace) {
+ // For next |printf()| call and possible others elsewhere.
+ buf[count] = '\0';
+
+ printf("read %d bytes: %s\n", count, buf);
+ }
+
+ Received(count);
+ total += count;
+ }
+ printf("read %d bytes\n", total);
+ return rv;
+ }
+
+ explicit nsShortReader(nsIInputStream* in) : mIn(in), mReceived(0) {
+ mMon = new ReentrantMonitor("nsShortReader");
+ }
+
+ void Received(uint32_t count) {
+ ReentrantMonitorAutoEnter mon(*mMon);
+ mReceived += count;
+ mon.Notify();
+ }
+
+ uint32_t WaitForReceipt(const uint32_t aWriteCount) {
+ ReentrantMonitorAutoEnter mon(*mMon);
+ uint32_t result = mReceived;
+
+ while (result < aWriteCount) {
+ mon.Wait();
+
+ EXPECT_TRUE(mReceived > result);
+ result = mReceived;
+ }
+
+ mReceived = 0;
+ return result;
+ }
+
+private:
+ ~nsShortReader() {}
+
+protected:
+ nsCOMPtr<nsIInputStream> mIn;
+ uint32_t mReceived;
+ ReentrantMonitor* mMon;
+};
+
+NS_IMPL_ISUPPORTS(nsShortReader, nsIRunnable)
+
+nsresult
+TestShortWrites(nsIInputStream* in, nsIOutputStream* out)
+{
+ RefPtr<nsShortReader> receiver = new nsShortReader(in);
+ if (!receiver)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_NewThread(getter_AddRefs(thread), receiver);
+ if (NS_FAILED(rv)) return rv;
+
+ uint32_t total = 0;
+ for (uint32_t i = 0; i < ITERATIONS; i++) {
+ uint32_t writeCount;
+ char* buf = PR_smprintf("%d %s", i, kTestPattern);
+ uint32_t len = strlen(buf);
+ len = len * rand() / RAND_MAX;
+ len = std::min(1u, len);
+ rv = WriteAll(out, buf, len, &writeCount);
+ if (NS_FAILED(rv)) return rv;
+ EXPECT_EQ(writeCount, len);
+ total += writeCount;
+
+ if (gTrace)
+ printf("wrote %d bytes: %s\n", writeCount, buf);
+ PR_smprintf_free(buf);
+ //printf("calling Flush\n");
+ out->Flush();
+ //printf("calling WaitForReceipt\n");
+
+#ifdef DEBUG
+ const uint32_t received =
+ receiver->WaitForReceipt(writeCount);
+ EXPECT_EQ(received, writeCount);
+#endif
+ }
+ rv = out->Close();
+ if (NS_FAILED(rv)) return rv;
+
+ thread->Shutdown();
+
+ printf("wrote %d bytes\n", total);
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class nsPump final : public nsIRunnable
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ nsresult rv;
+ uint32_t count;
+ while (true) {
+ rv = mOut->WriteFrom(mIn, ~0U, &count);
+ if (NS_FAILED(rv)) {
+ printf("Write failed\n");
+ break;
+ }
+ if (count == 0) {
+ printf("EOF count = %d\n", mCount);
+ break;
+ }
+
+ if (gTrace) {
+ printf("Wrote: %d\n", count);
+ }
+ mCount += count;
+ }
+ mOut->Close();
+ return rv;
+ }
+
+ nsPump(nsIInputStream* in,
+ nsIOutputStream* out)
+ : mIn(in), mOut(out), mCount(0) {
+ }
+
+private:
+ ~nsPump() {}
+
+protected:
+ nsCOMPtr<nsIInputStream> mIn;
+ nsCOMPtr<nsIOutputStream> mOut;
+ uint32_t mCount;
+};
+
+NS_IMPL_ISUPPORTS(nsPump, nsIRunnable)
+
+TEST(Pipes, ChainedPipes)
+{
+ nsresult rv;
+ if (gTrace) {
+ printf("TestChainedPipes\n");
+ }
+
+ nsCOMPtr<nsIInputStream> in1;
+ nsCOMPtr<nsIOutputStream> out1;
+ rv = NS_NewPipe(getter_AddRefs(in1), getter_AddRefs(out1), 20, 1999);
+ if (NS_FAILED(rv)) return;
+
+ nsCOMPtr<nsIInputStream> in2;
+ nsCOMPtr<nsIOutputStream> out2;
+ rv = NS_NewPipe(getter_AddRefs(in2), getter_AddRefs(out2), 200, 401);
+ if (NS_FAILED(rv)) return;
+
+ RefPtr<nsPump> pump = new nsPump(in1, out2);
+ if (pump == nullptr) return;
+
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_NewThread(getter_AddRefs(thread), pump);
+ if (NS_FAILED(rv)) return;
+
+ RefPtr<nsReceiver> receiver = new nsReceiver(in2);
+ if (receiver == nullptr) return;
+
+ nsCOMPtr<nsIThread> receiverThread;
+ rv = NS_NewThread(getter_AddRefs(receiverThread), receiver);
+ if (NS_FAILED(rv)) return;
+
+ uint32_t total = 0;
+ for (uint32_t i = 0; i < ITERATIONS; i++) {
+ uint32_t writeCount;
+ char* buf = PR_smprintf("%d %s", i, kTestPattern);
+ uint32_t len = strlen(buf);
+ len = len * rand() / RAND_MAX;
+ len = std::max(1u, len);
+ rv = WriteAll(out1, buf, len, &writeCount);
+ if (NS_FAILED(rv)) return;
+ EXPECT_EQ(writeCount, len);
+ total += writeCount;
+
+ if (gTrace)
+ printf("wrote %d bytes: %s\n", writeCount, buf);
+
+ PR_smprintf_free(buf);
+ }
+ if (gTrace) {
+ printf("wrote total of %d bytes\n", total);
+ }
+ rv = out1->Close();
+ if (NS_FAILED(rv)) return;
+
+ thread->Shutdown();
+ receiverThread->Shutdown();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void
+RunTests(uint32_t segSize, uint32_t segCount)
+{
+ nsresult rv;
+ nsCOMPtr<nsIInputStream> in;
+ nsCOMPtr<nsIOutputStream> out;
+ uint32_t bufSize = segSize * segCount;
+ if (gTrace) {
+ printf("Testing New Pipes: segment size %d buffer size %d\n", segSize, bufSize);
+ printf("Testing long writes...\n");
+ }
+ rv = NS_NewPipe(getter_AddRefs(in), getter_AddRefs(out), segSize, bufSize);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ rv = TestPipe(in, out);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ if (gTrace) {
+ printf("Testing short writes...\n");
+ }
+ rv = NS_NewPipe(getter_AddRefs(in), getter_AddRefs(out), segSize, bufSize);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ rv = TestShortWrites(in, out);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+}
+
+TEST(Pipes, Main)
+{
+ RunTests(16, 1);
+ RunTests(4096, 16);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+static const uint32_t DEFAULT_SEGMENT_SIZE = 4 * 1024;
+
+// An alternate pipe testing routing that uses NS_ConsumeStream() instead of
+// manual read loop.
+static void TestPipe2(uint32_t aNumBytes,
+ uint32_t aSegmentSize = DEFAULT_SEGMENT_SIZE)
+{
+ nsCOMPtr<nsIInputStream> reader;
+ nsCOMPtr<nsIOutputStream> writer;
+
+ uint32_t maxSize = std::max(aNumBytes, aSegmentSize);
+
+ nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer),
+ aSegmentSize, maxSize);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> inputData;
+ testing::CreateData(aNumBytes, inputData);
+ testing::WriteAllAndClose(writer, inputData);
+ testing::ConsumeAndValidateStream(reader, inputData);
+}
+
+} // namespace
+
+TEST(Pipes, Blocking_32k)
+{
+ TestPipe2(32 * 1024);
+}
+
+TEST(Pipes, Blocking_64k)
+{
+ TestPipe2(64 * 1024);
+}
+
+TEST(Pipes, Blocking_128k)
+{
+ TestPipe2(128 * 1024);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+// Utility routine to validate pipe clone before. There are many knobs.
+//
+// aTotalBytes Total number of bytes to write to the pipe.
+// aNumWrites How many separate write calls should be made. Bytes
+// are evenly distributed over these write calls.
+// aNumInitialClones How many clones of the pipe input stream should be
+// made before writing begins.
+// aNumToCloseAfterWrite How many streams should be closed after each write.
+// One stream is always kept open. This verifies that
+// closing one stream does not effect other open
+// streams.
+// aNumToCloneAfterWrite How many clones to create after each write. Occurs
+// after closing any streams. This tests cloning
+// active streams on a pipe that is being written to.
+// aNumStreamToReadPerWrite How many streams to read fully after each write.
+// This tests reading cloned streams at different rates
+// while the pipe is being written to.
+static void TestPipeClone(uint32_t aTotalBytes,
+ uint32_t aNumWrites,
+ uint32_t aNumInitialClones,
+ uint32_t aNumToCloseAfterWrite,
+ uint32_t aNumToCloneAfterWrite,
+ uint32_t aNumStreamsToReadPerWrite,
+ uint32_t aSegmentSize = DEFAULT_SEGMENT_SIZE)
+{
+ nsCOMPtr<nsIInputStream> reader;
+ nsCOMPtr<nsIOutputStream> writer;
+
+ uint32_t maxSize = std::max(aTotalBytes, aSegmentSize);
+
+ // Use async input streams so we can NS_ConsumeStream() the current data
+ // while the pipe is still being written to.
+ nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer),
+ aSegmentSize, maxSize,
+ true, false); // non-blocking - reader, writer
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(reader);
+ ASSERT_TRUE(cloneable);
+ ASSERT_TRUE(cloneable->GetCloneable());
+
+ nsTArray<nsCString> outputDataList;
+
+ nsTArray<nsCOMPtr<nsIInputStream>> streamList;
+
+ // first stream is our original reader from the pipe
+ streamList.AppendElement(reader);
+ outputDataList.AppendElement();
+
+ // Clone the initial input stream the specified number of times
+ // before performing any writes.
+ for (uint32_t i = 0; i < aNumInitialClones; ++i) {
+ nsCOMPtr<nsIInputStream>* clone = streamList.AppendElement();
+ rv = cloneable->Clone(getter_AddRefs(*clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(*clone);
+
+ outputDataList.AppendElement();
+ }
+
+ nsTArray<char> inputData;
+ testing::CreateData(aTotalBytes, inputData);
+
+ const uint32_t bytesPerWrite = ((aTotalBytes - 1)/ aNumWrites) + 1;
+ uint32_t offset = 0;
+ uint32_t remaining = aTotalBytes;
+ uint32_t nextStreamToRead = 0;
+
+ while (remaining) {
+ uint32_t numToWrite = std::min(bytesPerWrite, remaining);
+ testing::Write(writer, inputData, offset, numToWrite);
+ offset += numToWrite;
+ remaining -= numToWrite;
+
+ // Close the specified number of streams. This allows us to
+ // test that one closed clone does not break other open clones.
+ for (uint32_t i = 0; i < aNumToCloseAfterWrite &&
+ streamList.Length() > 1; ++i) {
+
+ uint32_t lastIndex = streamList.Length() - 1;
+ streamList[lastIndex]->Close();
+ streamList.RemoveElementAt(lastIndex);
+ outputDataList.RemoveElementAt(lastIndex);
+
+ if (nextStreamToRead >= streamList.Length()) {
+ nextStreamToRead = 0;
+ }
+ }
+
+ // Create the specified number of clones. This lets us verify
+ // that we can create clones in the middle of pipe reading and
+ // writing.
+ for (uint32_t i = 0; i < aNumToCloneAfterWrite; ++i) {
+ nsCOMPtr<nsIInputStream>* clone = streamList.AppendElement();
+ rv = cloneable->Clone(getter_AddRefs(*clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(*clone);
+
+ // Initialize the new output data to make whats been read to data for
+ // the original stream. First stream is always the original stream.
+ nsCString* outputData = outputDataList.AppendElement();
+ *outputData = outputDataList[0];
+ }
+
+ // Read the specified number of streams. This lets us verify that we
+ // can read from the clones at different rates while the pipe is being
+ // written to.
+ for (uint32_t i = 0; i < aNumStreamsToReadPerWrite; ++i) {
+ nsCOMPtr<nsIInputStream>& stream = streamList[nextStreamToRead];
+ nsCString& outputData = outputDataList[nextStreamToRead];
+
+ // Can't use ConsumeAndValidateStream() here because we're not
+ // guaranteed the exact amount read. It should just be at least
+ // as many as numToWrite.
+ nsAutoCString tmpOutputData;
+ rv = NS_ConsumeStream(stream, UINT32_MAX, tmpOutputData);
+ ASSERT_TRUE(rv == NS_BASE_STREAM_WOULD_BLOCK || NS_SUCCEEDED(rv));
+ ASSERT_GE(tmpOutputData.Length(), numToWrite);
+
+ outputData += tmpOutputData;
+
+ nextStreamToRead += 1;
+ if (nextStreamToRead >= streamList.Length()) {
+ // Note: When we wrap around on the streams being read, its possible
+ // we will trigger a segment to be deleted from the pipe. It
+ // would be nice to validate this here, but we don't have any
+ // QI'able interface that would let us check easily.
+
+ nextStreamToRead = 0;
+ }
+ }
+ }
+
+ rv = writer->Close();
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
+
+ // Finally, read the remaining bytes from each stream. This may be
+ // different amounts of data depending on how much reading we did while
+ // writing. Verify that the end result matches the input data.
+ for (uint32_t i = 0; i < streamList.Length(); ++i) {
+ nsCOMPtr<nsIInputStream>& stream = streamList[i];
+ nsCString& outputData = outputDataList[i];
+
+ nsAutoCString tmpOutputData;
+ rv = NS_ConsumeStream(stream, UINT32_MAX, tmpOutputData);
+ ASSERT_TRUE(rv == NS_BASE_STREAM_WOULD_BLOCK || NS_SUCCEEDED(rv));
+ stream->Close();
+
+ // Append to total amount read from the stream
+ outputData += tmpOutputData;
+
+ ASSERT_EQ(inputString.Length(), outputData.Length());
+ ASSERT_TRUE(inputString.Equals(outputData));
+ }
+}
+
+} // namespace
+
+TEST(Pipes, Clone_BeforeWrite_ReadAtEnd)
+{
+ TestPipeClone(32 * 1024, // total bytes
+ 16, // num writes
+ 3, // num initial clones
+ 0, // num streams to close after each write
+ 0, // num clones to add after each write
+ 0); // num streams to read after each write
+}
+
+TEST(Pipes, Clone_BeforeWrite_ReadDuringWrite)
+{
+ // Since this reads all streams on every write, it should trigger the
+ // pipe cursor roll back optimization. Currently we can only verify
+ // this with logging.
+
+ TestPipeClone(32 * 1024, // total bytes
+ 16, // num writes
+ 3, // num initial clones
+ 0, // num streams to close after each write
+ 0, // num clones to add after each write
+ 4); // num streams to read after each write
+}
+
+TEST(Pipes, Clone_DuringWrite_ReadAtEnd)
+{
+ TestPipeClone(32 * 1024, // total bytes
+ 16, // num writes
+ 0, // num initial clones
+ 0, // num streams to close after each write
+ 1, // num clones to add after each write
+ 0); // num streams to read after each write
+}
+
+TEST(Pipes, Clone_DuringWrite_ReadDuringWrite)
+{
+ TestPipeClone(32 * 1024, // total bytes
+ 16, // num writes
+ 0, // num initial clones
+ 0, // num streams to close after each write
+ 1, // num clones to add after each write
+ 1); // num streams to read after each write
+}
+
+TEST(Pipes, Clone_DuringWrite_ReadDuringWrite_CloseDuringWrite)
+{
+ // Since this reads streams faster than we clone new ones, it should
+ // trigger pipe segment deletion periodically. Currently we can
+ // only verify this with logging.
+
+ TestPipeClone(32 * 1024, // total bytes
+ 16, // num writes
+ 1, // num initial clones
+ 1, // num streams to close after each write
+ 2, // num clones to add after each write
+ 3); // num streams to read after each write
+}
+
+TEST(Pipes, Write_AsyncWait)
+{
+ nsCOMPtr<nsIAsyncInputStream> reader;
+ nsCOMPtr<nsIAsyncOutputStream> writer;
+
+ const uint32_t segmentSize = 1024;
+ const uint32_t numSegments = 1;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+ true, true, // non-blocking - reader, writer
+ segmentSize, numSegments);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> inputData;
+ testing::CreateData(segmentSize, inputData);
+
+ uint32_t numWritten = 0;
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
+
+ RefPtr<testing::OutputStreamCallback> cb =
+ new testing::OutputStreamCallback();
+
+ rv = writer->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_FALSE(cb->Called());
+
+ testing::ConsumeAndValidateStream(reader, inputData);
+
+ ASSERT_TRUE(cb->Called());
+}
+
+TEST(Pipes, Write_AsyncWait_Clone)
+{
+ nsCOMPtr<nsIAsyncInputStream> reader;
+ nsCOMPtr<nsIAsyncOutputStream> writer;
+
+ const uint32_t segmentSize = 1024;
+ const uint32_t numSegments = 1;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+ true, true, // non-blocking - reader, writer
+ segmentSize, numSegments);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = NS_CloneInputStream(reader, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> inputData;
+ testing::CreateData(segmentSize, inputData);
+
+ uint32_t numWritten = 0;
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // This attempts to write data beyond the original pipe size limit. It
+ // should fail since neither side of the clone has been read yet.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
+
+ RefPtr<testing::OutputStreamCallback> cb =
+ new testing::OutputStreamCallback();
+
+ rv = writer->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_FALSE(cb->Called());
+
+ // Consume data on the original stream, but the clone still has not been read.
+ testing::ConsumeAndValidateStream(reader, inputData);
+
+ // A clone that is not being read should not stall the other input stream
+ // reader. Therefore the writer callback should trigger when the fastest
+ // reader drains the other input stream.
+ ASSERT_TRUE(cb->Called());
+
+ // Attempt to write data. This will buffer data beyond the pipe size limit in
+ // order for the clone stream to still work. This is allowed because the
+ // other input stream has drained its buffered segments and is ready for more
+ // data.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Again, this should fail since the origin stream has not been read again.
+ // The pipe size should still restrict how far ahead we can buffer even
+ // when there is a cloned stream not being read.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ cb = new testing::OutputStreamCallback();
+ rv = writer->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // The write should again be blocked since we have written data and the
+ // main reader is at its maximum advance buffer.
+ ASSERT_FALSE(cb->Called());
+
+ nsTArray<char> expectedCloneData;
+ expectedCloneData.AppendElements(inputData);
+ expectedCloneData.AppendElements(inputData);
+
+ // We should now be able to consume the entire backlog of buffered data on
+ // the cloned stream.
+ testing::ConsumeAndValidateStream(clone, expectedCloneData);
+
+ // Draining the clone side should also trigger the AsyncWait() writer
+ // callback
+ ASSERT_TRUE(cb->Called());
+
+ // Finally, we should be able to consume the remaining data on the original
+ // reader.
+ testing::ConsumeAndValidateStream(reader, inputData);
+}
+
+TEST(Pipes, Write_AsyncWait_Clone_CloseOriginal)
+{
+ nsCOMPtr<nsIAsyncInputStream> reader;
+ nsCOMPtr<nsIAsyncOutputStream> writer;
+
+ const uint32_t segmentSize = 1024;
+ const uint32_t numSegments = 1;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+ true, true, // non-blocking - reader, writer
+ segmentSize, numSegments);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = NS_CloneInputStream(reader, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> inputData;
+ testing::CreateData(segmentSize, inputData);
+
+ uint32_t numWritten = 0;
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // This attempts to write data beyond the original pipe size limit. It
+ // should fail since neither side of the clone has been read yet.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
+
+ RefPtr<testing::OutputStreamCallback> cb =
+ new testing::OutputStreamCallback();
+
+ rv = writer->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_FALSE(cb->Called());
+
+ // Consume data on the original stream, but the clone still has not been read.
+ testing::ConsumeAndValidateStream(reader, inputData);
+
+ // A clone that is not being read should not stall the other input stream
+ // reader. Therefore the writer callback should trigger when the fastest
+ // reader drains the other input stream.
+ ASSERT_TRUE(cb->Called());
+
+ // Attempt to write data. This will buffer data beyond the pipe size limit in
+ // order for the clone stream to still work. This is allowed because the
+ // other input stream has drained its buffered segments and is ready for more
+ // data.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Again, this should fail since the origin stream has not been read again.
+ // The pipe size should still restrict how far ahead we can buffer even
+ // when there is a cloned stream not being read.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ cb = new testing::OutputStreamCallback();
+ rv = writer->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // The write should again be blocked since we have written data and the
+ // main reader is at its maximum advance buffer.
+ ASSERT_FALSE(cb->Called());
+
+ // Close the original reader input stream. This was the fastest reader,
+ // so we should have a single stream that is buffered beyond our nominal
+ // limit.
+ reader->Close();
+
+ // Because the clone stream is still buffered the writable callback should
+ // not be fired.
+ ASSERT_FALSE(cb->Called());
+
+ // And we should not be able to perform a write.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ // Create another clone stream. Now we have two streams that exceed our
+ // maximum size limit
+ nsCOMPtr<nsIInputStream> clone2;
+ rv = NS_CloneInputStream(clone, getter_AddRefs(clone2));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> expectedCloneData;
+ expectedCloneData.AppendElements(inputData);
+ expectedCloneData.AppendElements(inputData);
+
+ // We should now be able to consume the entire backlog of buffered data on
+ // the cloned stream.
+ testing::ConsumeAndValidateStream(clone, expectedCloneData);
+
+ // The pipe should now be writable because we have two open streams, one of which
+ // is completely drained.
+ ASSERT_TRUE(cb->Called());
+
+ // Write again to reach our limit again.
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // The stream is again non-writeable.
+ cb = new testing::OutputStreamCallback();
+ rv = writer->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_FALSE(cb->Called());
+
+ // Close the empty stream. This is different from our previous close since
+ // before we were closing a stream with some data still buffered.
+ clone->Close();
+
+ // The pipe should not be writable. The second clone is still fully buffered
+ // over our limit.
+ ASSERT_FALSE(cb->Called());
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ // Finally consume all of the buffered data on the second clone.
+ expectedCloneData.AppendElements(inputData);
+ testing::ConsumeAndValidateStream(clone2, expectedCloneData);
+
+ // Draining the final clone should make the pipe writable again.
+ ASSERT_TRUE(cb->Called());
+}
+
+TEST(Pipes, Read_AsyncWait)
+{
+ nsCOMPtr<nsIAsyncInputStream> reader;
+ nsCOMPtr<nsIAsyncOutputStream> writer;
+
+ const uint32_t segmentSize = 1024;
+ const uint32_t numSegments = 1;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+ true, true, // non-blocking - reader, writer
+ segmentSize, numSegments);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> inputData;
+ testing::CreateData(segmentSize, inputData);
+
+ RefPtr<testing::InputStreamCallback> cb =
+ new testing::InputStreamCallback();
+
+ rv = reader->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_FALSE(cb->Called());
+
+ uint32_t numWritten = 0;
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_TRUE(cb->Called());
+
+ testing::ConsumeAndValidateStream(reader, inputData);
+}
+
+TEST(Pipes, Read_AsyncWait_Clone)
+{
+ nsCOMPtr<nsIAsyncInputStream> reader;
+ nsCOMPtr<nsIAsyncOutputStream> writer;
+
+ const uint32_t segmentSize = 1024;
+ const uint32_t numSegments = 1;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+ true, true, // non-blocking - reader, writer
+ segmentSize, numSegments);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = NS_CloneInputStream(reader, getter_AddRefs(clone));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIAsyncInputStream> asyncClone = do_QueryInterface(clone);
+ ASSERT_TRUE(asyncClone);
+
+ nsTArray<char> inputData;
+ testing::CreateData(segmentSize, inputData);
+
+ RefPtr<testing::InputStreamCallback> cb =
+ new testing::InputStreamCallback();
+
+ RefPtr<testing::InputStreamCallback> cb2 =
+ new testing::InputStreamCallback();
+
+ rv = reader->AsyncWait(cb, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_FALSE(cb->Called());
+
+ rv = asyncClone->AsyncWait(cb2, 0, 0, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_FALSE(cb2->Called());
+
+ uint32_t numWritten = 0;
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_TRUE(cb->Called());
+ ASSERT_TRUE(cb2->Called());
+
+ testing::ConsumeAndValidateStream(reader, inputData);
+}
+
+namespace {
+
+nsresult
+CloseDuringReadFunc(nsIInputStream *aReader,
+ void* aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCountOut)
+{
+ MOZ_RELEASE_ASSERT(aReader);
+ MOZ_RELEASE_ASSERT(aClosure);
+ MOZ_RELEASE_ASSERT(aFromSegment);
+ MOZ_RELEASE_ASSERT(aWriteCountOut);
+ MOZ_RELEASE_ASSERT(aToOffset == 0);
+
+ // This is insanity and you probably should not do this under normal
+ // conditions. We want to simulate the case where the pipe is closed
+ // (possibly from other end on another thread) simultaneously with the
+ // read. This is the easiest way to do trigger this case in a synchronous
+ // gtest.
+ MOZ_ALWAYS_SUCCEEDS(aReader->Close());
+
+ nsTArray<char>* buffer = static_cast<nsTArray<char>*>(aClosure);
+ buffer->AppendElements(aFromSegment, aCount);
+
+ *aWriteCountOut = aCount;
+
+ return NS_OK;
+}
+
+void
+TestCloseDuringRead(uint32_t aSegmentSize, uint32_t aDataSize)
+{
+ nsCOMPtr<nsIInputStream> reader;
+ nsCOMPtr<nsIOutputStream> writer;
+
+ const uint32_t maxSize = aSegmentSize;
+
+ nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer),
+ aSegmentSize, maxSize);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> inputData;
+
+ testing::CreateData(aDataSize, inputData);
+
+ uint32_t numWritten = 0;
+ rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<char> outputData;
+
+ uint32_t numRead = 0;
+ rv = reader->ReadSegments(CloseDuringReadFunc, &outputData,
+ inputData.Length(), &numRead);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_EQ(inputData.Length(), numRead);
+
+ ASSERT_EQ(inputData, outputData);
+
+ uint64_t available;
+ rv = reader->Available(&available);
+ ASSERT_EQ(NS_BASE_STREAM_CLOSED, rv);
+}
+
+} // namespace
+
+TEST(Pipes, Close_During_Read_Partial_Segment)
+{
+ TestCloseDuringRead(1024, 512);
+}
+
+TEST(Pipes, Close_During_Read_Full_Segment)
+{
+ TestCloseDuringRead(1024, 1024);
+}
+
+TEST(Pipes, Interfaces)
+{
+ nsCOMPtr<nsIInputStream> reader;
+ nsCOMPtr<nsIOutputStream> writer;
+
+ nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIAsyncInputStream> readerType1 = do_QueryInterface(reader);
+ ASSERT_TRUE(readerType1);
+
+ nsCOMPtr<nsISeekableStream> readerType2 = do_QueryInterface(reader);
+ ASSERT_TRUE(readerType2);
+
+ nsCOMPtr<nsISearchableInputStream> readerType3 = do_QueryInterface(reader);
+ ASSERT_TRUE(readerType3);
+
+ nsCOMPtr<nsICloneableInputStream> readerType4 = do_QueryInterface(reader);
+ ASSERT_TRUE(readerType4);
+
+ nsCOMPtr<nsIClassInfo> readerType5 = do_QueryInterface(reader);
+ ASSERT_TRUE(readerType5);
+
+ nsCOMPtr<nsIBufferedInputStream> readerType6 = do_QueryInterface(reader);
+ ASSERT_TRUE(readerType6);
+}
diff --git a/xpcom/tests/gtest/TestPriorityQueue.cpp b/xpcom/tests/gtest/TestPriorityQueue.cpp
new file mode 100644
index 000000000..eeb2f1e09
--- /dev/null
+++ b/xpcom/tests/gtest/TestPriorityQueue.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTPriorityQueue.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "gtest/gtest.h"
+
+template<class T, class Compare>
+void
+CheckPopSequence(const nsTPriorityQueue<T, Compare>& aQueue,
+ const T* aExpectedSequence, const uint32_t aSequenceLength)
+{
+ nsTPriorityQueue<T, Compare> copy(aQueue);
+
+ for (uint32_t i = 0; i < aSequenceLength; i++) {
+ EXPECT_FALSE(copy.IsEmpty());
+
+ T pop = copy.Pop();
+ EXPECT_EQ(pop, aExpectedSequence[i]);
+ }
+
+ EXPECT_TRUE(copy.IsEmpty());
+}
+
+template<class A>
+class MaxCompare {
+public:
+ bool LessThan(const A& a, const A& b) {
+ return a > b;
+ }
+};
+
+TEST(PriorityQueue, Main)
+{
+ nsTPriorityQueue<int> queue;
+
+ EXPECT_TRUE(queue.IsEmpty());
+
+ queue.Push(8);
+ queue.Push(6);
+ queue.Push(4);
+ queue.Push(2);
+ queue.Push(10);
+ queue.Push(6);
+ EXPECT_EQ(queue.Top(), 2);
+ EXPECT_EQ(queue.Length(), 6u);
+ EXPECT_FALSE(queue.IsEmpty());
+ int expected[] = { 2, 4, 6, 6, 8, 10 };
+ CheckPopSequence(queue, expected, sizeof(expected) / sizeof(expected[0]));
+
+ // copy ctor is tested by using CheckPopSequence, but check default assignment
+ // operator
+ nsTPriorityQueue<int> queue2;
+ queue2 = queue;
+ CheckPopSequence(queue2, expected, sizeof(expected) / sizeof(expected[0]));
+
+ queue.Clear();
+ EXPECT_TRUE(queue.IsEmpty());
+
+ // try same sequence with a max heap
+ nsTPriorityQueue<int, MaxCompare<int> > max_queue;
+ max_queue.Push(8);
+ max_queue.Push(6);
+ max_queue.Push(4);
+ max_queue.Push(2);
+ max_queue.Push(10);
+ max_queue.Push(6);
+ EXPECT_EQ(max_queue.Top(), 10);
+ int expected_max[] = { 10, 8, 6, 6, 4, 2 };
+ CheckPopSequence(max_queue, expected_max,
+ sizeof(expected_max) / sizeof(expected_max[0]));
+}
diff --git a/xpcom/tests/gtest/TestRacingServiceManager.cpp b/xpcom/tests/gtest/TestRacingServiceManager.cpp
new file mode 100644
index 000000000..b0638db02
--- /dev/null
+++ b/xpcom/tests/gtest/TestRacingServiceManager.cpp
@@ -0,0 +1,300 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIFactory.h"
+#include "mozilla/Module.h"
+#include "nsXULAppAPI.h"
+#include "nsIThread.h"
+#include "nsIComponentRegistrar.h"
+
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCIDInternal.h"
+#include "pratom.h"
+#include "prmon.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+#include "mozilla/ReentrantMonitor.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+/* f93f6bdc-88af-42d7-9d64-1b43c649a3e5 */
+#define FACTORY_CID1 \
+{ \
+ 0xf93f6bdc, \
+ 0x88af, \
+ 0x42d7, \
+ { 0x9d, 0x64, 0x1b, 0x43, 0xc6, 0x49, 0xa3, 0xe5 } \
+}
+NS_DEFINE_CID(kFactoryCID1, FACTORY_CID1);
+
+/* ef38ad65-6595-49f0-8048-e819f81d15e2 */
+#define FACTORY_CID2 \
+{ \
+ 0xef38ad65, \
+ 0x6595, \
+ 0x49f0, \
+ { 0x80, 0x48, 0xe8, 0x19, 0xf8, 0x1d, 0x15, 0xe2 } \
+}
+NS_DEFINE_CID(kFactoryCID2, FACTORY_CID2);
+
+#define FACTORY_CONTRACTID \
+ "TestRacingThreadManager/factory;1"
+
+namespace TestRacingServiceManager
+{
+int32_t gComponent1Count = 0;
+int32_t gComponent2Count = 0;
+
+ReentrantMonitor* gReentrantMonitor = nullptr;
+
+bool gCreateInstanceCalled = false;
+bool gMainThreadWaiting = false;
+
+class AutoCreateAndDestroyReentrantMonitor
+{
+public:
+ explicit AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr)
+ : mReentrantMonitorPtr(aReentrantMonitorPtr) {
+ *aReentrantMonitorPtr =
+ new ReentrantMonitor("TestRacingServiceManager::AutoMon");
+ MOZ_RELEASE_ASSERT(*aReentrantMonitorPtr, "Out of memory!");
+ }
+
+ ~AutoCreateAndDestroyReentrantMonitor() {
+ if (*mReentrantMonitorPtr) {
+ delete *mReentrantMonitorPtr;
+ *mReentrantMonitorPtr = nullptr;
+ }
+ }
+
+private:
+ ReentrantMonitor** mReentrantMonitorPtr;
+};
+
+class Factory final : public nsIFactory
+{
+ ~Factory() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ Factory() : mFirstComponentCreated(false) { }
+
+ NS_IMETHOD CreateInstance(nsISupports* aDelegate,
+ const nsIID& aIID,
+ void** aResult) override;
+
+ NS_IMETHOD LockFactory(bool aLock) override {
+ return NS_OK;
+ }
+
+ bool mFirstComponentCreated;
+};
+
+NS_IMPL_ISUPPORTS(Factory, nsIFactory)
+
+class Component1 final : public nsISupports
+{
+ ~Component1() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ Component1() {
+ // This is the real test - make sure that only one instance is ever created.
+ int32_t count = PR_AtomicIncrement(&gComponent1Count);
+ MOZ_RELEASE_ASSERT(count == 1, "Too many components created!");
+ }
+};
+
+NS_IMPL_ADDREF(Component1)
+NS_IMPL_RELEASE(Component1)
+
+NS_INTERFACE_MAP_BEGIN(Component1)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+class Component2 final : public nsISupports
+{
+ ~Component2() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ Component2() {
+ // This is the real test - make sure that only one instance is ever created.
+ int32_t count = PR_AtomicIncrement(&gComponent2Count);
+ EXPECT_EQ(count, int32_t(1)) << "Too many components created!";
+ }
+};
+
+NS_IMPL_ADDREF(Component2)
+NS_IMPL_RELEASE(Component2)
+
+NS_INTERFACE_MAP_BEGIN(Component2)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+Factory::CreateInstance(nsISupports* aDelegate,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // Make sure that the second thread beat the main thread to the getService
+ // call.
+ MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+ {
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+
+ gCreateInstanceCalled = true;
+ mon.Notify();
+
+ mon.Wait(PR_MillisecondsToInterval(3000));
+ }
+
+ NS_ENSURE_FALSE(aDelegate, NS_ERROR_NO_AGGREGATION);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsCOMPtr<nsISupports> instance;
+
+ if (!mFirstComponentCreated) {
+ instance = new Component1();
+ }
+ else {
+ instance = new Component2();
+ }
+ NS_ENSURE_TRUE(instance, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = instance->QueryInterface(aIID, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+class TestRunnable : public Runnable
+{
+public:
+ NS_DECL_NSIRUNNABLE
+
+ TestRunnable() : mFirstRunnableDone(false) { }
+
+ bool mFirstRunnableDone;
+};
+
+NS_IMETHODIMP
+TestRunnable::Run()
+{
+ {
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+
+ while (!gMainThreadWaiting) {
+ mon.Wait();
+ }
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISupports> component;
+
+ if (!mFirstRunnableDone) {
+ component = do_GetService(kFactoryCID1, &rv);
+ }
+ else {
+ component = do_GetService(FACTORY_CONTRACTID, &rv);
+ }
+ EXPECT_TRUE(NS_SUCCEEDED(rv)) << "GetService failed!";
+
+ return NS_OK;
+}
+
+static Factory* gFactory;
+
+static already_AddRefed<nsIFactory>
+CreateFactory(const mozilla::Module& module, const mozilla::Module::CIDEntry& entry)
+{
+ if (!gFactory) {
+ gFactory = new Factory();
+ NS_ADDREF(gFactory);
+ }
+ nsCOMPtr<nsIFactory> ret = gFactory;
+ return ret.forget();
+}
+
+static const mozilla::Module::CIDEntry kLocalCIDs[] = {
+ { &kFactoryCID1, false, CreateFactory, nullptr },
+ { &kFactoryCID2, false, CreateFactory, nullptr },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kLocalContracts[] = {
+ { FACTORY_CONTRACTID, &kFactoryCID2 },
+ { nullptr }
+};
+
+static const mozilla::Module kLocalModule = {
+ mozilla::Module::kVersion,
+ kLocalCIDs,
+ kLocalContracts
+};
+
+TEST(RacingServiceManager, Test)
+{
+ nsresult rv;
+ XRE_AddStaticComponent(&kLocalModule);
+
+ AutoCreateAndDestroyReentrantMonitor mon1(&gReentrantMonitor);
+
+ RefPtr<TestRunnable> runnable = new TestRunnable();
+ ASSERT_TRUE(runnable);
+
+ // Run the classID test
+ nsCOMPtr<nsIThread> newThread;
+ rv = NS_NewThread(getter_AddRefs(newThread), runnable);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ {
+ ReentrantMonitorAutoEnter mon2(*gReentrantMonitor);
+
+ gMainThreadWaiting = true;
+ mon2.Notify();
+
+ while (!gCreateInstanceCalled) {
+ mon2.Wait();
+ }
+ }
+
+ nsCOMPtr<nsISupports> component(do_GetService(kFactoryCID1, &rv));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Reset for the contractID test
+ gMainThreadWaiting = gCreateInstanceCalled = false;
+ gFactory->mFirstComponentCreated = runnable->mFirstRunnableDone = true;
+ component = nullptr;
+
+ rv = newThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ {
+ ReentrantMonitorAutoEnter mon3(*gReentrantMonitor);
+
+ gMainThreadWaiting = true;
+ mon3.Notify();
+
+ while (!gCreateInstanceCalled) {
+ mon3.Wait();
+ }
+ }
+
+ component = do_GetService(FACTORY_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ NS_RELEASE(gFactory);
+}
+
+} // namespace TestRacingServiceManager
diff --git a/xpcom/tests/gtest/TestSTLWrappers.cpp b/xpcom/tests/gtest/TestSTLWrappers.cpp
new file mode 100644
index 000000000..9559548a3
--- /dev/null
+++ b/xpcom/tests/gtest/TestSTLWrappers.cpp
@@ -0,0 +1,78 @@
+#include <stdio.h>
+
+#include <algorithm>
+#ifndef mozilla_algorithm_h
+# error "failed to wrap <algorithm>"
+#endif
+
+#include <vector>
+#ifndef mozilla_vector_h
+# error "failed to wrap <vector>"
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsCOMPtr.h"
+#include "nsICrashReporter.h"
+#include "nsServiceManagerUtils.h"
+#endif
+
+// gcc errors out if we |try ... catch| with -fno-exceptions, but we
+// can still test on windows
+#ifdef _MSC_VER
+ // C4530 will be generated whenever try...catch is used without
+ // enabling exceptions. We know we don't enbale exceptions.
+# pragma warning( disable : 4530 )
+# define TRY try
+# define CATCH(e) catch (e)
+#else
+# define TRY
+# define CATCH(e) if (0)
+#endif
+
+
+#if defined(XP_UNIX)
+extern unsigned int _gdb_sleep_duration;
+#endif
+
+void ShouldAbort()
+{
+#if defined(XP_UNIX)
+ _gdb_sleep_duration = 0;
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+ nsCOMPtr<nsICrashReporter> crashreporter =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (crashreporter) {
+ crashreporter->SetEnabled(false);
+ }
+#endif
+
+ std::vector<int> v;
+ int rv = 1;
+
+ TRY {
+ // v.at(1) on empty v should abort; NOT throw an exception
+
+ // (Do some arithmetic with result of v.at() to avoid
+ // compiler warnings for unused variable/result.)
+ rv += v.at(1) ? 1 : 2;
+ } CATCH(const std::out_of_range&) {
+ fputs("TEST-FAIL | TestSTLWrappers.cpp | caught an exception?\n",
+ stderr);
+ return;
+ }
+
+ fputs("TEST-FAIL | TestSTLWrappers.cpp | didn't abort()?\n",
+ stderr);
+ return;
+}
+
+#ifdef XP_WIN
+TEST(STLWrapper, DISABLED_ShouldAbortDeathTest)
+#else
+TEST(STLWrapper, ShouldAbortDeathTest)
+#endif
+{
+ ASSERT_DEATH_IF_SUPPORTED(ShouldAbort(), "terminate called after throwing an instance of 'std::out_of_range'|vector::_M_range_check");
+}
diff --git a/xpcom/tests/gtest/TestSlicedInputStream.cpp b/xpcom/tests/gtest/TestSlicedInputStream.cpp
new file mode 100644
index 000000000..ccad0a6a8
--- /dev/null
+++ b/xpcom/tests/gtest/TestSlicedInputStream.cpp
@@ -0,0 +1,266 @@
+#include "gtest/gtest.h"
+
+#include "nsCOMPtr.h"
+#include "nsIInputStream.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+#include "SlicedInputStream.h"
+
+/* We want to ensure that sliced streams work with both seekable and
+ * non-seekable input streams. As our string streams are seekable, we need to
+ * provide a string stream that doesn't permit seeking, so we can test the
+ * logic that emulates seeking in sliced input streams.
+ */
+class NonSeekableStringStream final : public nsIInputStream
+{
+ nsCOMPtr<nsIInputStream> mStream;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit NonSeekableStringStream(const nsACString& aBuffer)
+ {
+ NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
+ }
+
+ NS_IMETHOD
+ Available(uint64_t* aLength) override
+ {
+ return mStream->Available(aLength);
+ }
+
+ NS_IMETHOD
+ Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
+ {
+ return mStream->Read(aBuffer, aCount, aReadCount);
+ }
+
+ NS_IMETHOD
+ ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t *aResult) override
+ {
+ return mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+ }
+
+ NS_IMETHOD
+ Close() override
+ {
+ return mStream->Close();
+ }
+
+ NS_IMETHOD
+ IsNonBlocking(bool* aNonBlocking) override
+ {
+ return mStream->IsNonBlocking(aNonBlocking);
+ }
+
+private:
+ ~NonSeekableStringStream() {}
+};
+
+NS_IMPL_ISUPPORTS(NonSeekableStringStream, nsIInputStream)
+
+// Helper function for creating a seekable nsIInputStream + a SlicedInputStream.
+SlicedInputStream*
+CreateSeekableStreams(uint32_t aSize, uint64_t aStart, uint64_t aLength,
+ nsCString& aBuffer)
+{
+ aBuffer.SetLength(aSize);
+ for (uint32_t i = 0; i < aSize; ++i) {
+ aBuffer.BeginWriting()[i] = i % 10;
+ }
+
+ nsCOMPtr<nsIInputStream> stream;
+ NS_NewCStringInputStream(getter_AddRefs(stream), aBuffer);
+ return new SlicedInputStream(stream, aStart, aLength);
+}
+
+// Helper function for creating a non-seekable nsIInputStream + a
+// SlicedInputStream.
+SlicedInputStream*
+CreateNonSeekableStreams(uint32_t aSize, uint64_t aStart, uint64_t aLength,
+ nsCString& aBuffer)
+{
+ aBuffer.SetLength(aSize);
+ for (uint32_t i = 0; i < aSize; ++i) {
+ aBuffer.BeginWriting()[i] = i % 10;
+ }
+
+ RefPtr<NonSeekableStringStream> stream = new NonSeekableStringStream(aBuffer);
+ return new SlicedInputStream(stream, aStart, aLength);
+}
+
+// Same start, same length.
+TEST(TestSlicedInputStream, Simple) {
+ const size_t kBufSize = 4096;
+
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateSeekableStreams(kBufSize, 0, kBufSize, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)kBufSize, length);
+
+ char buf2[kBufSize];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ(count, buf.Length());
+ ASSERT_TRUE(nsCString(buf.get()).Equals(nsCString(buf2)));
+}
+
+// Simple sliced stream - seekable
+TEST(TestSlicedInputStream, Sliced) {
+ const size_t kBufSize = 4096;
+
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateSeekableStreams(kBufSize, 10, 100, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)100, length);
+
+ char buf2[kBufSize / 2];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)100, count);
+ ASSERT_TRUE(nsCString(buf.get() + 10, count).Equals(nsCString(buf2, count)));
+}
+
+// Simple sliced stream - non seekable
+TEST(TestSlicedInputStream, SlicedNoSeek) {
+ const size_t kBufSize = 4096;
+
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateNonSeekableStreams(kBufSize, 10, 100, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)100, length);
+
+ char buf2[kBufSize / 2];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)100, count);
+ ASSERT_TRUE(nsCString(buf.get() + 10, count).Equals(nsCString(buf2, count)));
+}
+
+// Big inputStream - seekable
+TEST(TestSlicedInputStream, BigSliced) {
+ const size_t kBufSize = 4096 * 40;
+
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateSeekableStreams(kBufSize, 4096 * 5, 4096 * 10, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)4096 * 10, length);
+
+ char buf2[kBufSize / 2];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)4096 * 10, count);
+ ASSERT_TRUE(nsCString(buf.get() + 4096 * 5, count).Equals(nsCString(buf2, count)));
+}
+
+// Big inputStream - non seekable
+TEST(TestSlicedInputStream, BigSlicedNoSeek) {
+ const size_t kBufSize = 4096 * 40;
+
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateNonSeekableStreams(kBufSize, 4096 * 5, 4096 * 10, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)4096 * 10, length);
+
+ char buf2[kBufSize / 2];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)4096 * 10, count);
+ ASSERT_TRUE(nsCString(buf.get() + 4096 * 5, count).Equals(nsCString(buf2, count)));
+}
+
+// Available size.
+TEST(TestSlicedInputStream, Available) {
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateNonSeekableStreams(500000, 4, 400000, buf);
+
+ uint64_t toRead = 400000;
+ for (uint32_t i = 0; i < 400; ++i) {
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ(toRead, length);
+
+ char buf2[1000];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)1000, count);
+ ASSERT_TRUE(nsCString(buf.get() + 4 + (1000 * i), count).Equals(nsCString(buf2, count)));
+
+ toRead -= count;
+ }
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)0, length);
+
+ char buf2[4096];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)0, count);
+}
+
+// What if start is > then the size of the buffer?
+TEST(TestSlicedInputStream, StartBiggerThan) {
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateNonSeekableStreams(500, 4000, 1, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)0, length);
+
+ char buf2[4096];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)0, count);
+}
+
+// What if the length is > than the size of the buffer?
+TEST(TestSlicedInputStream, LengthBiggerThan) {
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateNonSeekableStreams(500, 0, 500000, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)500, length);
+
+ char buf2[4096];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)500, count);
+}
+
+// What if the length is 0?
+TEST(TestSlicedInputStream, Length0) {
+ nsCString buf;
+ RefPtr<SlicedInputStream> sis =
+ CreateNonSeekableStreams(500, 0, 0, buf);
+
+ uint64_t length;
+ ASSERT_EQ(NS_OK, sis->Available(&length));
+ ASSERT_EQ((uint64_t)0, length);
+
+ char buf2[4096];
+ uint32_t count;
+ ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
+ ASSERT_EQ((uint64_t)0, count);
+}
diff --git a/xpcom/tests/gtest/TestSnappyStreams.cpp b/xpcom/tests/gtest/TestSnappyStreams.cpp
new file mode 100644
index 000000000..99f41120b
--- /dev/null
+++ b/xpcom/tests/gtest/TestSnappyStreams.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+#include "gtest/gtest.h"
+#include "Helpers.h"
+#include "mozilla/SnappyCompressOutputStream.h"
+#include "mozilla/SnappyUncompressInputStream.h"
+#include "nsIPipe.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+#include "nsTArray.h"
+
+namespace {
+
+using mozilla::SnappyCompressOutputStream;
+using mozilla::SnappyUncompressInputStream;
+
+static already_AddRefed<nsIOutputStream>
+CompressPipe(nsIInputStream** aReaderOut)
+{
+ nsCOMPtr<nsIOutputStream> pipeWriter;
+
+ nsresult rv = NS_NewPipe(aReaderOut, getter_AddRefs(pipeWriter));
+ if (NS_FAILED(rv)) { return nullptr; }
+
+ nsCOMPtr<nsIOutputStream> compress =
+ new SnappyCompressOutputStream(pipeWriter);
+ return compress.forget();
+}
+
+// Verify the given number of bytes compresses to a smaller number of bytes.
+static void TestCompress(uint32_t aNumBytes)
+{
+ // Don't permit this test on small data sizes as snappy can slightly
+ // bloat very small content.
+ ASSERT_GT(aNumBytes, 1024u);
+
+ nsCOMPtr<nsIInputStream> pipeReader;
+ nsCOMPtr<nsIOutputStream> compress = CompressPipe(getter_AddRefs(pipeReader));
+ ASSERT_TRUE(compress);
+
+ nsTArray<char> inputData;
+ testing::CreateData(aNumBytes, inputData);
+
+ testing::WriteAllAndClose(compress, inputData);
+
+ nsAutoCString outputData;
+ nsresult rv = NS_ConsumeStream(pipeReader, UINT32_MAX, outputData);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_LT(outputData.Length(), inputData.Length());
+}
+
+// Verify that the given number of bytes can be compressed and uncompressed
+// successfully.
+static void TestCompressUncompress(uint32_t aNumBytes)
+{
+ nsCOMPtr<nsIInputStream> pipeReader;
+ nsCOMPtr<nsIOutputStream> compress = CompressPipe(getter_AddRefs(pipeReader));
+ ASSERT_TRUE(compress);
+
+ nsCOMPtr<nsIInputStream> uncompress =
+ new SnappyUncompressInputStream(pipeReader);
+
+ nsTArray<char> inputData;
+ testing::CreateData(aNumBytes, inputData);
+
+ testing::WriteAllAndClose(compress, inputData);
+
+ nsAutoCString outputData;
+ nsresult rv = NS_ConsumeStream(uncompress, UINT32_MAX, outputData);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_EQ(inputData.Length(), outputData.Length());
+ for (uint32_t i = 0; i < inputData.Length(); ++i) {
+ EXPECT_EQ(inputData[i], outputData.get()[i]) << "Byte " << i;
+ }
+}
+
+static void TestUncompressCorrupt(const char* aCorruptData,
+ uint32_t aCorruptLength)
+{
+ nsCOMPtr<nsIInputStream> source;
+ nsresult rv = NS_NewByteInputStream(getter_AddRefs(source), aCorruptData,
+ aCorruptLength);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIInputStream> uncompress =
+ new SnappyUncompressInputStream(source);
+
+ nsAutoCString outputData;
+ rv = NS_ConsumeStream(uncompress, UINT32_MAX, outputData);
+ ASSERT_EQ(NS_ERROR_CORRUPTED_CONTENT, rv);
+}
+
+} // namespace
+
+TEST(SnappyStream, Compress_32k)
+{
+ TestCompress(32 * 1024);
+}
+
+TEST(SnappyStream, Compress_64k)
+{
+ TestCompress(64 * 1024);
+}
+
+TEST(SnappyStream, Compress_128k)
+{
+ TestCompress(128 * 1024);
+}
+
+TEST(SnappyStream, CompressUncompress_0)
+{
+ TestCompressUncompress(0);
+}
+
+TEST(SnappyStream, CompressUncompress_1)
+{
+ TestCompressUncompress(1);
+}
+
+TEST(SnappyStream, CompressUncompress_32)
+{
+ TestCompressUncompress(32);
+}
+
+TEST(SnappyStream, CompressUncompress_1k)
+{
+ TestCompressUncompress(1024);
+}
+
+TEST(SnappyStream, CompressUncompress_32k)
+{
+ TestCompressUncompress(32 * 1024);
+}
+
+TEST(SnappyStream, CompressUncompress_64k)
+{
+ TestCompressUncompress(64 * 1024);
+}
+
+TEST(SnappyStream, CompressUncompress_128k)
+{
+ TestCompressUncompress(128 * 1024);
+}
+
+// Test buffers that are not exactly power-of-2 in length to try to
+// exercise more edge cases. The number 13 is arbitrary.
+
+TEST(SnappyStream, CompressUncompress_256k_less_13)
+{
+ TestCompressUncompress((256 * 1024) - 13);
+}
+
+TEST(SnappyStream, CompressUncompress_256k)
+{
+ TestCompressUncompress(256 * 1024);
+}
+
+TEST(SnappyStream, CompressUncompress_256k_plus_13)
+{
+ TestCompressUncompress((256 * 1024) + 13);
+}
+
+TEST(SnappyStream, UncompressCorruptStreamIdentifier)
+{
+ static const char data[] = "This is not a valid compressed stream";
+ TestUncompressCorrupt(data, strlen(data));
+}
+
+TEST(SnappyStream, UncompressCorruptCompressedDataLength)
+{
+ static const char data[] = "\xff\x06\x00\x00sNaPpY" // stream identifier
+ "\x00\x99\x00\x00This is not a valid compressed stream";
+ static const uint32_t dataLength = (sizeof(data) / sizeof(const char)) - 1;
+ TestUncompressCorrupt(data, dataLength);
+}
+
+TEST(SnappyStream, UncompressCorruptCompressedDataContent)
+{
+ static const char data[] = "\xff\x06\x00\x00sNaPpY" // stream identifier
+ "\x00\x25\x00\x00This is not a valid compressed stream";
+ static const uint32_t dataLength = (sizeof(data) / sizeof(const char)) - 1;
+ TestUncompressCorrupt(data, dataLength);
+}
diff --git a/xpcom/tests/gtest/TestStateWatching.cpp b/xpcom/tests/gtest/TestStateWatching.cpp
new file mode 100644
index 000000000..16d06a5ff
--- /dev/null
+++ b/xpcom/tests/gtest/TestStateWatching.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/StateWatching.h"
+#include "mozilla/TaskQueue.h"
+#include "nsISupportsImpl.h"
+#include "VideoUtils.h"
+
+namespace TestStateWatching {
+
+using namespace mozilla;
+
+struct Foo {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Foo)
+ void Notify() { mNotified = true; }
+ bool mNotified = false;
+private:
+ ~Foo() {}
+};
+
+TEST(WatchManager, Shutdown)
+{
+ RefPtr<TaskQueue> queue = new TaskQueue(
+ GetMediaThreadPool(MediaThreadType::PLAYBACK));
+
+ RefPtr<Foo> p = new Foo;
+ WatchManager<Foo> manager(p, queue);
+ Watchable<bool> notifier(false, "notifier");
+
+ queue->Dispatch(NS_NewRunnableFunction([&] () {
+ manager.Watch(notifier, &Foo::Notify);
+ notifier = true; // Trigger the call to Foo::Notify().
+ manager.Shutdown(); // Shutdown() should cancel the call.
+ }));
+
+ queue->BeginShutdown();
+ queue->AwaitShutdownAndIdle();
+ EXPECT_FALSE(p->mNotified);
+}
+
+} // namespace TestStateWatching
diff --git a/xpcom/tests/gtest/TestStorageStream.cpp b/xpcom/tests/gtest/TestStorageStream.cpp
new file mode 100644
index 000000000..a49d6f6bc
--- /dev/null
+++ b/xpcom/tests/gtest/TestStorageStream.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include "gtest/gtest.h"
+#include "Helpers.h"
+#include "nsCOMPtr.h"
+#include "nsICloneableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIStorageStream.h"
+#include "nsTArray.h"
+
+namespace {
+
+void
+WriteData(nsIOutputStream* aOut, nsTArray<char>& aData, uint32_t aNumBytes,
+ nsACString& aDataWritten)
+{
+ uint32_t n;
+ nsresult rv = aOut->Write(aData.Elements(), aNumBytes, &n);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ aDataWritten.Append(aData.Elements(), aNumBytes);
+}
+
+} // namespace
+
+TEST(StorageStreams, Main)
+{
+ // generate some test data we will write in 4k chunks to the stream
+ nsTArray<char> kData;
+ testing::CreateData(4096, kData);
+
+ // track how much data was written so we can compare at the end
+ nsAutoCString dataWritten;
+
+ nsresult rv;
+ nsCOMPtr<nsIStorageStream> stor;
+
+ rv = NS_NewStorageStream(kData.Length(), UINT32_MAX, getter_AddRefs(stor));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIOutputStream> out;
+ rv = stor->GetOutputStream(0, getter_AddRefs(out));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ WriteData(out, kData, kData.Length(), dataWritten);
+ WriteData(out, kData, kData.Length(), dataWritten);
+
+ rv = out->Close();
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ out = nullptr;
+
+ nsCOMPtr<nsIInputStream> in;
+ rv = stor->NewInputStream(0, getter_AddRefs(in));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(in);
+ ASSERT_TRUE(cloneable != nullptr);
+ ASSERT_TRUE(cloneable->GetCloneable());
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = cloneable->Clone(getter_AddRefs(clone));
+
+ testing::ConsumeAndValidateStream(in, dataWritten);
+ testing::ConsumeAndValidateStream(clone, dataWritten);
+ in = nullptr;
+ clone = nullptr;
+
+ // now, write 3 more full 4k segments + 11 bytes, starting at 8192
+ // total written equals 20491 bytes
+
+ rv = stor->GetOutputStream(dataWritten.Length(), getter_AddRefs(out));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ WriteData(out, kData, kData.Length(), dataWritten);
+ WriteData(out, kData, kData.Length(), dataWritten);
+ WriteData(out, kData, kData.Length(), dataWritten);
+ WriteData(out, kData, 11, dataWritten);
+
+ rv = out->Close();
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ out = nullptr;
+
+ // now, read all
+ rv = stor->NewInputStream(0, getter_AddRefs(in));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ testing::ConsumeAndValidateStream(in, dataWritten);
+ in = nullptr;
+}
+
+TEST(StorageStreams, EarlyInputStream)
+{
+ // generate some test data we will write in 4k chunks to the stream
+ nsTArray<char> kData;
+ testing::CreateData(4096, kData);
+
+ // track how much data was written so we can compare at the end
+ nsAutoCString dataWritten;
+
+ nsresult rv;
+ nsCOMPtr<nsIStorageStream> stor;
+
+ rv = NS_NewStorageStream(kData.Length(), UINT32_MAX, getter_AddRefs(stor));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ // Get input stream before writing data into the output stream
+ nsCOMPtr<nsIInputStream> in;
+ rv = stor->NewInputStream(0, getter_AddRefs(in));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ // Write data to output stream
+ nsCOMPtr<nsIOutputStream> out;
+ rv = stor->GetOutputStream(0, getter_AddRefs(out));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ WriteData(out, kData, kData.Length(), dataWritten);
+ WriteData(out, kData, kData.Length(), dataWritten);
+
+ rv = out->Close();
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ out = nullptr;
+
+ // Should be able to consume input stream
+ testing::ConsumeAndValidateStream(in, dataWritten);
+ in = nullptr;
+}
diff --git a/xpcom/tests/gtest/TestStringStream.cpp b/xpcom/tests/gtest/TestStringStream.cpp
new file mode 100644
index 000000000..5591ed588
--- /dev/null
+++ b/xpcom/tests/gtest/TestStringStream.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "Helpers.h"
+#include "nsICloneableInputStream.h"
+#include "nsStringStream.h"
+#include "nsTArray.h"
+#include "nsIInputStream.h"
+#include "nsCOMPtr.h"
+
+namespace {
+
+static void TestStringStream(uint32_t aNumBytes)
+{
+ nsTArray<char> inputData;
+ testing::CreateData(aNumBytes, inputData);
+ nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> stream;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ testing::ConsumeAndValidateStream(stream, inputString);
+}
+
+static void TestStringStreamClone(uint32_t aNumBytes)
+{
+ nsTArray<char> inputData;
+ testing::CreateData(aNumBytes, inputData);
+ nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
+
+ nsCOMPtr<nsIInputStream> stream;
+ nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), inputString);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(stream);
+ ASSERT_TRUE(cloneable != nullptr);
+ ASSERT_TRUE(cloneable->GetCloneable());
+
+ nsCOMPtr<nsIInputStream> clone;
+ rv = cloneable->Clone(getter_AddRefs(clone));
+
+ testing::ConsumeAndValidateStream(stream, inputString);
+
+ // Release the stream to verify that the clone's string survives correctly.
+ stream = nullptr;
+
+ testing::ConsumeAndValidateStream(clone, inputString);
+}
+
+} // namespace
+
+TEST(StringStream, Simple_4k)
+{
+ TestStringStream(1024 * 4);
+}
+
+TEST(StringStream, Clone_4k)
+{
+ TestStringStreamClone(1024 * 4);
+}
diff --git a/xpcom/tests/gtest/TestStrings.cpp b/xpcom/tests/gtest/TestStrings.cpp
new file mode 100644
index 000000000..285021b8e
--- /dev/null
+++ b/xpcom/tests/gtest/TestStrings.cpp
@@ -0,0 +1,982 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nsString.h"
+#include "nsStringBuffer.h"
+#include "nsReadableUtils.h"
+#include "nsCRTGlue.h"
+#include "mozilla/RefPtr.h"
+#include "nsTArray.h"
+#include "gtest/gtest.h"
+
+namespace TestStrings {
+
+using mozilla::fallible;
+
+void test_assign_helper(const nsACString& in, nsACString &_retval)
+{
+ _retval = in;
+}
+
+TEST(Strings, assign)
+{
+ nsCString result;
+ test_assign_helper(NS_LITERAL_CSTRING("a") + NS_LITERAL_CSTRING("b"), result);
+ EXPECT_STREQ(result.get(), "ab");
+}
+
+TEST(Strings, assign_c)
+{
+ nsCString c; c.Assign('c');
+ EXPECT_STREQ(c.get(), "c");
+}
+
+TEST(Strings, test1)
+{
+ NS_NAMED_LITERAL_STRING(empty, "");
+ const nsAString& aStr = empty;
+
+ nsAutoString buf(aStr);
+
+ int32_t n = buf.FindChar(',');
+ EXPECT_EQ(n, kNotFound);
+
+ n = buf.Length();
+
+ buf.Cut(0, n + 1);
+ n = buf.FindChar(',');
+
+ EXPECT_EQ(n, kNotFound);
+}
+
+TEST(Strings, test2)
+{
+ nsCString data("hello world");
+ const nsACString& aStr = data;
+
+ nsCString temp(aStr);
+ temp.Cut(0, 6);
+
+ EXPECT_STREQ(temp.get(), "world");
+}
+
+TEST(Strings, find)
+{
+ nsCString src("<!DOCTYPE blah blah blah>");
+
+ int32_t i = src.Find("DOCTYPE", true, 2, 1);
+ EXPECT_EQ(i, 2);
+}
+
+TEST(Strings, rfind)
+{
+ const char text[] = "<!DOCTYPE blah blah blah>";
+ const char term[] = "bLaH";
+ nsCString src(text);
+ int32_t i;
+
+ i = src.RFind(term, true, 3, -1);
+ EXPECT_EQ(i, kNotFound);
+
+ i = src.RFind(term, true, -1, -1);
+ EXPECT_EQ(i, 20);
+
+ i = src.RFind(term, true, 13, -1);
+ EXPECT_EQ(i, 10);
+
+ i = src.RFind(term, true, 22, 3);
+ EXPECT_EQ(i, 20);
+}
+
+TEST(Strings, rfind_2)
+{
+ const char text[] = "<!DOCTYPE blah blah blah>";
+ nsCString src(text);
+ int32_t i = src.RFind("TYPE", false, 5, -1);
+ EXPECT_EQ(i, 5);
+}
+
+TEST(Strings, rfind_3)
+{
+ const char text[] = "urn:mozilla:locale:en-US:necko";
+ nsAutoCString value(text);
+ int32_t i = value.RFind(":");
+ EXPECT_EQ(i, 24);
+}
+
+TEST(Strings, rfind_4)
+{
+ nsCString value("a.msf");
+ int32_t i = value.RFind(".msf");
+ EXPECT_EQ(i, 1);
+}
+
+TEST(Strings, findinreadable)
+{
+ const char text[] = "jar:jar:file:///c:/software/mozilla/mozilla_2006_02_21.jar!/browser/chrome/classic.jar!/";
+ nsAutoCString value(text);
+
+ nsACString::const_iterator begin, end;
+ value.BeginReading(begin);
+ value.EndReading(end);
+ nsACString::const_iterator delim_begin (begin),
+ delim_end (end);
+
+ // Search for last !/ at the end of the string
+ EXPECT_TRUE(FindInReadable(NS_LITERAL_CSTRING("!/"), delim_begin, delim_end));
+ char *r = ToNewCString(Substring(delim_begin, delim_end));
+ // Should match the first "!/" but not the last
+ EXPECT_NE(delim_end, end);
+ EXPECT_STREQ(r, "!/");
+ free(r);
+
+ delim_begin = begin;
+ delim_end = end;
+
+ // Search for first jar:
+ EXPECT_TRUE(FindInReadable(NS_LITERAL_CSTRING("jar:"), delim_begin, delim_end));
+
+ r = ToNewCString(Substring(delim_begin, delim_end));
+ // Should not match the first jar:, but the second one
+ EXPECT_EQ(delim_begin, begin);
+ EXPECT_STREQ(r, "jar:");
+ free(r);
+
+ // Search for jar: in a Substring
+ delim_begin = begin; delim_begin++;
+ delim_end = end;
+ EXPECT_TRUE(FindInReadable(NS_LITERAL_CSTRING("jar:"), delim_begin, delim_end));
+
+ r = ToNewCString(Substring(delim_begin, delim_end));
+ // Should not match the first jar:, but the second one
+ EXPECT_NE(delim_begin, begin);
+ EXPECT_STREQ(r, "jar:");
+ free(r);
+
+ // Should not find a match
+ EXPECT_FALSE(FindInReadable(NS_LITERAL_CSTRING("gecko"), delim_begin, delim_end));
+
+ // When no match is found, range should be empty
+ EXPECT_EQ(delim_begin, delim_end);
+
+ // Should not find a match (search not beyond Substring)
+ delim_begin = begin; for (int i=0;i<6;i++) delim_begin++;
+ delim_end = end;
+ EXPECT_FALSE(FindInReadable(NS_LITERAL_CSTRING("jar:"), delim_begin, delim_end));
+
+ // When no match is found, range should be empty
+ EXPECT_EQ(delim_begin, delim_end);
+
+ // Should not find a match (search not beyond Substring)
+ delim_begin = begin;
+ delim_end = end; for (int i=0;i<7;i++) delim_end--;
+ EXPECT_FALSE(FindInReadable(NS_LITERAL_CSTRING("classic"), delim_begin, delim_end));
+
+ // When no match is found, range should be empty
+ EXPECT_EQ(delim_begin, delim_end);
+}
+
+TEST(Strings, rfindinreadable)
+{
+ const char text[] = "jar:jar:file:///c:/software/mozilla/mozilla_2006_02_21.jar!/browser/chrome/classic.jar!/";
+ nsAutoCString value(text);
+
+ nsACString::const_iterator begin, end;
+ value.BeginReading(begin);
+ value.EndReading(end);
+ nsACString::const_iterator delim_begin (begin),
+ delim_end (end);
+
+ // Search for last !/ at the end of the string
+ EXPECT_TRUE(RFindInReadable(NS_LITERAL_CSTRING("!/"), delim_begin, delim_end));
+ char *r = ToNewCString(Substring(delim_begin, delim_end));
+ // Should match the last "!/"
+ EXPECT_EQ(delim_end, end);
+ EXPECT_STREQ(r, "!/");
+ free(r);
+
+ delim_begin = begin;
+ delim_end = end;
+
+ // Search for last jar: but not the first one...
+ EXPECT_TRUE(RFindInReadable(NS_LITERAL_CSTRING("jar:"), delim_begin, delim_end));
+
+ r = ToNewCString(Substring(delim_begin, delim_end));
+ // Should not match the first jar:, but the second one
+ EXPECT_NE(delim_begin, begin);
+ EXPECT_STREQ(r, "jar:");
+ free(r);
+
+ // Search for jar: in a Substring
+ delim_begin = begin;
+ delim_end = begin; for (int i=0;i<6;i++) delim_end++;
+ EXPECT_TRUE(RFindInReadable(NS_LITERAL_CSTRING("jar:"), delim_begin, delim_end));
+
+ r = ToNewCString(Substring(delim_begin, delim_end));
+ // Should not match the first jar:, but the second one
+ EXPECT_EQ(delim_begin, begin);
+ EXPECT_STREQ(r, "jar:");
+ free(r);
+
+ // Should not find a match
+ delim_begin = begin;
+ delim_end = end;
+ EXPECT_FALSE(RFindInReadable(NS_LITERAL_CSTRING("gecko"), delim_begin, delim_end));
+
+ // When no match is found, range should be empty
+ EXPECT_EQ(delim_begin, delim_end);
+
+ // Should not find a match (search not before Substring)
+ delim_begin = begin; for (int i=0;i<6;i++) delim_begin++;
+ delim_end = end;
+ EXPECT_FALSE(RFindInReadable(NS_LITERAL_CSTRING("jar:"), delim_begin, delim_end));
+
+ // When no match is found, range should be empty
+ EXPECT_EQ(delim_begin, delim_end);
+
+ // Should not find a match (search not beyond Substring)
+ delim_begin = begin;
+ delim_end = end; for (int i=0;i<7;i++) delim_end--;
+ EXPECT_FALSE(RFindInReadable(NS_LITERAL_CSTRING("classic"), delim_begin, delim_end));
+
+ // When no match is found, range should be empty
+ EXPECT_EQ(delim_begin, delim_end);
+}
+
+TEST(Strings, distance)
+{
+ const char text[] = "abc-xyz";
+ nsCString s(text);
+ nsCString::const_iterator begin, end;
+ s.BeginReading(begin);
+ s.EndReading(end);
+ size_t d = Distance(begin, end);
+ EXPECT_EQ(d, sizeof(text) - 1);
+}
+
+TEST(Strings, length)
+{
+ const char text[] = "abc-xyz";
+ nsCString s(text);
+ size_t d = s.Length();
+ EXPECT_EQ(d, sizeof(text) - 1);
+}
+
+TEST(Strings, trim)
+{
+ const char text[] = " a\t $ ";
+ const char set[] = " \t$";
+
+ nsCString s(text);
+ s.Trim(set);
+ EXPECT_STREQ(s.get(), "a");
+}
+
+TEST(Strings, replace_substr)
+{
+ const char text[] = "abc-ppp-qqq-ppp-xyz";
+ nsCString s(text);
+ s.ReplaceSubstring("ppp", "www");
+ EXPECT_STREQ(s.get(), "abc-www-qqq-www-xyz");
+
+ s.Assign("foobar");
+ s.ReplaceSubstring("foo", "bar");
+ s.ReplaceSubstring("bar", "");
+ EXPECT_STREQ(s.get(), "");
+
+ s.Assign("foofoofoo");
+ s.ReplaceSubstring("foo", "foo");
+ EXPECT_STREQ(s.get(), "foofoofoo");
+
+ s.Assign("foofoofoo");
+ s.ReplaceSubstring("of", "fo");
+ EXPECT_STREQ(s.get(), "fofoofooo");
+}
+
+TEST(Strings, replace_substr_2)
+{
+ const char *oldName = nullptr;
+ const char *newName = "user";
+ nsString acctName; acctName.AssignLiteral("forums.foo.com");
+ nsAutoString newAcctName, oldVal, newVal;
+ oldVal.AssignWithConversion(oldName);
+ newVal.AssignWithConversion(newName);
+ newAcctName.Assign(acctName);
+
+ // here, oldVal is empty. we are testing that this function
+ // does not hang. see bug 235355.
+ newAcctName.ReplaceSubstring(oldVal, newVal);
+
+ // we expect that newAcctName will be unchanged.
+ EXPECT_TRUE(newAcctName.Equals(acctName));
+}
+
+TEST(Strings, replace_substr_3)
+{
+ nsCString s;
+ s.Assign("abcabcabc");
+ s.ReplaceSubstring("ca", "X");
+ EXPECT_STREQ(s.get(), "abXbXbc");
+
+ s.Assign("abcabcabc");
+ s.ReplaceSubstring("ca", "XYZ");
+ EXPECT_STREQ(s.get(), "abXYZbXYZbc");
+
+ s.Assign("abcabcabc");
+ s.ReplaceSubstring("ca", "XY");
+ EXPECT_STREQ(s.get(), "abXYbXYbc");
+
+ s.Assign("abcabcabc");
+ s.ReplaceSubstring("ca", "XYZ!");
+ EXPECT_STREQ(s.get(), "abXYZ!bXYZ!bc");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("bcd", "X");
+ EXPECT_STREQ(s.get(), "aXaXaX");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("bcd", "XYZ!");
+ EXPECT_STREQ(s.get(), "aXYZ!aXYZ!aXYZ!");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("bcd", "XY");
+ EXPECT_STREQ(s.get(), "aXYaXYaXY");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("bcd", "XYZABC");
+ EXPECT_STREQ(s.get(), "aXYZABCaXYZABCaXYZABC");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("bcd", "XYZ");
+ EXPECT_STREQ(s.get(), "aXYZaXYZaXYZ");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("bcd", "XYZ!");
+ EXPECT_STREQ(s.get(), "aXYZ!aXYZ!aXYZ!");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("ab", "X");
+ EXPECT_STREQ(s.get(), "XcdXcdXcd");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("ab", "XYZABC");
+ EXPECT_STREQ(s.get(), "XYZABCcdXYZABCcdXYZABCcd");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("ab", "XY");
+ EXPECT_STREQ(s.get(), "XYcdXYcdXYcd");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("ab", "XYZ!");
+ EXPECT_STREQ(s.get(), "XYZ!cdXYZ!cdXYZ!cd");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("notfound", "X");
+ EXPECT_STREQ(s.get(), "abcdabcdabcd");
+
+ s.Assign("abcdabcdabcd");
+ s.ReplaceSubstring("notfound", "longlongstring");
+ EXPECT_STREQ(s.get(), "abcdabcdabcd");
+}
+
+TEST(Strings, strip_ws)
+{
+ const char text[] = " a $ ";
+ nsCString s(text);
+ s.StripWhitespace();
+ EXPECT_STREQ(s.get(), "a$");
+}
+
+TEST(Strings, equals_ic)
+{
+ nsCString s;
+ EXPECT_FALSE(s.LowerCaseEqualsLiteral("view-source"));
+}
+
+TEST(Strings, fixed_string)
+{
+ char buf[256] = "hello world";
+
+ nsFixedCString s(buf, sizeof(buf));
+
+ EXPECT_EQ(s.Length(), strlen(buf));
+
+ EXPECT_STREQ(s.get(), buf);
+
+ s.Assign("foopy doopy doo");
+ EXPECT_EQ(s.get(), buf);
+}
+
+TEST(Strings, concat)
+{
+ nsCString bar("bar");
+ const nsACString& barRef = bar;
+
+ const nsPromiseFlatCString& result =
+ PromiseFlatCString(NS_LITERAL_CSTRING("foo") +
+ NS_LITERAL_CSTRING(",") +
+ barRef);
+ EXPECT_STREQ(result.get(), "foo,bar");
+}
+
+TEST(Strings, concat_2)
+{
+ nsCString fieldTextStr("xyz");
+ nsCString text("text");
+ const nsACString& aText = text;
+
+ nsAutoCString result( fieldTextStr + aText );
+
+ EXPECT_STREQ(result.get(), "xyztext");
+}
+
+TEST(Strings, concat_3)
+{
+ nsCString result;
+ nsCString ab("ab"), c("c");
+
+ result = ab + result + c;
+ EXPECT_STREQ(result.get(), "abc");
+}
+
+TEST(Strings, xpidl_string)
+{
+ nsXPIDLCString a, b;
+ a = b;
+ EXPECT_TRUE(a == b);
+
+ a.Adopt(0);
+ EXPECT_TRUE(a == b);
+
+ a.Append("foopy");
+ a.Assign(b);
+ EXPECT_TRUE(a == b);
+
+ a.Insert("", 0);
+ a.Assign(b);
+ EXPECT_TRUE(a == b);
+
+ const char text[] = "hello world";
+ *getter_Copies(a) = NS_strdup(text);
+ EXPECT_STREQ(a, text);
+
+ b = a;
+ EXPECT_STREQ(a, b);
+
+ a.Adopt(0);
+ nsACString::const_iterator begin, end;
+ a.BeginReading(begin);
+ a.EndReading(end);
+ char *r = ToNewCString(Substring(begin, end));
+ EXPECT_STREQ(r, "");
+ free(r);
+
+ a.Adopt(0);
+ EXPECT_TRUE(a.IsVoid());
+
+ int32_t index = a.FindCharInSet("xyz");
+ EXPECT_EQ(index, kNotFound);
+}
+
+TEST(Strings, empty_assign)
+{
+ nsCString a;
+ a.AssignLiteral("");
+
+ a.AppendLiteral("");
+
+ nsCString b;
+ b.SetCapacity(0);
+}
+
+TEST(Strings, set_length)
+{
+ const char kText[] = "Default Plugin";
+ nsCString buf;
+ buf.SetCapacity(sizeof(kText)-1);
+ buf.Assign(kText);
+ buf.SetLength(sizeof(kText)-1);
+ EXPECT_STREQ(buf.get(), kText);
+}
+
+TEST(Strings, substring)
+{
+ nsCString super("hello world"), sub("hello");
+
+ // this tests that |super| starts with |sub|,
+
+ EXPECT_TRUE(sub.Equals(StringHead(super, sub.Length())));
+
+ // and verifies that |sub| does not start with |super|.
+
+ EXPECT_FALSE(super.Equals(StringHead(sub, super.Length())));
+}
+
+#define test_append_expect(str, int, suffix, expect) \
+ str.Truncate(); \
+ str.AppendInt(suffix = int); \
+ EXPECT_TRUE(str.EqualsLiteral(expect));
+
+#define test_appends_expect(int, suffix, expect) \
+ test_append_expect(str, int, suffix, expect) \
+ test_append_expect(cstr, int, suffix, expect)
+
+#define test_appendbase(str, prefix, int, suffix, base) \
+ str.Truncate(); \
+ str.AppendInt(suffix = prefix ## int ## suffix, base); \
+ EXPECT_TRUE(str.EqualsLiteral(#int));
+
+#define test_appendbases(prefix, int, suffix, base) \
+ test_appendbase(str, prefix, int, suffix, base) \
+ test_appendbase(cstr, prefix, int, suffix, base)
+
+TEST(Strings, appendint)
+{
+ nsString str;
+ nsCString cstr;
+ int32_t L;
+ uint32_t UL;
+ int64_t LL;
+ uint64_t ULL;
+ test_appends_expect(INT32_MAX, L, "2147483647")
+ test_appends_expect(INT32_MIN, L, "-2147483648")
+ test_appends_expect(UINT32_MAX, UL, "4294967295")
+ test_appends_expect(INT64_MAX, LL, "9223372036854775807")
+ test_appends_expect(INT64_MIN, LL, "-9223372036854775808")
+ test_appends_expect(UINT64_MAX, ULL, "18446744073709551615")
+ test_appendbases(0, 17777777777, L, 8)
+ test_appendbases(0, 20000000000, L, 8)
+ test_appendbases(0, 37777777777, UL, 8)
+ test_appendbases(0, 777777777777777777777, LL, 8)
+ test_appendbases(0, 1000000000000000000000, LL, 8)
+ test_appendbases(0, 1777777777777777777777, ULL, 8)
+ test_appendbases(0x, 7fffffff, L, 16)
+ test_appendbases(0x, 80000000, L, 16)
+ test_appendbases(0x, ffffffff, UL, 16)
+ test_appendbases(0x, 7fffffffffffffff, LL, 16)
+ test_appendbases(0x, 8000000000000000, LL, 16)
+ test_appendbases(0x, ffffffffffffffff, ULL, 16)
+}
+
+TEST(Strings, appendint64)
+{
+ nsCString str;
+
+ int64_t max = INT64_MAX;
+ static const char max_expected[] = "9223372036854775807";
+ int64_t min = INT64_MIN;
+ static const char min_expected[] = "-9223372036854775808";
+ static const char min_expected_oct[] = "1000000000000000000000";
+ int64_t maxint_plus1 = 1LL << 32;
+ static const char maxint_plus1_expected[] = "4294967296";
+ static const char maxint_plus1_expected_x[] = "100000000";
+
+ str.AppendInt(max);
+
+ EXPECT_TRUE(str.Equals(max_expected));
+
+ str.Truncate();
+ str.AppendInt(min);
+ EXPECT_TRUE(str.Equals(min_expected));
+ str.Truncate();
+ str.AppendInt(min, 8);
+ EXPECT_TRUE(str.Equals(min_expected_oct));
+
+
+ str.Truncate();
+ str.AppendInt(maxint_plus1);
+ EXPECT_TRUE(str.Equals(maxint_plus1_expected));
+ str.Truncate();
+ str.AppendInt(maxint_plus1, 16);
+ EXPECT_TRUE(str.Equals(maxint_plus1_expected_x));
+}
+
+TEST(Strings, appendfloat)
+{
+ nsCString str;
+ double bigdouble = 11223344556.66;
+ static const char double_expected[] = "11223344556.66";
+ static const char float_expected[] = "0.01";
+
+ // AppendFloat is used to append doubles, therefore the precision must be
+ // large enough (see bug 327719)
+ str.AppendFloat( bigdouble );
+ EXPECT_TRUE(str.Equals(double_expected));
+
+ str.Truncate();
+ // AppendFloat is used to append floats (bug 327719 #27)
+ str.AppendFloat( 0.1f * 0.1f );
+ EXPECT_TRUE(str.Equals(float_expected));
+}
+
+TEST(Strings, findcharinset)
+{
+ nsCString buf("hello, how are you?");
+
+ int32_t index = buf.FindCharInSet(",?", 5);
+ EXPECT_EQ(index, 5);
+
+ index = buf.FindCharInSet("helo", 0);
+ EXPECT_EQ(index, 0);
+
+ index = buf.FindCharInSet("z?", 6);
+ EXPECT_EQ(index, (int32_t) buf.Length() - 1);
+}
+
+TEST(Strings, rfindcharinset)
+{
+ nsCString buf("hello, how are you?");
+
+ int32_t index = buf.RFindCharInSet(",?", 5);
+ EXPECT_EQ(index, 5);
+
+ index = buf.RFindCharInSet("helo", 0);
+ EXPECT_EQ(index, 0);
+
+ index = buf.RFindCharInSet("z?", 6);
+ EXPECT_EQ(index, kNotFound);
+
+ index = buf.RFindCharInSet("l", 5);
+ EXPECT_EQ(index, 3);
+
+ buf.Assign("abcdefghijkabc");
+
+ index = buf.RFindCharInSet("ab");
+ EXPECT_EQ(index, 12);
+
+ index = buf.RFindCharInSet("ab", 11);
+ EXPECT_EQ(index, 11);
+
+ index = buf.RFindCharInSet("ab", 10);
+ EXPECT_EQ(index, 1);
+
+ index = buf.RFindCharInSet("ab", 0);
+ EXPECT_EQ(index, 0);
+
+ index = buf.RFindCharInSet("cd", 1);
+ EXPECT_EQ(index, kNotFound);
+
+ index = buf.RFindCharInSet("h");
+ EXPECT_EQ(index, 7);
+}
+
+TEST(Strings, stringbuffer)
+{
+ const char kData[] = "hello world";
+
+ RefPtr<nsStringBuffer> buf;
+
+ buf = nsStringBuffer::Alloc(sizeof(kData));
+ EXPECT_TRUE(!!buf);
+
+ buf = nsStringBuffer::Alloc(sizeof(kData));
+ EXPECT_TRUE(!!buf);
+ char *data = (char *) buf->Data();
+ memcpy(data, kData, sizeof(kData));
+
+ nsCString str;
+ buf->ToString(sizeof(kData)-1, str);
+
+ nsStringBuffer *buf2;
+ buf2 = nsStringBuffer::FromString(str);
+
+ EXPECT_EQ(buf, buf2);
+}
+
+TEST(Strings, voided)
+{
+ const char kData[] = "hello world";
+
+ nsXPIDLCString str;
+ EXPECT_FALSE(str);
+ EXPECT_TRUE(str.IsVoid());
+ EXPECT_TRUE(str.IsEmpty());
+
+ str.Assign(kData);
+ EXPECT_STREQ(str, kData);
+
+ str.SetIsVoid(true);
+ EXPECT_FALSE(str);
+ EXPECT_TRUE(str.IsVoid());
+ EXPECT_TRUE(str.IsEmpty());
+
+ str.SetIsVoid(false);
+ EXPECT_STREQ(str, "");
+}
+
+TEST(Strings, voided_autostr)
+{
+ const char kData[] = "hello world";
+
+ nsAutoCString str;
+ EXPECT_FALSE(str.IsVoid());
+ EXPECT_TRUE(str.IsEmpty());
+
+ str.Assign(kData);
+ EXPECT_STREQ(str.get(), kData);
+
+ str.SetIsVoid(true);
+ EXPECT_TRUE(str.IsVoid());
+ EXPECT_TRUE(str.IsEmpty());
+
+ str.Assign(kData);
+ EXPECT_FALSE(str.IsVoid());
+ EXPECT_FALSE(str.IsEmpty());
+ EXPECT_STREQ(str.get(), kData);
+}
+
+TEST(Strings, voided_assignment)
+{
+ nsCString a, b;
+ b.SetIsVoid(true);
+ a = b;
+ EXPECT_TRUE(a.IsVoid());
+ EXPECT_EQ(a.get(), b.get());
+}
+
+TEST(Strings, empty_assignment)
+{
+ nsCString a, b;
+ a = b;
+ EXPECT_EQ(a.get(), b.get());
+}
+
+struct ToIntegerTest
+{
+ const char *str;
+ uint32_t radix;
+ int32_t result;
+ nsresult rv;
+};
+
+static const ToIntegerTest kToIntegerTests[] = {
+ { "123", 10, 123, NS_OK },
+ { "7b", 16, 123, NS_OK },
+ { "90194313659", 10, 0, NS_ERROR_ILLEGAL_VALUE },
+ { nullptr, 0, 0, NS_OK }
+};
+
+TEST(Strings, string_tointeger)
+{
+ nsresult rv;
+ for (const ToIntegerTest* t = kToIntegerTests; t->str; ++t) {
+ int32_t result = nsAutoCString(t->str).ToInteger(&rv, t->radix);
+ EXPECT_EQ(rv, t->rv);
+ EXPECT_EQ(result, t->result);
+ result = nsAutoCString(t->str).ToInteger(&rv, t->radix);
+ EXPECT_EQ(rv, t->rv);
+ EXPECT_EQ(result, t->result);
+ }
+}
+
+static void test_parse_string_helper(const char* str, char separator, int len,
+ const char* s1, const char* s2)
+{
+ nsCString data(str);
+ nsTArray<nsCString> results;
+ EXPECT_TRUE(ParseString(data, separator, results));
+ EXPECT_EQ(int(results.Length()), len);
+ const char* strings[] = { s1, s2 };
+ for (int i = 0; i < len; ++i) {
+ EXPECT_TRUE(results[i].Equals(strings[i]));
+ }
+}
+
+static void test_parse_string_helper0(const char* str, char separator)
+{
+ test_parse_string_helper(str, separator, 0, nullptr, nullptr);
+}
+
+static void test_parse_string_helper1(const char* str, char separator, const char* s1)
+{
+ test_parse_string_helper(str, separator, 1, s1, nullptr);
+}
+
+static void test_parse_string_helper2(const char* str, char separator, const char* s1, const char* s2)
+{
+ test_parse_string_helper(str, separator, 2, s1, s2);
+}
+
+TEST(String, parse_string)
+{
+ test_parse_string_helper1("foo, bar", '_', "foo, bar");
+ test_parse_string_helper2("foo, bar", ',', "foo", " bar");
+ test_parse_string_helper2("foo, bar ", ' ', "foo,", "bar");
+ test_parse_string_helper2("foo,bar", 'o', "f", ",bar");
+ test_parse_string_helper0("", '_');
+ test_parse_string_helper0(" ", ' ');
+ test_parse_string_helper1(" foo", ' ', "foo");
+ test_parse_string_helper1(" foo", ' ', "foo");
+}
+
+static void test_strip_chars_helper(const char16_t* str, const char16_t* strip, const nsAString& result, uint32_t offset=0)
+{
+ nsAutoString tmp(str);
+ nsAString& data = tmp;
+ data.StripChars(strip, offset);
+ EXPECT_TRUE(data.Equals(result));
+}
+
+TEST(String, strip_chars)
+{
+ test_strip_chars_helper(u"foo \r \nbar",
+ u" \n\r",
+ NS_LITERAL_STRING("foobar"));
+ test_strip_chars_helper(u"\r\nfoo\r\n",
+ u" \n\r",
+ NS_LITERAL_STRING("foo"));
+ test_strip_chars_helper(u"foo",
+ u" \n\r",
+ NS_LITERAL_STRING("foo"));
+ test_strip_chars_helper(u"foo",
+ u"fo",
+ NS_LITERAL_STRING(""));
+ test_strip_chars_helper(u"foo",
+ u"foo",
+ NS_LITERAL_STRING(""));
+ test_strip_chars_helper(u" foo",
+ u" ",
+ NS_LITERAL_STRING(" foo"), 1);
+}
+
+TEST(Strings, huge_capacity)
+{
+ nsString a, b, c, d, e, f, g, h, i, j, k, l, m, n;
+ nsCString n1;
+
+ // Ignore the result if the address space is less than 64-bit because
+ // some of the allocations above will exhaust the address space.
+ if (sizeof(void*) >= 8) {
+ EXPECT_TRUE(a.SetCapacity(1, fallible));
+ EXPECT_FALSE(a.SetCapacity(nsString::size_type(-1)/2, fallible));
+ EXPECT_TRUE(a.SetCapacity(0, fallible)); // free the allocated memory
+
+ EXPECT_TRUE(b.SetCapacity(1, fallible));
+ EXPECT_FALSE(b.SetCapacity(nsString::size_type(-1)/2 - 1, fallible));
+ EXPECT_TRUE(b.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(c.SetCapacity(1, fallible));
+ EXPECT_FALSE(c.SetCapacity(nsString::size_type(-1)/2, fallible));
+ EXPECT_TRUE(c.SetCapacity(0, fallible));
+
+ EXPECT_FALSE(d.SetCapacity(nsString::size_type(-1)/2 - 1, fallible));
+ EXPECT_FALSE(d.SetCapacity(nsString::size_type(-1)/2, fallible));
+ EXPECT_TRUE(d.SetCapacity(0, fallible));
+
+ EXPECT_FALSE(e.SetCapacity(nsString::size_type(-1)/4, fallible));
+ EXPECT_FALSE(e.SetCapacity(nsString::size_type(-1)/4 + 1, fallible));
+ EXPECT_TRUE(e.SetCapacity(0, fallible));
+
+ EXPECT_FALSE(f.SetCapacity(nsString::size_type(-1)/2, fallible));
+ EXPECT_TRUE(f.SetCapacity(0, fallible));
+
+ EXPECT_FALSE(g.SetCapacity(nsString::size_type(-1)/4 + 1000, fallible));
+ EXPECT_FALSE(g.SetCapacity(nsString::size_type(-1)/4 + 1001, fallible));
+ EXPECT_TRUE(g.SetCapacity(0, fallible));
+
+ EXPECT_FALSE(h.SetCapacity(nsString::size_type(-1)/4+1, fallible));
+ EXPECT_FALSE(h.SetCapacity(nsString::size_type(-1)/2, fallible));
+ EXPECT_TRUE(h.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(i.SetCapacity(1, fallible));
+ EXPECT_TRUE(i.SetCapacity(nsString::size_type(-1)/4 - 1000, fallible));
+ EXPECT_FALSE(i.SetCapacity(nsString::size_type(-1)/4 + 1, fallible));
+ EXPECT_TRUE(i.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(j.SetCapacity(nsString::size_type(-1)/4 - 1000, fallible));
+ EXPECT_FALSE(j.SetCapacity(nsString::size_type(-1)/4 + 1, fallible));
+ EXPECT_TRUE(j.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(k.SetCapacity(nsString::size_type(-1)/8 - 1000, fallible));
+ EXPECT_TRUE(k.SetCapacity(nsString::size_type(-1)/4 - 1001, fallible));
+ EXPECT_TRUE(k.SetCapacity(nsString::size_type(-1)/4 - 998, fallible));
+ EXPECT_FALSE(k.SetCapacity(nsString::size_type(-1)/4 + 1, fallible));
+ EXPECT_TRUE(k.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(l.SetCapacity(nsString::size_type(-1)/8, fallible));
+ EXPECT_TRUE(l.SetCapacity(nsString::size_type(-1)/8 + 1, fallible));
+ EXPECT_TRUE(l.SetCapacity(nsString::size_type(-1)/8 + 2, fallible));
+ EXPECT_TRUE(l.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(m.SetCapacity(nsString::size_type(-1)/8 + 1000, fallible));
+ EXPECT_TRUE(m.SetCapacity(nsString::size_type(-1)/8 + 1001, fallible));
+ EXPECT_TRUE(m.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(n.SetCapacity(nsString::size_type(-1)/8+1, fallible));
+ EXPECT_FALSE(n.SetCapacity(nsString::size_type(-1)/4, fallible));
+ EXPECT_TRUE(n.SetCapacity(0, fallible));
+
+ EXPECT_TRUE(n.SetCapacity(0, fallible));
+ EXPECT_TRUE(n.SetCapacity((nsString::size_type(-1)/2 - sizeof(nsStringBuffer)) / 2 - 2, fallible));
+ EXPECT_TRUE(n.SetCapacity(0, fallible));
+ EXPECT_FALSE(n.SetCapacity((nsString::size_type(-1)/2 - sizeof(nsStringBuffer)) / 2 - 1, fallible));
+ EXPECT_TRUE(n.SetCapacity(0, fallible));
+ EXPECT_TRUE(n1.SetCapacity(0, fallible));
+ EXPECT_TRUE(n1.SetCapacity((nsCString::size_type(-1)/2 - sizeof(nsStringBuffer)) / 1 - 2, fallible));
+ EXPECT_TRUE(n1.SetCapacity(0, fallible));
+ EXPECT_FALSE(n1.SetCapacity((nsCString::size_type(-1)/2 - sizeof(nsStringBuffer)) / 1 - 1, fallible));
+ EXPECT_TRUE(n1.SetCapacity(0, fallible));
+ }
+}
+
+static void test_tofloat_helper(const nsString& aStr, float aExpected, bool aSuccess)
+{
+ nsresult result;
+ EXPECT_EQ(aStr.ToFloat(&result), aExpected);
+ if (aSuccess) {
+ EXPECT_EQ(result, NS_OK);
+ } else {
+ EXPECT_NE(result, NS_OK);
+ }
+}
+
+TEST(Strings, tofloat)
+{
+ test_tofloat_helper(NS_LITERAL_STRING("42"), 42.f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("42.0"), 42.f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("-42"), -42.f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("+42"), 42, true);
+ test_tofloat_helper(NS_LITERAL_STRING("13.37"), 13.37f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("1.23456789"), 1.23456789f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("1.98765432123456"), 1.98765432123456f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("0"), 0.f, true);
+ test_tofloat_helper(NS_LITERAL_STRING("1.e5"), 100000, true);
+ test_tofloat_helper(NS_LITERAL_STRING(""), 0.f, false);
+ test_tofloat_helper(NS_LITERAL_STRING("42foo"), 42.f, false);
+ test_tofloat_helper(NS_LITERAL_STRING("foo"), 0.f, false);
+}
+
+static void test_todouble_helper(const nsString& aStr, double aExpected, bool aSuccess)
+{
+ nsresult result;
+ EXPECT_EQ(aStr.ToDouble(&result), aExpected);
+ if (aSuccess) {
+ EXPECT_EQ(result, NS_OK);
+ } else {
+ EXPECT_NE(result, NS_OK);
+ }
+}
+
+TEST(Strings, todouble)
+{
+ test_todouble_helper(NS_LITERAL_STRING("42"), 42, true);
+ test_todouble_helper(NS_LITERAL_STRING("42.0"), 42, true);
+ test_todouble_helper(NS_LITERAL_STRING("-42"), -42, true);
+ test_todouble_helper(NS_LITERAL_STRING("+42"), 42, true);
+ test_todouble_helper(NS_LITERAL_STRING("13.37"), 13.37, true);
+ test_todouble_helper(NS_LITERAL_STRING("1.23456789"), 1.23456789, true);
+ test_todouble_helper(NS_LITERAL_STRING("1.98765432123456"), 1.98765432123456, true);
+ test_todouble_helper(NS_LITERAL_STRING("123456789.98765432123456"), 123456789.98765432123456, true);
+ test_todouble_helper(NS_LITERAL_STRING("0"), 0, true);
+ test_todouble_helper(NS_LITERAL_STRING("1.e5"), 100000, true);
+ test_todouble_helper(NS_LITERAL_STRING(""), 0, false);
+ test_todouble_helper(NS_LITERAL_STRING("42foo"), 42, false);
+ test_todouble_helper(NS_LITERAL_STRING("foo"), 0, false);
+}
+
+} // namespace TestStrings
diff --git a/xpcom/tests/gtest/TestSynchronization.cpp b/xpcom/tests/gtest/TestSynchronization.cpp
new file mode 100644
index 000000000..7ec55545a
--- /dev/null
+++ b/xpcom/tests/gtest/TestSynchronization.cpp
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/CondVar.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Mutex.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+static PRThread*
+spawn(void (*run)(void*), void* arg)
+{
+ return PR_CreateThread(PR_SYSTEM_THREAD,
+ run,
+ arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+}
+
+//-----------------------------------------------------------------------------
+// Sanity check: tests that can be done on a single thread
+//
+TEST(Synchronization, Sanity)
+{
+ Mutex lock("sanity::lock");
+ lock.Lock();
+ lock.AssertCurrentThreadOwns();
+ lock.Unlock();
+
+ {
+ MutexAutoLock autolock(lock);
+ lock.AssertCurrentThreadOwns();
+ }
+
+ lock.Lock();
+ lock.AssertCurrentThreadOwns();
+ {
+ MutexAutoUnlock autounlock(lock);
+ }
+ lock.AssertCurrentThreadOwns();
+ lock.Unlock();
+
+ ReentrantMonitor mon("sanity::monitor");
+ mon.Enter();
+ mon.AssertCurrentThreadIn();
+ mon.Enter();
+ mon.AssertCurrentThreadIn();
+ mon.Exit();
+ mon.AssertCurrentThreadIn();
+ mon.Exit();
+
+ {
+ ReentrantMonitorAutoEnter automon(mon);
+ mon.AssertCurrentThreadIn();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Mutex contention tests
+//
+static Mutex* gLock1;
+
+static void
+MutexContention_thread(void* /*arg*/)
+{
+ for (int i = 0; i < 100000; ++i) {
+ gLock1->Lock();
+ gLock1->AssertCurrentThreadOwns();
+ gLock1->Unlock();
+ }
+}
+
+TEST(Synchronization, MutexContention)
+{
+ gLock1 = new Mutex("lock1");
+ // PURPOSELY not checking for OOM. YAY!
+
+ PRThread* t1 = spawn(MutexContention_thread, nullptr);
+ PRThread* t2 = spawn(MutexContention_thread, nullptr);
+ PRThread* t3 = spawn(MutexContention_thread, nullptr);
+
+ PR_JoinThread(t1);
+ PR_JoinThread(t2);
+ PR_JoinThread(t3);
+
+ delete gLock1;
+}
+
+//-----------------------------------------------------------------------------
+// Monitor tests
+//
+static Monitor* gMon1;
+
+static void
+MonitorContention_thread(void* /*arg*/)
+{
+ for (int i = 0; i < 100000; ++i) {
+ gMon1->Lock();
+ gMon1->AssertCurrentThreadOwns();
+ gMon1->Unlock();
+ }
+}
+
+TEST(Synchronization, MonitorContention)
+{
+ gMon1 = new Monitor("mon1");
+
+ PRThread* t1 = spawn(MonitorContention_thread, nullptr);
+ PRThread* t2 = spawn(MonitorContention_thread, nullptr);
+ PRThread* t3 = spawn(MonitorContention_thread, nullptr);
+
+ PR_JoinThread(t1);
+ PR_JoinThread(t2);
+ PR_JoinThread(t3);
+
+ delete gMon1;
+}
+
+
+static ReentrantMonitor* gMon2;
+
+static void
+MonitorContention2_thread(void* /*arg*/)
+{
+ for (int i = 0; i < 100000; ++i) {
+ gMon2->Enter();
+ gMon2->AssertCurrentThreadIn();
+ {
+ gMon2->Enter();
+ gMon2->AssertCurrentThreadIn();
+ gMon2->Exit();
+ }
+ gMon2->AssertCurrentThreadIn();
+ gMon2->Exit();
+ }
+}
+
+TEST(Synchronization, MonitorContention2)
+{
+ gMon2 = new ReentrantMonitor("mon1");
+
+ PRThread* t1 = spawn(MonitorContention2_thread, nullptr);
+ PRThread* t2 = spawn(MonitorContention2_thread, nullptr);
+ PRThread* t3 = spawn(MonitorContention2_thread, nullptr);
+
+ PR_JoinThread(t1);
+ PR_JoinThread(t2);
+ PR_JoinThread(t3);
+
+ delete gMon2;
+}
+
+
+static ReentrantMonitor* gMon3;
+static int32_t gMonFirst;
+
+static void
+MonitorSyncSanity_thread(void* /*arg*/)
+{
+ gMon3->Enter();
+ gMon3->AssertCurrentThreadIn();
+ if (gMonFirst) {
+ gMonFirst = 0;
+ gMon3->Wait();
+ gMon3->Enter();
+ } else {
+ gMon3->Notify();
+ gMon3->Enter();
+ }
+ gMon3->AssertCurrentThreadIn();
+ gMon3->Exit();
+ gMon3->AssertCurrentThreadIn();
+ gMon3->Exit();
+}
+
+TEST(Synchronization, MonitorSyncSanity)
+{
+ gMon3 = new ReentrantMonitor("monitor::syncsanity");
+
+ for (int32_t i = 0; i < 10000; ++i) {
+ gMonFirst = 1;
+ PRThread* ping = spawn(MonitorSyncSanity_thread, nullptr);
+ PRThread* pong = spawn(MonitorSyncSanity_thread, nullptr);
+ PR_JoinThread(ping);
+ PR_JoinThread(pong);
+ }
+
+ delete gMon3;
+}
+
+//-----------------------------------------------------------------------------
+// Condvar tests
+//
+static Mutex* gCvlock1;
+static CondVar* gCv1;
+static int32_t gCvFirst;
+
+static void
+CondVarSanity_thread(void* /*arg*/)
+{
+ gCvlock1->Lock();
+ gCvlock1->AssertCurrentThreadOwns();
+ if (gCvFirst) {
+ gCvFirst = 0;
+ gCv1->Wait();
+ } else {
+ gCv1->Notify();
+ }
+ gCvlock1->AssertCurrentThreadOwns();
+ gCvlock1->Unlock();
+}
+
+TEST(Synchronization, CondVarSanity)
+{
+ gCvlock1 = new Mutex("cvlock1");
+ gCv1 = new CondVar(*gCvlock1, "cvlock1");
+
+ for (int32_t i = 0; i < 10000; ++i) {
+ gCvFirst = 1;
+ PRThread* ping = spawn(CondVarSanity_thread, nullptr);
+ PRThread* pong = spawn(CondVarSanity_thread, nullptr);
+ PR_JoinThread(ping);
+ PR_JoinThread(pong);
+ }
+
+ delete gCv1;
+ delete gCvlock1;
+}
+
+//-----------------------------------------------------------------------------
+// AutoLock tests
+//
+TEST(Synchronization, AutoLock)
+{
+ Mutex l1("autolock");
+ MutexAutoLock autol1(l1);
+
+ l1.AssertCurrentThreadOwns();
+
+ {
+ Mutex l2("autolock2");
+ MutexAutoLock autol2(l2);
+
+ l1.AssertCurrentThreadOwns();
+ l2.AssertCurrentThreadOwns();
+ }
+
+ l1.AssertCurrentThreadOwns();
+}
+
+//-----------------------------------------------------------------------------
+// AutoUnlock tests
+//
+TEST(Synchronization, AutoUnlock)
+{
+ Mutex l1("autounlock");
+ Mutex l2("autounlock2");
+
+ l1.Lock();
+ l1.AssertCurrentThreadOwns();
+
+ {
+ MutexAutoUnlock autol1(l1);
+ {
+ l2.Lock();
+ l2.AssertCurrentThreadOwns();
+
+ MutexAutoUnlock autol2(l2);
+ }
+ l2.AssertCurrentThreadOwns();
+ l2.Unlock();
+ }
+ l1.AssertCurrentThreadOwns();
+
+ l1.Unlock();
+}
+
+//-----------------------------------------------------------------------------
+// AutoMonitor tests
+//
+TEST(Synchronization, AutoMonitor)
+{
+ ReentrantMonitor m1("automonitor");
+ ReentrantMonitor m2("automonitor2");
+
+ m1.Enter();
+ m1.AssertCurrentThreadIn();
+ {
+ ReentrantMonitorAutoEnter autom1(m1);
+ m1.AssertCurrentThreadIn();
+
+ m2.Enter();
+ m2.AssertCurrentThreadIn();
+ {
+ ReentrantMonitorAutoEnter autom2(m2);
+ m1.AssertCurrentThreadIn();
+ m2.AssertCurrentThreadIn();
+ }
+ m2.AssertCurrentThreadIn();
+ m2.Exit();
+
+ m1.AssertCurrentThreadIn();
+ }
+ m1.AssertCurrentThreadIn();
+ m1.Exit();
+}
diff --git a/xpcom/tests/gtest/TestTArray.cpp b/xpcom/tests/gtest/TestTArray.cpp
new file mode 100644
index 000000000..10e33664f
--- /dev/null
+++ b/xpcom/tests/gtest/TestTArray.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTArray.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+namespace TestTArray {
+
+struct Copyable
+{
+ Copyable()
+ : mDestructionCounter(nullptr)
+ {
+ }
+
+ ~Copyable()
+ {
+ if (mDestructionCounter) {
+ (*mDestructionCounter)++;
+ }
+ }
+
+ Copyable(const Copyable&) = default;
+ Copyable& operator=(const Copyable&) = default;
+
+ uint32_t* mDestructionCounter;
+};
+
+struct Movable
+{
+ Movable()
+ : mDestructionCounter(nullptr)
+ {
+ }
+
+ ~Movable()
+ {
+ if (mDestructionCounter) {
+ (*mDestructionCounter)++;
+ }
+ }
+
+ Movable(Movable&& aOther)
+ : mDestructionCounter(aOther.mDestructionCounter)
+ {
+ aOther.mDestructionCounter = nullptr;
+ }
+
+ uint32_t* mDestructionCounter;
+};
+
+} // namespace TestTArray
+
+template<>
+struct nsTArray_CopyChooser<TestTArray::Copyable>
+{
+ typedef nsTArray_CopyWithConstructors<TestTArray::Copyable> Type;
+};
+
+template<>
+struct nsTArray_CopyChooser<TestTArray::Movable>
+{
+ typedef nsTArray_CopyWithConstructors<TestTArray::Movable> Type;
+};
+
+namespace TestTArray {
+
+const nsTArray<int>& DummyArray()
+{
+ static nsTArray<int> sArray;
+ if (sArray.IsEmpty()) {
+ const int data[] = {4, 1, 2, 8};
+ sArray.AppendElements(data, ArrayLength(data));
+ }
+ return sArray;
+}
+
+// This returns an invalid nsTArray with a huge length in order to test that
+// fallible operations actually fail.
+#ifdef DEBUG
+const nsTArray<int>& FakeHugeArray()
+{
+ static nsTArray<int> sArray;
+ if (sArray.IsEmpty()) {
+ sArray.AppendElement();
+ ((nsTArrayHeader*)sArray.DebugGetHeader())->mLength = UINT32_MAX;
+ }
+ return sArray;
+}
+#endif
+
+TEST(TArray, AppendElementsRvalue)
+{
+ nsTArray<int> array;
+
+ nsTArray<int> temp(DummyArray());
+ array.AppendElements(Move(temp));
+ ASSERT_EQ(DummyArray(), array);
+ ASSERT_TRUE(temp.IsEmpty());
+
+ temp = DummyArray();
+ array.AppendElements(Move(temp));
+ nsTArray<int> expected;
+ expected.AppendElements(DummyArray());
+ expected.AppendElements(DummyArray());
+ ASSERT_EQ(expected, array);
+ ASSERT_TRUE(temp.IsEmpty());
+}
+
+TEST(TArray, Assign)
+{
+ nsTArray<int> array;
+ array.Assign(DummyArray());
+ ASSERT_EQ(DummyArray(), array);
+
+ ASSERT_TRUE(array.Assign(DummyArray(), fallible));
+ ASSERT_EQ(DummyArray(), array);
+
+#ifdef DEBUG
+ ASSERT_FALSE(array.Assign(FakeHugeArray(), fallible));
+#endif
+
+ nsTArray<int> array2;
+ array2.Assign(Move(array));
+ ASSERT_TRUE(array.IsEmpty());
+ ASSERT_EQ(DummyArray(), array2);
+}
+
+TEST(TArray, AssignmentOperatorSelfAssignment)
+{
+ nsTArray<int> array;
+ array = DummyArray();
+
+ array = array;
+ ASSERT_EQ(DummyArray(), array);
+ array = Move(array);
+ ASSERT_EQ(DummyArray(), array);
+}
+
+TEST(TArray, CopyOverlappingForwards)
+{
+ nsTArray<Movable> array;
+ const size_t rangeLength = 8;
+ const size_t initialLength = 2 * rangeLength;
+ array.AppendElements(initialLength);
+
+ uint32_t destructionCounters[initialLength];
+ for (uint32_t i = 0; i < initialLength; ++i) {
+ destructionCounters[i] = 0;
+ }
+ for (uint32_t i = 0; i < initialLength; ++i) {
+ array[i].mDestructionCounter = &destructionCounters[i];
+ }
+
+ const size_t removedLength = rangeLength / 2;
+ array.RemoveElementsAt(0, removedLength);
+
+ for (uint32_t i = 0; i < removedLength; ++i) {
+ ASSERT_EQ(destructionCounters[i], 1u);
+ }
+ for (uint32_t i = removedLength; i < initialLength; ++i) {
+ ASSERT_EQ(destructionCounters[i], 0u);
+ }
+}
+
+// The code to copy overlapping regions had a bug in that it wouldn't correctly
+// destroy all over the source elements being copied.
+TEST(TArray, CopyOverlappingBackwards)
+{
+ nsTArray<Copyable> array;
+ const size_t rangeLength = 8;
+ const size_t initialLength = 2 * rangeLength;
+ array.SetCapacity(3 * rangeLength);
+ array.AppendElements(initialLength);
+ // To tickle the bug, we need to copy a source region:
+ //
+ // ..XXXXX..
+ //
+ // such that it overlaps the destination region:
+ //
+ // ....XXXXX
+ //
+ // so we are forced to copy back-to-front to ensure correct behavior.
+ // The easiest way to do that is to call InsertElementsAt, which will force
+ // the desired kind of shift.
+ uint32_t destructionCounters[initialLength];
+ for (uint32_t i = 0; i < initialLength; ++i) {
+ destructionCounters[i] = 0;
+ }
+ for (uint32_t i = 0; i < initialLength; ++i) {
+ array[i].mDestructionCounter = &destructionCounters[i];
+ }
+
+ array.InsertElementsAt(0, rangeLength);
+
+ for (uint32_t i = 0; i < initialLength; ++i) {
+ ASSERT_EQ(destructionCounters[i], 1u);
+ }
+}
+
+} // namespace TestTArray
diff --git a/xpcom/tests/gtest/TestTArray2.cpp b/xpcom/tests/gtest/TestTArray2.cpp
new file mode 100644
index 000000000..421927104
--- /dev/null
+++ b/xpcom/tests/gtest/TestTArray2.cpp
@@ -0,0 +1,1033 @@
+/* -*- 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 "mozilla/ArrayUtils.h"
+#include "mozilla/Unused.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <iostream>
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsXPCOM.h"
+#include "nsIFile.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+namespace TestTArray {
+
+// Define this so we can use test_basic_array in test_comptr_array
+template <class T>
+inline bool operator<(const nsCOMPtr<T>& lhs, const nsCOMPtr<T>& rhs) {
+ return lhs.get() < rhs.get();
+}
+
+//----
+
+template <class ElementType>
+static bool test_basic_array(ElementType *data,
+ size_t dataLen,
+ const ElementType& extra) {
+ nsTArray<ElementType> ary;
+ ary.AppendElements(data, dataLen);
+ if (ary.Length() != dataLen) {
+ return false;
+ }
+ if (!(ary == ary)) {
+ return false;
+ }
+ size_t i;
+ for (i = 0; i < ary.Length(); ++i) {
+ if (ary[i] != data[i])
+ return false;
+ }
+ for (i = 0; i < ary.Length(); ++i) {
+ if (ary.SafeElementAt(i, extra) != data[i])
+ return false;
+ }
+ if (ary.SafeElementAt(ary.Length(), extra) != extra ||
+ ary.SafeElementAt(ary.Length() * 10, extra) != extra)
+ return false;
+ // ensure sort results in ascending order
+ ary.Sort();
+ size_t j = 0, k = ary.IndexOfFirstElementGt(extra);
+ if (k != 0 && ary[k-1] == extra)
+ return false;
+ for (i = 0; i < ary.Length(); ++i) {
+ k = ary.IndexOfFirstElementGt(ary[i]);
+ if (k == 0 || ary[k-1] != ary[i])
+ return false;
+ if (k < j)
+ return false;
+ j = k;
+ }
+ for (i = ary.Length(); --i; ) {
+ if (ary[i] < ary[i - 1])
+ return false;
+ if (ary[i] == ary[i - 1])
+ ary.RemoveElementAt(i);
+ }
+ if (!(ary == ary)) {
+ return false;
+ }
+ for (i = 0; i < ary.Length(); ++i) {
+ if (ary.BinaryIndexOf(ary[i]) != i)
+ return false;
+ }
+ if (ary.BinaryIndexOf(extra) != ary.NoIndex)
+ return false;
+ size_t oldLen = ary.Length();
+ ary.RemoveElement(data[dataLen / 2]);
+ if (ary.Length() != (oldLen - 1))
+ return false;
+ if (!(ary == ary))
+ return false;
+
+ size_t index = ary.Length() / 2;
+ if (!ary.InsertElementAt(index, extra))
+ return false;
+ if (!(ary == ary))
+ return false;
+ if (ary[index] != extra)
+ return false;
+ if (ary.IndexOf(extra) == ary.NoIndex)
+ return false;
+ if (ary.LastIndexOf(extra) == ary.NoIndex)
+ return false;
+ // ensure proper searching
+ if (ary.IndexOf(extra) > ary.LastIndexOf(extra))
+ return false;
+ if (ary.IndexOf(extra, index) != ary.LastIndexOf(extra, index))
+ return false;
+
+ nsTArray<ElementType> copy(ary);
+ if (!(ary == copy))
+ return false;
+ for (i = 0; i < copy.Length(); ++i) {
+ if (ary[i] != copy[i])
+ return false;
+ }
+ if (!ary.AppendElements(copy))
+ return false;
+ size_t cap = ary.Capacity();
+ ary.RemoveElementsAt(copy.Length(), copy.Length());
+ ary.Compact();
+ if (ary.Capacity() == cap)
+ return false;
+
+ ary.Clear();
+ if (ary.IndexOf(extra) != ary.NoIndex)
+ return false;
+ if (ary.LastIndexOf(extra) != ary.NoIndex)
+ return false;
+
+ ary.Clear();
+ if (!ary.IsEmpty() || ary.Elements() == nullptr)
+ return false;
+ if (!(ary == nsTArray<ElementType>()))
+ return false;
+ if (ary == copy)
+ return false;
+ if (ary.SafeElementAt(0, extra) != extra ||
+ ary.SafeElementAt(10, extra) != extra)
+ return false;
+
+ ary = copy;
+ if (!(ary == copy))
+ return false;
+ for (i = 0; i < copy.Length(); ++i) {
+ if (ary[i] != copy[i])
+ return false;
+ }
+
+ if (!ary.InsertElementsAt(0, copy))
+ return false;
+ if (ary == copy)
+ return false;
+ ary.RemoveElementsAt(0, copy.Length());
+ for (i = 0; i < copy.Length(); ++i) {
+ if (ary[i] != copy[i])
+ return false;
+ }
+
+ // These shouldn't crash!
+ nsTArray<ElementType> empty;
+ ary.AppendElements(reinterpret_cast<ElementType *>(0), 0);
+ ary.AppendElements(empty);
+
+ // See bug 324981
+ ary.RemoveElement(extra);
+ ary.RemoveElement(extra);
+
+ return true;
+}
+
+TEST(TArray, test_int_array) {
+ int data[] = {4,6,8,2,4,1,5,7,3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), int(14)));
+}
+
+TEST(TArray, test_int64_array) {
+ int64_t data[] = {4,6,8,2,4,1,5,7,3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), int64_t(14)));
+}
+
+TEST(TArray, test_char_array) {
+ char data[] = {4,6,8,2,4,1,5,7,3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), char(14)));
+}
+
+TEST(TArray, test_uint32_array) {
+ uint32_t data[] = {4,6,8,2,4,1,5,7,3};
+ ASSERT_TRUE(test_basic_array(data, ArrayLength(data), uint32_t(14)));
+}
+
+//----
+
+class Object {
+ public:
+ Object() : mNum(0) {
+ }
+ Object(const char *str, uint32_t num) : mStr(str), mNum(num) {
+ }
+ Object(const Object& other) : mStr(other.mStr), mNum(other.mNum) {
+ }
+ ~Object() {}
+
+ Object& operator=(const Object& other) {
+ mStr = other.mStr;
+ mNum = other.mNum;
+ return *this;
+ }
+
+ bool operator==(const Object& other) const {
+ return mStr == other.mStr && mNum == other.mNum;
+ }
+
+ bool operator<(const Object& other) const {
+ // sort based on mStr only
+ return mStr.Compare(other.mStr.get()) < 0;
+ }
+
+ const char *Str() const { return mStr.get(); }
+ uint32_t Num() const { return mNum; }
+
+ private:
+ nsCString mStr;
+ uint32_t mNum;
+};
+
+TEST(TArray, test_object_array) {
+ nsTArray<Object> objArray;
+ const char kdata[] = "hello world";
+ size_t i;
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ char x[] = {kdata[i],'\0'};
+ ASSERT_TRUE(objArray.AppendElement(Object(x, i)));
+ }
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ ASSERT_EQ(objArray[i].Str()[0], kdata[i]);
+ ASSERT_EQ(objArray[i].Num(), i);
+ }
+ objArray.Sort();
+ const char ksorted[] = "\0 dehllloorw";
+ for (i = 0; i < ArrayLength(kdata)-1; ++i) {
+ ASSERT_EQ(objArray[i].Str()[0], ksorted[i]);
+ }
+}
+
+class Countable {
+ static int sCount;
+
+ public:
+ Countable()
+ {
+ sCount++;
+ }
+
+ Countable(const Countable& aOther)
+ {
+ sCount++;
+ }
+
+ static int Count() { return sCount; }
+};
+
+class Moveable {
+ static int sCount;
+
+ public:
+ Moveable()
+ {
+ sCount++;
+ }
+
+ Moveable(const Moveable& aOther)
+ {
+ sCount++;
+ }
+
+ Moveable(Moveable&& aOther)
+ {
+ // Do not increment sCount
+ }
+
+ static int Count() { return sCount; }
+};
+
+/* static */ int Countable::sCount = 0;
+/* static */ int Moveable::sCount = 0;
+
+static nsTArray<int> returns_by_value() {
+ nsTArray<int> result;
+ return result;
+}
+
+TEST(TArray, test_return_by_value) {
+ nsTArray<int> result = returns_by_value();
+ ASSERT_TRUE(true); // This is just a compilation test.
+}
+
+TEST(TArray, test_move_array) {
+ nsTArray<Countable> countableArray;
+ uint32_t i;
+ for (i = 0; i < 4; ++i) {
+ ASSERT_TRUE(countableArray.AppendElement(Countable()));
+ }
+
+ ASSERT_EQ(Countable::Count(), 8);
+
+ const nsTArray<Countable>& constRefCountableArray = countableArray;
+
+ ASSERT_EQ(Countable::Count(), 8);
+
+ nsTArray<Countable> copyCountableArray(constRefCountableArray);
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ nsTArray<Countable>&& moveRefCountableArray = Move(countableArray);
+ moveRefCountableArray.Length(); // Make compilers happy.
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ nsTArray<Countable> movedCountableArray(Move(countableArray));
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ // Test ctor
+ FallibleTArray<Countable> differentAllocatorCountableArray(Move(copyCountableArray));
+ // operator=
+ copyCountableArray = Move(differentAllocatorCountableArray);
+ differentAllocatorCountableArray = Move(copyCountableArray);
+ // And the other ctor
+ nsTArray<Countable> copyCountableArray2(Move(differentAllocatorCountableArray));
+ // with auto
+ AutoTArray<Countable, 3> autoCountableArray(Move(copyCountableArray2));
+ // operator=
+ copyCountableArray2 = Move(autoCountableArray);
+ // Mix with FallibleTArray
+ FallibleTArray<Countable> differentAllocatorCountableArray2(Move(copyCountableArray2));
+ AutoTArray<Countable, 4> autoCountableArray2(Move(differentAllocatorCountableArray2));
+ differentAllocatorCountableArray2 = Move(autoCountableArray2);
+
+ ASSERT_EQ(Countable::Count(), 12);
+
+ nsTArray<Moveable> moveableArray;
+ for (i = 0; i < 4; ++i) {
+ ASSERT_TRUE(moveableArray.AppendElement(Moveable()));
+ }
+
+ ASSERT_EQ(Moveable::Count(), 4);
+
+ const nsTArray<Moveable>& constRefMoveableArray = moveableArray;
+
+ ASSERT_EQ(Moveable::Count(), 4);
+
+ nsTArray<Moveable> copyMoveableArray(constRefMoveableArray);
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ nsTArray<Moveable>&& moveRefMoveableArray = Move(moveableArray);
+ moveRefMoveableArray.Length(); // Make compilers happy.
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ nsTArray<Moveable> movedMoveableArray(Move(moveableArray));
+
+ ASSERT_EQ(Moveable::Count(), 8);
+
+ // Test ctor
+ FallibleTArray<Moveable> differentAllocatorMoveableArray(Move(copyMoveableArray));
+ // operator=
+ copyMoveableArray = Move(differentAllocatorMoveableArray);
+ differentAllocatorMoveableArray = Move(copyMoveableArray);
+ // And the other ctor
+ nsTArray<Moveable> copyMoveableArray2(Move(differentAllocatorMoveableArray));
+ // with auto
+ AutoTArray<Moveable, 3> autoMoveableArray(Move(copyMoveableArray2));
+ // operator=
+ copyMoveableArray2 = Move(autoMoveableArray);
+ // Mix with FallibleTArray
+ FallibleTArray<Moveable> differentAllocatorMoveableArray2(Move(copyMoveableArray2));
+ AutoTArray<Moveable, 4> autoMoveableArray2(Move(differentAllocatorMoveableArray2));
+ differentAllocatorMoveableArray2 = Move(autoMoveableArray2);
+
+ ASSERT_EQ(Moveable::Count(), 8);
+}
+
+//----
+
+TEST(TArray, test_string_array) {
+ nsTArray<nsCString> strArray;
+ const char kdata[] = "hello world";
+ size_t i;
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ nsCString str;
+ str.Assign(kdata[i]);
+ ASSERT_TRUE(strArray.AppendElement(str));
+ }
+ for (i = 0; i < ArrayLength(kdata); ++i) {
+ ASSERT_EQ(strArray[i].CharAt(0), kdata[i]);
+ }
+
+ const char kextra[] = "foo bar";
+ size_t oldLen = strArray.Length();
+ ASSERT_TRUE(strArray.AppendElement(kextra));
+ strArray.RemoveElement(kextra);
+ ASSERT_EQ(oldLen, strArray.Length());
+
+ ASSERT_EQ(strArray.IndexOf("e"), size_t(1));
+
+ strArray.Sort();
+ const char ksorted[] = "\0 dehllloorw";
+ for (i = ArrayLength(kdata); i--; ) {
+ ASSERT_EQ(strArray[i].CharAt(0), ksorted[i]);
+ if (i > 0 && strArray[i] == strArray[i - 1])
+ strArray.RemoveElementAt(i);
+ }
+ for (i = 0; i < strArray.Length(); ++i) {
+ ASSERT_EQ(strArray.BinaryIndexOf(strArray[i]), i);
+ }
+ auto no_index = strArray.NoIndex; // Fixes gtest compilation error
+ ASSERT_EQ(strArray.BinaryIndexOf(EmptyCString()), no_index);
+
+ nsCString rawArray[MOZ_ARRAY_LENGTH(kdata) - 1];
+ for (i = 0; i < ArrayLength(rawArray); ++i)
+ rawArray[i].Assign(kdata + i); // substrings of kdata
+
+ ASSERT_TRUE(test_basic_array(rawArray, ArrayLength(rawArray),
+ nsCString("foopy")));
+}
+
+//----
+
+typedef nsCOMPtr<nsIFile> FilePointer;
+
+class nsFileNameComparator {
+ public:
+ bool Equals(const FilePointer &a, const char *b) const {
+ nsAutoCString name;
+ a->GetNativeLeafName(name);
+ return name.Equals(b);
+ }
+};
+
+TEST(TArray, test_comptr_array) {
+ FilePointer tmpDir;
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
+ ASSERT_TRUE(tmpDir);
+ const char *kNames[] = {
+ "foo.txt", "bar.html", "baz.gif"
+ };
+ nsTArray<FilePointer> fileArray;
+ size_t i;
+ for (i = 0; i < ArrayLength(kNames); ++i) {
+ FilePointer f;
+ tmpDir->Clone(getter_AddRefs(f));
+ ASSERT_TRUE(f);
+ ASSERT_FALSE(NS_FAILED(f->AppendNative(nsDependentCString(kNames[i]))));
+ fileArray.AppendElement(f);
+ }
+
+ ASSERT_EQ(fileArray.IndexOf(kNames[1], 0, nsFileNameComparator()), size_t(1));
+
+ // It's unclear what 'operator<' means for nsCOMPtr, but whatever...
+ ASSERT_TRUE(test_basic_array(fileArray.Elements(), fileArray.Length(),
+ tmpDir));
+}
+
+//----
+
+class RefcountedObject {
+ public:
+ RefcountedObject() : rc(0) {}
+ void AddRef() {
+ ++rc;
+ }
+ void Release() {
+ if (--rc == 0)
+ delete this;
+ }
+ ~RefcountedObject() {}
+ private:
+ int32_t rc;
+};
+
+TEST(TArray, test_refptr_array) {
+ nsTArray< RefPtr<RefcountedObject> > objArray;
+
+ RefcountedObject *a = new RefcountedObject(); a->AddRef();
+ RefcountedObject *b = new RefcountedObject(); b->AddRef();
+ RefcountedObject *c = new RefcountedObject(); c->AddRef();
+
+ objArray.AppendElement(a);
+ objArray.AppendElement(b);
+ objArray.AppendElement(c);
+
+ ASSERT_EQ(objArray.IndexOf(b), size_t(1));
+
+ a->Release();
+ b->Release();
+ c->Release();
+}
+
+//----
+
+TEST(TArray, test_ptrarray) {
+ nsTArray<uint32_t*> ary;
+ ASSERT_EQ(ary.SafeElementAt(0), nullptr);
+ ASSERT_EQ(ary.SafeElementAt(1000), nullptr);
+
+ uint32_t a = 10;
+ ary.AppendElement(&a);
+ ASSERT_EQ(*ary[0], a);
+ ASSERT_EQ(*ary.SafeElementAt(0), a);
+
+ nsTArray<const uint32_t*> cary;
+ ASSERT_EQ(cary.SafeElementAt(0), nullptr);
+ ASSERT_EQ(cary.SafeElementAt(1000), nullptr);
+
+ const uint32_t b = 14;
+ cary.AppendElement(&a);
+ cary.AppendElement(&b);
+ ASSERT_EQ(*cary[0], a);
+ ASSERT_EQ(*cary[1], b);
+ ASSERT_EQ(*cary.SafeElementAt(0), a);
+ ASSERT_EQ(*cary.SafeElementAt(1), b);
+}
+
+//----
+
+// This test relies too heavily on the existence of DebugGetHeader to be
+// useful in non-debug builds.
+#ifdef DEBUG
+TEST(TArray, test_autoarray) {
+ uint32_t data[] = {4,6,8,2,4,1,5,7,3};
+ AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data)> array;
+
+ void* hdr = array.DebugGetHeader();
+ ASSERT_NE(hdr, nsTArray<uint32_t>().DebugGetHeader());
+ ASSERT_NE(hdr, (AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data)>().DebugGetHeader()));
+
+ array.AppendElement(1u);
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+
+ array.RemoveElement(1u);
+ array.AppendElements(data, ArrayLength(data));
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+
+ array.AppendElement(2u);
+ ASSERT_NE(hdr, array.DebugGetHeader());
+
+ array.Clear();
+ array.Compact();
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+ array.AppendElements(data, ArrayLength(data));
+ ASSERT_EQ(hdr, array.DebugGetHeader());
+
+ nsTArray<uint32_t> array2;
+ void* emptyHdr = array2.DebugGetHeader();
+ array.SwapElements(array2);
+ ASSERT_NE(emptyHdr, array.DebugGetHeader());
+ ASSERT_NE(hdr, array2.DebugGetHeader());
+ size_t i;
+ for (i = 0; i < ArrayLength(data); ++i) {
+ ASSERT_EQ(array2[i], data[i]);
+ }
+ ASSERT_TRUE(array.IsEmpty());
+
+ array.Compact();
+ array.AppendElements(data, ArrayLength(data));
+ uint32_t data3[] = {5, 7, 11};
+ AutoTArray<uint32_t, MOZ_ARRAY_LENGTH(data3)> array3;
+ array3.AppendElements(data3, ArrayLength(data3));
+ array.SwapElements(array3);
+ for (i = 0; i < ArrayLength(data); ++i) {
+ ASSERT_EQ(array3[i], data[i]);
+ }
+ for (i = 0; i < ArrayLength(data3); ++i) {
+ ASSERT_EQ(array[i], data3[i]);
+ }
+}
+#endif
+
+//----
+
+// IndexOf used to potentially scan beyond the end of the array. Test for
+// this incorrect behavior by adding a value (5), removing it, then seeing
+// if IndexOf finds it.
+TEST(TArray, test_indexof) {
+ nsTArray<int> array;
+ array.AppendElement(0);
+ // add and remove the 5
+ array.AppendElement(5);
+ array.RemoveElementAt(1);
+ // we should not find the 5!
+ auto no_index = array.NoIndex; // Fixes gtest compilation error.
+ ASSERT_EQ(array.IndexOf(5, 1), no_index);
+}
+
+//----
+
+template <class Array>
+static bool is_heap(const Array& ary, size_t len) {
+ size_t index = 1;
+ while (index < len) {
+ if (ary[index] > ary[(index - 1) >> 1])
+ return false;
+ index++;
+ }
+ return true;
+}
+
+//----
+
+// An array |arr| is using its auto buffer if |&arr < arr.Elements()| and
+// |arr.Elements() - &arr| is small.
+
+#define IS_USING_AUTO(arr) \
+ ((uintptr_t) &(arr) < (uintptr_t) arr.Elements() && \
+ ((ptrdiff_t)arr.Elements() - (ptrdiff_t)&arr) <= 16)
+
+#define CHECK_IS_USING_AUTO(arr) \
+ do { \
+ ASSERT_TRUE(IS_USING_AUTO(arr)); \
+ } while(0)
+
+#define CHECK_NOT_USING_AUTO(arr) \
+ do { \
+ ASSERT_FALSE(IS_USING_AUTO(arr)); \
+ } while(0)
+
+#define CHECK_USES_SHARED_EMPTY_HDR(arr) \
+ do { \
+ nsTArray<int> _empty; \
+ ASSERT_EQ(_empty.Elements(), arr.Elements()); \
+ } while(0)
+
+#define CHECK_EQ_INT(actual, expected) \
+ do { \
+ ASSERT_EQ((actual), (expected)); \
+ } while(0)
+
+#define CHECK_ARRAY(arr, data) \
+ do { \
+ CHECK_EQ_INT((arr).Length(), (size_t)ArrayLength(data)); \
+ for (size_t _i = 0; _i < ArrayLength(data); _i++) { \
+ CHECK_EQ_INT((arr)[_i], (data)[_i]); \
+ } \
+ } while(0)
+
+TEST(TArray, test_swap) {
+ // Test nsTArray::SwapElements. Unfortunately there are many cases.
+ int data1[] = {8, 6, 7, 5};
+ int data2[] = {3, 0, 9};
+
+ // Swap two auto arrays.
+ {
+ AutoTArray<int, 8> a;
+ AutoTArray<int, 6> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+ b.AppendElements(data2, ArrayLength(data2));
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+ CHECK_ARRAY(a, data2);
+ CHECK_ARRAY(b, data1);
+ }
+
+ // Swap two auto arrays -- one whose data lives on the heap, the other whose
+ // data lives on the stack -- which each fits into the other's auto storage.
+ {
+ AutoTArray<int, 3> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+ a.RemoveElementAt(3);
+ b.AppendElements(data2, ArrayLength(data2));
+
+ // Here and elsewhere, we assert that if we start with an auto array
+ // capable of storing N elements, we store N+1 elements into the array, and
+ // then we remove one element, that array is still not using its auto
+ // buffer.
+ //
+ // This isn't at all required by the TArray API. It would be fine if, when
+ // we shrink back to N elements, the TArray frees its heap storage and goes
+ // back to using its stack storage. But we assert here as a check that the
+ // test does what we expect. If the TArray implementation changes, just
+ // change the failing assertions.
+ CHECK_NOT_USING_AUTO(a);
+
+ // This check had better not change, though.
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(b);
+ CHECK_ARRAY(a, data2);
+ int expectedB[] = {8, 6, 7};
+ CHECK_ARRAY(b, expectedB);
+ }
+
+ // Swap two auto arrays which are using heap storage such that one fits into
+ // the other's auto storage, but the other needs to stay on the heap.
+ {
+ AutoTArray<int, 3> a;
+ AutoTArray<int, 2> b;
+ a.AppendElements(data1, ArrayLength(data1));
+ a.RemoveElementAt(3);
+
+ b.AppendElements(data2, ArrayLength(data2));
+ b.RemoveElementAt(2);
+
+ CHECK_NOT_USING_AUTO(a);
+ CHECK_NOT_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_NOT_USING_AUTO(b);
+
+ int expected1[] = {3, 0};
+ int expected2[] = {8, 6, 7};
+
+ CHECK_ARRAY(a, expected1);
+ CHECK_ARRAY(b, expected2);
+ }
+
+ // Swap two arrays, neither of which fits into the other's auto-storage.
+ {
+ AutoTArray<int, 1> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+ b.AppendElements(data2, ArrayLength(data2));
+
+ a.SwapElements(b);
+
+ CHECK_ARRAY(a, data2);
+ CHECK_ARRAY(b, data1);
+ }
+
+ // Swap an empty nsTArray with a non-empty AutoTArray.
+ {
+ nsTArray<int> a;
+ AutoTArray<int, 3> b;
+
+ b.AppendElements(data2, ArrayLength(data2));
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_ARRAY(a, data2);
+ CHECK_EQ_INT(b.Length(), size_t(0));
+ CHECK_IS_USING_AUTO(b);
+ }
+
+ // Swap two big auto arrays.
+ {
+ const unsigned size = 8192;
+ AutoTArray<unsigned, size> a;
+ AutoTArray<unsigned, size> b;
+
+ for (unsigned i = 0; i < size; i++) {
+ a.AppendElement(i);
+ b.AppendElement(i + 1);
+ }
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+
+ CHECK_EQ_INT(a.Length(), size_t(size));
+ CHECK_EQ_INT(b.Length(), size_t(size));
+
+ for (unsigned i = 0; i < size; i++) {
+ CHECK_EQ_INT(a[i], i + 1);
+ CHECK_EQ_INT(b[i], i);
+ }
+ }
+
+ // Swap two arrays and make sure that their capacities don't increase
+ // unnecessarily.
+ {
+ nsTArray<int> a;
+ nsTArray<int> b;
+ b.AppendElements(data2, ArrayLength(data2));
+
+ CHECK_EQ_INT(a.Capacity(), size_t(0));
+ size_t bCapacity = b.Capacity();
+
+ a.SwapElements(b);
+
+ // Make sure that we didn't increase the capacity of either array.
+ CHECK_ARRAY(a, data2);
+ CHECK_EQ_INT(b.Length(), size_t(0));
+ CHECK_EQ_INT(b.Capacity(), size_t(0));
+ CHECK_EQ_INT(a.Capacity(), bCapacity);
+ }
+
+ // Swap an auto array with a TArray, then clear the auto array and make sure
+ // it doesn't forget the fact that it has an auto buffer.
+ {
+ nsTArray<int> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ a.SwapElements(b);
+
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ CHECK_ARRAY(b, data1);
+
+ b.Clear();
+
+ CHECK_USES_SHARED_EMPTY_HDR(a);
+ CHECK_IS_USING_AUTO(b);
+ }
+
+ // Same thing as the previous test, but with more auto arrays.
+ {
+ AutoTArray<int, 16> a;
+ AutoTArray<int, 3> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ a.SwapElements(b);
+
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ CHECK_ARRAY(b, data1);
+
+ b.Clear();
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_IS_USING_AUTO(b);
+ }
+
+ // Swap an empty nsTArray and an empty AutoTArray.
+ {
+ AutoTArray<int, 8> a;
+ nsTArray<int> b;
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_NOT_USING_AUTO(b);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ CHECK_EQ_INT(b.Length(), size_t(0));
+ }
+
+ // Swap empty auto array with non-empty AutoTArray using malloc'ed storage.
+ // I promise, all these tests have a point.
+ {
+ AutoTArray<int, 2> a;
+ AutoTArray<int, 1> b;
+
+ a.AppendElements(data1, ArrayLength(data1));
+
+ a.SwapElements(b);
+
+ CHECK_IS_USING_AUTO(a);
+ CHECK_NOT_USING_AUTO(b);
+ CHECK_ARRAY(b, data1);
+ CHECK_EQ_INT(a.Length(), size_t(0));
+ }
+}
+
+// Bug 1171296: Disabled on andoid due to crashes.
+#if !defined(ANDROID)
+TEST(TArray, test_fallible)
+{
+ // Test that FallibleTArray works properly; that is, it never OOMs, but
+ // instead eventually returns false.
+ //
+ // This test is only meaningful on 32-bit systems. On a 64-bit system, we
+ // might never OOM.
+ if (sizeof(void*) > 4) {
+ ASSERT_TRUE(true);
+ return;
+ }
+
+ // Allocate a bunch of 128MB arrays. Larger allocations will fail on some
+ // platforms without actually hitting OOM.
+ //
+ // 36 * 128MB > 4GB, so we should definitely OOM by the 36th array.
+ const unsigned numArrays = 36;
+ FallibleTArray<char> arrays[numArrays];
+ bool oomed = false;
+ for (size_t i = 0; i < numArrays; i++) {
+ // SetCapacity allocates the requested capacity + a header, and we want to
+ // avoid allocating more than 128MB overall because of the size padding it
+ // will cause, which depends on allocator behavior, so use 128MB - an
+ // arbitrary size larger than the array header, so that chances are good
+ // that allocations will always be 128MB.
+ bool success = arrays[i].SetCapacity(128 * 1024 * 1024 - 1024, fallible);
+ if (!success) {
+ // We got our OOM. Check that it didn't come too early.
+ oomed = true;
+ ASSERT_GE(i, size_t(8)) << "Got OOM on iteration " << i << ". Too early!";
+ }
+ }
+
+ ASSERT_TRUE(oomed) << "Didn't OOM or crash? nsTArray::SetCapacity"
+ "must be lying.";
+}
+#endif
+
+TEST(TArray, test_conversion_operator) {
+ FallibleTArray<int> f;
+ const FallibleTArray<int> fconst;
+
+ InfallibleTArray<int> i;
+ const InfallibleTArray<int> iconst;
+
+ nsTArray<int> t;
+ const nsTArray<int> tconst;
+ AutoTArray<int, 8> tauto;
+ const AutoTArray<int, 8> tautoconst;
+
+#define CHECK_ARRAY_CAST(type) \
+ do { \
+ const type<int>& z1 = f; \
+ ASSERT_EQ((void*)&z1, (void*)&f); \
+ const type<int>& z2 = fconst; \
+ ASSERT_EQ((void*)&z2, (void*)&fconst); \
+ const type<int>& z5 = i; \
+ ASSERT_EQ((void*)&z5, (void*)&i); \
+ const type<int>& z6 = iconst; \
+ ASSERT_EQ((void*)&z6, (void*)&iconst); \
+ const type<int>& z9 = t; \
+ ASSERT_EQ((void*)&z9, (void*)&t); \
+ const type<int>& z10 = tconst; \
+ ASSERT_EQ((void*)&z10, (void*)&tconst); \
+ const type<int>& z11 = tauto; \
+ ASSERT_EQ((void*)&z11, (void*)&tauto); \
+ const type<int>& z12 = tautoconst; \
+ ASSERT_EQ((void*)&z12, (void*)&tautoconst); \
+ } while (0)
+
+ CHECK_ARRAY_CAST(FallibleTArray);
+ CHECK_ARRAY_CAST(InfallibleTArray);
+ CHECK_ARRAY_CAST(nsTArray);
+
+#undef CHECK_ARRAY_CAST
+}
+
+template<class T>
+struct BufAccessor : public T
+{
+ void* GetHdr() { return T::mHdr; }
+};
+
+TEST(TArray, test_SetLengthAndRetainStorage_no_ctor) {
+ // 1050 because sizeof(int)*1050 is more than a page typically.
+ const int N = 1050;
+ FallibleTArray<int> f;
+
+ InfallibleTArray<int> i;
+
+ nsTArray<int> t;
+ AutoTArray<int, N> tauto;
+
+#define LPAREN (
+#define RPAREN )
+#define FOR_EACH(pre, post) \
+ do { \
+ pre f post; \
+ pre i post; \
+ pre t post; \
+ pre tauto post; \
+ } while (0)
+
+ // Setup test arrays.
+ FOR_EACH(; Unused << , .SetLength(N, fallible));
+ for (int n = 0; n < N; ++n) {
+ FOR_EACH(;, [n] = n);
+ }
+
+ void* initial_Hdrs[] = {
+ static_cast<BufAccessor<FallibleTArray<int> >&>(f).GetHdr(),
+ static_cast<BufAccessor<InfallibleTArray<int> >&>(i).GetHdr(),
+ static_cast<BufAccessor<nsTArray<int> >&>(t).GetHdr(),
+ static_cast<BufAccessor<AutoTArray<int, N> >&>(tauto).GetHdr(),
+ nullptr
+ };
+
+ // SetLengthAndRetainStorage(n), should NOT overwrite memory when T hasn't
+ // a default constructor.
+ FOR_EACH(;, .SetLengthAndRetainStorage(8));
+ FOR_EACH(;, .SetLengthAndRetainStorage(12));
+ for (int n = 0; n < 12; ++n) {
+ ASSERT_EQ(f[n], n);
+ ASSERT_EQ(i[n], n);
+ ASSERT_EQ(t[n], n);
+ ASSERT_EQ(tauto[n], n);
+ }
+ FOR_EACH(;, .SetLengthAndRetainStorage(0));
+ FOR_EACH(;, .SetLengthAndRetainStorage(N));
+ for (int n = 0; n < N; ++n) {
+ ASSERT_EQ(f[n], n);
+ ASSERT_EQ(i[n], n);
+ ASSERT_EQ(t[n], n);
+ ASSERT_EQ(tauto[n], n);
+ }
+
+ void* current_Hdrs[] = {
+ static_cast<BufAccessor<FallibleTArray<int> >&>(f).GetHdr(),
+ static_cast<BufAccessor<InfallibleTArray<int> >&>(i).GetHdr(),
+ static_cast<BufAccessor<nsTArray<int> >&>(t).GetHdr(),
+ static_cast<BufAccessor<AutoTArray<int, N> >&>(tauto).GetHdr(),
+ nullptr
+ };
+
+ // SetLengthAndRetainStorage(n) should NOT have reallocated the internal
+ // memory.
+ ASSERT_EQ(sizeof(initial_Hdrs), sizeof(current_Hdrs));
+ for (size_t n = 0; n < sizeof(current_Hdrs) / sizeof(current_Hdrs[0]); ++n) {
+ ASSERT_EQ(current_Hdrs[n], initial_Hdrs[n]);
+ }
+
+
+#undef FOR_EACH
+#undef LPAREN
+#undef RPAREN
+}
+
+} // namespace TestTArray
diff --git a/xpcom/tests/gtest/TestTextFormatter.cpp b/xpcom/tests/gtest/TestTextFormatter.cpp
new file mode 100644
index 000000000..c98b766cc
--- /dev/null
+++ b/xpcom/tests/gtest/TestTextFormatter.cpp
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; 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 "nsTextFormatter.h"
+#include "nsString.h"
+#include "gtest/gtest.h"
+
+TEST(TextFormatter, Tests)
+{
+ nsAutoString fmt(NS_LITERAL_STRING("%3$s %4$S %1$d %2$d %2$d %3$s"));
+ char utf8[] = "Hello";
+ char16_t ucs2[]={'W', 'o', 'r', 'l', 'd', 0x4e00, 0xAc00, 0xFF45, 0x0103, 0x00};
+ int d=3;
+
+ char16_t buf[256];
+ nsTextFormatter::snprintf(buf, 256, fmt.get(), d, 333, utf8, ucs2);
+ nsAutoString out(buf);
+ ASSERT_STREQ("Hello World", NS_LossyConvertUTF16toASCII(out).get());
+
+ const char16_t *uout = out.get();
+ const char16_t expected[] = {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20,
+ 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x4E00,
+ 0xAC00, 0xFF45, 0x0103, 0x20, 0x33,
+ 0x20, 0x33, 0x33, 0x33, 0x20, 0x33,
+ 0x33, 0x33, 0x20, 0x48, 0x65, 0x6C,
+ 0x6C, 0x6F};
+
+ for (uint32_t i=0; i<out.Length(); i++) {
+ ASSERT_EQ(uout[i], expected[i]);
+ }
+}
+
diff --git a/xpcom/tests/gtest/TestThreadPool.cpp b/xpcom/tests/gtest/TestThreadPool.cpp
new file mode 100644
index 000000000..56abf7608
--- /dev/null
+++ b/xpcom/tests/gtest/TestThreadPool.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nsXPCOM.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsIThreadPool.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Monitor.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+class Task final : public nsIRunnable
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit Task(int i) : mIndex(i) {}
+
+ NS_IMETHOD Run() override
+ {
+ printf("###(%d) running from thread: %p\n", mIndex, (void *) PR_GetCurrentThread());
+ int r = (int) ((float) rand() * 200 / RAND_MAX);
+ PR_Sleep(PR_MillisecondsToInterval(r));
+ printf("###(%d) exiting from thread: %p\n", mIndex, (void *) PR_GetCurrentThread());
+ ++sCount;
+ return NS_OK;
+ }
+
+ static mozilla::Atomic<int> sCount;
+
+private:
+ ~Task() {}
+
+ int mIndex;
+};
+NS_IMPL_ISUPPORTS(Task, nsIRunnable)
+
+mozilla::Atomic<int> Task::sCount;
+
+TEST(ThreadPool, Main)
+{
+ nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+ EXPECT_TRUE(pool);
+
+ for (int i = 0; i < 100; ++i) {
+ nsCOMPtr<nsIRunnable> task = new Task(i);
+ EXPECT_TRUE(task);
+
+ pool->Dispatch(task, NS_DISPATCH_NORMAL);
+ }
+
+ pool->Shutdown();
+ EXPECT_EQ(Task::sCount, 100);
+}
+
+TEST(ThreadPool, Parallelism)
+{
+ nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+ EXPECT_TRUE(pool);
+
+ // Dispatch and sleep to ensure we have an idle thread
+ nsCOMPtr<nsIRunnable> r0 = new Runnable();
+ pool->Dispatch(r0, NS_DISPATCH_SYNC);
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ class Runnable1 : public Runnable {
+ public:
+ Runnable1(Monitor& aMonitor, bool& aDone)
+ : mMonitor(aMonitor), mDone(aDone) {}
+
+ NS_IMETHOD Run() override {
+ MonitorAutoLock mon(mMonitor);
+ if (!mDone) {
+ // Wait for a reasonable timeout since we don't want to block gtests
+ // forever should any regression happen.
+ mon.Wait(PR_SecondsToInterval(300));
+ }
+ EXPECT_TRUE(mDone);
+ return NS_OK;
+ }
+ private:
+ Monitor& mMonitor;
+ bool& mDone;
+ };
+
+ class Runnable2 : public Runnable {
+ public:
+ Runnable2(Monitor& aMonitor, bool& aDone)
+ : mMonitor(aMonitor), mDone(aDone) {}
+
+ NS_IMETHOD Run() override {
+ MonitorAutoLock mon(mMonitor);
+ mDone = true;
+ mon.NotifyAll();
+ return NS_OK;
+ }
+ private:
+ Monitor& mMonitor;
+ bool& mDone;
+ };
+
+ // Dispatch 2 events in a row. Since we are still within the thread limit,
+ // We should wake up the idle thread and spawn a new thread so these 2 events
+ // can run in parallel. We will time out if r1 and r2 run in sequence for r1
+ // won't finish until r2 finishes.
+ Monitor mon("ThreadPool::Parallelism");
+ bool done = false;
+ nsCOMPtr<nsIRunnable> r1 = new Runnable1(mon, done);
+ nsCOMPtr<nsIRunnable> r2 = new Runnable2(mon, done);
+ pool->Dispatch(r1, NS_DISPATCH_NORMAL);
+ pool->Dispatch(r2, NS_DISPATCH_NORMAL);
+
+ pool->Shutdown();
+}
diff --git a/xpcom/tests/gtest/TestThreadPoolListener.cpp b/xpcom/tests/gtest/TestThreadPoolListener.cpp
new file mode 100644
index 000000000..f95106fa8
--- /dev/null
+++ b/xpcom/tests/gtest/TestThreadPoolListener.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIThread.h"
+#include "nsIThreadPool.h"
+
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCIDInternal.h"
+#include "pratom.h"
+#include "prinrval.h"
+#include "prmon.h"
+#include "prthread.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+#include "mozilla/ReentrantMonitor.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+#define NUMBER_OF_THREADS 4
+
+// One hour... because test boxes can be slow!
+#define IDLE_THREAD_TIMEOUT 3600000
+
+namespace TestThreadPoolListener
+{
+static nsIThread** gCreatedThreadList = nullptr;
+static nsIThread** gShutDownThreadList = nullptr;
+
+static ReentrantMonitor* gReentrantMonitor = nullptr;
+
+static bool gAllRunnablesPosted = false;
+static bool gAllThreadsCreated = false;
+static bool gAllThreadsShutDown = false;
+
+class Listener final : public nsIThreadPoolListener
+{
+ ~Listener() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITHREADPOOLLISTENER
+};
+
+NS_IMPL_ISUPPORTS(Listener, nsIThreadPoolListener)
+
+NS_IMETHODIMP
+Listener::OnThreadCreated()
+{
+ nsCOMPtr<nsIThread> current(do_GetCurrentThread());
+ EXPECT_TRUE(current) << "Couldn't get current thread!";
+
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+
+ while (!gAllRunnablesPosted) {
+ mon.Wait();
+ }
+
+ for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
+ nsIThread* thread = gCreatedThreadList[i];
+ EXPECT_NE(thread, current) << "Saw the same thread twice!";
+
+ if (!thread) {
+ gCreatedThreadList[i] = current;
+ if (i == (NUMBER_OF_THREADS - 1)) {
+ gAllThreadsCreated = true;
+ mon.NotifyAll();
+ }
+ return NS_OK;
+ }
+ }
+
+ EXPECT_TRUE(false) << "Too many threads!";
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+Listener::OnThreadShuttingDown()
+{
+ nsCOMPtr<nsIThread> current(do_GetCurrentThread());
+ EXPECT_TRUE(current) << "Couldn't get current thread!";
+
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+
+ for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
+ nsIThread* thread = gShutDownThreadList[i];
+ EXPECT_NE(thread, current) << "Saw the same thread twice!";
+
+ if (!thread) {
+ gShutDownThreadList[i] = current;
+ if (i == (NUMBER_OF_THREADS - 1)) {
+ gAllThreadsShutDown = true;
+ mon.NotifyAll();
+ }
+ return NS_OK;
+ }
+ }
+
+ EXPECT_TRUE(false) << "Too many threads!";
+ return NS_ERROR_FAILURE;
+}
+
+class AutoCreateAndDestroyReentrantMonitor
+{
+public:
+ explicit AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr)
+ : mReentrantMonitorPtr(aReentrantMonitorPtr) {
+ *aReentrantMonitorPtr = new ReentrantMonitor("TestThreadPoolListener::AutoMon");
+ MOZ_RELEASE_ASSERT(*aReentrantMonitorPtr, "Out of memory!");
+ }
+
+ ~AutoCreateAndDestroyReentrantMonitor() {
+ delete *mReentrantMonitorPtr;
+ *mReentrantMonitorPtr = nullptr;
+ }
+
+private:
+ ReentrantMonitor** mReentrantMonitorPtr;
+};
+
+TEST(ThreadPoolListener, Test)
+{
+ nsIThread* createdThreadList[NUMBER_OF_THREADS] = { nullptr };
+ gCreatedThreadList = createdThreadList;
+
+ nsIThread* shutDownThreadList[NUMBER_OF_THREADS] = { nullptr };
+ gShutDownThreadList = shutDownThreadList;
+
+ AutoCreateAndDestroyReentrantMonitor newMon(&gReentrantMonitor);
+ ASSERT_TRUE(gReentrantMonitor);
+
+ nsresult rv;
+
+ nsCOMPtr<nsIThreadPool> pool =
+ do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = pool->SetThreadLimit(NUMBER_OF_THREADS);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = pool->SetIdleThreadLimit(NUMBER_OF_THREADS);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = pool->SetIdleThreadTimeout(IDLE_THREAD_TIMEOUT);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIThreadPoolListener> listener = new Listener();
+ ASSERT_TRUE(listener);
+
+ rv = pool->SetListener(listener);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ {
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+
+ for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
+ nsCOMPtr<nsIRunnable> runnable = new Runnable();
+ ASSERT_TRUE(runnable);
+
+ rv = pool->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ }
+
+ gAllRunnablesPosted = true;
+ mon.NotifyAll();
+ }
+
+ {
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+ while (!gAllThreadsCreated) {
+ mon.Wait();
+ }
+ }
+
+ rv = pool->Shutdown();
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ {
+ ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
+ while (!gAllThreadsShutDown) {
+ mon.Wait();
+ }
+ }
+
+ for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
+ nsIThread* created = gCreatedThreadList[i];
+ ASSERT_TRUE(created);
+
+ bool match = false;
+ for (uint32_t j = 0; j < NUMBER_OF_THREADS; j++) {
+ nsIThread* destroyed = gShutDownThreadList[j];
+ ASSERT_TRUE(destroyed);
+
+ if (destroyed == created) {
+ match = true;
+ break;
+ }
+ }
+
+ ASSERT_TRUE(match);
+ }
+}
+
+} // namespace TestThreadPoolListener
diff --git a/xpcom/tests/gtest/TestThreadUtils.cpp b/xpcom/tests/gtest/TestThreadUtils.cpp
new file mode 100644
index 000000000..0d5d2f234
--- /dev/null
+++ b/xpcom/tests/gtest/TestThreadUtils.cpp
@@ -0,0 +1,378 @@
+/* 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 "nsThreadUtils.h"
+
+#include "gtest/gtest.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,
+ TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT,
+ TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT,
+#ifdef HAVE_STDCALL
+ TEST_STDCALL_VOID_ARG_VOID_RETURN,
+ TEST_STDCALL_VOID_ARG_NONVOID_RETURN,
+ TEST_STDCALL_NONVOID_ARG_VOID_RETURN,
+ TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN,
+ TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT,
+#endif
+ TEST_CALL_NEWTHREAD_SUICIDAL,
+ MAX_TESTS
+};
+
+bool gRunnableExecuted[MAX_TESTS];
+
+class nsFoo : public nsISupports {
+ NS_DECL_ISUPPORTS
+ nsresult DoFoo(bool* aBool) {
+ *aBool = true;
+ return NS_OK;
+ }
+
+private:
+ virtual ~nsFoo() {}
+};
+
+NS_IMPL_ISUPPORTS0(nsFoo)
+
+class TestSuicide : public mozilla::Runnable {
+ NS_IMETHOD Run() override {
+ // Runs first time on thread "Suicide", then dies on MainThread
+ if (!NS_IsMainThread()) {
+ mThread = do_GetCurrentThread();
+ NS_DispatchToMainThread(this);
+ return NS_OK;
+ }
+ MOZ_RELEASE_ASSERT(mThread);
+ mThread->Shutdown();
+ gRunnableExecuted[TEST_CALL_NEWTHREAD_SUICIDAL] = true;
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mThread;
+};
+
+class nsBar : public nsISupports {
+ virtual ~nsBar() {}
+public:
+ NS_DECL_ISUPPORTS
+ 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;
+ }
+ void DoBar3(nsFoo* aFoo) {
+ aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN]);
+ }
+ nsresult DoBar4(nsFoo* aFoo) {
+ return aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN]);
+ }
+ void DoBar5(nsFoo* aFoo) {
+ if (aFoo)
+ gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
+ }
+ nsresult DoBar6(char* aFoo) {
+ if (strlen(aFoo))
+ gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT] = true;
+ return NS_OK;
+ }
+#ifdef HAVE_STDCALL
+ void __stdcall DoBar1std(void) {
+ gRunnableExecuted[TEST_STDCALL_VOID_ARG_VOID_RETURN] = true;
+ }
+ nsresult __stdcall DoBar2std(void) {
+ gRunnableExecuted[TEST_STDCALL_VOID_ARG_NONVOID_RETURN] = true;
+ return NS_OK;
+ }
+ void __stdcall DoBar3std(nsFoo* aFoo) {
+ aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN]);
+ }
+ nsresult __stdcall DoBar4std(nsFoo* aFoo) {
+ return aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN]);
+ }
+ void __stdcall DoBar5std(nsFoo* aFoo) {
+ if (aFoo)
+ gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
+ }
+ nsresult __stdcall DoBar6std(char* aFoo) {
+ if (strlen(aFoo))
+ gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true;
+ return NS_OK;
+ }
+#endif
+};
+
+NS_IMPL_ISUPPORTS0(nsBar)
+
+struct TestCopyWithNoMove
+{
+ explicit TestCopyWithNoMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {}
+ TestCopyWithNoMove(const TestCopyWithNoMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; };
+ // No 'move' declaration, allows passing object by rvalue copy.
+ // Destructor nulls member variable...
+ ~TestCopyWithNoMove() { mCopyCounter = nullptr; }
+ // ... so we can check that the object is called when still alive.
+ void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); }
+ int* mCopyCounter;
+};
+struct TestCopyWithDeletedMove
+{
+ explicit TestCopyWithDeletedMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {}
+ TestCopyWithDeletedMove(const TestCopyWithDeletedMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; };
+ // Deleted move prevents passing by rvalue (even if copy would work)
+ TestCopyWithDeletedMove(TestCopyWithDeletedMove&&) = delete;
+ ~TestCopyWithDeletedMove() { mCopyCounter = nullptr; }
+ void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); }
+ int* mCopyCounter;
+};
+struct TestMove
+{
+ explicit TestMove(int* aMoveCounter) : mMoveCounter(aMoveCounter) {}
+ TestMove(const TestMove&) = delete;
+ TestMove(TestMove&& a) : mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; }
+ ~TestMove() { mMoveCounter = nullptr; }
+ void operator()() { MOZ_RELEASE_ASSERT(mMoveCounter); }
+ int* mMoveCounter;
+};
+struct TestCopyMove
+{
+ TestCopyMove(int* aCopyCounter, int* aMoveCounter) : mCopyCounter(aCopyCounter), mMoveCounter(aMoveCounter) {}
+ TestCopyMove(const TestCopyMove& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mCopyCounter; };
+ TestCopyMove(TestCopyMove&& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; }
+ ~TestCopyMove() { mCopyCounter = nullptr; mMoveCounter = nullptr; }
+ void operator()() { MOZ_RELEASE_ASSERT(mCopyCounter); MOZ_RELEASE_ASSERT(mMoveCounter); }
+ int* mCopyCounter;
+ int* mMoveCounter;
+};
+
+static void Expect(const char* aContext, int aCounter, int aMaxExpected)
+{
+ EXPECT_LE(aCounter, aMaxExpected) << aContext;
+}
+
+TEST(ThreadUtils, NewRunnableFunction)
+{
+ // Test NS_NewRunnableFunction with copyable-only function object.
+ {
+ int copyCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestCopyWithNoMove tracker(&copyCounter);
+ trackedRunnable = NS_NewRunnableFunction(tracker);
+ // Original 'tracker' is destroyed here.
+ }
+ // Verify that the runnable contains a non-destroyed function object.
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable-only (and no move) function, copies",
+ copyCounter, 1);
+ }
+ {
+ int copyCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ // Passing as rvalue, but using copy.
+ // (TestCopyWithDeletedMove wouldn't allow this.)
+ trackedRunnable = NS_NewRunnableFunction(TestCopyWithNoMove(&copyCounter));
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies",
+ copyCounter, 1);
+ }
+ {
+ int copyCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestCopyWithDeletedMove tracker(&copyCounter);
+ trackedRunnable = NS_NewRunnableFunction(tracker);
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies",
+ copyCounter, 1);
+ }
+
+ // Test NS_NewRunnableFunction with movable-only function object.
+ {
+ int moveCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestMove tracker(&moveCounter);
+ trackedRunnable = NS_NewRunnableFunction(Move(tracker));
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with movable-only function, moves",
+ moveCounter, 1);
+ }
+ {
+ int moveCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ trackedRunnable = NS_NewRunnableFunction(TestMove(&moveCounter));
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
+ moveCounter, 1);
+ }
+
+ // Test NS_NewRunnableFunction with copyable&movable function object.
+ {
+ int copyCounter = 0;
+ int moveCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestCopyMove tracker(&copyCounter, &moveCounter);
+ trackedRunnable = NS_NewRunnableFunction(Move(tracker));
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable&movable function, copies",
+ copyCounter, 0);
+ Expect("NS_NewRunnableFunction with copyable&movable function, moves",
+ moveCounter, 1);
+ }
+ {
+ int copyCounter = 0;
+ int moveCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ trackedRunnable =
+ NS_NewRunnableFunction(TestCopyMove(&copyCounter, &moveCounter));
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies",
+ copyCounter, 0);
+ Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves",
+ moveCounter, 1);
+ }
+
+ // Test NS_NewRunnableFunction with copyable-only lambda capture.
+ {
+ int copyCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestCopyWithNoMove tracker(&copyCounter);
+ // Expect 2 copies (here -> local lambda -> runnable lambda).
+ trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); });
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable-only (and no move) capture, copies",
+ copyCounter, 2);
+ }
+ {
+ int copyCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestCopyWithDeletedMove tracker(&copyCounter);
+ // Expect 2 copies (here -> local lambda -> runnable lambda).
+ trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); });
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable-only (and deleted move) capture, copies",
+ copyCounter, 2);
+ }
+
+ // Note: Not possible to use move-only captures.
+ // (Until we can use C++14 generalized lambda captures)
+
+ // Test NS_NewRunnableFunction with copyable&movable lambda capture.
+ {
+ int copyCounter = 0;
+ int moveCounter = 0;
+ {
+ nsCOMPtr<nsIRunnable> trackedRunnable;
+ {
+ TestCopyMove tracker(&copyCounter, &moveCounter);
+ trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); });
+ // Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda).
+ }
+ trackedRunnable->Run();
+ }
+ Expect("NS_NewRunnableFunction with copyable&movable capture, copies",
+ copyCounter, 1);
+ Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
+ moveCounter, 1);
+ }
+}
+
+TEST(ThreadUtils, RunnableMethod)
+{
+ memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
+ // Scope the smart ptrs so that the runnables need to hold on to whatever they need
+ {
+ RefPtr<nsFoo> foo = new nsFoo();
+ RefPtr<nsBar> bar = new nsBar();
+ RefPtr<const nsBar> constBar = bar;
+
+ // This pointer will be freed at the end of the block
+ // Do not dereference this pointer in the runnable method!
+ RefPtr<nsFoo> rawFoo = new nsFoo();
+
+ // Read only string. Dereferencing in runnable method to check this works.
+ char* message = (char*)"Test message";
+
+ NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar1));
+ NS_DispatchToMainThread(NewRunnableMethod(constBar, &nsBar::DoBar1Const));
+ NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar2));
+ NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
+ (bar, &nsBar::DoBar3, foo));
+ NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
+ (bar, &nsBar::DoBar4, foo));
+ NS_DispatchToMainThread(NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5, rawFoo));
+ NS_DispatchToMainThread(NewRunnableMethod<char*>(bar, &nsBar::DoBar6, message));
+#ifdef HAVE_STDCALL
+ NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar1std));
+ NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar2std));
+ NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
+ (bar, &nsBar::DoBar3std, foo));
+ NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
+ (bar, &nsBar::DoBar4std, foo));
+ NS_DispatchToMainThread(NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5std, rawFoo));
+ NS_DispatchToMainThread(NewRunnableMethod<char*>(bar, &nsBar::DoBar6std, message));
+#endif
+ }
+
+ // Spin the event loop
+ NS_ProcessPendingEvents(nullptr);
+
+ // Now test a suicidal event in NS_New(Named)Thread
+ nsCOMPtr<nsIThread> thread;
+ NS_NewNamedThread("SuicideThread", getter_AddRefs(thread), new TestSuicide());
+ ASSERT_TRUE(thread);
+
+ while (!gRunnableExecuted[TEST_CALL_NEWTHREAD_SUICIDAL]) {
+ NS_ProcessPendingEvents(nullptr);
+ }
+
+ for (uint32_t i = 0; i < MAX_TESTS; i++) {
+ EXPECT_TRUE(gRunnableExecuted[i]) << "Error in test " << i;
+ }
+}
diff --git a/xpcom/tests/gtest/TestThreads.cpp b/xpcom/tests/gtest/TestThreads.cpp
new file mode 100644
index 000000000..4f6055dce
--- /dev/null
+++ b/xpcom/tests/gtest/TestThreads.cpp
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThreadUtils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "nspr.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsXPCOM.h"
+#include "mozilla/Monitor.h"
+#include "gtest/gtest.h"
+
+class nsRunner final : public nsIRunnable {
+ ~nsRunner() {}
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ printf("running %d on thread %p\n", mNum, (void *)thread.get());
+
+ // if we don't do something slow, we'll never see the other
+ // worker threads run
+ PR_Sleep(PR_MillisecondsToInterval(100));
+
+ return rv;
+ }
+
+ explicit nsRunner(int num) : mNum(num) {
+ }
+
+protected:
+ int mNum;
+};
+
+NS_IMPL_ISUPPORTS(nsRunner, nsIRunnable)
+
+TEST(Threads, Main)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIRunnable> event = new nsRunner(0);
+ EXPECT_TRUE(event);
+
+ nsCOMPtr<nsIThread> runner;
+ rv = NS_NewThread(getter_AddRefs(runner), event);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = runner->Shutdown(); // wait for the runner to die before quitting
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ PR_Sleep(PR_MillisecondsToInterval(100)); // hopefully the runner will quit here
+}
+
+class nsStressRunner final : public nsIRunnable {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ EXPECT_FALSE(mWasRun);
+ mWasRun = true;
+ PR_Sleep(1);
+ if (!PR_AtomicDecrement(&gNum)) {
+ printf(" last thread was %d\n", mNum);
+ }
+ return NS_OK;
+ }
+
+ explicit nsStressRunner(int num) : mNum(num), mWasRun(false) {
+ PR_AtomicIncrement(&gNum);
+ }
+
+ static int32_t GetGlobalCount() {return gNum;}
+
+private:
+ ~nsStressRunner() {
+ EXPECT_TRUE(mWasRun);
+ }
+
+protected:
+ static int32_t gNum;
+ int32_t mNum;
+ bool mWasRun;
+};
+
+int32_t nsStressRunner::gNum = 0;
+
+NS_IMPL_ISUPPORTS(nsStressRunner, nsIRunnable)
+
+TEST(Threads, Stress)
+{
+ const int loops = 1000;
+ const int threads = 50;
+
+ for (int i = 0; i < loops; i++) {
+ printf("Loop %d of %d\n", i+1, loops);
+
+ int k;
+ nsIThread** array = new nsIThread*[threads];
+
+ EXPECT_EQ(nsStressRunner::GetGlobalCount(), 0);
+
+ for (k = 0; k < threads; k++) {
+ nsCOMPtr<nsIThread> t;
+ nsresult rv = NS_NewThread(getter_AddRefs(t), new nsStressRunner(k));
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ NS_ADDREF(array[k] = t);
+ }
+
+ for (k = threads-1; k >= 0; k--) {
+ array[k]->Shutdown();
+ NS_RELEASE(array[k]);
+ }
+ delete [] array;
+ }
+}
+
+mozilla::Monitor* gAsyncShutdownReadyMonitor;
+mozilla::Monitor* gBeginAsyncShutdownMonitor;
+
+class AsyncShutdownPreparer : public nsIRunnable {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ EXPECT_FALSE(mWasRun);
+ mWasRun = true;
+
+ mozilla::MonitorAutoLock lock(*gAsyncShutdownReadyMonitor);
+ lock.Notify();
+
+ return NS_OK;
+ }
+
+ explicit AsyncShutdownPreparer() : mWasRun(false) {}
+
+private:
+ virtual ~AsyncShutdownPreparer() {
+ EXPECT_TRUE(mWasRun);
+ }
+
+protected:
+ bool mWasRun;
+};
+
+NS_IMPL_ISUPPORTS(AsyncShutdownPreparer, nsIRunnable)
+
+class AsyncShutdownWaiter : public nsIRunnable {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ EXPECT_FALSE(mWasRun);
+ mWasRun = true;
+
+ nsCOMPtr<nsIThread> t;
+ nsresult rv;
+
+ {
+ mozilla::MonitorAutoLock lock(*gBeginAsyncShutdownMonitor);
+
+ rv = NS_NewThread(getter_AddRefs(t), new AsyncShutdownPreparer());
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ lock.Wait();
+ }
+
+ rv = t->AsyncShutdown();
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ return NS_OK;
+ }
+
+ explicit AsyncShutdownWaiter() : mWasRun(false) {}
+
+private:
+ virtual ~AsyncShutdownWaiter() {
+ EXPECT_TRUE(mWasRun);
+ }
+
+protected:
+ bool mWasRun;
+};
+
+NS_IMPL_ISUPPORTS(AsyncShutdownWaiter, nsIRunnable)
+
+class SameThreadSentinel : public nsIRunnable {
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD Run() override {
+ mozilla::MonitorAutoLock lock(*gBeginAsyncShutdownMonitor);
+ lock.Notify();
+ return NS_OK;
+ }
+
+private:
+ virtual ~SameThreadSentinel() {}
+};
+
+NS_IMPL_ISUPPORTS(SameThreadSentinel, nsIRunnable)
+
+TEST(Threads, AsyncShutdown)
+{
+ gAsyncShutdownReadyMonitor = new mozilla::Monitor("gAsyncShutdownReady");
+ gBeginAsyncShutdownMonitor = new mozilla::Monitor("gBeginAsyncShutdown");
+
+ nsCOMPtr<nsIThread> t;
+ nsresult rv;
+
+ {
+ mozilla::MonitorAutoLock lock(*gAsyncShutdownReadyMonitor);
+
+ rv = NS_NewThread(getter_AddRefs(t), new AsyncShutdownWaiter());
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ lock.Wait();
+ }
+
+ NS_DispatchToCurrentThread(new SameThreadSentinel());
+ rv = t->Shutdown();
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ delete gAsyncShutdownReadyMonitor;
+ delete gBeginAsyncShutdownMonitor;
+}
+
+static void threadProc(void *arg)
+{
+ // printf(" running thread %d\n", (int) arg);
+ PR_Sleep(1);
+ EXPECT_EQ(PR_JOINABLE_THREAD, PR_GetThreadState(PR_GetCurrentThread()));
+}
+
+TEST(Threads, StressNSPR)
+{
+ const int loops = 1000;
+ const int threads = 50;
+
+ for (int i = 0; i < loops; i++) {
+ printf("Loop %d of %d\n", i+1, loops);
+
+ intptr_t k;
+ PRThread** array = new PRThread*[threads];
+
+ for (k = 0; k < threads; k++) {
+ array[k] = PR_CreateThread(PR_USER_THREAD,
+ threadProc, (void*) k,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ EXPECT_TRUE(array[k]);
+ }
+
+ for (k = 0; k < threads; k++) {
+ EXPECT_EQ(PR_JOINABLE_THREAD, PR_GetThreadState(array[k]));
+ }
+
+ for (k = threads-1; k >= 0; k--) {
+ PR_JoinThread(array[k]);
+ }
+ delete [] array;
+ }
+}
diff --git a/xpcom/tests/gtest/TestTimeStamp.cpp b/xpcom/tests/gtest/TestTimeStamp.cpp
new file mode 100644
index 000000000..4e85b7e24
--- /dev/null
+++ b/xpcom/tests/gtest/TestTimeStamp.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/TimeStamp.h"
+
+#include "prinrval.h"
+#include "prthread.h"
+
+#include "gtest/gtest.h"
+
+using mozilla::TimeStamp;
+using mozilla::TimeDuration;
+
+TEST(TimeStamp, Main)
+{
+ TimeDuration td;
+ EXPECT_TRUE(td.ToSeconds() == 0.0);
+ EXPECT_TRUE(TimeDuration::FromSeconds(5).ToSeconds() == 5.0);
+ EXPECT_TRUE(TimeDuration::FromMilliseconds(5000).ToSeconds() == 5.0);
+ EXPECT_TRUE(TimeDuration::FromSeconds(1) < TimeDuration::FromSeconds(2));
+ EXPECT_FALSE(TimeDuration::FromSeconds(1) < TimeDuration::FromSeconds(1));
+ EXPECT_TRUE(TimeDuration::FromSeconds(2) > TimeDuration::FromSeconds(1));
+ EXPECT_FALSE(TimeDuration::FromSeconds(1) > TimeDuration::FromSeconds(1));
+ EXPECT_TRUE(TimeDuration::FromSeconds(1) <= TimeDuration::FromSeconds(2));
+ EXPECT_TRUE(TimeDuration::FromSeconds(1) <= TimeDuration::FromSeconds(1));
+ EXPECT_FALSE(TimeDuration::FromSeconds(2) <= TimeDuration::FromSeconds(1));
+ EXPECT_TRUE(TimeDuration::FromSeconds(2) >= TimeDuration::FromSeconds(1));
+ EXPECT_TRUE(TimeDuration::FromSeconds(1) >= TimeDuration::FromSeconds(1));
+ EXPECT_FALSE(TimeDuration::FromSeconds(1) >= TimeDuration::FromSeconds(2));
+
+ TimeStamp ts;
+ EXPECT_TRUE(ts.IsNull());
+
+ ts = TimeStamp::Now();
+ EXPECT_TRUE(!ts.IsNull());
+ EXPECT_TRUE((ts - ts).ToSeconds() == 0.0);
+
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ TimeStamp ts2(TimeStamp::Now());
+ EXPECT_TRUE(ts2 > ts);
+ EXPECT_FALSE(ts > ts);
+ EXPECT_TRUE(ts < ts2);
+ EXPECT_FALSE(ts < ts);
+ EXPECT_TRUE(ts <= ts2);
+ EXPECT_TRUE(ts <= ts);
+ EXPECT_FALSE(ts2 <= ts);
+ EXPECT_TRUE(ts2 >= ts);
+ EXPECT_TRUE(ts2 >= ts);
+ EXPECT_FALSE(ts >= ts2);
+
+ // We can't be sure exactly how long PR_Sleep slept for. It should have
+ // slept for at least one second. We might have slept a lot longer due
+ // to process scheduling, but hopefully not more than 10 seconds.
+ td = ts2 - ts;
+ EXPECT_TRUE(td.ToSeconds() > 1.0);
+ EXPECT_TRUE(td.ToSeconds() < 20.0);
+ td = ts - ts2;
+ EXPECT_TRUE(td.ToSeconds() < -1.0);
+ EXPECT_TRUE(td.ToSeconds() > -20.0);
+
+ double resolution = TimeDuration::Resolution().ToSecondsSigDigits();
+ printf(" (platform timer resolution is ~%g s)\n", resolution);
+ EXPECT_TRUE(1e-10 < resolution);
+ // Don't upper-bound sanity check ... although NSPR reports 1ms
+ // resolution, it might be lying, so we shouldn't compare with it
+}
diff --git a/xpcom/tests/gtest/TestTimers.cpp b/xpcom/tests/gtest/TestTimers.cpp
new file mode 100644
index 000000000..fe7520ab1
--- /dev/null
+++ b/xpcom/tests/gtest/TestTimers.cpp
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIThread.h"
+#include "nsITimer.h"
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "prinrval.h"
+#include "prmon.h"
+#include "prthread.h"
+#include "mozilla/Attributes.h"
+
+#include "mozilla/ReentrantMonitor.h"
+
+#include <list>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+typedef nsresult(*TestFuncPtr)();
+
+class AutoTestThread
+{
+public:
+ AutoTestThread() {
+ nsCOMPtr<nsIThread> newThread;
+ nsresult rv = NS_NewThread(getter_AddRefs(newThread));
+ if (NS_FAILED(rv))
+ return;
+
+ newThread.swap(mThread);
+ }
+
+ ~AutoTestThread() {
+ mThread->Shutdown();
+ }
+
+ operator nsIThread*() const {
+ return mThread;
+ }
+
+ nsIThread* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
+ return mThread;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mThread;
+};
+
+class AutoCreateAndDestroyReentrantMonitor
+{
+public:
+ AutoCreateAndDestroyReentrantMonitor() {
+ mReentrantMonitor = new ReentrantMonitor("TestTimers::AutoMon");
+ MOZ_RELEASE_ASSERT(mReentrantMonitor, "Out of memory!");
+ }
+
+ ~AutoCreateAndDestroyReentrantMonitor() {
+ delete mReentrantMonitor;
+ }
+
+ operator ReentrantMonitor* () {
+ return mReentrantMonitor;
+ }
+
+private:
+ ReentrantMonitor* mReentrantMonitor;
+};
+
+class TimerCallback final : public nsITimerCallback
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ TimerCallback(nsIThread** aThreadPtr, ReentrantMonitor* aReentrantMonitor)
+ : mThreadPtr(aThreadPtr), mReentrantMonitor(aReentrantMonitor) { }
+
+ NS_IMETHOD Notify(nsITimer* aTimer) override {
+ MOZ_RELEASE_ASSERT(mThreadPtr, "Callback was not supposed to be called!");
+ nsCOMPtr<nsIThread> current(do_GetCurrentThread());
+
+ ReentrantMonitorAutoEnter mon(*mReentrantMonitor);
+
+ MOZ_RELEASE_ASSERT(!*mThreadPtr, "Timer called back more than once!");
+ *mThreadPtr = current;
+
+ mon.Notify();
+
+ return NS_OK;
+ }
+private:
+ ~TimerCallback() {}
+
+ nsIThread** mThreadPtr;
+ ReentrantMonitor* mReentrantMonitor;
+};
+
+NS_IMPL_ISUPPORTS(TimerCallback, nsITimerCallback)
+
+TEST(Timers, TargetedTimers)
+{
+ AutoCreateAndDestroyReentrantMonitor newMon;
+ ASSERT_TRUE(newMon);
+
+ AutoTestThread testThread;
+ ASSERT_TRUE(testThread);
+
+ nsresult rv;
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsIEventTarget* target = static_cast<nsIEventTarget*>(testThread);
+
+ rv = timer->SetTarget(target);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsIThread* notifiedThread = nullptr;
+
+ nsCOMPtr<nsITimerCallback> callback =
+ new TimerCallback(&notifiedThread, newMon);
+ ASSERT_TRUE(callback);
+
+ rv = timer->InitWithCallback(callback, 2000, nsITimer::TYPE_ONE_SHOT);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ReentrantMonitorAutoEnter mon(*newMon);
+ while (!notifiedThread) {
+ mon.Wait();
+ }
+ ASSERT_EQ(notifiedThread, testThread);
+}
+
+TEST(Timers, TimerWithStoppedTarget)
+{
+ AutoTestThread testThread;
+ ASSERT_TRUE(testThread);
+
+ nsresult rv;
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsIEventTarget* target = static_cast<nsIEventTarget*>(testThread);
+
+ rv = timer->SetTarget(target);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // If this is called, we'll assert
+ nsCOMPtr<nsITimerCallback> callback =
+ new TimerCallback(nullptr, nullptr);
+ ASSERT_TRUE(callback);
+
+ rv = timer->InitWithCallback(callback, 100, nsITimer::TYPE_ONE_SHOT);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ testThread->Shutdown();
+
+ PR_Sleep(400);
+}
+
+#define FUZZ_MAX_TIMEOUT 9
+class FuzzTestThreadState final : public nsITimerCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit FuzzTestThreadState(nsIThread* thread) :
+ mThread(thread),
+ mStopped(false)
+ {}
+
+ class StartRunnable final : public mozilla::Runnable {
+ public:
+ explicit StartRunnable(FuzzTestThreadState* threadState) :
+ mThreadState(threadState)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ mThreadState->ScheduleOrCancelTimers();
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<FuzzTestThreadState> mThreadState;
+ };
+
+ void Start()
+ {
+ nsCOMPtr<nsIRunnable> runnable = new StartRunnable(this);
+ nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch StartRunnable.");
+ }
+
+ void Stop()
+ {
+ mStopped = true;
+ }
+
+ NS_IMETHOD Notify(nsITimer* aTimer) override
+ {
+ bool onCorrectThread;
+ nsresult rv = mThread->IsOnCurrentThread(&onCorrectThread);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to perform thread check.");
+ MOZ_RELEASE_ASSERT(onCorrectThread, "Notify invoked on wrong thread.");
+
+ uint32_t delay;
+ rv = aTimer->GetDelay(&delay);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "GetDelay failed.");
+
+ MOZ_RELEASE_ASSERT(delay <= FUZZ_MAX_TIMEOUT,
+ "Delay was an invalid value for this test.");
+
+ uint32_t type;
+ rv = aTimer->GetType(&type);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to get timer type.");
+ MOZ_RELEASE_ASSERT(type <= nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
+
+ if (type == nsITimer::TYPE_ONE_SHOT) {
+ MOZ_RELEASE_ASSERT(!mOneShotTimersByDelay[delay].empty(),
+ "Unexpected one-shot timer.");
+
+ MOZ_RELEASE_ASSERT(mOneShotTimersByDelay[delay].front().get() == aTimer,
+ "One-shot timers have been reordered.");
+
+ mOneShotTimersByDelay[delay].pop_front();
+ --mTimersOutstanding;
+ } else if (mStopped) {
+ CancelRepeatingTimer(aTimer);
+ }
+
+ ScheduleOrCancelTimers();
+ RescheduleSomeTimers();
+ return NS_OK;
+ }
+
+ bool HasTimersOutstanding() const
+ {
+ return !!mTimersOutstanding;
+ }
+
+ private:
+ ~FuzzTestThreadState()
+ {
+ for (size_t i = 0; i <= FUZZ_MAX_TIMEOUT; ++i) {
+ MOZ_RELEASE_ASSERT(mOneShotTimersByDelay[i].empty(),
+ "Timers remain at end of test.");
+ }
+ }
+
+ uint32_t GetRandomType() const
+ {
+ return rand() % (nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP + 1);
+ }
+
+ size_t CountOneShotTimers() const
+ {
+ size_t count = 0;
+ for (size_t i = 0; i <= FUZZ_MAX_TIMEOUT; ++i) {
+ count += mOneShotTimersByDelay[i].size();
+ }
+ return count;
+ }
+
+ void ScheduleOrCancelTimers()
+ {
+ if (mStopped) {
+ return;
+ }
+
+ const size_t numTimersDesired = (rand() % 100) + 100;
+ MOZ_RELEASE_ASSERT(numTimersDesired >= 100);
+ MOZ_RELEASE_ASSERT(numTimersDesired < 200);
+ int adjustment = numTimersDesired - mTimersOutstanding;
+
+ while (adjustment > 0) {
+ CreateRandomTimer();
+ --adjustment;
+ }
+
+ while (adjustment < 0) {
+ CancelRandomTimer();
+ ++adjustment;
+ }
+
+ MOZ_RELEASE_ASSERT(numTimersDesired == mTimersOutstanding);
+ }
+
+ void RescheduleSomeTimers()
+ {
+ if (mStopped) {
+ return;
+ }
+
+ static const size_t kNumRescheduled = 40;
+
+ // Reschedule some timers with a Cancel first.
+ for (size_t i = 0; i < kNumRescheduled; ++i) {
+ InitRandomTimer(CancelRandomTimer().get());
+ }
+ // Reschedule some timers without a Cancel first.
+ for (size_t i = 0; i < kNumRescheduled; ++i) {
+ InitRandomTimer(RemoveRandomTimer().get());
+ }
+ }
+
+ void CreateRandomTimer()
+ {
+ nsresult rv;
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to create timer.");
+
+ rv = timer->SetTarget(static_cast<nsIEventTarget*>(mThread.get()));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to set target.");
+
+ InitRandomTimer(timer.get());
+ }
+
+ nsCOMPtr<nsITimer> CancelRandomTimer()
+ {
+ nsCOMPtr<nsITimer> timer(RemoveRandomTimer());
+ timer->Cancel();
+ return timer;
+ }
+
+ nsCOMPtr<nsITimer> RemoveRandomTimer()
+ {
+ MOZ_RELEASE_ASSERT(mTimersOutstanding);
+
+ if ((GetRandomType() == nsITimer::TYPE_ONE_SHOT && CountOneShotTimers())
+ || mRepeatingTimers.empty()) {
+ uint32_t delayToRemove = rand() % (FUZZ_MAX_TIMEOUT + 1);
+ while (mOneShotTimersByDelay[delayToRemove].empty()) {
+ // ++delayToRemove mod FUZZ_MAX_TIMEOUT + 1
+ delayToRemove = (delayToRemove + 1) % (FUZZ_MAX_TIMEOUT + 1);
+ }
+
+ uint32_t indexToRemove =
+ rand() % mOneShotTimersByDelay[delayToRemove].size();
+
+ for (auto it = mOneShotTimersByDelay[delayToRemove].begin();
+ it != mOneShotTimersByDelay[delayToRemove].end();
+ ++it) {
+ if (indexToRemove) {
+ --indexToRemove;
+ continue;
+ }
+
+ nsCOMPtr<nsITimer> removed = *it;
+ mOneShotTimersByDelay[delayToRemove].erase(it);
+ --mTimersOutstanding;
+ return removed;
+ }
+ } else {
+ size_t indexToRemove = rand() % mRepeatingTimers.size();
+ nsCOMPtr<nsITimer> removed(mRepeatingTimers[indexToRemove]);
+ mRepeatingTimers.erase(mRepeatingTimers.begin() + indexToRemove);
+ --mTimersOutstanding;
+ return removed;
+ }
+
+ MOZ_CRASH("Unable to remove a timer");
+ }
+
+ void InitRandomTimer(nsITimer* aTimer)
+ {
+ // Between 0 and FUZZ_MAX_TIMEOUT
+ uint32_t delay = rand() % (FUZZ_MAX_TIMEOUT + 1);
+ uint32_t type = GetRandomType();
+ nsresult rv = aTimer->InitWithCallback(this, delay, type);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "Failed to set timer.");
+
+ if (type == nsITimer::TYPE_ONE_SHOT) {
+ mOneShotTimersByDelay[delay].push_back(aTimer);
+ } else {
+ mRepeatingTimers.push_back(aTimer);
+ }
+ ++mTimersOutstanding;
+ }
+
+ void CancelRepeatingTimer(nsITimer* aTimer)
+ {
+ for (auto it = mRepeatingTimers.begin();
+ it != mRepeatingTimers.end();
+ ++it) {
+ if (it->get() == aTimer) {
+ mRepeatingTimers.erase(it);
+ aTimer->Cancel();
+ --mTimersOutstanding;
+ return;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIThread> mThread;
+ // Scheduled timers, indexed by delay between 0-9 ms, in lists
+ // with most recently scheduled last.
+ std::list<nsCOMPtr<nsITimer>> mOneShotTimersByDelay[FUZZ_MAX_TIMEOUT + 1];
+ std::vector<nsCOMPtr<nsITimer>> mRepeatingTimers;
+ Atomic<bool> mStopped;
+ Atomic<size_t> mTimersOutstanding;
+};
+
+NS_IMPL_ISUPPORTS(FuzzTestThreadState, nsITimerCallback)
+
+TEST(Timers, FuzzTestTimers)
+{
+ static const size_t kNumThreads(10);
+ AutoTestThread threads[kNumThreads];
+ RefPtr<FuzzTestThreadState> threadStates[kNumThreads];
+
+ for (size_t i = 0; i < kNumThreads; ++i) {
+ threadStates[i] = new FuzzTestThreadState(&*threads[i]);
+ threadStates[i]->Start();
+ }
+
+ PR_Sleep(PR_MillisecondsToInterval(20000));
+
+ for (size_t i = 0; i < kNumThreads; ++i) {
+ threadStates[i]->Stop();
+ }
+
+ // Wait at most 10 seconds for all outstanding timers to pop
+ PRIntervalTime start = PR_IntervalNow();
+ for (auto& threadState : threadStates) {
+ while (threadState->HasTimersOutstanding()) {
+ uint32_t elapsedMs = PR_IntervalToMilliseconds(PR_IntervalNow() - start);
+ ASSERT_LE(elapsedMs, uint32_t(10000)) << "Timed out waiting for all timers to pop";
+ PR_Sleep(PR_MillisecondsToInterval(10));
+ }
+ }
+}
diff --git a/xpcom/tests/gtest/TestTokenizer.cpp b/xpcom/tests/gtest/TestTokenizer.cpp
new file mode 100644
index 000000000..283bbd3b8
--- /dev/null
+++ b/xpcom/tests/gtest/TestTokenizer.cpp
@@ -0,0 +1,1134 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Tokenizer.h"
+#include "mozilla/IncrementalTokenizer.h"
+#include "mozilla/Unused.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+static bool IsOperator(char const c)
+{
+ return c == '+' || c == '*';
+}
+
+static bool HttpHeaderCharacter(char const c)
+{
+ return (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_') ||
+ (c == '-');
+}
+
+TEST(Tokenizer, HTTPResponse)
+{
+ Tokenizer::Token t;
+
+ // Real life test, HTTP response
+
+ Tokenizer p(NS_LITERAL_CSTRING(
+ "HTTP/1.0 304 Not modified\r\n"
+ "ETag: hallo\r\n"
+ "Content-Length: 16\r\n"
+ "\r\n"
+ "This is the body"));
+
+ EXPECT_TRUE(p.CheckWord("HTTP"));
+ EXPECT_TRUE(p.CheckChar('/'));
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER);
+ EXPECT_TRUE(t.AsInteger() == 1);
+ EXPECT_TRUE(p.CheckChar('.'));
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER);
+ EXPECT_TRUE(t.AsInteger() == 0);
+ p.SkipWhites();
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER);
+ EXPECT_TRUE(t.AsInteger() == 304);
+ p.SkipWhites();
+
+ p.Record();
+ while (p.Next(t) && t.Type() != Tokenizer::TOKEN_EOL);
+ EXPECT_FALSE(p.HasFailed());
+ nsAutoCString h;
+ p.Claim(h);
+ EXPECT_TRUE(h == "Not modified");
+
+ p.Record();
+ while (p.CheckChar(HttpHeaderCharacter));
+ p.Claim(h, Tokenizer::INCLUDE_LAST);
+ EXPECT_TRUE(h == "ETag");
+ p.SkipWhites();
+ EXPECT_TRUE(p.CheckChar(':'));
+ p.SkipWhites();
+ p.Record();
+ while (p.Next(t) && t.Type() != Tokenizer::TOKEN_EOL);
+ EXPECT_FALSE(p.HasFailed());
+ p.Claim(h);
+ EXPECT_TRUE(h == "hallo");
+
+ p.Record();
+ while (p.CheckChar(HttpHeaderCharacter));
+ p.Claim(h, Tokenizer::INCLUDE_LAST);
+ EXPECT_TRUE(h == "Content-Length");
+ p.SkipWhites();
+ EXPECT_TRUE(p.CheckChar(':'));
+ p.SkipWhites();
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t));
+ EXPECT_TRUE(t.AsInteger() == 16);
+ EXPECT_TRUE(p.CheckEOL());
+
+ EXPECT_TRUE(p.CheckEOL());
+
+ p.Record();
+ while (p.Next(t) && t.Type() != Tokenizer::TOKEN_EOF);
+ nsAutoCString b;
+ p.Claim(b);
+ EXPECT_TRUE(b == "This is the body");
+}
+
+TEST(Tokenizer, Main)
+{
+ Tokenizer::Token t;
+
+ // Synthetic code-specific test
+
+ Tokenizer p(NS_LITERAL_CSTRING("test123 ,15 \t*\r\n%xx,-15\r\r"));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_WORD);
+ EXPECT_TRUE(t.AsString() == "test123");
+
+ Tokenizer::Token u;
+ EXPECT_FALSE(p.Check(u));
+
+ EXPECT_FALSE(p.CheckChar('!'));
+
+ EXPECT_FALSE(p.Check(Tokenizer::Token::Number(123)));
+
+ EXPECT_TRUE(p.CheckWhite());
+
+ EXPECT_TRUE(p.CheckChar(','));
+
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(15)));
+
+ p.Rollback();
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(15)));
+
+ p.Rollback();
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER);
+ EXPECT_TRUE(t.AsInteger() == 15);
+
+ EXPECT_FALSE(p.CheckChar(IsOperator));
+
+ EXPECT_TRUE(p.CheckWhite());
+
+ p.SkipWhites();
+
+ EXPECT_FALSE(p.CheckWhite());
+
+ p.Rollback();
+
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckWhite());
+
+ p.Record(Tokenizer::EXCLUDE_LAST);
+
+ EXPECT_TRUE(p.CheckChar(IsOperator));
+
+ p.Rollback();
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(t.AsChar() == '*');
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOL);
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(t.AsChar() == '%');
+
+ nsAutoCString claim;
+ p.Claim(claim, Tokenizer::EXCLUDE_LAST);
+ EXPECT_TRUE(claim == "*\r\n");
+ p.Claim(claim, Tokenizer::INCLUDE_LAST);
+ EXPECT_TRUE(claim == "*\r\n%");
+
+ p.Rollback();
+ EXPECT_TRUE(p.CheckChar('%'));
+
+ p.Record(Tokenizer::INCLUDE_LAST);
+
+ EXPECT_FALSE(p.CheckWord("xy"));
+
+ EXPECT_TRUE(p.CheckWord("xx"));
+
+
+ p.Claim(claim, Tokenizer::INCLUDE_LAST);
+ EXPECT_TRUE(claim == "%xx");
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(t.AsChar() == ',');
+
+ EXPECT_TRUE(p.CheckChar('-'));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER);
+ EXPECT_TRUE(t.AsInteger() == 15);
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOL);
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOL);
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOF);
+
+ EXPECT_FALSE(p.Next(t));
+
+ p.Rollback();
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOF);
+
+ EXPECT_FALSE(p.Next(t));
+
+ p.Rollback();
+ EXPECT_TRUE(p.CheckEOF());
+
+ EXPECT_FALSE(p.CheckEOF());
+}
+
+TEST(Tokenizer, SingleWord)
+{
+ // Single word with numbers in it test
+
+ Tokenizer p(NS_LITERAL_CSTRING("test123"));
+
+ EXPECT_TRUE(p.CheckWord("test123"));
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, EndingAfterNumber)
+{
+ // An end handling after a number
+
+ Tokenizer p(NS_LITERAL_CSTRING("123"));
+
+ EXPECT_FALSE(p.CheckWord("123"));
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(123)));
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, BadInteger)
+{
+ Tokenizer::Token t;
+
+ // A bad integer test
+
+ Tokenizer p(NS_LITERAL_CSTRING("189234891274981758617846178651647620587135"));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_ERROR);
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, CheckExpectedTokenValue)
+{
+ Tokenizer::Token t;
+
+ // Check expected token value test
+
+ Tokenizer p(NS_LITERAL_CSTRING("blue velvet"));
+
+ EXPECT_FALSE(p.Check(Tokenizer::TOKEN_INTEGER, t));
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t));
+ EXPECT_TRUE(t.AsString() == "blue");
+
+ EXPECT_FALSE(p.Check(Tokenizer::TOKEN_WORD, t));
+
+ EXPECT_TRUE(p.CheckWhite());
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t));
+ EXPECT_TRUE(t.AsString() == "velvet");
+
+ EXPECT_TRUE(p.CheckEOF());
+
+ EXPECT_FALSE(p.Next(t));
+}
+
+TEST(Tokenizer, HasFailed)
+{
+ Tokenizer::Token t;
+
+ // HasFailed test
+
+ Tokenizer p1(NS_LITERAL_CSTRING("a b"));
+
+ while (p1.Next(t) && t.Type() != Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(p1.HasFailed());
+
+
+ Tokenizer p2(NS_LITERAL_CSTRING("a b ?!c"));
+
+ EXPECT_FALSE(p2.CheckChar('c'));
+ EXPECT_TRUE(p2.HasFailed());
+ EXPECT_TRUE(p2.CheckChar(HttpHeaderCharacter));
+ EXPECT_FALSE(p2.HasFailed());
+ p2.SkipWhites();
+ EXPECT_FALSE(p2.HasFailed());
+ EXPECT_FALSE(p2.CheckChar('c'));
+ EXPECT_TRUE(p2.HasFailed());
+ EXPECT_TRUE(p2.Next(t));
+ EXPECT_FALSE(p2.HasFailed());
+ EXPECT_TRUE(p2.Next(t));
+ EXPECT_FALSE(p2.HasFailed());
+ EXPECT_FALSE(p2.CheckChar('c'));
+ EXPECT_TRUE(p2.HasFailed());
+ EXPECT_TRUE(p2.Check(Tokenizer::TOKEN_CHAR, t));
+ EXPECT_FALSE(p2.HasFailed());
+ EXPECT_FALSE(p2.CheckChar('#'));
+ EXPECT_TRUE(p2.HasFailed());
+ t = Tokenizer::Token::Char('!');
+ EXPECT_TRUE(p2.Check(t));
+ EXPECT_FALSE(p2.HasFailed());
+
+ while (p2.Next(t) && t.Type() != Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(p2.HasFailed());
+}
+
+TEST(Tokenizer, Construction)
+{
+ {
+ nsCString a("test");
+ Tokenizer p1(a);
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+
+ {
+ nsAutoCString a("test");
+ Tokenizer p1(a);
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+
+ {
+ static const char _a[] = "test";
+ nsDependentCString a(_a);
+ Tokenizer p1(a);
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+
+ {
+ static const char* _a = "test";
+ nsDependentCString a(_a);
+ Tokenizer p1(a);
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+
+ {
+ Tokenizer p1(nsDependentCString("test"));
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+
+ {
+ Tokenizer p1(NS_LITERAL_CSTRING("test"));
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+
+ {
+ Tokenizer p1("test");
+ EXPECT_TRUE(p1.CheckWord("test"));
+ EXPECT_TRUE(p1.CheckEOF());
+ }
+}
+
+TEST(Tokenizer, Customization)
+{
+ Tokenizer p1(NS_LITERAL_CSTRING("test-custom*words and\tdefault-whites"), nullptr, "-*");
+ EXPECT_TRUE(p1.CheckWord("test-custom*words"));
+ EXPECT_TRUE(p1.CheckWhite());
+ EXPECT_TRUE(p1.CheckWord("and"));
+ EXPECT_TRUE(p1.CheckWhite());
+ EXPECT_TRUE(p1.CheckWord("default-whites"));
+
+ Tokenizer p2(NS_LITERAL_CSTRING("test, custom,whites"), ", ");
+ EXPECT_TRUE(p2.CheckWord("test"));
+ EXPECT_TRUE(p2.CheckWhite());
+ EXPECT_TRUE(p2.CheckWhite());
+ EXPECT_TRUE(p2.CheckWord("custom"));
+ EXPECT_TRUE(p2.CheckWhite());
+ EXPECT_TRUE(p2.CheckWord("whites"));
+
+ Tokenizer p3(NS_LITERAL_CSTRING("test, custom, whites-and#word-chars"), ",", "-#");
+ EXPECT_TRUE(p3.CheckWord("test"));
+ EXPECT_TRUE(p3.CheckWhite());
+ EXPECT_FALSE(p3.CheckWhite());
+ EXPECT_TRUE(p3.CheckChar(' '));
+ EXPECT_TRUE(p3.CheckWord("custom"));
+ EXPECT_TRUE(p3.CheckWhite());
+ EXPECT_FALSE(p3.CheckWhite());
+ EXPECT_TRUE(p3.CheckChar(' '));
+ EXPECT_TRUE(p3.CheckWord("whites-and#word-chars"));
+}
+
+TEST(Tokenizer, ShortcutChecks)
+{
+ Tokenizer p("test1 test2,123");
+
+ nsAutoCString test1;
+ nsDependentCSubstring test2;
+ char comma;
+ uint32_t integer;
+
+ EXPECT_TRUE(p.ReadWord(test1));
+ EXPECT_TRUE(test1 == "test1");
+ p.SkipWhites();
+ EXPECT_TRUE(p.ReadWord(test2));
+ EXPECT_TRUE(test2 == "test2");
+ EXPECT_TRUE(p.ReadChar(&comma));
+ EXPECT_TRUE(comma == ',');
+ EXPECT_TRUE(p.ReadInteger(&integer));
+ EXPECT_TRUE(integer == 123);
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+static bool ABChar(const char aChar)
+{
+ return aChar == 'a' || aChar == 'b';
+}
+
+TEST(Tokenizer, ReadCharClassified)
+{
+ Tokenizer p("abc");
+
+ char c;
+ EXPECT_TRUE(p.ReadChar(ABChar, &c));
+ EXPECT_TRUE(c == 'a');
+ EXPECT_TRUE(p.ReadChar(ABChar, &c));
+ EXPECT_TRUE(c == 'b');
+ EXPECT_FALSE(p.ReadChar(ABChar, &c));
+ nsDependentCSubstring w;
+ EXPECT_TRUE(p.ReadWord(w));
+ EXPECT_TRUE(w == "c");
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, ClaimSubstring)
+{
+ Tokenizer p(" abc ");
+
+ EXPECT_TRUE(p.CheckWhite());
+
+ p.Record();
+ EXPECT_TRUE(p.CheckWord("abc"));
+ nsDependentCSubstring v;
+ p.Claim(v, Tokenizer::INCLUDE_LAST);
+ EXPECT_TRUE(v == "abc");
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, Fragment)
+{
+ const char str[] = "ab;cd:10 ";
+ Tokenizer p(str);
+ nsDependentCSubstring f;
+
+ Tokenizer::Token t1, t2;
+
+ EXPECT_TRUE(p.Next(t1));
+ EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_WORD);
+ EXPECT_TRUE(t1.Fragment() == "ab");
+ EXPECT_TRUE(t1.Fragment().BeginReading() == &str[0]);
+
+ p.Rollback();
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t2));
+ EXPECT_TRUE(t2.Fragment() == "ab");
+ EXPECT_TRUE(t2.Fragment().BeginReading() == &str[0]);
+
+
+ EXPECT_TRUE(p.Next(t1));
+ EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(t1.Fragment() == ";");
+ EXPECT_TRUE(t1.Fragment().BeginReading() == &str[2]);
+
+ p.Rollback();
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_CHAR, t2));
+ EXPECT_TRUE(t2.Fragment() == ";");
+ EXPECT_TRUE(t2.Fragment().BeginReading() == &str[2]);
+
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t2));
+ EXPECT_TRUE(t2.Fragment() == "cd");
+ EXPECT_TRUE(t2.Fragment().BeginReading() == &str[3]);
+
+ p.Rollback();
+ EXPECT_TRUE(p.Next(t1));
+ EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_WORD);
+ EXPECT_TRUE(t1.Fragment() == "cd");
+ EXPECT_TRUE(t1.Fragment().BeginReading() == &str[3]);
+
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_CHAR, t2));
+ EXPECT_TRUE(t2.Fragment() == ":");
+ EXPECT_TRUE(t2.Fragment().BeginReading() == &str[5]);
+
+ p.Rollback();
+ EXPECT_TRUE(p.Next(t1));
+ EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_CHAR);
+ EXPECT_TRUE(t1.Fragment() == ":");
+ EXPECT_TRUE(t1.Fragment().BeginReading() == &str[5]);
+
+
+ EXPECT_TRUE(p.Next(t1));
+ EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_INTEGER);
+ EXPECT_TRUE(t1.Fragment() == "10");
+ EXPECT_TRUE(t1.Fragment().BeginReading() == &str[6]);
+
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WS, t2));
+ EXPECT_TRUE(t2.Fragment() == " ");
+ EXPECT_TRUE(t2.Fragment().BeginReading() == &str[8]);
+
+
+ EXPECT_TRUE(p.Check(Tokenizer::TOKEN_EOF, t1));
+ EXPECT_TRUE(t1.Fragment() == "");
+ EXPECT_TRUE(t1.Fragment().BeginReading() == &str[9]);
+}
+
+TEST(Tokenizer, SkipWhites)
+{
+ Tokenizer p("Text1 \nText2 \nText3\n Text4\n ");
+
+ EXPECT_TRUE(p.CheckWord("Text1"));
+ p.SkipWhites();
+ EXPECT_TRUE(p.CheckEOL());
+
+ EXPECT_TRUE(p.CheckWord("Text2"));
+ p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE);
+
+ EXPECT_TRUE(p.CheckWord("Text3"));
+ p.SkipWhites();
+ EXPECT_TRUE(p.CheckEOL());
+ p.SkipWhites();
+
+ EXPECT_TRUE(p.CheckWord("Text4"));
+ p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE);
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, SkipCustomWhites)
+{
+ Tokenizer p("Text1 \n\r\t.Text2 \n\r\t.", " \n\r\t.");
+
+ EXPECT_TRUE(p.CheckWord("Text1"));
+ p.SkipWhites();
+ EXPECT_TRUE(p.CheckWord("Text2"));
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckWhite());
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, IntegerReading)
+{
+#define INT_6_BITS 64U
+#define INT_30_BITS 1073741824UL
+#define INT_32_BITS 4294967295UL
+#define INT_50_BITS 1125899906842624ULL
+#define STR_INT_MORE_THAN_64_BITS "922337203685477580899"
+
+ {
+ Tokenizer p(NS_STRINGIFY(INT_6_BITS));
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ EXPECT_TRUE(p.ReadInteger(&u8));
+ EXPECT_TRUE(u8 == INT_6_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&u16));
+ EXPECT_TRUE(u16 == INT_6_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&u32));
+ EXPECT_TRUE(u32 == INT_6_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&u64));
+ EXPECT_TRUE(u64 == INT_6_BITS);
+
+ p.Rollback();
+
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ int64_t s64;
+ EXPECT_TRUE(p.ReadInteger(&s8));
+ EXPECT_TRUE(s8 == INT_6_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&s16));
+ EXPECT_TRUE(s16 == INT_6_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&s32));
+ EXPECT_TRUE(s32 == INT_6_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&s64));
+ EXPECT_TRUE(s64 == INT_6_BITS);
+
+ EXPECT_TRUE(p.CheckWord("U"));
+ EXPECT_TRUE(p.CheckEOF());
+ }
+
+ {
+ Tokenizer p(NS_STRINGIFY(INT_30_BITS));
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ EXPECT_FALSE(p.ReadInteger(&u8));
+ EXPECT_FALSE(p.ReadInteger(&u16));
+ EXPECT_TRUE(p.ReadInteger(&u32));
+ EXPECT_TRUE(u32 == INT_30_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&u64));
+ EXPECT_TRUE(u64 == INT_30_BITS);
+
+ p.Rollback();
+
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ int64_t s64;
+ EXPECT_FALSE(p.ReadInteger(&s8));
+ EXPECT_FALSE(p.ReadInteger(&s16));
+ EXPECT_TRUE(p.ReadInteger(&s32));
+ EXPECT_TRUE(s32 == INT_30_BITS);
+ p.Rollback();
+ EXPECT_TRUE(p.ReadInteger(&s64));
+ EXPECT_TRUE(s64 == INT_30_BITS);
+ EXPECT_TRUE(p.CheckWord("UL"));
+ EXPECT_TRUE(p.CheckEOF());
+ }
+
+ {
+ Tokenizer p(NS_STRINGIFY(INT_32_BITS));
+ uint32_t u32;
+ int32_t s32;
+ EXPECT_FALSE(p.ReadInteger(&s32));
+ EXPECT_TRUE(p.ReadInteger(&u32));
+ EXPECT_TRUE(u32 == INT_32_BITS);
+ EXPECT_TRUE(p.CheckWord("UL"));
+ EXPECT_TRUE(p.CheckEOF());
+ }
+
+ {
+ Tokenizer p(NS_STRINGIFY(INT_50_BITS));
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ EXPECT_FALSE(p.ReadInteger(&u8));
+ EXPECT_FALSE(p.ReadInteger(&u16));
+ EXPECT_FALSE(p.ReadInteger(&u32));
+ EXPECT_TRUE(p.ReadInteger(&u64));
+ EXPECT_TRUE(u64 == INT_50_BITS);
+ EXPECT_TRUE(p.CheckWord("ULL"));
+ EXPECT_TRUE(p.CheckEOF());
+ }
+
+ {
+ Tokenizer p(STR_INT_MORE_THAN_64_BITS);
+ int64_t i;
+ EXPECT_FALSE(p.ReadInteger(&i));
+ uint64_t u;
+ EXPECT_FALSE(p.ReadInteger(&u));
+ EXPECT_FALSE(p.CheckEOF());
+ }
+}
+
+TEST(Tokenizer, ReadUntil)
+{
+ Tokenizer p("Hello;test 4,");
+ nsDependentCSubstring f;
+ EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Char(';'), f));
+ EXPECT_TRUE(f == "Hello");
+ p.Rollback();
+
+ EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Char(';'), f, Tokenizer::INCLUDE_LAST));
+ EXPECT_TRUE(f == "Hello;");
+ p.Rollback();
+
+ EXPECT_FALSE(p.ReadUntil(Tokenizer::Token::Char('!'), f));
+ EXPECT_TRUE(f == "Hello;test 4,");
+ p.Rollback();
+
+ EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Word(NS_LITERAL_CSTRING("test")), f));
+ EXPECT_TRUE(f == "Hello;");
+ p.Rollback();
+
+ EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Word(NS_LITERAL_CSTRING("test")), f, Tokenizer::INCLUDE_LAST));
+ EXPECT_TRUE(f == "Hello;test");
+ EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Char(','), f));
+ EXPECT_TRUE(f == " 4");
+}
+
+TEST(Tokenizer, SkipUntil)
+{
+ {
+ Tokenizer p("test1,test2,,,test3");
+
+ p.SkipUntil(Tokenizer::Token::Char(','));
+ EXPECT_TRUE(p.CheckChar(','));
+ EXPECT_TRUE(p.CheckWord("test2"));
+
+ p.SkipUntil(Tokenizer::Token::Char(',')); // must not move
+ EXPECT_TRUE(p.CheckChar(',')); // check the first comma of the ',,,' string
+
+ p.Rollback(); // moves cursor back to the first comma of the ',,,' string
+
+ p.SkipUntil(Tokenizer::Token::Char(',')); // must not move, we are on the ',' char
+ EXPECT_TRUE(p.CheckChar(','));
+ EXPECT_TRUE(p.CheckChar(','));
+ EXPECT_TRUE(p.CheckChar(','));
+ EXPECT_TRUE(p.CheckWord("test3"));
+ p.Rollback();
+
+ p.SkipUntil(Tokenizer::Token::Char(','));
+ EXPECT_TRUE(p.CheckEOF());
+ }
+
+ {
+ Tokenizer p("test0,test1,test2");
+
+ p.SkipUntil(Tokenizer::Token::Char(','));
+ EXPECT_TRUE(p.CheckChar(','));
+
+ p.SkipUntil(Tokenizer::Token::Char(','));
+ p.Rollback();
+
+ EXPECT_TRUE(p.CheckWord("test1"));
+ EXPECT_TRUE(p.CheckChar(','));
+
+ p.SkipUntil(Tokenizer::Token::Char(','));
+ p.Rollback();
+
+ EXPECT_TRUE(p.CheckWord("test2"));
+ EXPECT_TRUE(p.CheckEOF());
+ }
+}
+
+TEST(Tokenizer, Custom)
+{
+ Tokenizer p("aaaaaacustom-1\r,custom-1,Custom-1,Custom-1,00custom-2xxxx,CUSTOM-2");
+
+ Tokenizer::Token c1 = p.AddCustomToken("custom-1", Tokenizer::CASE_INSENSITIVE);
+ Tokenizer::Token c2 = p.AddCustomToken("custom-2", Tokenizer::CASE_SENSITIVE);
+
+ // It's expected to NOT FIND the custom token if it's not on an edge
+ // between other recognizable tokens.
+ EXPECT_TRUE(p.CheckWord("aaaaaacustom"));
+ EXPECT_TRUE(p.CheckChar('-'));
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(1)));
+ EXPECT_TRUE(p.CheckEOL());
+ EXPECT_TRUE(p.CheckChar(','));
+
+ EXPECT_TRUE(p.Check(c1));
+ EXPECT_TRUE(p.CheckChar(','));
+
+ EXPECT_TRUE(p.Check(c1));
+ EXPECT_TRUE(p.CheckChar(','));
+
+ p.EnableCustomToken(c1, false);
+ EXPECT_TRUE(p.CheckWord("Custom"));
+ EXPECT_TRUE(p.CheckChar('-'));
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(1)));
+ EXPECT_TRUE(p.CheckChar(','));
+
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(0)));
+ EXPECT_TRUE(p.Check(c2));
+ EXPECT_TRUE(p.CheckWord("xxxx"));
+ EXPECT_TRUE(p.CheckChar(','));
+
+ EXPECT_TRUE(p.CheckWord("CUSTOM"));
+ EXPECT_TRUE(p.CheckChar('-'));
+ EXPECT_TRUE(p.Check(Tokenizer::Token::Number(2)));
+
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, CustomRaw)
+{
+ Tokenizer p("aaaaaacustom-1\r,custom-1,Custom-1,Custom-1,00custom-2xxxx,CUSTOM-2");
+
+ Tokenizer::Token c1 = p.AddCustomToken("custom-1", Tokenizer::CASE_INSENSITIVE);
+ Tokenizer::Token c2 = p.AddCustomToken("custom-2", Tokenizer::CASE_SENSITIVE);
+
+ // In this mode it's expected to find all custom tokens among any kind of input.
+ p.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
+
+ Tokenizer::Token t;
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
+ EXPECT_TRUE(t.Fragment().EqualsLiteral("aaaaaa"));
+
+ EXPECT_TRUE(p.Check(c1));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
+ EXPECT_TRUE(t.Fragment().EqualsLiteral("\r,"));
+
+ EXPECT_TRUE(p.Check(c1));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
+ EXPECT_TRUE(t.Fragment().EqualsLiteral(","));
+
+ EXPECT_TRUE(p.Check(c1));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
+ EXPECT_TRUE(t.Fragment().EqualsLiteral(","));
+
+ EXPECT_TRUE(p.Check(c1));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
+ EXPECT_TRUE(t.Fragment().EqualsLiteral(",00"));
+
+ EXPECT_TRUE(p.Check(c2));
+
+ EXPECT_TRUE(p.Next(t));
+ EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
+ EXPECT_TRUE(t.Fragment().EqualsLiteral("xxxx,CUSTOM-2"));
+
+ EXPECT_TRUE(p.CheckEOF());
+}
+
+TEST(Tokenizer, Incremental)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test1")))); break;
+ case 2: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 3: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); break;
+ case 4: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 5: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 7: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test3")))); break;
+ case 8: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+
+ return NS_OK;
+ });
+
+ NS_NAMED_LITERAL_CSTRING(input, "test1,test2,,,test3");
+ auto cur = input.BeginReading();
+ auto end = input.EndReading();
+ for (; cur < end; ++cur) {
+ i.FeedInput(nsDependentCSubstring(cur, 1));
+ }
+
+ EXPECT_TRUE(test == 6);
+ i.FinishInput();
+ EXPECT_TRUE(test == 8);
+}
+
+TEST(Tokenizer, IncrementalRollback)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test1")))); break;
+ case 2: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 3: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2"))));
+ i.Rollback(); // so that we get the token again
+ break;
+ case 4: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); break;
+ case 5: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 7: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
+ case 8: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test3")))); break;
+ case 9: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+
+ return NS_OK;
+ });
+
+ NS_NAMED_LITERAL_CSTRING(input, "test1,test2,,,test3");
+ auto cur = input.BeginReading();
+ auto end = input.EndReading();
+ for (; cur < end; ++cur) {
+ i.FeedInput(nsDependentCSubstring(cur, 1));
+ }
+
+ EXPECT_TRUE(test == 7);
+ i.FinishInput();
+ EXPECT_TRUE(test == 9);
+}
+
+TEST(Tokenizer, IncrementalNeedMoreInput)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ Token t2;
+ switch (++test) {
+ case 1:
+ EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("a"))));
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ EXPECT_TRUE(t.Equals(Token::Whitespace()));
+ if (i.Next(t2)) {
+ EXPECT_TRUE(test == 5);
+ EXPECT_TRUE(t2.Equals(Token::Word(NS_LITERAL_CSTRING("bb"))));
+ } else {
+ EXPECT_TRUE(test < 5);
+ i.NeedMoreInput();
+ }
+ break;
+ case 6:
+ EXPECT_TRUE(t.Equals(Token::Char(',')));
+ break;
+ case 7:
+ EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("c"))));
+ return NS_ERROR_FAILURE;
+ default:
+ EXPECT_TRUE(false);
+ break;
+ }
+
+ return NS_OK;
+ });
+
+ NS_NAMED_LITERAL_CSTRING(input, "a bb,c");
+ auto cur = input.BeginReading();
+ auto end = input.EndReading();
+
+ nsresult rv;
+ for (; cur < end; ++cur) {
+ rv = i.FeedInput(nsDependentCSubstring(cur, 1));
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ }
+
+ EXPECT_TRUE(rv == NS_OK);
+ EXPECT_TRUE(test == 6);
+
+ rv = i.FinishInput();
+ EXPECT_TRUE(rv == NS_ERROR_FAILURE);
+ EXPECT_TRUE(test == 7);
+}
+
+TEST(Tokenizer, IncrementalCustom)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ Token custom;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Equals(custom)); break;
+ case 2: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("bla")))); break;
+ case 3: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+
+ return NS_OK;
+ }, nullptr, "-");
+
+ custom = i.AddCustomToken("some-test", Tokenizer::CASE_SENSITIVE);
+ i.FeedInput(NS_LITERAL_CSTRING("some-"));
+ EXPECT_TRUE(test == 0);
+ i.FeedInput(NS_LITERAL_CSTRING("tes"));
+ EXPECT_TRUE(test == 0);
+ i.FeedInput(NS_LITERAL_CSTRING("tbla"));
+ EXPECT_TRUE(test == 1);
+ i.FinishInput();
+ EXPECT_TRUE(test == 3);
+}
+
+TEST(Tokenizer, IncrementalCustomRaw)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ Token custom;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("test1,")); break;
+ case 2: EXPECT_TRUE(t.Equals(custom)); break;
+ case 3: EXPECT_TRUE(t.Fragment().EqualsLiteral("!,,test3"));
+ i.Rollback();
+ i.SetTokenizingMode(Tokenizer::Mode::FULL);
+ break;
+ case 4: EXPECT_TRUE(t.Equals(Token::Char('!')));
+ i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
+ break;
+ case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral(",,test3")); break;
+ case 6: EXPECT_TRUE(t.Equals(custom)); break;
+ case 7: EXPECT_TRUE(t.Fragment().EqualsLiteral("tes")); break;
+ case 8: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+
+ return NS_OK;
+ });
+
+ custom = i.AddCustomToken("test2", Tokenizer::CASE_SENSITIVE);
+ i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
+
+ NS_NAMED_LITERAL_CSTRING(input, "test1,test2!,,test3test2tes");
+ auto cur = input.BeginReading();
+ auto end = input.EndReading();
+ for (; cur < end; ++cur) {
+ i.FeedInput(nsDependentCSubstring(cur, 1));
+ }
+
+ EXPECT_TRUE(test == 6);
+ i.FinishInput();
+ EXPECT_TRUE(test == 8);
+}
+
+TEST(Tokenizer, IncrementalCustomRemove)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ Token custom;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Equals(custom));
+ i.RemoveCustomToken(custom);
+ break;
+ case 2: EXPECT_FALSE(t.Equals(custom)); break;
+ case 3: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+
+ return NS_OK;
+ });
+
+ custom = i.AddCustomToken("custom1", Tokenizer::CASE_SENSITIVE);
+
+ NS_NAMED_LITERAL_CSTRING(input, "custom1custom1");
+ i.FeedInput(input);
+ EXPECT_TRUE(test == 1);
+ i.FinishInput();
+ EXPECT_TRUE(test == 3);
+}
+
+TEST(Tokenizer, IncrementalBuffering1)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ Token custom;
+ nsDependentCSubstring observedFragment;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("012")); break;
+ case 2: EXPECT_TRUE(t.Fragment().EqualsLiteral("3456789")); break;
+ case 3: EXPECT_TRUE(t.Equals(custom)); break;
+ case 4: EXPECT_TRUE(t.Fragment().EqualsLiteral("qwe")); break;
+ case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral("rt")); break;
+ case 6: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+
+ observedFragment.Rebind(t.Fragment().BeginReading(),
+ t.Fragment().Length());
+ return NS_OK;
+ }, nullptr, nullptr, 3);
+
+ custom = i.AddCustomToken("aaa", Tokenizer::CASE_SENSITIVE);
+ // This externally unused token is added only to check the internal algorithm
+ // does work correctly as expected when there are two different length tokens.
+ Unused << i.AddCustomToken("bb", Tokenizer::CASE_SENSITIVE);
+ i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
+
+ i.FeedInput(NS_LITERAL_CSTRING("01234"));
+ EXPECT_TRUE(test == 1);
+ EXPECT_TRUE(observedFragment.EqualsLiteral("012"));
+
+ i.FeedInput(NS_LITERAL_CSTRING("5"));
+ EXPECT_TRUE(test == 1);
+ i.FeedInput(NS_LITERAL_CSTRING("6789aa"));
+ EXPECT_TRUE(test == 2);
+ EXPECT_TRUE(observedFragment.EqualsLiteral("3456789"));
+
+ i.FeedInput(NS_LITERAL_CSTRING("aqwert"));
+ EXPECT_TRUE(test == 4);
+ EXPECT_TRUE(observedFragment.EqualsLiteral("qwe"));
+
+ i.FinishInput();
+ EXPECT_TRUE(test == 6);
+}
+
+TEST(Tokenizer, IncrementalBuffering2)
+{
+ typedef TokenizerBase::Token Token;
+
+ int test = 0;
+ Token custom;
+ IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
+ {
+ switch (++test) {
+ case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("01")); break;
+ case 2: EXPECT_TRUE(t.Fragment().EqualsLiteral("234567")); break;
+ case 3: EXPECT_TRUE(t.Fragment().EqualsLiteral("89")); break;
+ case 4: EXPECT_TRUE(t.Equals(custom)); break;
+ case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral("qwert")); break;
+ case 6: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
+ }
+ return NS_OK;
+ }, nullptr, nullptr, 3);
+
+ custom = i.AddCustomToken("aaa", Tokenizer::CASE_SENSITIVE);
+ // This externally unused token is added only to check the internal algorithm
+ // does work correctly as expected when there are two different length tokens.
+ Unused << i.AddCustomToken("bbbbb", Tokenizer::CASE_SENSITIVE);
+ i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
+
+ i.FeedInput(NS_LITERAL_CSTRING("01234"));
+ EXPECT_TRUE(test == 0);
+ i.FeedInput(NS_LITERAL_CSTRING("5"));
+ EXPECT_TRUE(test == 1);
+ i.FeedInput(NS_LITERAL_CSTRING("6789aa"));
+ EXPECT_TRUE(test == 2);
+ i.FeedInput(NS_LITERAL_CSTRING("aqwert"));
+ EXPECT_TRUE(test == 4);
+ i.FinishInput();
+ EXPECT_TRUE(test == 6);
+}
diff --git a/xpcom/tests/gtest/TestUTF.cpp b/xpcom/tests/gtest/TestUTF.cpp
new file mode 100644
index 000000000..14dc03abe
--- /dev/null
+++ b/xpcom/tests/gtest/TestUTF.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "nsString.h"
+#include "nsStringBuffer.h"
+#include "nsReadableUtils.h"
+#include "UTFStrings.h"
+#include "nsUnicharUtils.h"
+#include "mozilla/HashFunctions.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+namespace TestUTF {
+
+TEST(UTF, Valid)
+{
+ for (unsigned int i = 0; i < ArrayLength(ValidStrings); ++i) {
+ nsDependentCString str8(ValidStrings[i].m8);
+ nsDependentString str16(ValidStrings[i].m16);
+
+ EXPECT_TRUE(NS_ConvertUTF16toUTF8(str16).Equals(str8));
+
+ EXPECT_TRUE(NS_ConvertUTF8toUTF16(str8).Equals(str16));
+
+ nsCString tmp8("string ");
+ AppendUTF16toUTF8(str16, tmp8);
+ EXPECT_TRUE(tmp8.Equals(NS_LITERAL_CSTRING("string ") + str8));
+
+ nsString tmp16(NS_LITERAL_STRING("string "));
+ AppendUTF8toUTF16(str8, tmp16);
+ EXPECT_TRUE(tmp16.Equals(NS_LITERAL_STRING("string ") + str16));
+
+ EXPECT_EQ(CompareUTF8toUTF16(str8, str16), 0);
+ }
+}
+
+TEST(UTF, Invalid16)
+{
+ for (unsigned int i = 0; i < ArrayLength(Invalid16Strings); ++i) {
+ nsDependentString str16(Invalid16Strings[i].m16);
+ nsDependentCString str8(Invalid16Strings[i].m8);
+
+ EXPECT_TRUE(NS_ConvertUTF16toUTF8(str16).Equals(str8));
+
+ nsCString tmp8("string ");
+ AppendUTF16toUTF8(str16, tmp8);
+ EXPECT_TRUE(tmp8.Equals(NS_LITERAL_CSTRING("string ") + str8));
+
+ EXPECT_EQ(CompareUTF8toUTF16(str8, str16), 0);
+ }
+}
+
+TEST(UTF, Invalid8)
+{
+ for (unsigned int i = 0; i < ArrayLength(Invalid8Strings); ++i) {
+ nsDependentString str16(Invalid8Strings[i].m16);
+ nsDependentCString str8(Invalid8Strings[i].m8);
+
+ EXPECT_TRUE(NS_ConvertUTF8toUTF16(str8).Equals(str16));
+
+ nsString tmp16(NS_LITERAL_STRING("string "));
+ AppendUTF8toUTF16(str8, tmp16);
+ EXPECT_TRUE(tmp16.Equals(NS_LITERAL_STRING("string ") + str16));
+
+ EXPECT_EQ(CompareUTF8toUTF16(str8, str16), 0);
+ }
+}
+
+TEST(UTF, Malformed8)
+{
+// Don't run this test in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ for (unsigned int i = 0; i < ArrayLength(Malformed8Strings); ++i) {
+ nsDependentCString str8(Malformed8Strings[i]);
+
+ EXPECT_TRUE(NS_ConvertUTF8toUTF16(str8).IsEmpty());
+
+ nsString tmp16(NS_LITERAL_STRING("string"));
+ AppendUTF8toUTF16(str8, tmp16);
+ EXPECT_TRUE(tmp16.EqualsLiteral("string"));
+
+ EXPECT_NE(CompareUTF8toUTF16(str8, EmptyString()), 0);
+ }
+#endif
+}
+
+TEST(UTF, Hash16)
+{
+ for (unsigned int i = 0; i < ArrayLength(ValidStrings); ++i) {
+ nsDependentCString str8(ValidStrings[i].m8);
+ bool err;
+ EXPECT_EQ(HashString(ValidStrings[i].m16),
+ HashUTF8AsUTF16(str8.get(), str8.Length(), &err));
+ EXPECT_FALSE(err);
+ }
+
+ for (unsigned int i = 0; i < ArrayLength(Invalid8Strings); ++i) {
+ nsDependentCString str8(Invalid8Strings[i].m8);
+ bool err;
+ EXPECT_EQ(HashString(Invalid8Strings[i].m16),
+ HashUTF8AsUTF16(str8.get(), str8.Length(), &err));
+ EXPECT_FALSE(err);
+ }
+
+// Don't run this test in debug builds as that intentionally asserts.
+#ifndef DEBUG
+ for (unsigned int i = 0; i < ArrayLength(Malformed8Strings); ++i) {
+ nsDependentCString str8(Malformed8Strings[i]);
+ bool err;
+ EXPECT_EQ(HashUTF8AsUTF16(str8.get(), str8.Length(), &err), 0u);
+ EXPECT_TRUE(err);
+ }
+#endif
+}
+
+/**
+ * This tests the handling of a non-ascii character at various locations in a
+ * UTF-16 string that is being converted to UTF-8.
+ */
+void NonASCII16_helper(const size_t aStrSize)
+{
+ const size_t kTestSize = aStrSize;
+ const size_t kMaxASCII = 0x80;
+ const char16_t kUTF16Char = 0xC9;
+ const char kUTF8Surrogates[] = { char(0xC3), char(0x89) };
+
+ // Generate a string containing only ASCII characters.
+ nsString asciiString;
+ asciiString.SetLength(kTestSize);
+ nsCString asciiCString;
+ asciiCString.SetLength(kTestSize);
+
+ auto str_buff = asciiString.BeginWriting();
+ auto cstr_buff = asciiCString.BeginWriting();
+ for (size_t i = 0; i < kTestSize; i++) {
+ str_buff[i] = i % kMaxASCII;
+ cstr_buff[i] = i % kMaxASCII;
+ }
+
+ // Now go through and test conversion when exactly one character will
+ // result in a multibyte sequence.
+ for (size_t i = 0; i < kTestSize; i++) {
+ // Setup the UTF-16 string.
+ nsString unicodeString(asciiString);
+ auto buff = unicodeString.BeginWriting();
+ buff[i] = kUTF16Char;
+
+ // Do the conversion, make sure the length increased by 1.
+ nsCString dest;
+ AppendUTF16toUTF8(unicodeString, dest);
+ EXPECT_EQ(dest.Length(), unicodeString.Length() + 1);
+
+ // Build up the expected UTF-8 string.
+ nsCString expected;
+
+ // First add the leading ASCII chars.
+ expected.Append(asciiCString.BeginReading(), i);
+
+ // Now append the UTF-8 surrogate pair we expect the UTF-16 unicode char to
+ // be converted to.
+ for (auto& c : kUTF8Surrogates) {
+ expected.Append(c);
+ }
+
+ // And finish with the trailing ASCII chars.
+ expected.Append(asciiCString.BeginReading() + i + 1, kTestSize - i - 1);
+
+ EXPECT_STREQ(dest.BeginReading(), expected.BeginReading());
+ }
+}
+
+TEST(UTF, NonASCII16)
+{
+ // Test with various string sizes to catch any special casing.
+ NonASCII16_helper(1);
+ NonASCII16_helper(8);
+ NonASCII16_helper(16);
+ NonASCII16_helper(32);
+ NonASCII16_helper(512);
+}
+
+} // namespace TestUTF
diff --git a/xpcom/tests/gtest/TestXPIDLString.cpp b/xpcom/tests/gtest/TestXPIDLString.cpp
new file mode 100644
index 000000000..b26cc9aff
--- /dev/null
+++ b/xpcom/tests/gtest/TestXPIDLString.cpp
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsXPIDLString.h"
+#include "gtest/gtest.h"
+
+static void
+nsXPIDLStringTest_Value(char16_t** aResult)
+{
+ *aResult = ToNewUnicode(NS_LITERAL_STRING("Hello, World"));
+}
+
+TEST(XPIDLString, Main)
+{
+ nsXPIDLString s1;
+ nsXPIDLStringTest_Value(getter_Copies(s1));
+ EXPECT_TRUE(s1.EqualsLiteral("Hello, World"));
+}
+
diff --git a/xpcom/tests/gtest/UTFStrings.h b/xpcom/tests/gtest/UTFStrings.h
new file mode 100644
index 000000000..ec6bf15d3
--- /dev/null
+++ b/xpcom/tests/gtest/UTFStrings.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef utfstrings_h__
+#define utfstrings_h__
+
+struct UTFStringsStringPair
+ {
+ char16_t m16[16];
+ char m8[16];
+ };
+
+static const UTFStringsStringPair ValidStrings[] =
+ {
+ { { 'a', 'b', 'c', 'd' },
+ { 'a', 'b', 'c', 'd' } },
+ { { '1', '2', '3', '4' },
+ { '1', '2', '3', '4' } },
+ { { 0x7F, 'A', 0x80, 'B', 0x101, 0x200 },
+ { 0x7F, 'A', char(0xC2), char(0x80), 'B', char(0xC4), char(0x81), char(0xC8), char(0x80) } },
+ { { 0x7FF, 0x800, 0x1000 },
+ { char(0xDF), char(0xBF), char(0xE0), char(0xA0), char(0x80), char(0xE1), char(0x80), char(0x80) } },
+ { { 0xD7FF, 0xE000, 0xF00F, 'A', 0xFFF0 },
+ { char(0xED), char(0x9F), char(0xBF), char(0xEE), char(0x80), char(0x80), char(0xEF), char(0x80), char(0x8F), 'A', char(0xEF), char(0xBF), char(0xB0) } },
+ { { 0xFFF7, 0xFFFC, 0xFFFD, 0xFFFD },
+ { char(0xEF), char(0xBF), char(0xB7), char(0xEF), char(0xBF), char(0xBC), char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xD800, 0xDC00, 0xD800, 0xDCFF },
+ { char(0xF0), char(0x90), char(0x80), char(0x80), char(0xF0), char(0x90), char(0x83), char(0xBF) } },
+ { { 0xDBFF, 0xDFFF, 0xDBB7, 0xDCBA },
+ { char(0xF4), char(0x8F), char(0xBF), char(0xBF), char(0xF3), char(0xBD), char(0xB2), char(0xBA) } },
+ { { 0xFFFD, 0xFFFF },
+ { char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBF) } },
+ { { 0xFFFD, 0xFFFE, 0xFFFF },
+ { char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBE), char(0xEF), char(0xBF), char(0xBF) } },
+ };
+
+static const UTFStringsStringPair Invalid16Strings[] =
+ {
+ { { 'a', 'b', 0xD800 },
+ { 'a', 'b', char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xD8FF, 'b' },
+ { char(0xEF), char(0xBF), char(0xBD), 'b' } },
+ { { 0xD821 },
+ { char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xDC21 },
+ { char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xDC00, 0xD800, 'b' },
+ { char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBD), 'b' } },
+ { { 'b', 0xDC00, 0xD800 },
+ { 'b', char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xDC00, 0xD800 },
+ { char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xDC00, 0xD800, 0xDC00, 0xD800 },
+ { char(0xEF), char(0xBF), char(0xBD), char(0xF0), char(0x90), char(0x80), char(0x80), char(0xEF), char(0xBF), char(0xBD) } },
+ { { 0xDC00, 0xD800, 0xD800, 0xDC00 },
+ { char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBD), char(0xF0), char(0x90), char(0x80), char(0x80) } },
+ };
+
+static const UTFStringsStringPair Invalid8Strings[] =
+ {
+ { { 'a', 0xFFFD, 'b' },
+ { 'a', char(0xC0), char(0x80), 'b' } },
+ { { 0xFFFD, 0x80 },
+ { char(0xC1), char(0xBF), char(0xC2), char(0x80) } },
+ { { 0xFFFD },
+ { char(0xC1), char(0xBF) } },
+ { { 0xFFFD, 'x', 0x0800 },
+ { char(0xE0), char(0x80), char(0x80), 'x', char(0xE0), char(0xA0), char(0x80) } },
+ { { 0xFFFD, 'x', 0xFFFD },
+ { char(0xF0), char(0x80), char(0x80), char(0x80), 'x', char(0xF0), char(0x80), char(0x8F), char(0x80) } },
+ { { 0xFFFD, 0xFFFD },
+ { char(0xF4), char(0x90), char(0x80), char(0x80), char(0xF7), char(0xBF), char(0xBF), char(0xBF) } },
+ { { 0xFFFD, 'x', 0xD800, 0xDC00, 0xFFFD },
+ { char(0xF0), char(0x8F), char(0xBF), char(0xBF), 'x', char(0xF0), char(0x90), char(0x80), char(0x80), char(0xF0), char(0x8F), char(0xBF), char(0xBF) } },
+ { { 0xFFFD, 'x', 0xFFFD },
+ { char(0xF8), char(0x80), char(0x80), char(0x80), char(0x80), 'x', char(0xF8), char(0x88), char(0x80), char(0x80), char(0x80) } },
+ { { 0xFFFD, 0xFFFD },
+ { char(0xFB), char(0xBF), char(0xBF), char(0xBF), char(0xBF), char(0xFC), char(0xA0), char(0x80), char(0x80), char(0x80), char(0x80) } },
+ { { 0xFFFD, 0xFFFD },
+ { char(0xFC), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0xFD), char(0xBF), char(0xBF), char(0xBF), char(0xBF), char(0xBF) } },
+ };
+
+// Don't use this array in debug builds as that intentionally asserts.
+#ifndef DEBUG
+static const char Malformed8Strings[][16] =
+ {
+ { char(0x80) },
+ { 'a', char(0xC8), 'c' },
+ { 'a', char(0xC0) },
+ { 'a', char(0xE8), 'c' },
+ { 'a', char(0xE8), char(0x80), 'c' },
+ { 'a', char(0xE8), char(0x80) },
+ { char(0xE8), 0x7F, char(0x80) },
+ { 'a', char(0xE8), char(0xE8), char(0x80) },
+ { 'a', char(0xF4) },
+ { 'a', char(0xF4), char(0x80), char(0x80), 'c', 'c' },
+ { 'a', char(0xF4), char(0x80), 'x', char(0x80) },
+ { char(0xF4), char(0x80), char(0x80), char(0x80), char(0x80) },
+ { 'a', char(0xFA), 'c' },
+ { 'a', char(0xFA), char(0x80), char(0x80), 0x7F, char(0x80), 'c' },
+ { 'a', char(0xFA), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), 'c' },
+ { 'a', char(0xFD) },
+ { 'a', char(0xFD), char(0x80), char(0x80), char(0x80), char(0x80), 'c' },
+ { 'a', char(0xFD), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80) },
+ { 'a', char(0xFC), char(0x80), char(0x80), 0x40, char(0x80), char(0x80), 'c' },
+ };
+#endif
+
+#endif
diff --git a/xpcom/tests/gtest/moz.build b/xpcom/tests/gtest/moz.build
new file mode 100644
index 000000000..53836eaef
--- /dev/null
+++ b/xpcom/tests/gtest/moz.build
@@ -0,0 +1,77 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'Helpers.cpp',
+ 'TestAtoms.cpp',
+ 'TestAutoPtr.cpp',
+ 'TestAutoRef.cpp',
+ 'TestBase64.cpp',
+ 'TestCallTemplates.cpp',
+ 'TestCloneInputStream.cpp',
+ 'TestCOMArray.cpp',
+ 'TestCOMPtrEq.cpp',
+ 'TestCRT.cpp',
+ 'TestEncoding.cpp',
+ 'TestEscapeURL.cpp',
+ 'TestExpirationTracker.cpp',
+ 'TestFile.cpp',
+ 'TestID.cpp',
+ 'TestNSPRLogModulesParser.cpp',
+ 'TestObserverArray.cpp',
+ 'TestObserverService.cpp',
+ 'TestPipes.cpp',
+ 'TestPLDHash.cpp',
+ 'TestPriorityQueue.cpp',
+ 'TestRacingServiceManager.cpp',
+ 'TestSlicedInputStream.cpp',
+ 'TestSnappyStreams.cpp',
+ 'TestStateWatching.cpp',
+ 'TestStorageStream.cpp',
+ 'TestStrings.cpp',
+ 'TestStringStream.cpp',
+ 'TestSynchronization.cpp',
+ 'TestTArray.cpp',
+ 'TestTArray2.cpp',
+ 'TestTextFormatter.cpp',
+ 'TestThreadPool.cpp',
+ 'TestThreadPoolListener.cpp',
+ 'TestThreads.cpp',
+ 'TestThreadUtils.cpp',
+ 'TestTimers.cpp',
+ 'TestTimeStamp.cpp',
+ 'TestTokenizer.cpp',
+ 'TestUTF.cpp',
+ 'TestXPIDLString.cpp',
+]
+
+if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT') and CONFIG['OS_TARGET'] != 'Android':
+ # FIXME bug 523392: TestDeadlockDetector doesn't like Windows
+ # Bug 1054249: Doesn't work on Android
+ UNIFIED_SOURCES += [
+ 'TestDeadlockDetector.cpp',
+ 'TestDeadlockDetectorScalability.cpp',
+ ]
+
+if CONFIG['WRAP_STL_INCLUDES'] and not CONFIG['CLANG_CL']:
+ UNIFIED_SOURCES += [
+ 'TestSTLWrappers.cpp',
+ ]
+
+# Compile TestAllocReplacement separately so Windows headers don't pollute
+# the global namespace for other files.
+SOURCES += [
+ 'TestAllocReplacement.cpp',
+ 'TestCOMPtr.cpp', # Redefines IFoo and IBar
+ 'TestHashtables.cpp', # Redefines IFoo
+ 'TestNsRefPtr.cpp', # Redefines Foo
+]
+
+LOCAL_INCLUDES += [
+ '../../base',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
diff --git a/xpcom/tests/moz.build b/xpcom/tests/moz.build
new file mode 100644
index 000000000..b5fc138e7
--- /dev/null
+++ b/xpcom/tests/moz.build
@@ -0,0 +1,51 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+TEST_DIRS += [
+ 'external',
+ 'component',
+ 'bug656331_component',
+ 'component_no_aslr',
+ 'gtest',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ TEST_DIRS += ['windows']
+
+EXPORTS.testing += [
+ 'TestHarness.h',
+]
+
+SimplePrograms([
+ 'TestArguments',
+ 'TestBlockingProcess',
+ 'TestPRIntN',
+ 'TestQuickReturn',
+ 'TestUnicodeArguments',
+])
+
+XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
+
+if CONFIG['COMPILE_ENVIRONMENT']:
+ TEST_HARNESS_FILES.xpcshell.xpcom.tests.unit += [
+ '!/dist/bin/components/xpcomtest.xpt',
+ ]
+
+XPIDL_MODULE = 'xpcomtest'
+XPIDL_SOURCES += [
+ 'NotXPCOMTest.idl',
+]
+
+# Don't add our test-only .xpt files to the normal manifests
+XPIDL_NO_MANIFEST = True
+
+LOCAL_INCLUDES += [
+ '../ds',
+]
+
+RESOURCE_FILES += [
+ 'test.properties',
+]
diff --git a/xpcom/tests/resources.h b/xpcom/tests/resources.h
new file mode 100644
index 000000000..4deea759f
--- /dev/null
+++ b/xpcom/tests/resources.h
@@ -0,0 +1,19 @@
+/* -*- 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 resources_h___
+#define resources_h___
+
+#define TIMER_1SECOND 40000
+#define TIMER_5SECOND 40001
+#define TIMER_10SECOND 40002
+
+#define TIMER_1REPEAT 40003
+#define TIMER_5REPEAT 40004
+#define TIMER_10REPEAT 40005
+
+#define TIMER_CANCEL 40006
+#define TIMER_EXIT 40010
+
+#endif /* resources_h___ */
diff --git a/xpcom/tests/test.properties b/xpcom/tests/test.properties
new file mode 100644
index 000000000..19cae9702
--- /dev/null
+++ b/xpcom/tests/test.properties
@@ -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/.
+1=1
+ 2=2
+3 =3
+ 4 =4
+5=5
+6= 6
+7=7
+8= 8
+# this is a comment
+9=this is the first part of a continued line \
+ and here is the 2nd part
diff --git a/xpcom/tests/unit/bug725015.manifest b/xpcom/tests/unit/bug725015.manifest
new file mode 100644
index 000000000..2f432f0b2
--- /dev/null
+++ b/xpcom/tests/unit/bug725015.manifest
@@ -0,0 +1,3 @@
+category bug725015-test-category bug725015-category-entry @bug725015.test.contract
+component {05070380-6e6e-42ba-aaa5-3289fc55ca5a} dummyfile.js
+contract @bug725015.test.contract {05070380-6e6e-42ba-aaa5-3289fc55ca5a}
diff --git a/xpcom/tests/unit/compmgr_warnings.manifest b/xpcom/tests/unit/compmgr_warnings.manifest
new file mode 100644
index 000000000..6b60990db
--- /dev/null
+++ b/xpcom/tests/unit/compmgr_warnings.manifest
@@ -0,0 +1,9 @@
+# The following line is malformed (mismatched braces)
+component {94b346d7-0cde-4e6e-b819-95d6f200bbf6 MyComponent.js
+
+component 94b346d7-0cde-4e6e-b819-95d6f200bbf7 MyComponent.js
+# The following line re-registers an existing CID
+component {94b346d7-0cde-4e6e-b819-95d6f200bbf7} MyOtherComponent.js
+
+# The following line maps a contractID to a non-existent CID
+contract @testing/foo {0c07730f-f875-436b-8deb-90c4251920ec}
diff --git a/xpcom/tests/unit/data/SmallApp.app/Contents/Info.plist b/xpcom/tests/unit/data/SmallApp.app/Contents/Info.plist
new file mode 100644
index 000000000..8388fa2a5
--- /dev/null
+++ b/xpcom/tests/unit/data/SmallApp.app/Contents/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>SmallApp</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.SmallApp</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>SmallApp</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/xpcom/tests/unit/data/SmallApp.app/Contents/MacOS/SmallApp b/xpcom/tests/unit/data/SmallApp.app/Contents/MacOS/SmallApp
new file mode 100755
index 000000000..c821003d3
--- /dev/null
+++ b/xpcom/tests/unit/data/SmallApp.app/Contents/MacOS/SmallApp
Binary files differ
diff --git a/xpcom/tests/unit/data/SmallApp.app/Contents/PkgInfo b/xpcom/tests/unit/data/SmallApp.app/Contents/PkgInfo
new file mode 100644
index 000000000..bd04210fb
--- /dev/null
+++ b/xpcom/tests/unit/data/SmallApp.app/Contents/PkgInfo
@@ -0,0 +1 @@
+APPL???? \ No newline at end of file
diff --git a/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/InfoPlist.strings b/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/InfoPlist.strings
new file mode 100644
index 000000000..5e45963c3
--- /dev/null
+++ b/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/designable.nib b/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/designable.nib
new file mode 100644
index 000000000..59f8803c5
--- /dev/null
+++ b/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/designable.nib
@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.02">
+ <data>
+ <int key="IBDocument.SystemTarget">0</int>
+ <string key="IBDocument.SystemVersion">9E17</string>
+ <string key="IBDocument.InterfaceBuilderVersion">644</string>
+ <string key="IBDocument.AppKitVersion">949.33</string>
+ <string key="IBDocument.HIToolboxVersion">352.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="29"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">AMainMenu</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">NewApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="35465992">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="591987212">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <string key="NSTitle">NewApplication</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit NewApplication</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ </object>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">File</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ <object class="NSMenuItem" id="952259628">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Edit</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ <object class="NSMenuItem" id="626404410">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Format</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ <object class="NSMenuItem" id="713487014">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Window</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ <object class="NSMenuItem" id="391199113">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Help</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="591987212"/>
+ </object>
+ </object>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">369</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1049">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="1049"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="713487014"/>
+ <reference ref="694149608"/>
+ <reference ref="391199113"/>
+ <reference ref="952259628"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="626404410"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">MainMenu</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="713487014"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="110575045"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">103</int>
+ <reference key="object" ref="391199113"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ <string key="objectName">1</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">217</int>
+ <reference key="object" ref="952259628"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="632727374"/>
+ </object>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">1111</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">299</int>
+ <reference key="object" ref="626404410"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>103.IBPluginDependency</string>
+ <string>103.ImportedFromIB2</string>
+ <string>136.IBPluginDependency</string>
+ <string>136.ImportedFromIB2</string>
+ <string>19.IBPluginDependency</string>
+ <string>19.ImportedFromIB2</string>
+ <string>217.IBPluginDependency</string>
+ <string>217.ImportedFromIB2</string>
+ <string>29.IBEditorWindowLastContentRect</string>
+ <string>29.IBPluginDependency</string>
+ <string>29.ImportedFromIB2</string>
+ <string>29.WindowOrigin</string>
+ <string>29.editorWindowContentRectSynchronizationRect</string>
+ <string>295.IBPluginDependency</string>
+ <string>299.IBPluginDependency</string>
+ <string>56.IBPluginDependency</string>
+ <string>56.ImportedFromIB2</string>
+ <string>57.IBEditorWindowLastContentRect</string>
+ <string>57.IBPluginDependency</string>
+ <string>57.ImportedFromIB2</string>
+ <string>57.editorWindowContentRectSynchronizationRect</string>
+ <string>83.IBPluginDependency</string>
+ <string>83.ImportedFromIB2</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1" id="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{0, 975}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{74, 862}</string>
+ <string>{{6, 978}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{12, 952}, {218, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{23, 794}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">374</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes"/>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../SmallApp.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib b/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
new file mode 100644
index 000000000..bb27d4a5d
--- /dev/null
+++ b/xpcom/tests/unit/data/SmallApp.app/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/xpcom/tests/unit/data/bug121341-2.properties b/xpcom/tests/unit/data/bug121341-2.properties
new file mode 100644
index 000000000..f7885e4fc
--- /dev/null
+++ b/xpcom/tests/unit/data/bug121341-2.properties
@@ -0,0 +1,9 @@
+# this file contains invalid UTF-8 sequence
+# no property should be loaded
+
+1 = test
+
+# property with invalid UTF-8 sequence (0xa0)
+2 = a b
+
+3 = test2
diff --git a/xpcom/tests/unit/data/bug121341.properties b/xpcom/tests/unit/data/bug121341.properties
new file mode 100644
index 000000000..b45fc9698
--- /dev/null
+++ b/xpcom/tests/unit/data/bug121341.properties
@@ -0,0 +1,68 @@
+# simple check
+1=abc
+# test whitespace trimming in key and value
+ 2 = xy
+# test parsing of escaped values
+3 = \u1234\t\r\n\uAB\
+\u1\n
+# test multiline properties
+4 = this is \
+multiline property
+5 = this is \
+ another multiline property
+# property with DOS EOL
+6 = test\u0036
+# test multiline property with with DOS EOL
+7 = yet another multi\
+ line propery
+# trimming should not trim escaped whitespaces
+8 = \ttest5\u0020
+# another variant of #8
+9 = \ test6\t
+# test UTF-8 encoded property/value
+10aሴb = c췯d
+# next property should test unicode escaping at the boundary of parsing buffer
+# buffer size is expected to be 4096 so add comments to get to this offset
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+################################################################################
+###############################################################################
+11 = \uABCD
diff --git a/xpcom/tests/unit/data/child_process_directive_service.js b/xpcom/tests/unit/data/child_process_directive_service.js
new file mode 100644
index 000000000..9bc1c3206
--- /dev/null
+++ b/xpcom/tests/unit/data/child_process_directive_service.js
@@ -0,0 +1,21 @@
+/* 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/. */
+Components.utils.import("resource:///modules/XPCOMUtils.jsm");
+
+function TestProcessDirective() {}
+TestProcessDirective.prototype = {
+
+ /* Boilerplate */
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupportsString]),
+ contractID: "@mozilla.org/xpcom/tests/ChildProcessDirectiveTest;1",
+ classID: Components.ID("{4bd1ba60-45c4-11e4-916c-0800200c9a66}"),
+
+ type: Components.interfaces.nsISupportsString.TYPE_STRING,
+ data: "child process",
+ toString: function() {
+ return this.data;
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestProcessDirective]);
diff --git a/xpcom/tests/unit/data/iniparser01-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser01-utf16leBOM.ini
new file mode 100644
index 000000000..46b134b19
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser01-utf16leBOM.ini
@@ -0,0 +1 @@
+ÿþ \ No newline at end of file
diff --git a/xpcom/tests/unit/data/iniparser01-utf8BOM.ini b/xpcom/tests/unit/data/iniparser01-utf8BOM.ini
new file mode 100644
index 000000000..5f282702b
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser01-utf8BOM.ini
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/xpcom/tests/unit/data/iniparser01.ini b/xpcom/tests/unit/data/iniparser01.ini
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser01.ini
diff --git a/xpcom/tests/unit/data/iniparser02-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser02-utf16leBOM.ini
new file mode 100644
index 000000000..49cc8ef0e
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser02-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser02-utf8BOM.ini b/xpcom/tests/unit/data/iniparser02-utf8BOM.ini
new file mode 100644
index 000000000..e02abfc9b
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser02-utf8BOM.ini
@@ -0,0 +1 @@
+
diff --git a/xpcom/tests/unit/data/iniparser02.ini b/xpcom/tests/unit/data/iniparser02.ini
new file mode 100644
index 000000000..d3f5a12fa
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser02.ini
@@ -0,0 +1 @@
+
diff --git a/xpcom/tests/unit/data/iniparser03-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser03-utf16leBOM.ini
new file mode 100644
index 000000000..05255100a
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser03-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser03-utf8BOM.ini b/xpcom/tests/unit/data/iniparser03-utf8BOM.ini
new file mode 100644
index 000000000..b76e44e19
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser03-utf8BOM.ini
@@ -0,0 +1 @@
+[]
diff --git a/xpcom/tests/unit/data/iniparser03.ini b/xpcom/tests/unit/data/iniparser03.ini
new file mode 100644
index 000000000..60b074253
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser03.ini
@@ -0,0 +1 @@
+[]
diff --git a/xpcom/tests/unit/data/iniparser04-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser04-utf16leBOM.ini
new file mode 100644
index 000000000..e95d97113
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser04-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser04-utf8BOM.ini b/xpcom/tests/unit/data/iniparser04-utf8BOM.ini
new file mode 100644
index 000000000..47ef32c0a
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser04-utf8BOM.ini
@@ -0,0 +1 @@
+[section1]
diff --git a/xpcom/tests/unit/data/iniparser04.ini b/xpcom/tests/unit/data/iniparser04.ini
new file mode 100644
index 000000000..23a50d155
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser04.ini
@@ -0,0 +1 @@
+[section1]
diff --git a/xpcom/tests/unit/data/iniparser05-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser05-utf16leBOM.ini
new file mode 100644
index 000000000..a49491816
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser05-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser05-utf8BOM.ini b/xpcom/tests/unit/data/iniparser05-utf8BOM.ini
new file mode 100644
index 000000000..eb33b5ccf
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser05-utf8BOM.ini
@@ -0,0 +1 @@
+[section1]junk
diff --git a/xpcom/tests/unit/data/iniparser05.ini b/xpcom/tests/unit/data/iniparser05.ini
new file mode 100644
index 000000000..ade137337
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser05.ini
@@ -0,0 +1 @@
+[section1]junk
diff --git a/xpcom/tests/unit/data/iniparser06-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser06-utf16leBOM.ini
new file mode 100644
index 000000000..e9023ac7c
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser06-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser06-utf8BOM.ini b/xpcom/tests/unit/data/iniparser06-utf8BOM.ini
new file mode 100644
index 000000000..073d841cf
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser06-utf8BOM.ini
@@ -0,0 +1,2 @@
+[section1]
+
diff --git a/xpcom/tests/unit/data/iniparser06.ini b/xpcom/tests/unit/data/iniparser06.ini
new file mode 100644
index 000000000..c24821e6e
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser06.ini
@@ -0,0 +1,2 @@
+[section1]
+
diff --git a/xpcom/tests/unit/data/iniparser07-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser07-utf16leBOM.ini
new file mode 100644
index 000000000..d1c167e6e
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser07-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser07-utf8BOM.ini b/xpcom/tests/unit/data/iniparser07-utf8BOM.ini
new file mode 100644
index 000000000..38176d944
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser07-utf8BOM.ini
@@ -0,0 +1,2 @@
+[section1]
+name1
diff --git a/xpcom/tests/unit/data/iniparser07.ini b/xpcom/tests/unit/data/iniparser07.ini
new file mode 100644
index 000000000..49816873b
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser07.ini
@@ -0,0 +1,2 @@
+[section1]
+name1
diff --git a/xpcom/tests/unit/data/iniparser08-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser08-utf16leBOM.ini
new file mode 100644
index 000000000..e450566a0
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser08-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser08-utf8BOM.ini b/xpcom/tests/unit/data/iniparser08-utf8BOM.ini
new file mode 100644
index 000000000..5fa7d2495
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser08-utf8BOM.ini
@@ -0,0 +1,2 @@
+[section1]
+name1=
diff --git a/xpcom/tests/unit/data/iniparser08.ini b/xpcom/tests/unit/data/iniparser08.ini
new file mode 100644
index 000000000..cfa15c9ff
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser08.ini
@@ -0,0 +1,2 @@
+[section1]
+name1=
diff --git a/xpcom/tests/unit/data/iniparser09-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser09-utf16leBOM.ini
new file mode 100644
index 000000000..ef1da39e2
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser09-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser09-utf8BOM.ini b/xpcom/tests/unit/data/iniparser09-utf8BOM.ini
new file mode 100644
index 000000000..e3edce4d4
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser09-utf8BOM.ini
@@ -0,0 +1,2 @@
+[section1]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser09.ini b/xpcom/tests/unit/data/iniparser09.ini
new file mode 100644
index 000000000..1c87762ba
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser09.ini
@@ -0,0 +1,2 @@
+[section1]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser10-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser10-utf16leBOM.ini
new file mode 100644
index 000000000..e5e70b661
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser10-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser10-utf8BOM.ini b/xpcom/tests/unit/data/iniparser10-utf8BOM.ini
new file mode 100644
index 000000000..bda15fcc7
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser10-utf8BOM.ini
@@ -0,0 +1,3 @@
+
+[section1]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser10.ini b/xpcom/tests/unit/data/iniparser10.ini
new file mode 100644
index 000000000..037fd7930
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser10.ini
@@ -0,0 +1,3 @@
+
+[section1]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser11-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser11-utf16leBOM.ini
new file mode 100644
index 000000000..932d4004b
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser11-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser11-utf8BOM.ini b/xpcom/tests/unit/data/iniparser11-utf8BOM.ini
new file mode 100644
index 000000000..78caafaba
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser11-utf8BOM.ini
@@ -0,0 +1,3 @@
+# comment
+[section1]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser11.ini b/xpcom/tests/unit/data/iniparser11.ini
new file mode 100644
index 000000000..f8d573a28
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser11.ini
@@ -0,0 +1,3 @@
+# comment
+[section1]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser12-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser12-utf16leBOM.ini
new file mode 100644
index 000000000..8a2912722
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser12-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser12-utf8BOM.ini b/xpcom/tests/unit/data/iniparser12-utf8BOM.ini
new file mode 100644
index 000000000..09ca62779
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser12-utf8BOM.ini
@@ -0,0 +1,3 @@
+[section1]
+# [sectionBAD]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser12.ini b/xpcom/tests/unit/data/iniparser12.ini
new file mode 100644
index 000000000..2727940c0
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser12.ini
@@ -0,0 +1,3 @@
+[section1]
+# [sectionBAD]
+name1=value1
diff --git a/xpcom/tests/unit/data/iniparser13-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser13-utf16leBOM.ini
new file mode 100644
index 000000000..ebd9a51d3
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser13-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser13-utf8BOM.ini b/xpcom/tests/unit/data/iniparser13-utf8BOM.ini
new file mode 100644
index 000000000..8c9499b66
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser13-utf8BOM.ini
@@ -0,0 +1,3 @@
+[section1]
+name1=value1
+# nameBAD=valueBAD
diff --git a/xpcom/tests/unit/data/iniparser13.ini b/xpcom/tests/unit/data/iniparser13.ini
new file mode 100644
index 000000000..21d40b140
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser13.ini
@@ -0,0 +1,3 @@
+[section1]
+name1=value1
+# nameBAD=valueBAD
diff --git a/xpcom/tests/unit/data/iniparser14-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser14-utf16leBOM.ini
new file mode 100644
index 000000000..bbc3413aa
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser14-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser14-utf8BOM.ini b/xpcom/tests/unit/data/iniparser14-utf8BOM.ini
new file mode 100644
index 000000000..d109052c8
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser14-utf8BOM.ini
@@ -0,0 +1,6 @@
+[section1]
+name1=value1
+name2=value2
+[section2]
+name1=value1
+name2=foopy
diff --git a/xpcom/tests/unit/data/iniparser14.ini b/xpcom/tests/unit/data/iniparser14.ini
new file mode 100644
index 000000000..744af4cb6
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser14.ini
@@ -0,0 +1,6 @@
+[section1]
+name1=value1
+name2=value2
+[section2]
+name1=value1
+name2=foopy
diff --git a/xpcom/tests/unit/data/iniparser15-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser15-utf16leBOM.ini
new file mode 100644
index 000000000..e60525dec
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser15-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser15-utf8BOM.ini b/xpcom/tests/unit/data/iniparser15-utf8BOM.ini
new file mode 100644
index 000000000..172803f90
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser15-utf8BOM.ini
@@ -0,0 +1,6 @@
+[section1]
+name1=value1
+[section2]
+name1=foopy
+[section1]
+name1=newValue1
diff --git a/xpcom/tests/unit/data/iniparser15.ini b/xpcom/tests/unit/data/iniparser15.ini
new file mode 100644
index 000000000..608a27d8f
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser15.ini
@@ -0,0 +1,6 @@
+[section1]
+name1=value1
+[section2]
+name1=foopy
+[section1]
+name1=newValue1
diff --git a/xpcom/tests/unit/data/iniparser16-utf16leBOM.ini b/xpcom/tests/unit/data/iniparser16-utf16leBOM.ini
new file mode 100644
index 000000000..142b17590
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser16-utf16leBOM.ini
Binary files differ
diff --git a/xpcom/tests/unit/data/iniparser16-utf8BOM.ini b/xpcom/tests/unit/data/iniparser16-utf8BOM.ini
new file mode 100644
index 000000000..bba1018da
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser16-utf8BOM.ini
@@ -0,0 +1,13 @@
+#á¿»á¹Ò³Ï–·Ì˄ȡǨŅ©&
+[☺♫]
+#ѼΏá¹Ò³Ï–
+♫=☻
+#·Ì˄ȡǨŅ©
+♪=♥
+#‽ἧᵿΏá¹Ò³
+#ϖ·Ì˄ȡǨŅ©&
+[☼]
+♣=♠
+♦=♥
+#‽ἧᵿΏá¹Ò³
+#·Ì˄ȡǨŅ©
diff --git a/xpcom/tests/unit/data/iniparser16.ini b/xpcom/tests/unit/data/iniparser16.ini
new file mode 100644
index 000000000..b94607d15
--- /dev/null
+++ b/xpcom/tests/unit/data/iniparser16.ini
@@ -0,0 +1,13 @@
+#á¿»á¹Ò³Ï–·Ì˄ȡǨŅ©&
+[☺♫]
+#ѼΏá¹Ò³Ï–
+♫=☻
+#·Ì˄ȡǨŅ©
+♪=♥
+#‽ἧᵿΏá¹Ò³
+#ϖ·Ì˄ȡǨŅ©&
+[☼]
+♣=♠
+♦=♥
+#‽ἧᵿΏá¹Ò³
+#·Ì˄ȡǨŅ©
diff --git a/xpcom/tests/unit/data/main_process_directive_service.js b/xpcom/tests/unit/data/main_process_directive_service.js
new file mode 100644
index 000000000..f4eed11c9
--- /dev/null
+++ b/xpcom/tests/unit/data/main_process_directive_service.js
@@ -0,0 +1,21 @@
+/* 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/. */
+Components.utils.import("resource:///modules/XPCOMUtils.jsm");
+
+function TestProcessDirective() {}
+TestProcessDirective.prototype = {
+
+ /* Boilerplate */
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupportsString]),
+ contractID: "@mozilla.org/xpcom/tests/MainProcessDirectiveTest;1",
+ classID: Components.ID("{9b6f4160-45be-11e4-916c-0800200c9a66}"),
+
+ type: Components.interfaces.nsISupportsString.TYPE_STRING,
+ data: "main process",
+ toString: function() {
+ return this.data;
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestProcessDirective]);
diff --git a/xpcom/tests/unit/data/presentation.key/.typeAttributes.dict b/xpcom/tests/unit/data/presentation.key/.typeAttributes.dict
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/xpcom/tests/unit/data/presentation.key/.typeAttributes.dict
diff --git a/xpcom/tests/unit/data/presentation.key/Contents/PkgInfo b/xpcom/tests/unit/data/presentation.key/Contents/PkgInfo
new file mode 100644
index 000000000..b0bc8e076
--- /dev/null
+++ b/xpcom/tests/unit/data/presentation.key/Contents/PkgInfo
@@ -0,0 +1 @@
+???????? \ No newline at end of file
diff --git a/xpcom/tests/unit/data/presentation.key/index.apxl.gz b/xpcom/tests/unit/data/presentation.key/index.apxl.gz
new file mode 100644
index 000000000..26178d809
--- /dev/null
+++ b/xpcom/tests/unit/data/presentation.key/index.apxl.gz
Binary files differ
diff --git a/xpcom/tests/unit/data/presentation.key/thumbs/st0.tiff b/xpcom/tests/unit/data/presentation.key/thumbs/st0.tiff
new file mode 100644
index 000000000..8b49316b4
--- /dev/null
+++ b/xpcom/tests/unit/data/presentation.key/thumbs/st0.tiff
Binary files differ
diff --git a/xpcom/tests/unit/data/process_directive.manifest b/xpcom/tests/unit/data/process_directive.manifest
new file mode 100644
index 000000000..9cbf7f241
--- /dev/null
+++ b/xpcom/tests/unit/data/process_directive.manifest
@@ -0,0 +1,5 @@
+component {9b6f4160-45be-11e4-916c-0800200c9a66} main_process_directive_service.js process=main
+contract @mozilla.org/xpcom/tests/MainProcessDirectiveTest;1 {9b6f4160-45be-11e4-916c-0800200c9a66} process=main
+
+component {4bd1ba60-45c4-11e4-916c-0800200c9a66} child_process_directive_service.js process=content
+contract @mozilla.org/xpcom/tests/ChildProcessDirectiveTest;1 {4bd1ba60-45c4-11e4-916c-0800200c9a66} process=content
diff --git a/xpcom/tests/unit/head_xpcom.js b/xpcom/tests/unit/head_xpcom.js
new file mode 100644
index 000000000..eacd5b4e6
--- /dev/null
+++ b/xpcom/tests/unit/head_xpcom.js
@@ -0,0 +1,21 @@
+function get_test_program(prog)
+{
+ var progPath = do_get_cwd();
+ progPath.append(prog);
+ progPath.leafName = progPath.leafName + mozinfo.bin_suffix;
+ return progPath;
+}
+
+function set_process_running_environment()
+{
+ var envSvc = Components.classes["@mozilla.org/process/environment;1"].
+ getService(Components.interfaces.nsIEnvironment);
+ var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties);
+ var greBinDir = dirSvc.get("GreBinD", Components.interfaces.nsIFile);
+ envSvc.set("DYLD_LIBRARY_PATH", greBinDir.path);
+ // For Linux
+ envSvc.set("LD_LIBRARY_PATH", greBinDir.path);
+ //XXX: handle windows
+}
+
diff --git a/xpcom/tests/unit/test_bug121341.js b/xpcom/tests/unit/test_bug121341.js
new file mode 100644
index 000000000..3aa04186e
--- /dev/null
+++ b/xpcom/tests/unit/test_bug121341.js
@@ -0,0 +1,71 @@
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+function run_test() {
+ var ios = Components.classes["@mozilla.org/network/io-service;1"].
+ getService(Components.interfaces.nsIIOService);
+
+ var dataFile = do_get_file("data/bug121341.properties");
+ var channel = NetUtil.newChannel({
+ uri: ios.newFileURI(dataFile, null, null),
+ loadUsingSystemPrincipal: true
+ });
+ var inp = channel.open2();
+
+ var properties = Components.classes["@mozilla.org/persistent-properties;1"].
+ createInstance(Components.interfaces.nsIPersistentProperties);
+ properties.load(inp);
+
+ var value;
+
+ value = properties.getStringProperty("1");
+ do_check_eq(value, "abc");
+
+ value = properties.getStringProperty("2");
+ do_check_eq(value, "xy");
+
+ value = properties.getStringProperty("3");
+ do_check_eq(value, "\u1234\t\r\n\u00AB\u0001\n");
+
+ value = properties.getStringProperty("4");
+ do_check_eq(value, "this is multiline property");
+
+ value = properties.getStringProperty("5");
+ do_check_eq(value, "this is another multiline property");
+
+ value = properties.getStringProperty("6");
+ do_check_eq(value, "test\u0036");
+
+ value = properties.getStringProperty("7");
+ do_check_eq(value, "yet another multiline propery");
+
+ value = properties.getStringProperty("8");
+ do_check_eq(value, "\ttest5\u0020");
+
+ value = properties.getStringProperty("9");
+ do_check_eq(value, " test6\t");
+
+ value = properties.getStringProperty("10a\u1234b");
+ do_check_eq(value, "c\uCDEFd");
+
+ value = properties.getStringProperty("11");
+ do_check_eq(value, "\uABCD");
+
+ dataFile = do_get_file("data/bug121341-2.properties");
+
+ var channel = NetUtil.newChannel({
+ uri: ios.newFileURI(dataFile, null, null),
+ loadUsingSystemPrincipal: true
+ });
+ inp = channel.open2();
+
+ var properties2 = Components.classes["@mozilla.org/persistent-properties;1"].
+ createInstance(Components.interfaces.nsIPersistentProperties);
+ try {
+ properties2.load(inp);
+ do_throw("load() didn't fail");
+ }
+ catch (e) {
+ }
+}
diff --git a/xpcom/tests/unit/test_bug325418.js b/xpcom/tests/unit/test_bug325418.js
new file mode 100644
index 000000000..b18866d7e
--- /dev/null
+++ b/xpcom/tests/unit/test_bug325418.js
@@ -0,0 +1,63 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+// 5 seconds.
+const kExpectedDelay1 = 5;
+// 1 second.
+const kExpectedDelay2 = 1;
+
+var gStartTime1;
+var gStartTime2;
+var timer;
+
+var observer1 = {
+ observe: function observeTC1(subject, topic, data) {
+ if (topic == "timer-callback") {
+ // Stop timer, so it doesn't repeat (if test runs slowly).
+ timer.cancel();
+
+ // Actual delay may not be exact, so convert to seconds and round.
+ do_check_eq(Math.round((Date.now() - gStartTime1) / 1000),
+ kExpectedDelay1);
+
+ timer = null;
+
+ do_print("1st timer triggered (before being cancelled). Should not have happened!");
+ do_check_true(false);
+ }
+ }
+};
+
+var observer2 = {
+ observe: function observeTC2(subject, topic, data) {
+ if (topic == "timer-callback") {
+ // Stop timer, so it doesn't repeat (if test runs slowly).
+ timer.cancel();
+
+ // Actual delay may not be exact, so convert to seconds and round.
+ do_check_eq(Math.round((Date.now() - gStartTime2) / 1000),
+ kExpectedDelay2);
+
+ timer = null;
+
+ do_test_finished();
+ }
+ }
+};
+
+function run_test() {
+ do_test_pending();
+
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+ // Initialize the timer (with some delay), then cancel it.
+ gStartTime1 = Date.now();
+ timer.init(observer1, kExpectedDelay1 * 1000,
+ timer.TYPE_REPEATING_PRECISE_CAN_SKIP);
+ timer.cancel();
+
+ // Re-initialize the timer (with a different delay).
+ gStartTime2 = Date.now();
+ timer.init(observer2, kExpectedDelay2 * 1000,
+ timer.TYPE_REPEATING_PRECISE_CAN_SKIP);
+}
diff --git a/xpcom/tests/unit/test_bug332389.js b/xpcom/tests/unit/test_bug332389.js
new file mode 100644
index 000000000..91f8943e8
--- /dev/null
+++ b/xpcom/tests/unit/test_bug332389.js
@@ -0,0 +1,19 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function run_test() {
+ var f =
+ Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).get("CurProcD", Ci.nsIFile);
+
+ var terminated = false;
+ for (var i = 0; i < 100; i++) {
+ if (f == null) {
+ terminated = true;
+ break;
+ }
+ f = f.parent;
+ }
+
+ do_check_true(terminated);
+}
diff --git a/xpcom/tests/unit/test_bug333505.js b/xpcom/tests/unit/test_bug333505.js
new file mode 100644
index 000000000..856468605
--- /dev/null
+++ b/xpcom/tests/unit/test_bug333505.js
@@ -0,0 +1,10 @@
+function run_test()
+{
+ var dirEntries = do_get_cwd().directoryEntries;
+
+ while (dirEntries.hasMoreElements())
+ dirEntries.getNext();
+
+ // We ensure there is no crash
+ dirEntries.hasMoreElements();
+}
diff --git a/xpcom/tests/unit/test_bug364285-1.js b/xpcom/tests/unit/test_bug364285-1.js
new file mode 100644
index 000000000..f8bd6ee15
--- /dev/null
+++ b/xpcom/tests/unit/test_bug364285-1.js
@@ -0,0 +1,51 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+var nameArray = [
+ "ascii", // ASCII
+ "fran\u00E7ais", // Latin-1
+ "\u0420\u0443\u0441\u0441\u043A\u0438\u0439", // Cyrillic
+ "\u65E5\u672C\u8A9E", // Japanese
+ "\u4E2D\u6587", // Chinese
+ "\uD55C\uAD6D\uC5B4", // Korean
+ "\uD801\uDC0F\uD801\uDC2D\uD801\uDC3B\uD801\uDC2B" // Deseret
+];
+
+function getTempDir()
+{
+ var dirService = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties);
+ return dirService.get("TmpD", Ci.nsILocalFile);
+}
+
+function create_file(fileName)
+{
+ var outFile = getTempDir();
+ outFile.append(fileName);
+ outFile.createUnique(outFile.NORMAL_FILE_TYPE, 0o600);
+
+ var stream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(outFile, 0x02 | 0x08 | 0x20, 0o600, 0);
+ stream.write("foo", 3);
+ stream.close();
+
+ do_check_eq(outFile.leafName.substr(0, fileName.length), fileName);
+
+ return outFile;
+}
+
+function test_create(fileName)
+{
+ var file1 = create_file(fileName);
+ var file2 = create_file(fileName);
+ file1.remove(false);
+ file2.remove(false);
+}
+
+function run_test()
+{
+ for (var i = 0; i < nameArray.length; ++i) {
+ test_create(nameArray[i]);
+ }
+}
diff --git a/xpcom/tests/unit/test_bug374754.js b/xpcom/tests/unit/test_bug374754.js
new file mode 100644
index 000000000..9b0ccf26e
--- /dev/null
+++ b/xpcom/tests/unit/test_bug374754.js
@@ -0,0 +1,59 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+var addedTopic = "xpcom-category-entry-added";
+var removedTopic = "xpcom-category-entry-removed";
+var testCategory = "bug-test-category";
+var testEntry = "@mozilla.org/bug-test-entry;1";
+
+var testValue= "check validity";
+var result = "";
+var expected = "add remove add remove ";
+var timer;
+
+var observer = {
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ observe: function(subject, topic, data) {
+ if (topic == "timer-callback") {
+ do_check_eq(result, expected);
+
+ var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+ observerService.removeObserver(this, addedTopic);
+ observerService.removeObserver(this, removedTopic);
+
+ do_test_finished();
+
+ timer = null;
+ }
+
+ if (subject.QueryInterface(Ci.nsISupportsCString).data != testEntry || data != testCategory)
+ return;
+
+ if (topic == addedTopic)
+ result += "add ";
+ else if (topic == removedTopic)
+ result += "remove ";
+ }
+};
+
+function run_test() {
+ do_test_pending();
+
+ var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+ observerService.addObserver(observer, addedTopic, false);
+ observerService.addObserver(observer, removedTopic, false);
+
+ var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+ categoryManager.addCategoryEntry(testCategory, testEntry, testValue, false, true);
+ categoryManager.addCategoryEntry(testCategory, testEntry, testValue, false, true);
+ categoryManager.deleteCategoryEntry(testCategory, testEntry, false);
+
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.init(observer, 0, timer.TYPE_ONE_SHOT);
+}
diff --git a/xpcom/tests/unit/test_bug476919.js b/xpcom/tests/unit/test_bug476919.js
new file mode 100644
index 000000000..21cd9253e
--- /dev/null
+++ b/xpcom/tests/unit/test_bug476919.js
@@ -0,0 +1,27 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function run_test() {
+ // skip this test on Windows
+ if (mozinfo.os != "win") {
+ var testDir = __LOCATION__.parent;
+ // create a test file, then symlink it, then check that we think it's a symlink
+ var targetFile = testDir.clone();
+ targetFile.append("target.txt");
+ if (!targetFile.exists())
+ targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
+
+ var link = testDir.clone();
+ link.append("link");
+ if (link.exists())
+ link.remove(false);
+
+ var ln = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ ln.initWithPath("/bin/ln");
+ var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+ process.init(ln);
+ var args = ["-s", targetFile.path, link.path];
+ process.run(true, args, args.length);
+ do_check_true(link.isSymlink());
+ }
+}
diff --git a/xpcom/tests/unit/test_bug478086.js b/xpcom/tests/unit/test_bug478086.js
new file mode 100644
index 000000000..bd6ea0d08
--- /dev/null
+++ b/xpcom/tests/unit/test_bug478086.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+function run_test() {
+ var nsILocalFile = Components.interfaces.nsILocalFile;
+ var root = Components.classes["@mozilla.org/file/local;1"].
+ createInstance(nsILocalFile);
+
+ // copied from http://mxr.mozilla.org/mozilla-central/source/image/test/unit/test_imgtools.js#135
+ // nsIXULRuntime.OS doesn't seem to be available in xpcshell, so we'll use
+ // this as a kludgy way to figure out if we're running on Windows.
+ if (mozinfo.os == "win") {
+ root.initWithPath("\\\\.");
+ } else {
+ return; // XXX disabled, since this causes intermittent failures on Mac (bug 481369).
+ root.initWithPath("/");
+ }
+ var drives = root.directoryEntries;
+ do_check_true(drives.hasMoreElements());
+ while (drives.hasMoreElements()) {
+ var newPath = drives.getNext().QueryInterface(nsILocalFile).path;
+ do_check_eq(newPath.indexOf("\0"), -1);
+ }
+}
diff --git a/xpcom/tests/unit/test_bug656331.js b/xpcom/tests/unit/test_bug656331.js
new file mode 100644
index 000000000..3bc1f82c0
--- /dev/null
+++ b/xpcom/tests/unit/test_bug656331.js
@@ -0,0 +1,39 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function info(s) {
+ dump("TEST-INFO | test_bug656331.js | " + s + "\n");
+}
+
+var gMessageExpected = /Native module.*has version 3.*expected/;
+var gFound = false;
+
+const kConsoleListener = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),
+
+ observe: function listener_observe(message) {
+ if (gMessageExpected.test(message.message))
+ gFound = true;
+ }
+};
+
+function run_test() {
+ let cs = Components.classes["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService);
+ cs.registerListener(kConsoleListener);
+
+ let manifest = do_get_file('components/bug656331.manifest');
+ registerAppManifest(manifest);
+
+ do_check_false("{f18fb09b-28b4-4435-bc5b-8027f18df743}" in Components.classesByID);
+
+ do_test_pending();
+ Components.classes["@mozilla.org/thread-manager;1"].
+ getService(Ci.nsIThreadManager).mainThread.dispatch(function() {
+ cs.unregisterListener(kConsoleListener);
+ do_check_true(gFound);
+ do_test_finished();
+ }, 0);
+}
diff --git a/xpcom/tests/unit/test_bug725015.js b/xpcom/tests/unit/test_bug725015.js
new file mode 100644
index 000000000..d6f62c509
--- /dev/null
+++ b/xpcom/tests/unit/test_bug725015.js
@@ -0,0 +1,39 @@
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const manifest = do_get_file('bug725015.manifest');
+const contract = "@bug725015.test.contract";
+const observerTopic = "xpcom-category-entry-added";
+const category = "bug725015-test-category";
+const entry = "bug725015-category-entry";
+const cid = Components.ID("{05070380-6e6e-42ba-aaa5-3289fc55ca5a}");
+
+function observe_category(subj, topic, data) {
+ try {
+ do_check_eq(topic, observerTopic);
+ if (data != category)
+ return;
+
+ var thisentry = subj.QueryInterface(Ci.nsISupportsCString).data;
+ do_check_eq(thisentry, entry);
+
+ do_check_eq(Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager).getCategoryEntry(category, entry), contract);
+ do_check_true(Cc[contract].equals(cid));
+ }
+ catch (e) {
+ do_throw(e);
+ }
+ do_test_finished();
+}
+
+function run_test() {
+ do_test_pending();
+ Services.obs.addObserver(observe_category, observerTopic, false);
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(manifest);
+}
diff --git a/xpcom/tests/unit/test_bug745466.js b/xpcom/tests/unit/test_bug745466.js
new file mode 100644
index 000000000..22a911ac5
--- /dev/null
+++ b/xpcom/tests/unit/test_bug745466.js
@@ -0,0 +1,6 @@
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+
+function run_test()
+{
+ do_check_true(FileUtils.File("~").equals(FileUtils.getDir("Home", [])));
+}
diff --git a/xpcom/tests/unit/test_comp_no_aslr.js b/xpcom/tests/unit/test_comp_no_aslr.js
new file mode 100644
index 000000000..7e15731c6
--- /dev/null
+++ b/xpcom/tests/unit/test_comp_no_aslr.js
@@ -0,0 +1,18 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function run_test() {
+ let manifest = do_get_file('components/testcompnoaslr.manifest');
+ registerAppManifest(manifest);
+ var sysInfo = Cc["@mozilla.org/system-info;1"].
+ getService(Ci.nsIPropertyBag2);
+ var ver = parseFloat(sysInfo.getProperty("version"));
+ if (ver < 6.0) {
+ // This is disabled on pre-Vista OSs.
+ do_check_true("{335fb596-e52d-418f-b01c-1bf16ce5e7e4}" in Components.classesByID);
+ } else {
+ do_check_false("{335fb596-e52d-418f-b01c-1bf16ce5e7e4}" in Components.classesByID);
+ }
+}
diff --git a/xpcom/tests/unit/test_compmgr_warnings.js b/xpcom/tests/unit/test_compmgr_warnings.js
new file mode 100644
index 000000000..be77b0d1b
--- /dev/null
+++ b/xpcom/tests/unit/test_compmgr_warnings.js
@@ -0,0 +1,71 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function info(s) {
+ dump("TEST-INFO | test_compmgr_warnings.js | " + s + "\n");
+}
+
+var gMessagesExpected = [
+ { line: 2, message: /Malformed CID/, found: false },
+ { line: 6, message: /re-register/, found: false },
+ { line: 9, message: /Could not/, found: false },
+ { line: 2, message: /binary component twice/, found: false },
+ { line: 3, message: /binary component twice/, found: false },
+];
+
+const kConsoleListener = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),
+
+ observe: function listener_observe(message) {
+ if (!(message instanceof Ci.nsIScriptError)) {
+ info("Not a script error: " + message.message);
+ return;
+ }
+
+ info("Script error... " + message.sourceName + ":" + message.lineNumber + ": " + message.errorMessage);
+ for (let expected of gMessagesExpected) {
+ if (message.lineNumber != expected.line)
+ continue;
+
+ if (!expected.message.test(message.errorMessage))
+ continue;
+
+ info("Found expected message: " + expected.message);
+ do_check_false(expected.found);
+
+ expected.found = true;
+ }
+ }
+};
+
+function run_deferred_event(fn) {
+ do_test_pending();
+ Components.classes["@mozilla.org/thread-manager;1"].
+ getService(Ci.nsIThreadManager).mainThread.dispatch(function() {
+ fn();
+ do_test_finished();
+ }, 0);
+}
+
+function run_test()
+{
+ let cs = Components.classes["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService);
+ cs.registerListener(kConsoleListener);
+
+ var manifest = do_get_file('compmgr_warnings.manifest');
+ registerAppManifest(manifest);
+ manifest = do_get_file('components/testcomponent.manifest');
+ registerAppManifest(manifest);
+
+ run_deferred_event(function() {
+ cs.unregisterListener(kConsoleListener);
+
+ for (let expected of gMessagesExpected) {
+ info("checking " + expected.message);
+ do_check_true(expected.found);
+ }
+ });
+}
diff --git a/xpcom/tests/unit/test_debugger_malloc_size_of.js b/xpcom/tests/unit/test_debugger_malloc_size_of.js
new file mode 100644
index 000000000..450d62793
--- /dev/null
+++ b/xpcom/tests/unit/test_debugger_malloc_size_of.js
@@ -0,0 +1,34 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// This is just a sanity test that Gecko is giving SpiderMonkey a MallocSizeOf
+// function for new JSRuntimes. There is more extensive testing around the
+// expected byte sizes within SpiderMonkey's jit-tests, we just want to make
+// sure that Gecko is providing SpiderMonkey with the callback it needs.
+
+var Cu = Components.utils;
+const { byteSize } = Cu.getJSTestingFunctions();
+
+function run_test()
+{
+ const objects = [
+ {},
+ { w: 1, x: 2, y: 3, z:4, a: 5 },
+ [],
+ Array(10).fill(null),
+ new RegExp("(2|two) problems", "g"),
+ new Date(),
+ new Uint8Array(64),
+ Promise.resolve(1),
+ function f() {},
+ Object
+ ];
+
+ for (let obj of objects) {
+ do_print(uneval(obj));
+ ok(byteSize(obj), "We should get some (non-zero) byte size");
+ }
+}
diff --git a/xpcom/tests/unit/test_file_createUnique.js b/xpcom/tests/unit/test_file_createUnique.js
new file mode 100644
index 000000000..1ab204bab
--- /dev/null
+++ b/xpcom/tests/unit/test_file_createUnique.js
@@ -0,0 +1,29 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+
+function run_test()
+{
+ // Generate a leaf name that is 255 characters long.
+ var longLeafName = new Array(256).join("T");
+
+ // Generate the path for a file located in a directory with a long name.
+ var tempFile = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
+ tempFile.append(longLeafName);
+ tempFile.append("test.txt");
+
+ try {
+ tempFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+ do_throw("Creating an item in a folder with a very long name should throw");
+ }
+ catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH)) {
+ // We expect the function not to crash but to raise this exception.
+ }
+}
diff --git a/xpcom/tests/unit/test_file_equality.js b/xpcom/tests/unit/test_file_equality.js
new file mode 100644
index 000000000..235792560
--- /dev/null
+++ b/xpcom/tests/unit/test_file_equality.js
@@ -0,0 +1,43 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+var Cr = Components.results;
+var Ci = Components.interfaces;
+
+var CC = Components.Constructor;
+var LocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
+
+function run_test()
+{
+ test_normalized_vs_non_normalized();
+}
+
+function test_normalized_vs_non_normalized()
+{
+ // get a directory that exists on all platforms
+ var dirProvider = Components.classes["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+ var tmp1 = dirProvider.get("TmpD", Ci.nsILocalFile);
+ var exists = tmp1.exists();
+ do_check_true(exists);
+ if (!exists)
+ return;
+
+ // the test logic below assumes we're starting with a normalized path, but the
+ // default location on macos is a symbolic link, so resolve it before starting
+ tmp1.normalize();
+
+ // this has the same exact path as tmp1, it should equal tmp1
+ var tmp2 = new LocalFile(tmp1.path);
+ do_check_true(tmp1.equals(tmp2));
+
+ // this is a non-normalized version of tmp1, it should not equal tmp1
+ tmp2.appendRelativePath(".");
+ do_check_false(tmp1.equals(tmp2));
+
+ // normalize and make sure they are equivalent again
+ tmp2.normalize();
+ do_check_true(tmp1.equals(tmp2));
+}
diff --git a/xpcom/tests/unit/test_file_renameTo.js b/xpcom/tests/unit/test_file_renameTo.js
new file mode 100644
index 000000000..0a7196fe2
--- /dev/null
+++ b/xpcom/tests/unit/test_file_renameTo.js
@@ -0,0 +1,61 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function run_test()
+{
+ // Create the base directory.
+ let base = Cc['@mozilla.org/file/directory_service;1']
+ .getService(Ci.nsIProperties)
+ .get('TmpD', Ci.nsILocalFile);
+ base.append('renameTesting');
+ if (base.exists()) {
+ base.remove(true);
+ }
+ base.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0777', 8));
+
+ // Create a sub directory under the base.
+ let subdir = base.clone();
+ subdir.append('subdir');
+ subdir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0777', 8));
+
+ // Create a file under the sub directory.
+ let tempFile = subdir.clone();
+ tempFile.append('file0.txt');
+ tempFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt('0777', 8));
+
+ // Test renameTo in the base directory
+ tempFile.renameTo(null, 'file1.txt');
+ do_check_true(exists(subdir, 'file1.txt'));
+
+ // Test moving across directories
+ tempFile = subdir.clone();
+ tempFile.append('file1.txt');
+ tempFile.renameTo(base, '');
+ do_check_true(exists(base, 'file1.txt'));
+
+ // Test moving across directories and renaming at the same time
+ tempFile = base.clone();
+ tempFile.append('file1.txt');
+ tempFile.renameTo(subdir, 'file2.txt');
+ do_check_true(exists(subdir, 'file2.txt'));
+
+ // Test moving a directory
+ subdir.renameTo(base, 'renamed');
+ do_check_true(exists(base, 'renamed'));
+ let renamed = base.clone();
+ renamed.append('renamed');
+ do_check_true(exists(renamed, 'file2.txt'));
+
+ base.remove(true);
+}
+
+function exists(parent, filename) {
+ let file = parent.clone();
+ file.append(filename);
+ return file.exists();
+}
diff --git a/xpcom/tests/unit/test_hidden_files.js b/xpcom/tests/unit/test_hidden_files.js
new file mode 100644
index 000000000..3383ba11b
--- /dev/null
+++ b/xpcom/tests/unit/test_hidden_files.js
@@ -0,0 +1,28 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+const NS_OS_TEMP_DIR = "TmpD";
+
+const CWD = do_get_cwd();
+
+var hiddenUnixFile;
+function createUNIXHiddenFile() {
+ var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+ var tmpDir = dirSvc.get(NS_OS_TEMP_DIR, Ci.nsIFile);
+ hiddenUnixFile = tmpDir.clone();
+ hiddenUnixFile.append(".foo");
+ // we don't care if this already exists because we don't care
+ // about the file's contents (just the name)
+ if (!hiddenUnixFile.exists())
+ hiddenUnixFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ return hiddenUnixFile.exists();
+}
+
+function run_test() {
+ // Skip this test on Windows
+ if (mozinfo.os == "win")
+ return;
+
+ do_check_true(createUNIXHiddenFile());
+ do_check_true(hiddenUnixFile.isHidden());
+}
+
diff --git a/xpcom/tests/unit/test_home.js b/xpcom/tests/unit/test_home.js
new file mode 100644
index 000000000..61fa5b344
--- /dev/null
+++ b/xpcom/tests/unit/test_home.js
@@ -0,0 +1,24 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+const CWD = do_get_cwd();
+function checkOS(os) {
+ const nsILocalFile_ = "nsILocalFile" + os;
+ return nsILocalFile_ in Components.interfaces &&
+ CWD instanceof Components.interfaces[nsILocalFile_];
+}
+
+const isWin = checkOS("Win");
+
+function run_test() {
+ var envVar = isWin ? "USERPROFILE" : "HOME";
+
+ var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+ var homeDir = dirSvc.get("Home", Ci.nsIFile);
+
+ var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ var expected = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ expected.initWithPath(env.get(envVar));
+
+ do_check_eq(homeDir.path, expected.path);
+}
diff --git a/xpcom/tests/unit/test_iniProcessor.js b/xpcom/tests/unit/test_iniProcessor.js
new file mode 100644
index 000000000..3d3886d35
--- /dev/null
+++ b/xpcom/tests/unit/test_iniProcessor.js
@@ -0,0 +1,288 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cr = Components.results;
+
+var testnum = 0;
+var factory;
+
+function parserForFile(filename) {
+ let parser = null;
+ try {
+ let file = do_get_file(filename);
+ do_check_true(!!file);
+ parser = factory.createINIParser(file);
+ do_check_true(!!parser);
+ } catch(e) {
+ dump("INFO | caught error: " + e);
+ // checkParserOutput will handle a null parser when it's expected.
+ }
+ return parser;
+
+}
+
+function checkParserOutput(parser, expected) {
+ // If the expected output is null, we expect the parser to have
+ // failed (and vice-versa).
+ if (!parser || !expected) {
+ do_check_eq(parser, null);
+ do_check_eq(expected, null);
+ return;
+ }
+
+ let output = getParserOutput(parser);
+ for (let section in expected) {
+ do_check_true(section in output);
+ for (let key in expected[section]) {
+ do_check_true(key in output[section]);
+ do_check_eq(output[section][key], expected[section][key]);
+ delete output[section][key];
+ }
+ for (let key in output[section])
+ do_check_eq(key, "wasn't expecting this key!");
+ delete output[section];
+ }
+ for (let section in output)
+ do_check_eq(section, "wasn't expecting this section!");
+}
+
+function getParserOutput(parser) {
+ let output = {};
+
+ let sections = parser.getSections();
+ do_check_true(!!sections);
+ while (sections.hasMore()) {
+ let section = sections.getNext();
+ do_check_false(section in output); // catch dupes
+ output[section] = {};
+
+ let keys = parser.getKeys(section);
+ do_check_true(!!keys);
+ while (keys.hasMore()) {
+ let key = keys.getNext();
+ do_check_false(key in output[section]); // catch dupes
+ let value = parser.getString(section, key);
+ output[section][key] = value;
+ }
+ }
+ return output;
+}
+
+function run_test() {
+try {
+
+var testdata = [
+ { filename: "data/iniparser01.ini", reference: {} },
+ { filename: "data/iniparser02.ini", reference: {} },
+ { filename: "data/iniparser03.ini", reference: {} },
+ { filename: "data/iniparser04.ini", reference: {} },
+ { filename: "data/iniparser05.ini", reference: {} },
+ { filename: "data/iniparser06.ini", reference: {} },
+ { filename: "data/iniparser07.ini", reference: {} },
+ { filename: "data/iniparser08.ini", reference: { section1: { name1: "" }} },
+ { filename: "data/iniparser09.ini", reference: { section1: { name1: "value1" } } },
+ { filename: "data/iniparser10.ini", reference: { section1: { name1: "value1" } } },
+ { filename: "data/iniparser11.ini", reference: { section1: { name1: "value1" } } },
+ { filename: "data/iniparser12.ini", reference: { section1: { name1: "value1" } } },
+ { filename: "data/iniparser13.ini", reference: { section1: { name1: "value1" } } },
+ { filename: "data/iniparser14.ini", reference:
+ { section1: { name1: "value1", name2: "value2" },
+ section2: { name1: "value1", name2: "foopy" }} },
+ { filename: "data/iniparser15.ini", reference:
+ { section1: { name1: "newValue1" },
+ section2: { name1: "foopy" }} },
+ { filename: "data/iniparser16.ini", reference:
+ { "☺♫": { "♫": "☻", "♪": "♥" },
+ "☼": { "♣": "♠", "♦": "♥" }} },
+
+ ];
+
+ testdata.push( { filename: "data/iniparser01-utf8BOM.ini",
+ reference: testdata[0].reference } );
+ testdata.push( { filename: "data/iniparser02-utf8BOM.ini",
+ reference: testdata[1].reference } );
+ testdata.push( { filename: "data/iniparser03-utf8BOM.ini",
+ reference: testdata[2].reference } );
+ testdata.push( { filename: "data/iniparser04-utf8BOM.ini",
+ reference: testdata[3].reference } );
+ testdata.push( { filename: "data/iniparser05-utf8BOM.ini",
+ reference: testdata[4].reference } );
+ testdata.push( { filename: "data/iniparser06-utf8BOM.ini",
+ reference: testdata[5].reference } );
+ testdata.push( { filename: "data/iniparser07-utf8BOM.ini",
+ reference: testdata[6].reference } );
+ testdata.push( { filename: "data/iniparser08-utf8BOM.ini",
+ reference: testdata[7].reference } );
+ testdata.push( { filename: "data/iniparser09-utf8BOM.ini",
+ reference: testdata[8].reference } );
+ testdata.push( { filename: "data/iniparser10-utf8BOM.ini",
+ reference: testdata[9].reference } );
+ testdata.push( { filename: "data/iniparser11-utf8BOM.ini",
+ reference: testdata[10].reference } );
+ testdata.push( { filename: "data/iniparser12-utf8BOM.ini",
+ reference: testdata[11].reference } );
+ testdata.push( { filename: "data/iniparser13-utf8BOM.ini",
+ reference: testdata[12].reference } );
+ testdata.push( { filename: "data/iniparser14-utf8BOM.ini",
+ reference: testdata[13].reference } );
+ testdata.push( { filename: "data/iniparser15-utf8BOM.ini",
+ reference: testdata[14].reference } );
+ testdata.push( { filename: "data/iniparser16-utf8BOM.ini",
+ reference: testdata[15].reference } );
+
+ let os = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime).OS;
+ if("WINNT" === os) {
+ testdata.push( { filename: "data/iniparser01-utf16leBOM.ini",
+ reference: testdata[0].reference } );
+ testdata.push( { filename: "data/iniparser02-utf16leBOM.ini",
+ reference: testdata[1].reference } );
+ testdata.push( { filename: "data/iniparser03-utf16leBOM.ini",
+ reference: testdata[2].reference } );
+ testdata.push( { filename: "data/iniparser04-utf16leBOM.ini",
+ reference: testdata[3].reference } );
+ testdata.push( { filename: "data/iniparser05-utf16leBOM.ini",
+ reference: testdata[4].reference } );
+ testdata.push( { filename: "data/iniparser06-utf16leBOM.ini",
+ reference: testdata[5].reference } );
+ testdata.push( { filename: "data/iniparser07-utf16leBOM.ini",
+ reference: testdata[6].reference } );
+ testdata.push( { filename: "data/iniparser08-utf16leBOM.ini",
+ reference: testdata[7].reference } );
+ testdata.push( { filename: "data/iniparser09-utf16leBOM.ini",
+ reference: testdata[8].reference } );
+ testdata.push( { filename: "data/iniparser10-utf16leBOM.ini",
+ reference: testdata[9].reference } );
+ testdata.push( { filename: "data/iniparser11-utf16leBOM.ini",
+ reference: testdata[10].reference } );
+ testdata.push( { filename: "data/iniparser12-utf16leBOM.ini",
+ reference: testdata[11].reference } );
+ testdata.push( { filename: "data/iniparser13-utf16leBOM.ini",
+ reference: testdata[12].reference } );
+ testdata.push( { filename: "data/iniparser14-utf16leBOM.ini",
+ reference: testdata[13].reference } );
+ testdata.push( { filename: "data/iniparser15-utf16leBOM.ini",
+ reference: testdata[14].reference } );
+ testdata.push( { filename: "data/iniparser16-utf16leBOM.ini",
+ reference: testdata[15].reference } );
+ }
+
+/* ========== 0 ========== */
+factory = Cc["@mozilla.org/xpcom/ini-processor-factory;1"].
+ getService(Ci.nsIINIParserFactory);
+do_check_true(!!factory);
+
+// Test reading from a variety of files. While we're at it, write out each one
+// and read it back to ensure that nothing changed.
+while (testnum < testdata.length) {
+ dump("\nINFO | test #" + ++testnum);
+ let filename = testdata[testnum -1].filename;
+ dump(", filename " + filename + "\n");
+ let parser = parserForFile(filename);
+ checkParserOutput(parser, testdata[testnum - 1].reference);
+ if (!parser)
+ continue;
+ do_check_true(parser instanceof Ci.nsIINIParserWriter);
+ // write contents out to a new file
+ let newfilename = filename + ".new";
+ let newfile = do_get_file(filename);
+ newfile.leafName += ".new";
+ parser.writeFile(newfile);
+ // read new file and make sure the contents are the same.
+ parser = parserForFile(newfilename);
+ checkParserOutput(parser, testdata[testnum - 1].reference);
+ // cleanup after the test
+ newfile.remove(false);
+}
+
+dump("INFO | test #" + ++testnum + "\n");
+
+// test writing to a new file.
+var newfile = do_get_file("data/");
+newfile.append("nonexistent-file.ini");
+if (newfile.exists())
+ newfile.remove(false);
+do_check_false(newfile.exists());
+
+var parser = factory.createINIParser(newfile);
+do_check_true(!!parser);
+do_check_true(parser instanceof Ci.nsIINIParserWriter);
+checkParserOutput(parser, {});
+parser.writeFile();
+do_check_true(newfile.exists());
+
+// test adding a new section and new key
+parser.setString("section", "key", "value");
+parser.writeFile();
+do_check_true(newfile.exists());
+checkParserOutput(parser, {section: {key: "value"} });
+// read it in again, check for same data.
+parser = parserForFile("data/nonexistent-file.ini");
+checkParserOutput(parser, {section: {key: "value"} });
+// cleanup after the test
+newfile.remove(false);
+
+dump("INFO | test #" + ++testnum + "\n");
+
+// test modifying a existing key's value (in an existing section)
+parser = parserForFile("data/iniparser09.ini");
+checkParserOutput(parser, {section1: {name1: "value1"} });
+
+do_check_true(parser instanceof Ci.nsIINIParserWriter);
+parser.setString("section1", "name1", "value2");
+checkParserOutput(parser, {section1: {name1: "value2"} });
+
+dump("INFO | test #" + ++testnum + "\n");
+
+// test trying to set illegal characters
+var caughtError;
+caughtError = false;
+checkParserOutput(parser, {section1: {name1: "value2"} });
+
+// Bad characters in section name
+try { parser.SetString("bad\0", "ok", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("bad\r", "ok", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("bad\n", "ok", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("bad[", "ok", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("bad]", "ok", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+
+// Bad characters in key name
+caughtError = false;
+try { parser.SetString("ok", "bad\0", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("ok", "bad\r", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("ok", "bad\n", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("ok", "bad=", "ok"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+
+// Bad characters in value
+caughtError = false;
+try { parser.SetString("ok", "ok", "bad\0"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("ok", "ok", "bad\r"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("ok", "ok", "bad\n"); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+caughtError = false;
+try { parser.SetString("ok", "ok", "bad="); } catch (e) { caughtError = true; }
+do_check_true(caughtError);
+
+} catch(e) {
+ throw "FAILED in test #" + testnum + " -- " + e;
+}
+}
diff --git a/xpcom/tests/unit/test_ioutil.js b/xpcom/tests/unit/test_ioutil.js
new file mode 100644
index 000000000..ef27f584f
--- /dev/null
+++ b/xpcom/tests/unit/test_ioutil.js
@@ -0,0 +1,33 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+
+const util = Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
+
+function run_test()
+{
+ try {
+ util.inputStreamIsBuffered(null);
+ do_throw("inputStreamIsBuffered should have thrown");
+ } catch (e) {
+ do_check_eq(e.result, Cr.NS_ERROR_INVALID_POINTER);
+ }
+
+ try {
+ util.outputStreamIsBuffered(null);
+ do_throw("outputStreamIsBuffered should have thrown");
+ } catch (e) {
+ do_check_eq(e.result, Cr.NS_ERROR_INVALID_POINTER);
+ }
+
+ var s = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ var body = "This is a test";
+ s.setData(body, body.length);
+ do_check_eq(util.inputStreamIsBuffered(s), true);
+}
diff --git a/xpcom/tests/unit/test_localfile.js b/xpcom/tests/unit/test_localfile.js
new file mode 100644
index 000000000..25f4bf34b
--- /dev/null
+++ b/xpcom/tests/unit/test_localfile.js
@@ -0,0 +1,151 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+var Cr = Components.results;
+var CC = Components.Constructor;
+var Ci = Components.interfaces;
+
+const MAX_TIME_DIFFERENCE = 2500;
+const MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
+
+var LocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
+
+function run_test()
+{
+ test_toplevel_parent_is_null();
+ test_normalize_crash_if_media_missing();
+ test_file_modification_time();
+ test_directory_modification_time();
+ test_diskSpaceAvailable();
+}
+
+function test_toplevel_parent_is_null()
+{
+ try
+ {
+ var lf = new LocalFile("C:\\");
+
+ // not required by API, but a property on which the implementation of
+ // parent == null relies for correctness
+ do_check_true(lf.path.length == 2);
+
+ do_check_true(lf.parent === null);
+ }
+ catch (e)
+ {
+ // not Windows
+ do_check_eq(e.result, Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH);
+ }
+}
+
+function test_normalize_crash_if_media_missing()
+{
+ const a="a".charCodeAt(0);
+ const z="z".charCodeAt(0);
+ for (var i = a; i <= z; ++i)
+ {
+ try
+ {
+ LocalFile(String.fromCharCode(i)+":.\\test").normalize();
+ }
+ catch (e)
+ {
+ }
+ }
+}
+
+// Tests that changing a file's modification time is possible
+function test_file_modification_time()
+{
+ var file = do_get_profile();
+ file.append("testfile");
+
+ // Should never happen but get rid of it anyway
+ if (file.exists())
+ file.remove(true);
+
+ var now = Date.now();
+ file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
+ do_check_true(file.exists());
+
+ // Modification time may be out by up to 2 seconds on FAT filesystems. Test
+ // with a bit of leeway, close enough probably means it is correct.
+ var diff = Math.abs(file.lastModifiedTime - now);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ var yesterday = now - MILLIS_PER_DAY;
+ file.lastModifiedTime = yesterday;
+
+ diff = Math.abs(file.lastModifiedTime - yesterday);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ var tomorrow = now - MILLIS_PER_DAY;
+ file.lastModifiedTime = tomorrow;
+
+ diff = Math.abs(file.lastModifiedTime - tomorrow);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ var bug377307 = 1172950238000;
+ file.lastModifiedTime = bug377307;
+
+ diff = Math.abs(file.lastModifiedTime - bug377307);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ file.remove(true);
+}
+
+// Tests that changing a directory's modification time is possible
+function test_directory_modification_time()
+{
+ var dir = do_get_profile();
+ dir.append("testdir");
+
+ // Should never happen but get rid of it anyway
+ if (dir.exists())
+ dir.remove(true);
+
+ var now = Date.now();
+ dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ do_check_true(dir.exists());
+
+ // Modification time may be out by up to 2 seconds on FAT filesystems. Test
+ // with a bit of leeway, close enough probably means it is correct.
+ var diff = Math.abs(dir.lastModifiedTime - now);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ var yesterday = now - MILLIS_PER_DAY;
+ dir.lastModifiedTime = yesterday;
+
+ diff = Math.abs(dir.lastModifiedTime - yesterday);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ var tomorrow = now - MILLIS_PER_DAY;
+ dir.lastModifiedTime = tomorrow;
+
+ diff = Math.abs(dir.lastModifiedTime - tomorrow);
+ do_check_true(diff < MAX_TIME_DIFFERENCE);
+
+ dir.remove(true);
+}
+
+function test_diskSpaceAvailable()
+{
+ let file = do_get_profile();
+ file.QueryInterface(Ci.nsILocalFile);
+
+ let bytes = file.diskSpaceAvailable;
+ do_check_true(bytes > 0);
+
+ file.append("testfile");
+ if (file.exists())
+ file.remove(true);
+ file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
+
+ bytes = file.diskSpaceAvailable;
+ do_check_true(bytes > 0);
+
+ file.remove(true);
+}
diff --git a/xpcom/tests/unit/test_mac_bundle.js b/xpcom/tests/unit/test_mac_bundle.js
new file mode 100644
index 000000000..550a4abd6
--- /dev/null
+++ b/xpcom/tests/unit/test_mac_bundle.js
@@ -0,0 +1,18 @@
+function run_test() {
+ // this is a hack to skip the rest of the code on non-Mac platforms,
+ // since #ifdef is not available to xpcshell tests...
+ if (mozinfo.os != "mac") {
+ return;
+ }
+
+ // OK, here's the real part of the test:
+ // make sure these two test bundles are recognized as bundles (or "packages")
+ var keynoteBundle = do_get_file("data/presentation.key");
+ var appBundle = do_get_file("data/SmallApp.app");
+
+ do_check_true(keynoteBundle instanceof Components.interfaces.nsILocalFileMac);
+ do_check_true(appBundle instanceof Components.interfaces.nsILocalFileMac);
+
+ do_check_true(keynoteBundle.isPackage());
+ do_check_true(appBundle.isPackage());
+}
diff --git a/xpcom/tests/unit/test_notxpcom_scriptable.js b/xpcom/tests/unit/test_notxpcom_scriptable.js
new file mode 100644
index 000000000..5d5e24bd9
--- /dev/null
+++ b/xpcom/tests/unit/test_notxpcom_scriptable.js
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const kCID = Components.ID("{1f9f7181-e6c5-4f4c-8f71-08005cec8468}");
+const kContract = "@testing/notxpcomtest";
+
+function run_test()
+{
+ let manifest = do_get_file("xpcomtest.manifest");
+ let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ registrar.autoRegister(manifest);
+
+ ok(Ci.ScriptableWithNotXPCOM);
+
+ let method1Called = false;
+
+ let testObject = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.ScriptableOK,
+ Ci.ScriptableWithNotXPCOM,
+ Ci.ScriptableWithNotXPCOMBase]),
+
+ method1: function() {
+ method1Called = true;
+ },
+
+ method2: function() {
+ ok(false, "method2 should not have been called!");
+ },
+
+ method3: function() {
+ ok(false, "mehod3 should not have been called!");
+ },
+
+ jsonly: true,
+ };
+
+ let factory = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
+
+ createInstance: function(outer, iid) {
+ if (outer) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ return testObject.QueryInterface(iid);
+ },
+ };
+
+ registrar.registerFactory(kCID, null, kContract, factory);
+
+ let xpcomObject = Cc[kContract].createInstance();
+ ok(xpcomObject);
+ strictEqual(xpcomObject.jsonly, undefined);
+
+ xpcomObject.QueryInterface(Ci.ScriptableOK);
+
+ xpcomObject.method1();
+ ok(method1Called);
+
+ try {
+ xpcomObject.QueryInterface(Ci.ScriptableWithNotXPCOM);
+ ok(false, "Should not have implemented ScriptableWithNotXPCOM");
+ }
+ catch(e) {
+ ok(true, "Should not have implemented ScriptableWithNotXPCOM. Correctly threw error: " + e);
+ }
+ strictEqual(xpcomObject.method2, undefined);
+
+ try {
+ xpcomObject.QueryInterface(Ci.ScriptableWithNotXPCOMBase);
+ ok(false, "Should not have implemented ScriptableWithNotXPCOMBase");
+ }
+ catch (e) {
+ ok(true, "Should not have implemented ScriptableWithNotXPCOMBase. Correctly threw error: " + e);
+ }
+ strictEqual(xpcomObject.method3, undefined);
+}
+
diff --git a/xpcom/tests/unit/test_nsIMutableArray.js b/xpcom/tests/unit/test_nsIMutableArray.js
new file mode 100644
index 000000000..b491aee96
--- /dev/null
+++ b/xpcom/tests/unit/test_nsIMutableArray.js
@@ -0,0 +1,184 @@
+/* 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/. */
+
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var Cc = Components.classes;
+var CC = Components.Constructor;
+
+var MutableArray = CC("@mozilla.org/array;1", "nsIMutableArray");
+var SupportsString = CC("@mozilla.org/supports-string;1", "nsISupportsString");
+
+function create_n_element_array(n)
+{
+ var arr = new MutableArray();
+ for (let i=0; i<n; i++) {
+ let str = new SupportsString();
+ str.data = "element " + i;
+ arr.appendElement(str, false);
+ }
+ return arr;
+}
+
+function test_appending_null_actually_inserts()
+{
+ var arr = new MutableArray();
+ do_check_eq(0, arr.length);
+ arr.appendElement(null, false);
+ do_check_eq(1, arr.length);
+}
+
+function test_object_gets_appended()
+{
+ var arr = new MutableArray();
+ var str = new SupportsString();
+ str.data = "hello";
+ arr.appendElement(str, false);
+ do_check_eq(1, arr.length);
+ var obj = arr.queryElementAt(0, Ci.nsISupportsString);
+ do_check_eq(str, obj);
+}
+
+function test_insert_at_beginning()
+{
+ var arr = create_n_element_array(5);
+ // just a sanity check
+ do_check_eq(5, arr.length);
+ var str = new SupportsString();
+ str.data = "hello";
+ arr.insertElementAt(str, 0, false);
+ do_check_eq(6, arr.length);
+ var obj = arr.queryElementAt(0, Ci.nsISupportsString);
+ do_check_eq(str, obj);
+ // check the data of all the other objects
+ for (let i=1; i<arr.length; i++) {
+ let obj = arr.queryElementAt(i, Ci.nsISupportsString);
+ do_check_eq("element " + (i-1), obj.data);
+ }
+}
+
+function test_replace_element()
+{
+ var arr = create_n_element_array(5);
+ // just a sanity check
+ do_check_eq(5, arr.length);
+ var str = new SupportsString();
+ str.data = "hello";
+ // replace first element
+ arr.replaceElementAt(str, 0, false);
+ do_check_eq(5, arr.length);
+ var obj = arr.queryElementAt(0, Ci.nsISupportsString);
+ do_check_eq(str, obj);
+ // replace last element
+ arr.replaceElementAt(str, arr.length - 1, false);
+ do_check_eq(5, arr.length);
+ obj = arr.queryElementAt(arr.length - 1, Ci.nsISupportsString);
+ do_check_eq(str, obj);
+ // replace after last element, should insert empty elements
+ arr.replaceElementAt(str, 9, false);
+ do_check_eq(10, arr.length);
+ obj = arr.queryElementAt(9, Ci.nsISupportsString);
+ do_check_eq(str, obj);
+ // AFAIK there's no way to check the empty elements, since you can't QI them.
+}
+
+function test_clear()
+{
+ var arr = create_n_element_array(5);
+ // just a sanity check
+ do_check_eq(5, arr.length);
+ arr.clear();
+ do_check_eq(0, arr.length);
+}
+
+function test_enumerate()
+{
+ var arr = create_n_element_array(5);
+ do_check_eq(5, arr.length);
+ var en = arr.enumerate();
+ var i = 0;
+ while (en.hasMoreElements()) {
+ let str = en.getNext();
+ do_check_true(str instanceof Ci.nsISupportsString);
+ do_check_eq(str.data, "element " + i);
+ i++;
+ }
+ do_check_eq(arr.length, i);
+}
+
+function test_nssupportsarray_interop() {
+ // Tests to check that an nsSupportsArray instance can behave like an
+ // nsIArray.
+ let test = Components.classes["@mozilla.org/supports-array;1"]
+ .createInstance(Ci.nsISupportsArray);
+
+ let str = new SupportsString();
+ str.data = "element";
+ test.AppendElement(str);
+
+ // Now query to an nsIArray.
+ let iarray = test.QueryInterface(Ci.nsIArray);
+ do_check_neq(iarray, null);
+
+ // Make sure |nsIArray.length| works.
+ do_check_eq(iarray.length, 1);
+
+ // Make sure |nsIArray.queryElementAt| works.
+ let elm = iarray.queryElementAt(0, Ci.nsISupportsString);
+ do_check_eq(elm.data, "element");
+
+ // Make sure |nsIArray.indexOf| works.
+ let idx = iarray.indexOf(0, str);
+ do_check_eq(idx, 0);
+
+ // Make sure |nsIArray.enumerate| works.
+ let en = iarray.enumerate();
+ do_check_neq(en, null);
+ let i = 0;
+ while (en.hasMoreElements()) {
+ let str = en.getNext();
+ do_check_true(str instanceof Ci.nsISupportsString);
+ do_check_eq(str.data, "element");
+ i++;
+ }
+
+ do_check_eq(iarray.length, i);
+}
+
+function test_nsiarrayextensions() {
+ // Tests to check that the extensions that make an nsArray act like an
+ // nsISupportsArray for iteration purposes works.
+ // Note: we do not want to QI here, just want to make sure the magic glue
+ // works as a drop-in replacement.
+
+ let fake_nsisupports_array = create_n_element_array(5);
+
+ // Check that |Count| works.
+ do_check_eq(5, fake_nsisupports_array.Count());
+
+ for (let i = 0; i < fake_nsisupports_array.Count(); i++) {
+ // Check that the generic |GetElementAt| works.
+ let elm = fake_nsisupports_array.GetElementAt(i);
+ do_check_neq(elm, null);
+ let str = elm.QueryInterface(Ci.nsISupportsString);
+ do_check_neq(str, null);
+ do_check_eq(str.data, "element " + i);
+ }
+}
+
+var tests = [
+ test_appending_null_actually_inserts,
+ test_object_gets_appended,
+ test_insert_at_beginning,
+ test_replace_element,
+ test_clear,
+ test_enumerate,
+ test_nssupportsarray_interop,
+ test_nsiarrayextensions,
+];
+
+function run_test() {
+ for (var i = 0; i < tests.length; i++)
+ tests[i]();
+}
diff --git a/xpcom/tests/unit/test_nsIProcess.js b/xpcom/tests/unit/test_nsIProcess.js
new file mode 100644
index 000000000..6c6a5a683
--- /dev/null
+++ b/xpcom/tests/unit/test_nsIProcess.js
@@ -0,0 +1,185 @@
+/* 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/. */
+// nsIProcess unit test
+const TEST_ARGS = ["mozilla", "firefox", "thunderbird", "seamonkey", "foo",
+ "bar", "argument with spaces", "\"argument with quotes\""];
+
+const TEST_UNICODE_ARGS = ["M\u00F8z\u00EEll\u00E5",
+ "\u041C\u043E\u0437\u0438\u043B\u043B\u0430",
+ "\u09AE\u09CB\u099C\u09BF\u09B2\u09BE",
+ "\uD808\uDE2C\uD808\uDF63\uD808\uDDB7"];
+
+// test if a process can be started, polled for its running status
+// and then killed
+function test_kill()
+{
+ var file = get_test_program("TestBlockingProcess");
+
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ do_check_false(process.isRunning);
+
+ try {
+ process.kill();
+ do_throw("Attempting to kill a not-running process should throw");
+ }
+ catch (e) { }
+
+ process.run(false, [], 0);
+
+ do_check_true(process.isRunning);
+
+ process.kill();
+
+ do_check_false(process.isRunning);
+
+ try {
+ process.kill();
+ do_throw("Attempting to kill a not-running process should throw");
+ }
+ catch (e) { }
+}
+
+// test if we can get an exit value from an application that is
+// guaranteed to return an exit value of 42
+function test_quick()
+{
+ var file = get_test_program("TestQuickReturn");
+
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ // to get an exit value it must be a blocking process
+ process.run(true, [], 0);
+
+ do_check_eq(process.exitValue, 42);
+}
+
+function test_args(file, args, argsAreASCII)
+{
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ if (argsAreASCII)
+ process.run(true, args, args.length);
+ else
+ process.runw(true, args, args.length);
+
+ do_check_eq(process.exitValue, 0);
+}
+
+// test if an argument can be successfully passed to an application
+// that will return 0 if "mozilla" is the only argument
+function test_arguments()
+{
+ test_args(get_test_program("TestArguments"), TEST_ARGS, true);
+}
+
+// test if Unicode arguments can be successfully passed to an application
+function test_unicode_arguments()
+{
+ test_args(get_test_program("TestUnicodeArguments"), TEST_UNICODE_ARGS, false);
+}
+
+function rename_and_test(asciiName, unicodeName, args, argsAreASCII)
+{
+ var asciiFile = get_test_program(asciiName);
+ var asciiLeaf = asciiFile.leafName;
+ var unicodeLeaf = asciiLeaf.replace(asciiName, unicodeName);
+
+ asciiFile.moveTo(null, unicodeLeaf);
+
+ var unicodeFile = get_test_program(unicodeName);
+
+ test_args(unicodeFile, args, argsAreASCII);
+
+ unicodeFile.moveTo(null, asciiLeaf);
+}
+
+// test passing ASCII and Unicode arguments to an application with a Unicode name
+function test_unicode_app()
+{
+ rename_and_test("TestArguments",
+ // "Unicode" in Tamil
+ "\u0BAF\u0BC1\u0BA9\u0BBF\u0B95\u0BCB\u0B9F\u0BCD",
+ TEST_ARGS, true);
+
+ rename_and_test("TestUnicodeArguments",
+ // "Unicode" in Thai
+ "\u0E22\u0E39\u0E19\u0E34\u0E42\u0E04\u0E14",
+ TEST_UNICODE_ARGS, false);
+}
+
+// test if we get notified about a blocking process
+function test_notify_blocking()
+{
+ var file = get_test_program("TestQuickReturn");
+
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ process.runAsync([], 0, {
+ observe: function(subject, topic, data) {
+ process = subject.QueryInterface(Components.interfaces.nsIProcess);
+ do_check_eq(topic, "process-finished");
+ do_check_eq(process.exitValue, 42);
+ test_notify_nonblocking();
+ }
+ });
+}
+
+// test if we get notified about a non-blocking process
+function test_notify_nonblocking()
+{
+ var file = get_test_program("TestArguments");
+
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ process.runAsync(TEST_ARGS, TEST_ARGS.length, {
+ observe: function(subject, topic, data) {
+ process = subject.QueryInterface(Components.interfaces.nsIProcess);
+ do_check_eq(topic, "process-finished");
+ do_check_eq(process.exitValue, 0);
+ test_notify_killed();
+ }
+ });
+}
+
+// test if we get notified about a killed process
+function test_notify_killed()
+{
+ var file = get_test_program("TestBlockingProcess");
+
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ process.runAsync([], 0, {
+ observe: function(subject, topic, data) {
+ process = subject.QueryInterface(Components.interfaces.nsIProcess);
+ do_check_eq(topic, "process-finished");
+ do_test_finished();
+ }
+ });
+
+ process.kill();
+}
+
+function run_test() {
+ set_process_running_environment();
+ test_kill();
+ test_quick();
+ test_arguments();
+ test_unicode_arguments();
+ test_unicode_app();
+ do_test_pending();
+ test_notify_blocking();
+}
diff --git a/xpcom/tests/unit/test_nsIProcess_stress.js b/xpcom/tests/unit/test_nsIProcess_stress.js
new file mode 100644
index 000000000..63799141d
--- /dev/null
+++ b/xpcom/tests/unit/test_nsIProcess_stress.js
@@ -0,0 +1,27 @@
+function run_test() {
+ set_process_running_environment();
+
+ var file = get_test_program("TestQuickReturn");
+ var thread = Components.classes["@mozilla.org/thread-manager;1"]
+ .getService().currentThread;
+
+ for (var i = 0; i < 1000; i++) {
+ var process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(file);
+
+ process.run(false, [], 0);
+
+ try {
+ process.kill();
+ }
+ catch (e) { }
+
+ // We need to ensure that we process any events on the main thread -
+ // this allow threads to clean up properly and avoid out of memory
+ // errors during the test.
+ while (thread.hasPendingEvents())
+ thread.processNextEvent(false);
+ }
+
+} \ No newline at end of file
diff --git a/xpcom/tests/unit/test_pipe.js b/xpcom/tests/unit/test_pipe.js
new file mode 100644
index 000000000..c16b33ab0
--- /dev/null
+++ b/xpcom/tests/unit/test_pipe.js
@@ -0,0 +1,63 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var CC = Components.Constructor;
+
+var Pipe = CC("@mozilla.org/pipe;1", "nsIPipe", "init");
+
+function run_test()
+{
+ test_not_initialized();
+ test_ends_are_threadsafe();
+}
+
+function test_not_initialized()
+{
+ var p = Cc["@mozilla.org/pipe;1"]
+ .createInstance(Ci.nsIPipe);
+ try
+ {
+ var dummy = p.outputStream;
+ throw Cr.NS_ERROR_FAILURE;
+ }
+ catch (e)
+ {
+ if (e.result != Cr.NS_ERROR_NOT_INITIALIZED)
+ do_throw("using a pipe before initializing it should throw NS_ERROR_NOT_INITIALIZED");
+ }
+}
+
+function test_ends_are_threadsafe()
+{
+ var p, is, os;
+
+ p = new Pipe(true, true, 1024, 1, null);
+ is = p.inputStream.QueryInterface(Ci.nsIClassInfo);
+ os = p.outputStream.QueryInterface(Ci.nsIClassInfo);
+ do_check_true(Boolean(is.flags & Ci.nsIClassInfo.THREADSAFE));
+ do_check_true(Boolean(os.flags & Ci.nsIClassInfo.THREADSAFE));
+
+ p = new Pipe(true, false, 1024, 1, null);
+ is = p.inputStream.QueryInterface(Ci.nsIClassInfo);
+ os = p.outputStream.QueryInterface(Ci.nsIClassInfo);
+ do_check_true(Boolean(is.flags & Ci.nsIClassInfo.THREADSAFE));
+ do_check_true(Boolean(os.flags & Ci.nsIClassInfo.THREADSAFE));
+
+ p = new Pipe(false, true, 1024, 1, null);
+ is = p.inputStream.QueryInterface(Ci.nsIClassInfo);
+ os = p.outputStream.QueryInterface(Ci.nsIClassInfo);
+ do_check_true(Boolean(is.flags & Ci.nsIClassInfo.THREADSAFE));
+ do_check_true(Boolean(os.flags & Ci.nsIClassInfo.THREADSAFE));
+
+ p = new Pipe(false, false, 1024, 1, null);
+ is = p.inputStream.QueryInterface(Ci.nsIClassInfo);
+ os = p.outputStream.QueryInterface(Ci.nsIClassInfo);
+ do_check_true(Boolean(is.flags & Ci.nsIClassInfo.THREADSAFE));
+ do_check_true(Boolean(os.flags & Ci.nsIClassInfo.THREADSAFE));
+}
diff --git a/xpcom/tests/unit/test_process_directives.js b/xpcom/tests/unit/test_process_directives.js
new file mode 100644
index 000000000..807246f46
--- /dev/null
+++ b/xpcom/tests/unit/test_process_directives.js
@@ -0,0 +1,25 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+Components.utils.import("resource:///modules/Services.jsm");
+
+function run_test()
+{
+ Components.manager.autoRegister(do_get_file("data/process_directive.manifest"));
+
+ let isChild = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
+
+ if (isChild) {
+ do_check_false("@mozilla.org/xpcom/tests/MainProcessDirectiveTest;1" in Cc);
+ } else {
+ let svc = Cc["@mozilla.org/xpcom/tests/MainProcessDirectiveTest;1"].createInstance(Ci.nsISupportsString);
+ do_check_eq(svc.data, "main process");
+ }
+
+ if (!isChild) {
+ do_check_false("@mozilla.org/xpcom/tests/ChildProcessDirectiveTest;1" in Cc);
+ } else {
+ let svc = Cc["@mozilla.org/xpcom/tests/ChildProcessDirectiveTest;1"].createInstance(Ci.nsISupportsString);
+ do_check_eq(svc.data, "child process");
+ }
+}
diff --git a/xpcom/tests/unit/test_process_directives_child.js b/xpcom/tests/unit/test_process_directives_child.js
new file mode 100644
index 000000000..dca63b356
--- /dev/null
+++ b/xpcom/tests/unit/test_process_directives_child.js
@@ -0,0 +1,3 @@
+function run_test() {
+ run_test_in_child("test_process_directives.js");
+}
diff --git a/xpcom/tests/unit/test_seek_multiplex.js b/xpcom/tests/unit/test_seek_multiplex.js
new file mode 100644
index 000000000..31232d134
--- /dev/null
+++ b/xpcom/tests/unit/test_seek_multiplex.js
@@ -0,0 +1,173 @@
+/* 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/. */
+
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var CC = Components.Constructor;
+var Cc = Components.classes;
+
+// The string we use as data.
+const data = "0123456789";
+// Number of streams in the multiplex stream.
+const count = 10;
+
+function test_multiplex_streams() {
+ var MultiplexStream = CC("@mozilla.org/io/multiplex-input-stream;1",
+ "nsIMultiplexInputStream");
+ do_check_eq(1, 1);
+
+ var BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream");
+ var BinaryOutputStream = Components.Constructor("@mozilla.org/binaryoutputstream;1",
+ "nsIBinaryOutputStream",
+ "setOutputStream");
+ var multiplex = new MultiplexStream();
+ for (var i = 0; i < count; ++i) {
+ let s = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ s.setData(data, data.length);
+
+ multiplex.appendStream(s);
+ }
+ var seekable = multiplex.QueryInterface(Ci.nsISeekableStream);
+ var sis = Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+ sis.init(seekable);
+ // Read some data.
+ var readData = sis.read(20);
+ do_check_eq(readData, data + data);
+ // -- Tests for NS_SEEK_SET
+ // Seek to a non-zero, non-stream-boundary offset.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 2);
+ do_check_eq(seekable.tell(), 2);
+ do_check_eq(seekable.available(), 98);
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 9);
+ do_check_eq(seekable.tell(), 9);
+ do_check_eq(seekable.available(), 91);
+ // Seek across stream boundary.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 35);
+ do_check_eq(seekable.tell(), 35);
+ do_check_eq(seekable.available(), 65);
+ readData = sis.read(5);
+ do_check_eq(readData, data.slice(5));
+ do_check_eq(seekable.available(), 60);
+ // Seek at stream boundaries.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 40);
+ do_check_eq(seekable.tell(), 40);
+ do_check_eq(seekable.available(), 60);
+ readData = sis.read(10);
+ do_check_eq(readData, data);
+ do_check_eq(seekable.tell(), 50);
+ do_check_eq(seekable.available(), 50);
+ // Rewind and read across streams.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 39);
+ do_check_eq(seekable.tell(), 39);
+ do_check_eq(seekable.available(), 61);
+ readData = sis.read(11);
+ do_check_eq(readData, data.slice(9) + data);
+ do_check_eq(seekable.tell(), 50);
+ do_check_eq(seekable.available(), 50);
+ // Rewind to the beginning.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
+ do_check_eq(seekable.tell(), 0);
+ do_check_eq(seekable.available(), 100);
+ // Seek to some random location
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 50);
+ // -- Tests for NS_SEEK_CUR
+ // Positive seek.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_CUR, 15);
+ do_check_eq(seekable.tell(), 65);
+ do_check_eq(seekable.available(), 35);
+ readData = sis.read(10);
+ do_check_eq(readData, data.slice(5) + data.slice(0, 5));
+ do_check_eq(seekable.tell(), 75);
+ do_check_eq(seekable.available(), 25);
+ // Negative seek.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_CUR, -15);
+ do_check_eq(seekable.tell(), 60);
+ do_check_eq(seekable.available(), 40);
+ readData = sis.read(10);
+ do_check_eq(readData, data);
+ do_check_eq(seekable.tell(), 70);
+ do_check_eq(seekable.available(), 30);
+
+ // -- Tests for NS_SEEK_END
+ // Normal read.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_END, -5);
+ do_check_eq(seekable.tell(), data.length * count - 5);
+ readData = sis.read(5);
+ do_check_eq(readData, data.slice(5));
+ do_check_eq(seekable.tell(), data.length * count);
+ // Read across streams.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_END, -15);
+ do_check_eq(seekable.tell(), data.length * count - 15);
+ readData = sis.read(15);
+ do_check_eq(readData, data.slice(5) + data);
+ do_check_eq(seekable.tell(), data.length * count);
+
+ // -- Try to do various edge cases
+ // Forward seek from the end, should throw.
+ var caught = false;
+ try {
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_END, 15);
+ } catch(e) {
+ caught = true;
+ }
+ do_check_eq(caught, true);
+ do_check_eq(seekable.tell(), data.length * count);
+ // Backward seek from the beginning, should be clamped.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
+ do_check_eq(seekable.tell(), 0);
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_CUR, -15);
+ do_check_eq(seekable.tell(), 0);
+ // Seek too far: should be clamped.
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
+ do_check_eq(seekable.tell(), 0);
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_CUR, 3 * data.length * count);
+ do_check_eq(seekable.tell(), 100);
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, data.length * count);
+ do_check_eq(seekable.tell(), 100);
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_CUR, -2 * data.length * count);
+ do_check_eq(seekable.tell(), 0);
+}
+
+function test_multiplex_bug797871() {
+
+ var data = "1234567890123456789012345678901234567890";
+
+ var MultiplexStream = CC("@mozilla.org/io/multiplex-input-stream;1",
+ "nsIMultiplexInputStream");
+ do_check_eq(1, 1);
+
+ var BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream");
+ var BinaryOutputStream = Components.Constructor("@mozilla.org/binaryoutputstream;1",
+ "nsIBinaryOutputStream",
+ "setOutputStream");
+ var multiplex = new MultiplexStream();
+ let s = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ s.setData(data, data.length);
+
+ multiplex.appendStream(s);
+
+ var seekable = multiplex.QueryInterface(Ci.nsISeekableStream);
+ var sis = Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+ sis.init(seekable);
+
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 8);
+ do_check_eq(seekable.tell(), 8);
+ readData = sis.read(2);
+ do_check_eq(seekable.tell(), 10);
+
+ seekable.seek(Ci.nsISeekableStream.NS_SEEK_SET, 20);
+ do_check_eq(seekable.tell(), 20);
+}
+
+function run_test() {
+ test_multiplex_streams();
+ test_multiplex_bug797871();
+}
+
diff --git a/xpcom/tests/unit/test_storagestream.js b/xpcom/tests/unit/test_storagestream.js
new file mode 100644
index 000000000..33fdc2202
--- /dev/null
+++ b/xpcom/tests/unit/test_storagestream.js
@@ -0,0 +1,162 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+
+function run_test()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+}
+
+/**
+ * Checks that getting an input stream from a storage stream which has never had
+ * anything written to it throws a not-initialized exception.
+ */
+function test1()
+{
+ var ss = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+ ss.init(1024, 1024, null);
+
+ var out = ss.getOutputStream(0);
+ var inp2 = ss.newInputStream(0);
+ do_check_eq(inp2.available(), 0);
+ do_check_true(inp2.isNonBlocking());
+
+ var sis =
+ Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+ sis.init(inp2);
+
+ var threw = false;
+ try {
+ sis.read(1);
+ } catch (ex if ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK) {
+ threw = true;
+ }
+ do_check_true(threw);
+}
+
+/**
+ * Checks that getting an input stream from a storage stream to which 0 bytes of
+ * data have been explicitly written doesn't throw an exception.
+ */
+function test2()
+{
+ var ss = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+ ss.init(1024, 1024, null);
+
+ var out = ss.getOutputStream(0);
+ out.write("", 0);
+ try
+ {
+ var inp2 = ss.newInputStream(0);
+ }
+ catch (e)
+ {
+ do_throw("shouldn't throw exception when new input stream created");
+ }
+}
+
+/**
+ * Checks that reading any non-zero amount of data from a storage stream
+ * which has had 0 bytes written to it explicitly works correctly.
+ */
+function test3()
+{
+ var ss = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+ ss.init(1024, 1024, null);
+
+ var out = ss.getOutputStream(0);
+ out.write("", 0);
+ try
+ {
+ var inp = ss.newInputStream(0);
+ }
+ catch (e)
+ {
+ do_throw("newInputStream(0) shouldn't throw if write() is called: " + e);
+ }
+
+ do_check_true(inp.isNonBlocking(), "next test expects a non-blocking stream");
+
+ try
+ {
+ var threw = false;
+ var bis = BIS(inp);
+ var dummy = bis.readByteArray(5);
+ }
+ catch (e)
+ {
+ if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK)
+ do_throw("wrong error thrown: " + e);
+ threw = true;
+ }
+ do_check_true(threw,
+ "should have thrown (nsStorageInputStream is nonblocking)");
+}
+
+/**
+ * Basic functionality test for storagestream: write data to it, get an input
+ * stream, and read the data back to see that it matches.
+ */
+function test4()
+{
+ var bytes = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74];
+
+ var ss = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+ ss.init(1024, 1024, null);
+
+ var outStream = ss.getOutputStream(0);
+
+ var bos = Cc["@mozilla.org/binaryoutputstream;1"]
+ .createInstance(Ci.nsIBinaryOutputStream);
+ bos.setOutputStream(outStream);
+
+ bos.writeByteArray(bytes, bytes.length);
+ bos.close();
+
+ var inp = ss.newInputStream(0);
+ var bis = BIS(inp);
+
+ var count = 0;
+ while (count < bytes.length)
+ {
+ var data = bis.read8(1);
+ do_check_eq(data, bytes[count++]);
+ }
+
+ var threw = false;
+ try
+ {
+ data = bis.read8(1);
+ }
+ catch (e)
+ {
+ if (e.result != Cr.NS_ERROR_FAILURE)
+ do_throw("wrong error thrown: " + e);
+ threw = true;
+ }
+ if (!threw)
+ do_throw("should have thrown but instead returned: '" + data + "'");
+}
+
+
+function BIS(input)
+{
+ var bis = Cc["@mozilla.org/binaryinputstream;1"]
+ .createInstance(Ci.nsIBinaryInputStream);
+ bis.setInputStream(input);
+ return bis;
+}
diff --git a/xpcom/tests/unit/test_streams.js b/xpcom/tests/unit/test_streams.js
new file mode 100644
index 000000000..e668cb8d5
--- /dev/null
+++ b/xpcom/tests/unit/test_streams.js
@@ -0,0 +1,157 @@
+/* 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/. */
+
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var CC = Components.Constructor;
+
+var Pipe = CC("@mozilla.org/pipe;1",
+ "nsIPipe",
+ "init");
+var BinaryOutput = CC("@mozilla.org/binaryoutputstream;1",
+ "nsIBinaryOutputStream",
+ "setOutputStream");
+var BinaryInput = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+/**
+ * Binary stream tests.
+ */
+function test_binary_streams() {
+ var p, is, os;
+
+ p = new Pipe(false, false, 1024, 1, null);
+ is = new BinaryInput(p.inputStream);
+ os = new BinaryOutput(p.outputStream);
+
+ const LargeNum = Math.pow(2, 18) + Math.pow(2, 12) + 1;
+ const HugeNum = Math.pow(2, 62);
+ const HelloStr = "Hello World";
+ const HelloArray = Array.map(HelloStr, function(c) {return c.charCodeAt(0)});
+ var countObj = {};
+ var msg = {};
+ var buffer = new ArrayBuffer(HelloArray.length);
+
+ // Test reading immediately after writing.
+ os.writeBoolean(true);
+ do_check_eq(is.readBoolean(), true);
+ os.write8(4);
+ do_check_eq(is.read8(), 4);
+ os.write16(4);
+ do_check_eq(is.read16(), 4);
+ os.write16(1024);
+ do_check_eq(is.read16(), 1024);
+ os.write32(7);
+ do_check_eq(is.read32(), 7);
+ os.write32(LargeNum);
+ do_check_eq(is.read32(), LargeNum);
+ os.write64(LargeNum);
+ do_check_eq(is.read64(), LargeNum);
+ os.write64(1024);
+ do_check_eq(is.read64(), 1024);
+ os.write64(HugeNum);
+ do_check_eq(is.read64(), HugeNum);
+ os.writeFloat(2.5);
+ do_check_eq(is.readFloat(), 2.5);
+// os.writeDouble(Math.SQRT2);
+// do_check_eq(is.readDouble(), Math.SQRT2);
+ os.writeStringZ("Mozilla");
+ do_check_eq(is.readCString(), "Mozilla");
+ os.writeWStringZ("Gecko");
+ do_check_eq(is.readString(), "Gecko");
+ os.writeBytes(HelloStr, HelloStr.length);
+ do_check_eq(is.available(), HelloStr.length);
+ msg = is.readBytes(HelloStr.length);
+ do_check_eq(msg, HelloStr);
+ msg = null;
+ countObj.value = -1;
+ os.writeByteArray(HelloArray, HelloArray.length);
+ do_check_eq(is.available(), HelloStr.length);
+ msg = is.readByteArray(HelloStr.length)
+ do_check_eq(typeof msg, typeof HelloArray);
+ do_check_eq(msg.toSource(), HelloArray.toSource());
+ do_check_eq(is.available(), 0);
+ os.writeByteArray(HelloArray, HelloArray.length);
+ do_check_eq(is.readArrayBuffer(buffer.byteLength, buffer), HelloArray.length);
+ do_check_eq([...(new Uint8Array(buffer))].toSource(), HelloArray.toSource());
+ do_check_eq(is.available(), 0);
+
+ // Test writing in one big chunk.
+ os.writeBoolean(true);
+ os.write8(4);
+ os.write16(4);
+ os.write16(1024);
+ os.write32(7);
+ os.write32(LargeNum);
+ os.write64(LargeNum);
+ os.write64(1024);
+ os.write64(HugeNum);
+ os.writeFloat(2.5);
+// os.writeDouble(Math.SQRT2);
+ os.writeStringZ("Mozilla");
+ os.writeWStringZ("Gecko");
+ os.writeBytes(HelloStr, HelloStr.length);
+ os.writeByteArray(HelloArray, HelloArray.length);
+ // Available should not be zero after a long write like this.
+ do_check_neq(is.available(), 0);
+
+ // Test reading in one big chunk.
+ do_check_eq(is.readBoolean(), true);
+ do_check_eq(is.read8(), 4);
+ do_check_eq(is.read16(), 4);
+ do_check_eq(is.read16(), 1024);
+ do_check_eq(is.read32(), 7);
+ do_check_eq(is.read32(), LargeNum);
+ do_check_eq(is.read64(), LargeNum);
+ do_check_eq(is.read64(), 1024);
+ do_check_eq(is.read64(), HugeNum);
+ do_check_eq(is.readFloat(), 2.5);
+// do_check_eq(is.readDouble(), Math.SQRT2);
+ do_check_eq(is.readCString(), "Mozilla");
+ do_check_eq(is.readString(), "Gecko");
+ // Remember, we wrote HelloStr twice - once as a string, and then as an array.
+ do_check_eq(is.available(), HelloStr.length * 2);
+ msg = is.readBytes(HelloStr.length);
+ do_check_eq(msg, HelloStr);
+ msg = null;
+ countObj.value = -1;
+ do_check_eq(is.available(), HelloStr.length);
+ msg = is.readByteArray(HelloStr.length)
+ do_check_eq(typeof msg, typeof HelloArray);
+ do_check_eq(msg.toSource(), HelloArray.toSource());
+ do_check_eq(is.available(), 0);
+
+ // Test for invalid actions.
+ os.close();
+ is.close();
+
+ try {
+ os.writeBoolean(false);
+ do_throw("Not reached!");
+ } catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_BASE_STREAM_CLOSED)) {
+ // do nothing
+ }
+
+ try {
+ is.available();
+ do_throw("Not reached!");
+ } catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_BASE_STREAM_CLOSED)) {
+ // do nothing
+ }
+
+ try {
+ is.readBoolean();
+ do_throw("Not reached!");
+ } catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_ERROR_FAILURE)) {
+ // do nothing
+ }
+}
+
+function run_test() {
+ test_binary_streams();
+}
diff --git a/xpcom/tests/unit/test_stringstream.js b/xpcom/tests/unit/test_stringstream.js
new file mode 100644
index 000000000..60503a58f
--- /dev/null
+++ b/xpcom/tests/unit/test_stringstream.js
@@ -0,0 +1,23 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+
+function run_test()
+{
+ var s = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ var body = "This is a test";
+ s.setData(body, body.length);
+ do_check_eq(s.available(), body.length);
+
+ var sis = Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+ sis.init(s);
+
+ do_check_eq(sis.read(body.length), body);
+}
diff --git a/xpcom/tests/unit/test_symlinks.js b/xpcom/tests/unit/test_symlinks.js
new file mode 100644
index 000000000..a615af8d5
--- /dev/null
+++ b/xpcom/tests/unit/test_symlinks.js
@@ -0,0 +1,144 @@
+const CWD = do_get_cwd();
+
+const DIR_TARGET = "target";
+const DIR_LINK = "link";
+const DIR_LINK_LINK = "link_link";
+const FILE_TARGET = "target.txt";
+const FILE_LINK = "link.txt";
+const FILE_LINK_LINK = "link_link.txt";
+
+const DOES_NOT_EXIST = "doesnotexist";
+const DANGLING_LINK = "dangling_link";
+const LOOP_LINK = "loop_link";
+
+const nsIFile = Components.interfaces.nsIFile;
+
+var process;
+function createSymLink(from, to) {
+ if (!process) {
+ var ln = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsILocalFile);
+ ln.initWithPath("/bin/ln");
+
+ process = Components.classes["@mozilla.org/process/util;1"]
+ .createInstance(Components.interfaces.nsIProcess);
+ process.init(ln);
+ }
+
+ const args = ["-s", from, to];
+ process.run(true, args, args.length);
+ do_check_eq(process.exitValue, 0);
+}
+
+function makeSymLink(from, toName, relative) {
+ var to = from.parent;
+ to.append(toName);
+
+ if (relative) {
+ createSymLink(from.leafName, to.path);
+ }
+ else {
+ createSymLink(from.path, to.path);
+ }
+
+ do_check_true(to.isSymlink());
+
+ print("---");
+ print(from.path);
+ print(to.path);
+ print(to.target);
+
+ if (from.leafName != DOES_NOT_EXIST && from.isSymlink()) {
+ // XXXjag wish I could set followLinks to false so we'd just get
+ // the symlink's direct target instead of the final target.
+ do_check_eq(from.target, to.target);
+ }
+ else {
+ do_check_eq(from.path, to.target);
+ }
+
+ return to;
+}
+
+function setupTestDir(testDir, relative) {
+ var targetDir = testDir.clone();
+ targetDir.append(DIR_TARGET);
+
+ if (testDir.exists()) {
+ testDir.remove(true);
+ }
+ do_check_true(!testDir.exists());
+
+ testDir.create(nsIFile.DIRECTORY_TYPE, 0o777);
+
+ targetDir.create(nsIFile.DIRECTORY_TYPE, 0o777);
+
+ var targetFile = testDir.clone();
+ targetFile.append(FILE_TARGET);
+ targetFile.create(nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ var imaginary = testDir.clone();
+ imaginary.append(DOES_NOT_EXIST);
+
+ var loop = testDir.clone();
+ loop.append(LOOP_LINK);
+
+ var dirLink = makeSymLink(targetDir, DIR_LINK, relative);
+ var fileLink = makeSymLink(targetFile, FILE_LINK, relative);
+
+ makeSymLink(dirLink, DIR_LINK_LINK, relative);
+ makeSymLink(fileLink, FILE_LINK_LINK, relative);
+
+ makeSymLink(imaginary, DANGLING_LINK, relative);
+
+ try {
+ makeSymLink(loop, LOOP_LINK, relative);
+ do_check_true(false);
+ }
+ catch (e) {
+ }
+}
+
+function createSpaces(dirs, files, links) {
+ function longest(a, b) {
+ return a.length > b.length ? a : b;
+ }
+ return dirs.concat(files, links).reduce(longest, "").replace(/./g, " ");
+}
+
+function testSymLinks(testDir, relative) {
+ setupTestDir(testDir, relative);
+
+ const dirLinks = [DIR_LINK, DIR_LINK_LINK];
+ const fileLinks = [FILE_LINK, FILE_LINK_LINK];
+ const otherLinks = [DANGLING_LINK, LOOP_LINK];
+ const dirs = [DIR_TARGET].concat(dirLinks);
+ const files = [FILE_TARGET].concat(fileLinks);
+ const links = otherLinks.concat(dirLinks, fileLinks);
+
+ const spaces = createSpaces(dirs, files, links);
+ const bools = {false: " false", true: " true "};
+ print(spaces + " dir file symlink");
+ var dirEntries = testDir.directoryEntries;
+ while (dirEntries.hasMoreElements()) {
+ const file = dirEntries.getNext().QueryInterface(nsIFile);
+ const name = file.leafName;
+ print(name + spaces.substring(name.length) + bools[file.isDirectory()] +
+ bools[file.isFile()] + bools[file.isSymlink()]);
+ do_check_eq(file.isDirectory(), dirs.indexOf(name) != -1);
+ do_check_eq(file.isFile(), files.indexOf(name) != -1);
+ do_check_eq(file.isSymlink(), links.indexOf(name) != -1);
+ }
+}
+
+function run_test() {
+ // Skip this test on Windows
+ if (mozinfo.os == "win")
+ return;
+
+ var testDir = CWD;
+ testDir.append("test_symlinks");
+
+ testSymLinks(testDir, false);
+ testSymLinks(testDir, true);
+}
diff --git a/xpcom/tests/unit/test_systemInfo.js b/xpcom/tests/unit/test_systemInfo.js
new file mode 100644
index 000000000..58fa8042c
--- /dev/null
+++ b/xpcom/tests/unit/test_systemInfo.js
@@ -0,0 +1,20 @@
+/* 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/. */
+
+function run_test() {
+ const PROPERTIES = ["name", "host", "arch", "version", "pagesize",
+ "pageshift", "memmapalign", "cpucount", "memsize"];
+ let sysInfo = Components.classes["@mozilla.org/system-info;1"].
+ getService(Components.interfaces.nsIPropertyBag2);
+
+ PROPERTIES.forEach(function(aPropertyName) {
+ print("Testing property: " + aPropertyName);
+ let value = sysInfo.getProperty(aPropertyName);
+ do_check_true(!!value);
+ });
+
+ // This property must exist, but its value might be zero.
+ print("Testing property: umask")
+ do_check_eq(typeof sysInfo.getProperty("umask"), "number");
+}
diff --git a/xpcom/tests/unit/test_versioncomparator.js b/xpcom/tests/unit/test_versioncomparator.js
new file mode 100644
index 000000000..35f8f6eee
--- /dev/null
+++ b/xpcom/tests/unit/test_versioncomparator.js
@@ -0,0 +1,59 @@
+// Versions to test listed in ascending order, none can be equal
+var comparisons = [
+ "0.9",
+ "0.9.1",
+ "1.0pre1",
+ "1.0pre2",
+ "1.0",
+ "1.1pre",
+ "1.1pre1a",
+ "1.1pre1",
+ "1.1pre10a",
+ "1.1pre10",
+ "1.1",
+ "1.1.0.1",
+ "1.1.1",
+ "1.1.*",
+ "1.*",
+ "2.0",
+ "2.1",
+ "3.0.-1",
+ "3.0"
+];
+
+// Every version in this list means the same version number
+var equality = [
+ "1.1pre",
+ "1.1pre0",
+ "1.0+"
+];
+
+function run_test()
+{
+ var vc = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
+ .getService(Components.interfaces.nsIVersionComparator);
+
+ for (var i = 0; i < comparisons.length; i++) {
+ for (var j = 0; j < comparisons.length; j++) {
+ var result = vc.compare(comparisons[i], comparisons[j]);
+ if (i == j) {
+ if (result != 0)
+ do_throw(comparisons[i] + " should be the same as itself");
+ }
+ else if (i < j) {
+ if (!(result < 0))
+ do_throw(comparisons[i] + " should be less than " + comparisons[j]);
+ }
+ else if (!(result > 0)) {
+ do_throw(comparisons[i] + " should be greater than " + comparisons[j]);
+ }
+ }
+ }
+
+ for (i = 0; i < equality.length; i++) {
+ for (j = 0; j < equality.length; j++) {
+ if (vc.compare(equality[i], equality[j]) != 0)
+ do_throw(equality[i] + " should equal " + equality[j]);
+ }
+ }
+}
diff --git a/xpcom/tests/unit/test_windows_cmdline_file.js b/xpcom/tests/unit/test_windows_cmdline_file.js
new file mode 100644
index 000000000..4a3a6cc5f
--- /dev/null
+++ b/xpcom/tests/unit/test_windows_cmdline_file.js
@@ -0,0 +1,21 @@
+let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+Cu.import("resource://gre/modules/Services.jsm");
+
+let executableFile = Services.dirsvc.get("CurProcD", Ci.nsIFile);
+executableFile.append("xpcshell.exe");
+function run_test() {
+ let quote = '"'; // Windows' cmd processor doesn't actually use single quotes.
+ for (let suffix of ["", " -osint", ` --blah "%PROGRAMFILES%"`]) {
+ let cmdline = quote + executableFile.path + quote + suffix;
+ do_print(`Testing with ${cmdline}`);
+ let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
+ f.initWithCommandLine(cmdline);
+ Assert.equal(f.path, executableFile.path, "Should be able to recover executable path");
+ }
+
+ let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
+ f.initWithCommandLine("%ComSpec% -c echo 'hi'");
+ let cmd = Services.dirsvc.get("SysD", Ci.nsIFile);
+ cmd.append("cmd.exe");
+ Assert.equal(f.path, cmd.path, "Should be able to replace env vars.");
+}
diff --git a/xpcom/tests/unit/test_windows_registry.js b/xpcom/tests/unit/test_windows_registry.js
new file mode 100644
index 000000000..9a17678f8
--- /dev/null
+++ b/xpcom/tests/unit/test_windows_registry.js
@@ -0,0 +1,205 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+
+/* 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/. */
+
+const Cr = Components.results;
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+const CC = Components.Constructor;
+
+const nsIWindowsRegKey = Ci.nsIWindowsRegKey;
+let regKeyComponent = Cc["@mozilla.org/windows-registry-key;1"];
+
+function run_test()
+{
+ //* create a key structure in a spot that's normally writable (somewhere under HKCU).
+ let testKey = regKeyComponent.createInstance(nsIWindowsRegKey);
+
+ // If it's already present because a previous test crashed or didn't clean up properly, clean it up first.
+ let keyName = BASE_PATH + "\\" + TESTDATA_KEYNAME;
+ setup_test_run(testKey, keyName);
+
+ //* test that the write* functions write stuff
+ test_writing_functions(testKey);
+
+ //* check that the valueCount/getValueName functions work for the values we just wrote
+ test_value_functions(testKey);
+
+ //* check that the get* functions work for the values we just wrote.
+ test_reading_functions(testKey);
+
+ //* check that the get* functions fail with the right exception codes if we ask for the wrong type or if the value name doesn't exist at all
+ test_invalidread_functions(testKey);
+
+ //* check that creating/enumerating/deleting child keys works
+ test_childkey_functions(testKey);
+
+ test_watching_functions(testKey);
+
+ //* clean up
+ cleanup_test_run(testKey, keyName);
+}
+
+function setup_test_run(testKey, keyName)
+{
+ do_print("Setup test run");
+ try {
+ testKey.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, keyName, nsIWindowsRegKey.ACCESS_READ);
+ do_print("Test key exists. Needs cleanup.");
+ cleanup_test_run(testKey, keyName);
+ }
+ catch (e if (e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE))
+ {
+ }
+
+ testKey.create(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, keyName, nsIWindowsRegKey.ACCESS_ALL);
+}
+
+function test_writing_functions(testKey)
+{
+ strictEqual(testKey.valueCount, 0);
+
+ strictEqual(testKey.hasValue(TESTDATA_STRNAME), false);
+ testKey.writeStringValue(TESTDATA_STRNAME, TESTDATA_STRVALUE);
+ strictEqual(testKey.hasValue(TESTDATA_STRNAME), true);
+
+ strictEqual(testKey.hasValue(TESTDATA_INTNAME), false);
+ testKey.writeIntValue(TESTDATA_INTNAME, TESTDATA_INTVALUE);
+
+ strictEqual(testKey.hasValue(TESTDATA_INT64NAME), false);
+ testKey.writeInt64Value(TESTDATA_INT64NAME, TESTDATA_INT64VALUE);
+
+ strictEqual(testKey.hasValue(TESTDATA_BINARYNAME), false);
+ testKey.writeBinaryValue(TESTDATA_BINARYNAME, TESTDATA_BINARYVALUE);
+}
+
+function test_value_functions(testKey)
+{
+ strictEqual(testKey.valueCount, 4);
+ strictEqual(testKey.getValueName(0), TESTDATA_STRNAME);
+ strictEqual(testKey.getValueName(1), TESTDATA_INTNAME);
+ strictEqual(testKey.getValueName(2), TESTDATA_INT64NAME);
+ strictEqual(testKey.getValueName(3), TESTDATA_BINARYNAME);
+}
+
+function test_reading_functions(testKey)
+{
+ strictEqual(testKey.getValueType(TESTDATA_STRNAME), nsIWindowsRegKey.TYPE_STRING);
+ strictEqual(testKey.readStringValue(TESTDATA_STRNAME), TESTDATA_STRVALUE);
+
+ strictEqual(testKey.getValueType(TESTDATA_INTNAME), nsIWindowsRegKey.TYPE_INT);
+ strictEqual(testKey.readIntValue(TESTDATA_INTNAME), TESTDATA_INTVALUE);
+
+ strictEqual(testKey.getValueType(TESTDATA_INT64NAME), nsIWindowsRegKey.TYPE_INT64);
+ strictEqual( testKey.readInt64Value(TESTDATA_INT64NAME), TESTDATA_INT64VALUE);
+
+ strictEqual(testKey.getValueType(TESTDATA_BINARYNAME), nsIWindowsRegKey.TYPE_BINARY);
+ strictEqual( testKey.readBinaryValue(TESTDATA_BINARYNAME), TESTDATA_BINARYVALUE);
+}
+
+function test_invalidread_functions(testKey)
+{
+ try {
+ testKey.readIntValue(TESTDATA_STRNAME);
+ do_throw("Reading an integer from a string registry value should throw.");
+ } catch (e if (e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)) {
+ }
+
+ try {
+ let val = testKey.readStringValue(TESTDATA_INTNAME);
+ do_throw("Reading an string from an Int registry value should throw." + val);
+ } catch (e if (e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)) {
+ }
+
+ try {
+ testKey.readStringValue(TESTDATA_INT64NAME);
+ do_throw("Reading an string from an Int64 registry value should throw.");
+ } catch (e if (e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)) {
+ }
+
+ try {
+ testKey.readStringValue(TESTDATA_BINARYNAME);
+ do_throw("Reading a string from an Binary registry value should throw.");
+ } catch (e if (e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)) {
+ }
+
+}
+
+function test_childkey_functions(testKey)
+{
+ strictEqual(testKey.childCount, 0);
+ strictEqual(testKey.hasChild(TESTDATA_CHILD_KEY), false);
+
+ let childKey = testKey.createChild(TESTDATA_CHILD_KEY, nsIWindowsRegKey.ACCESS_ALL);
+ childKey.close();
+
+ strictEqual(testKey.childCount, 1);
+ strictEqual(testKey.hasChild(TESTDATA_CHILD_KEY), true);
+ strictEqual(testKey.getChildName(0), TESTDATA_CHILD_KEY);
+
+ childKey = testKey.openChild(TESTDATA_CHILD_KEY, nsIWindowsRegKey.ACCESS_ALL);
+ testKey.removeChild(TESTDATA_CHILD_KEY);
+ strictEqual(testKey.childCount, 0);
+ strictEqual(testKey.hasChild(TESTDATA_CHILD_KEY), false);
+}
+
+function test_watching_functions(testKey)
+{
+ strictEqual(testKey.isWatching(), false);
+ strictEqual(testKey.hasChanged(), false);
+
+ testKey.startWatching(true);
+ strictEqual(testKey.isWatching(), true);
+
+ testKey.stopWatching();
+ strictEqual(testKey.isWatching(), false);
+
+ // Create a child key, and update a value
+ let childKey = testKey.createChild(TESTDATA_CHILD_KEY, nsIWindowsRegKey.ACCESS_ALL);
+ childKey.writeIntValue(TESTDATA_INTNAME, TESTDATA_INTVALUE);
+
+ // Start a recursive watch, and update the child's value
+ testKey.startWatching(true);
+ strictEqual(testKey.isWatching(), true);
+
+ childKey.writeIntValue(TESTDATA_INTNAME, 0);
+ strictEqual(testKey.hasChanged(), true);
+ testKey.stopWatching();
+ strictEqual(testKey.isWatching(), false);
+
+ childKey.removeValue(TESTDATA_INTNAME);
+ childKey.close();
+ testKey.removeChild(TESTDATA_CHILD_KEY);
+}
+
+function cleanup_test_run(testKey, keyName)
+{
+ do_print("Cleaning up test.");
+
+ for (var i = 0; i < testKey.childCount; i++) {
+ testKey.removeChild(testKey.getChildName(i));
+ }
+ testKey.close();
+
+ let baseKey = regKeyComponent.createInstance(nsIWindowsRegKey);
+ baseKey.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, BASE_PATH, nsIWindowsRegKey.ACCESS_ALL);
+ baseKey.removeChild(TESTDATA_KEYNAME);
+ baseKey.close();
+}
+
+// Test data used above.
+const BASE_PATH = "SOFTWARE";
+const TESTDATA_KEYNAME = "TestRegXPC";
+const TESTDATA_STRNAME = "AString";
+const TESTDATA_STRVALUE = "The quick brown fox jumps over the lazy dog.";
+const TESTDATA_INTNAME = "AnInteger";
+const TESTDATA_INTVALUE = 65536;
+const TESTDATA_INT64NAME = "AnInt64";
+const TESTDATA_INT64VALUE = 9223372036854775807;
+const TESTDATA_BINARYNAME = "ABinary";
+const TESTDATA_BINARYVALUE = "She sells seashells by the seashore";
+const TESTDATA_CHILD_KEY = "TestChildKey";
diff --git a/xpcom/tests/unit/test_windows_shortcut.js b/xpcom/tests/unit/test_windows_shortcut.js
new file mode 100644
index 000000000..42cb023ff
--- /dev/null
+++ b/xpcom/tests/unit/test_windows_shortcut.js
@@ -0,0 +1,279 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+
+/* 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/. */
+
+var Cr = Components.results;
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cu = Components.utils;
+var CC = Components.Constructor;
+
+const LocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function run_test()
+{
+ // This test makes sense only on Windows, so skip it on other platforms
+ if ("nsILocalFileWin" in Ci
+ && do_get_cwd() instanceof Ci.nsILocalFileWin) {
+
+ let tempDir = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
+ tempDir.append("shortcutTesting");
+ tempDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o666);
+
+ test_create_noargs(tempDir);
+ test_create_notarget(tempDir);
+ test_create_targetonly(tempDir);
+ test_create_normal(tempDir);
+ test_create_unicode(tempDir);
+
+ test_update_noargs(tempDir);
+ test_update_notarget(tempDir);
+ test_update_targetonly(tempDir);
+ test_update_normal(tempDir);
+ test_update_unicode(tempDir);
+ }
+}
+
+function test_create_noargs(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("shouldNeverExist.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ try
+ {
+ win.setShortcut();
+ do_throw("Creating a shortcut with no args (no target) should throw");
+ }
+ catch(e if (e instanceof Ci.nsIException
+ && e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST))
+ {
+
+ }
+}
+
+function test_create_notarget(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("shouldNeverExist2.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ try
+ {
+ win.setShortcut(null,
+ do_get_cwd(),
+ "arg1 arg2",
+ "Shortcut with no target");
+ do_throw("Creating a shortcut with no target should throw");
+ }
+ catch(e if (e instanceof Ci.nsIException
+ && e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST))
+ {
+
+ }
+}
+
+function test_create_targetonly(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile);
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(targetFile));
+}
+
+function test_create_normal(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(),
+ "arg1 arg2",
+ "Ordinary shortcut");
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(targetFile))
+}
+
+function test_create_unicode(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("ṩhогТϾừ†Target.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(), // XXX: This should probably be a unicode dir
+ "ᾶṟǵ1 ᾶṟǵ2",
+ "ῧṋіḉѻₑ");
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(targetFile))
+}
+
+function test_update_noargs(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(),
+ "arg1 arg2",
+ "A sample shortcut");
+
+ win.setShortcut();
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(targetFile))
+}
+
+function test_update_notarget(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(),
+ "arg1 arg2",
+ "A sample shortcut");
+
+ win.setShortcut(null,
+ do_get_profile(),
+ "arg3 arg4",
+ "An UPDATED shortcut");
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(targetFile))
+}
+
+function test_update_targetonly(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(),
+ "arg1 arg2",
+ "A sample shortcut");
+
+ let newTargetFile = tempDir.clone();
+ newTargetFile.append("shortcutTarget.exe");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ win.setShortcut(newTargetFile);
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(newTargetFile))
+}
+
+function test_update_normal(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(),
+ "arg1 arg2",
+ "A sample shortcut");
+
+ let newTargetFile = tempDir.clone();
+ newTargetFile.append("shortcutTarget.exe");
+ newTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ win.setShortcut(newTargetFile,
+ do_get_profile(),
+ "arg3 arg4",
+ "An UPDATED shortcut");
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(newTargetFile))
+}
+
+function test_update_unicode(tempDir)
+{
+ let shortcutFile = tempDir.clone();
+ shortcutFile.append("createdShortcut.lnk");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let targetFile = tempDir.clone();
+ targetFile.append("shortcutTarget.exe");
+ targetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ let win = shortcutFile.QueryInterface(Ci.nsILocalFileWin);
+
+ win.setShortcut(targetFile,
+ do_get_cwd(),
+ "arg1 arg2",
+ "A sample shortcut");
+
+ let newTargetFile = tempDir.clone();
+ newTargetFile.append("ṩhогТϾừ†Target.exe");
+ shortcutFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+
+ win.setShortcut(newTargetFile,
+ do_get_profile(), // XXX: This should probably be unicode
+ "ᾶṟǵ3 ᾶṟǵ4",
+ "A ῧṋіḉѻₑ shortcut");
+
+ let shortcutTarget = LocalFile(shortcutFile.target);
+ do_check_true(shortcutTarget.equals(newTargetFile))
+}
diff --git a/xpcom/tests/unit/xpcomtest.manifest b/xpcom/tests/unit/xpcomtest.manifest
new file mode 100644
index 000000000..43a4931c6
--- /dev/null
+++ b/xpcom/tests/unit/xpcomtest.manifest
@@ -0,0 +1 @@
+interfaces xpcomtest.xpt
diff --git a/xpcom/tests/unit/xpcshell.ini b/xpcom/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..cf8d93627
--- /dev/null
+++ b/xpcom/tests/unit/xpcshell.ini
@@ -0,0 +1,79 @@
+[DEFAULT]
+head = head_xpcom.js
+tail =
+support-files =
+ bug725015.manifest
+ compmgr_warnings.manifest
+ data/**
+ xpcomtest.xpt
+ xpcomtest.manifest
+generated-files =
+ xpcomtest.xpt
+
+[test_bug121341.js]
+[test_bug325418.js]
+[test_bug332389.js]
+[test_bug333505.js]
+[test_bug364285-1.js]
+# Bug 902073: test fails consistently on Android x86
+skip-if = os == "android"
+[test_bug374754.js]
+[test_bug476919.js]
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_bug478086.js]
+[test_bug656331.js]
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_bug725015.js]
+[test_debugger_malloc_size_of.js]
+[test_compmgr_warnings.js]
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_file_createUnique.js]
+[test_file_equality.js]
+[test_hidden_files.js]
+[test_home.js]
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_iniProcessor.js]
+[test_ioutil.js]
+[test_localfile.js]
+[test_mac_bundle.js]
+[test_nsIMutableArray.js]
+[test_nsIProcess.js]
+skip-if = os == "win" || os == "linux" # bug 582821, bug 1325609
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_nsIProcess_stress.js]
+skip-if = os == "win" # bug 676412, test isn't needed on windows and runs really slowly
+[test_pipe.js]
+[test_process_directives.js]
+skip-if = os == "android"
+[test_process_directives_child.js]
+skip-if = os == "android"
+[test_storagestream.js]
+[test_streams.js]
+[test_seek_multiplex.js]
+[test_stringstream.js]
+[test_symlinks.js]
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_systemInfo.js]
+# Bug 902081: test fails consistently on Android 2.2, passes on 4.0
+skip-if = os == "android"
+[test_versioncomparator.js]
+[test_comp_no_aslr.js]
+skip-if = os != "win"
+[test_windows_shortcut.js]
+skip-if = os != "win"
+[test_windows_cmdline_file.js]
+skip-if = os != "win"
+[test_bug745466.js]
+skip-if = os == "win"
+# Bug 676998: test fails consistently on Android
+fail-if = os == "android"
+[test_file_renameTo.js]
+[test_notxpcom_scriptable.js]
+[test_windows_registry.js]
+skip-if = os != "win"
diff --git a/xpcom/tests/windows/TestCOM.cpp b/xpcom/tests/windows/TestCOM.cpp
new file mode 100644
index 000000000..9f0b9854c
--- /dev/null
+++ b/xpcom/tests/windows/TestCOM.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; 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 <windows.h>
+#include <unknwn.h>
+#include <stdio.h>
+#include "nsISupports.h"
+#include "nsIFactory.h"
+
+// unknwn.h is needed to build with WIN32_LEAN_AND_MEAN
+#include <unknwn.h>
+
+#include "gtest/gtest.h"
+
+// {5846BA30-B856-11d1-A98A-00805F8A7AC4}
+#define NS_ITEST_COM_IID \
+{ 0x5846ba30, 0xb856, 0x11d1, \
+ { 0xa9, 0x8a, 0x0, 0x80, 0x5f, 0x8a, 0x7a, 0xc4 } }
+
+class nsITestCom: public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITEST_COM_IID)
+ NS_IMETHOD Test() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITestCom, NS_ITEST_COM_IID)
+
+/*
+ * nsTestCom
+ */
+
+class nsTestCom final : public nsITestCom {
+ NS_DECL_ISUPPORTS
+
+public:
+ nsTestCom() {
+ }
+
+ NS_IMETHOD Test() {
+ return NS_OK;
+ }
+
+ static int sDestructions;
+
+private:
+ ~nsTestCom() {
+ sDestructions++;
+ }
+};
+
+int nsTestCom::sDestructions;
+
+NS_IMPL_QUERY_INTERFACE(nsTestCom, nsITestCom)
+
+MozExternalRefCountType nsTestCom::AddRef()
+{
+ nsrefcnt res = ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "nsTestCom", sizeof(*this));
+ return res;
+}
+
+MozExternalRefCountType nsTestCom::Release()
+{
+ nsrefcnt res = --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "nsTestCom");
+ if (res == 0) {
+ delete this;
+ }
+ return res;
+}
+
+class nsTestComFactory final : public nsIFactory {
+ ~nsTestComFactory() { sDestructions++; }
+ NS_DECL_ISUPPORTS
+public:
+ nsTestComFactory() {
+ }
+
+ NS_IMETHOD CreateInstance(nsISupports *aOuter,
+ const nsIID &aIID,
+ void **aResult);
+
+ NS_IMETHOD LockFactory(bool aLock) {
+ return NS_OK;
+ }
+
+ static int sDestructions;
+};
+
+int nsTestComFactory::sDestructions;
+
+NS_IMPL_ISUPPORTS(nsTestComFactory, nsIFactory)
+
+nsresult nsTestComFactory::CreateInstance(nsISupports *aOuter,
+ const nsIID &aIID,
+ void **aResult)
+{
+ if (aOuter != nullptr) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsTestCom *t = new nsTestCom();
+
+ if (t == nullptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(t);
+ nsresult res = t->QueryInterface(aIID, aResult);
+ NS_RELEASE(t);
+
+ return res;
+}
+
+TEST(TestCOM, WindowsInterop)
+{
+ nsTestComFactory *inst = new nsTestComFactory();
+
+ // Test we can QI nsIFactory to an IClassFactory.
+ IClassFactory *iFactory = nullptr;
+ nsresult rv = inst->QueryInterface(NS_GET_IID(nsIFactory),
+ (void **) &iFactory);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(iFactory);
+
+ // Test we can CreateInstance with an IUnknown.
+ IUnknown *iUnknown = nullptr;
+
+ HRESULT hr = iFactory->LockServer(TRUE);
+ ASSERT_TRUE(SUCCEEDED(hr));
+ hr = iFactory->CreateInstance(nullptr, IID_IUnknown, (void **) &iUnknown);
+ ASSERT_TRUE(SUCCEEDED(hr));
+ ASSERT_TRUE(iUnknown);
+ hr = iFactory->LockServer(FALSE);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ // Test we can QI an IUnknown to nsITestCom.
+ nsITestCom *iTestCom = nullptr;
+ GUID testGUID = NS_ITEST_COM_IID;
+ hr = iUnknown->QueryInterface(testGUID,
+ (void **) &iTestCom);
+ ASSERT_TRUE(SUCCEEDED(hr));
+ ASSERT_TRUE(iTestCom);
+
+ // Make sure we can call our test function (and the pointer is valid).
+ rv = iTestCom->Test();
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ iUnknown->Release();
+ iTestCom->Release();
+ iFactory->Release();
+
+ ASSERT_EQ(nsTestComFactory::sDestructions, 1);
+ ASSERT_EQ(nsTestCom::sDestructions, 1);
+}
diff --git a/xpcom/tests/windows/TestHelloXPLoop.cpp b/xpcom/tests/windows/TestHelloXPLoop.cpp
new file mode 100644
index 000000000..ffb4442e0
--- /dev/null
+++ b/xpcom/tests/windows/TestHelloXPLoop.cpp
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+#include "nsCNativeApp.h"
+#include "nsIEventLoop.h"
+#include <windows.h>
+
+static NS_DEFINE_CID(kNativeAppCID, NS_NATIVE_APP_CID);
+
+LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+void ErrorBox(LPSTR text)
+{
+ MessageBox(nullptr, text, "XP Event Loop", MB_OK | MB_ICONSTOP);
+}
+
+void InfoBox(LPSTR text)
+{
+ MessageBox(nullptr, text, "XP Event Loop", MB_OK | MB_ICONINFORMATION);
+}
+
+int WINAPI WinMain(HINSTANCE inst,
+ HINSTANCE prevInstance,
+ LPSTR lpszCmdLine,
+ int nShowCmd)
+{
+ char* lpszAppName = "HelloWorld";
+ HWND wnd;
+ WNDCLASSEX wndclass;
+ int retCode;
+
+ { // Needed to scope all nsCOMPtr within XPCOM Init and Shutdown
+ nsresult rv;
+ nsCOMPtr<nsIServiceManager> servMan;
+ rv = NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
+ if(NS_FAILED(rv))
+ {
+ ErrorBox("Failed to initialize xpcom.");
+ return -1;
+ }
+
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
+ NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
+ registrar->AutoRegister(nullptr);
+
+ nsCOMPtr<nsINativeApp> nativeAppService(do_GetService(kNativeAppCID, &rv));
+
+ if(NS_FAILED(rv))
+ {
+ ErrorBox("Failed to get nativeAppService");
+ return -1;
+ }
+ wndclass.cbSize = sizeof(wndclass);
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = inst;
+ wndclass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
+ wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+ wndclass.lpszMenuName = nullptr;
+ wndclass.lpszClassName = lpszAppName;
+ wndclass.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
+
+ RegisterClassEx(&wndclass) ;
+
+ wnd = CreateWindow(lpszAppName, "The Hello World",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ nullptr, nullptr, inst, nullptr);
+
+ ShowWindow(wnd, nShowCmd);
+ UpdateWindow(wnd);
+
+ nsCOMPtr<nsIEventLoop> eventLoop;
+
+ if(NS_FAILED(nativeAppService->CreateEventLoop(L"_MainLoop",
+ nsEventLoopTypes::MainAppLoop, getter_AddRefs(eventLoop))))
+ {
+ ErrorBox("Failed to create event Loop");
+ return 0;
+ }
+
+ eventLoop->Run(nullptr, nullptr, nullptr, &retCode);
+ eventLoop = nullptr; // Clear out before Shutting down XPCOM
+
+ InfoBox("Hello World app is out of loop");
+ }
+ NS_ShutdownXPCOM(nullptr);
+ InfoBox("Hello World app is exiting");
+ return retCode;
+}
+
+LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HDC hdc;
+ PAINTSTRUCT ps;
+ RECT rect;
+
+ switch(msg)
+ {
+ case WM_PAINT:
+ hdc = BeginPaint(wnd, &ps);
+
+ GetClientRect(wnd, &rect);
+
+ DrawText(hdc, "Hello, XP Event Loop!", -1, &rect,
+ DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ EndPaint(wnd, &ps);
+ return 0;
+
+ case WM_DESTROY:
+ {
+ nsresult rv;
+ nsCOMPtr<nsINativeApp> nativeAppService =
+ do_GetService(kNativeAppCID, &rv);
+ if(NS_FAILED(rv))
+ {
+ ErrorBox("Could not get NativeAppService");
+ return 0;
+ }
+ nsCOMPtr<nsIEventLoop> eventLoop;
+
+ if(NS_FAILED(nativeAppService->FindEventLoop(L"_MainLoop",
+ getter_AddRefs(eventLoop))))
+ {
+ ErrorBox("Failed to find event Loop");
+ return 0;
+ }
+ eventLoop->Exit(0);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(wnd, msg, wParam, lParam);
+}
diff --git a/xpcom/tests/windows/TestNTFSPermissions.cpp b/xpcom/tests/windows/TestNTFSPermissions.cpp
new file mode 100644
index 000000000..062a9e650
--- /dev/null
+++ b/xpcom/tests/windows/TestNTFSPermissions.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Test for NTFS File Permissions being correctly changed to match the new
+ * directory upon moving a file. (Bug 224692.)
+ */
+
+#include "../TestHarness.h"
+#include "nsEmbedString.h"
+#include "nsIFile.h"
+#include <windows.h>
+#include <aclapi.h>
+
+#define BUFFSIZE 512
+
+
+
+nsresult TestPermissions()
+{
+
+ nsresult rv; // Return value
+
+ // File variables
+ HANDLE tempFileHandle;
+ nsCOMPtr<nsIFile> tempFile;
+ nsCOMPtr<nsIFile> tempDirectory1;
+ nsCOMPtr<nsIFile> tempDirectory2;
+ WCHAR filePath[MAX_PATH];
+ WCHAR dir1Path[MAX_PATH];
+ WCHAR dir2Path[MAX_PATH];
+
+ // Security variables
+ DWORD result;
+ PSID everyoneSID = nullptr, adminSID = nullptr;
+ PACL dirACL = nullptr, fileACL = nullptr;
+ PSECURITY_DESCRIPTOR dirSD = nullptr, fileSD = nullptr;
+ EXPLICIT_ACCESS ea[2];
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld =
+ SECURITY_WORLD_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
+ SECURITY_ATTRIBUTES sa;
+ TRUSTEE everyoneTrustee;
+ ACCESS_MASK everyoneRights;
+
+ // Create a well-known SID for the Everyone group.
+ if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
+ SECURITY_WORLD_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &everyoneSID))
+ {
+ fail("NTFS Permissions: AllocateAndInitializeSid Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create a SID for the Administrators group.
+ if(! AllocateAndInitializeSid(&SIDAuthNT, 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &adminSID))
+ {
+ fail("NTFS Permissions: AllocateAndInitializeSid Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Initialize an EXPLICIT_ACCESS structure for an ACE.
+ // The ACE will allow Everyone read access to the directory.
+ ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
+ ea[0].grfAccessPermissions = GENERIC_READ;
+ ea[0].grfAccessMode = SET_ACCESS;
+ ea[0].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+ ea[0].Trustee.ptstrName = (LPTSTR) everyoneSID;
+
+ // Initialize an EXPLICIT_ACCESS structure for an ACE.
+ // The ACE will allow the Administrators group full access
+ ea[1].grfAccessPermissions = GENERIC_ALL | STANDARD_RIGHTS_ALL;
+ ea[1].grfAccessMode = SET_ACCESS;
+ ea[1].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+ ea[1].Trustee.ptstrName = (LPTSTR) adminSID;
+
+ // Create a new ACL that contains the new ACEs.
+ result = SetEntriesInAcl(2, ea, nullptr, &dirACL);
+ if (ERROR_SUCCESS != result)
+ {
+ fail("NTFS Permissions: SetEntriesInAcl Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Initialize a security descriptor.
+ dirSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
+ SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (nullptr == dirSD)
+ {
+ fail("NTFS Permissions: LocalAlloc Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!InitializeSecurityDescriptor(dirSD,
+ SECURITY_DESCRIPTOR_REVISION))
+ {
+ fail("NTFS Permissions: InitializeSecurityDescriptor Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Add the ACL to the security descriptor.
+ if (!SetSecurityDescriptorDacl(dirSD, true, dirACL, false))
+ {
+ fail("NTFS Permissions: SetSecurityDescriptorDacl Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Initialize a security attributes structure.
+ sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = dirSD;
+ sa.bInheritHandle = false;
+
+ // Create and open first temporary directory
+ if(!CreateDirectoryW(L".\\NTFSPERMTEMP1", &sa))
+ {
+ fail("NTFS Permissions: Creating Temporary Directory");
+ return NS_ERROR_FAILURE;
+ }
+
+ GetFullPathNameW((LPCWSTR)L".\\NTFSPERMTEMP1", MAX_PATH, dir1Path,
+ nullptr);
+
+ rv = NS_NewLocalFile(nsEmbedString(dir1Path), false,
+ getter_AddRefs(tempDirectory1));
+ if (NS_FAILED(rv))
+ {
+ fail("NTFS Permissions: Opening Temporary Directory 1");
+ return rv;
+ }
+
+
+ // Create and open temporary file
+ tempFileHandle = CreateFileW(L".\\NTFSPERMTEMP1\\NTFSPerm.tmp",
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ nullptr, //default security
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+
+ if(tempFileHandle == INVALID_HANDLE_VALUE)
+ {
+ fail("NTFS Permissions: Creating Temporary File");
+ return NS_ERROR_FAILURE;
+ }
+
+ CloseHandle(tempFileHandle);
+
+ GetFullPathNameW((LPCWSTR)L".\\NTFSPERMTEMP1\\NTFSPerm.tmp",
+ MAX_PATH, filePath, nullptr);
+
+ rv = NS_NewLocalFile(nsEmbedString(filePath), false,
+ getter_AddRefs(tempFile));
+ if (NS_FAILED(rv))
+ {
+ fail("NTFS Permissions: Opening Temporary File");
+ return rv;
+ }
+
+ // Update Everyone Explict_Acess to full access.
+ ea[0].grfAccessPermissions = GENERIC_ALL | STANDARD_RIGHTS_ALL;
+
+ // Update the ACL to contain the new ACEs.
+ result = SetEntriesInAcl(2, ea, nullptr, &dirACL);
+ if (ERROR_SUCCESS != result)
+ {
+ fail("NTFS Permissions: SetEntriesInAcl 2 Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Add the new ACL to the security descriptor.
+ if (!SetSecurityDescriptorDacl(dirSD, true, dirACL, false))
+ {
+ fail("NTFS Permissions: SetSecurityDescriptorDacl 2 Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create and open second temporary directory
+ if(!CreateDirectoryW(L".\\NTFSPERMTEMP2", &sa))
+ {
+ fail("NTFS Permissions: Creating Temporary Directory 2");
+ return NS_ERROR_FAILURE;
+ }
+
+ GetFullPathNameW((LPCWSTR)L".\\NTFSPERMTEMP2", MAX_PATH, dir2Path,
+ nullptr);
+
+ rv = NS_NewLocalFile(nsEmbedString(dir2Path), false,
+ getter_AddRefs(tempDirectory2));
+ if (NS_FAILED(rv))
+ {
+ fail("NTFS Permissions: Opening Temporary Directory 2");
+ return rv;
+ }
+
+ // Move the file.
+ rv = tempFile->MoveTo(tempDirectory2, EmptyString());
+
+ if (NS_FAILED(rv))
+ {
+ fail("NTFS Permissions: Moving");
+ return rv;
+ }
+
+ // Access the ACL of the file
+ result = GetNamedSecurityInfoW(L".\\NTFSPERMTEMP2\\NTFSPerm.tmp",
+ SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION |
+ UNPROTECTED_DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, &fileACL, nullptr,
+ &fileSD);
+ if (ERROR_SUCCESS != result)
+ {
+ fail("NTFS Permissions: GetNamedSecurityDescriptor Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Build a trustee representing "Everyone"
+ BuildTrusteeWithSid(&everyoneTrustee, everyoneSID);
+
+ // Get Everyone's effective rights.
+ result = GetEffectiveRightsFromAcl(fileACL, &everyoneTrustee,
+ &everyoneRights);
+ if (ERROR_SUCCESS != result)
+ {
+ fail("NTFS Permissions: GetEffectiveRightsFromAcl Error");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Check for delete access, which we won't have unless permissions have
+ // updated
+ if((everyoneRights & DELETE) == (DELETE))
+ {
+ passed("NTFS Permissions Test");
+ rv = NS_OK;
+ }
+ else
+ {
+ fail("NTFS Permissions: Access check.");
+ rv = NS_ERROR_FAILURE;
+ }
+
+ // Cleanup
+ if (everyoneSID)
+ FreeSid(everyoneSID);
+ if (adminSID)
+ FreeSid(adminSID);
+ if (dirACL)
+ LocalFree(dirACL);
+ if (dirSD)
+ LocalFree(dirSD);
+ if(fileACL)
+ LocalFree(fileACL);
+
+ tempDirectory1->Remove(true);
+ tempDirectory2->Remove(true);
+
+ return rv;
+}
+
+int main(int argc, char** argv)
+{
+ ScopedXPCOM xpcom("NTFSPermissionsTests"); // name for tests being run
+ if (xpcom.failed())
+ return 1;
+
+ int rv = 0;
+
+ if(NS_FAILED(TestPermissions()))
+ rv = 1;
+
+ return rv;
+
+}
+
diff --git a/xpcom/tests/windows/TestNtPathToDosPath.cpp b/xpcom/tests/windows/TestNtPathToDosPath.cpp
new file mode 100644
index 000000000..b826d4f20
--- /dev/null
+++ b/xpcom/tests/windows/TestNtPathToDosPath.cpp
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+#include <winnetwk.h>
+
+#include "mozilla/FileUtilsWin.h"
+#include "mozilla/DebugOnly.h"
+#include "nsCRTGlue.h"
+
+#include "gtest/gtest.h"
+
+class DriveMapping
+{
+public:
+ DriveMapping(const nsAString& aRemoteUNCPath);
+ ~DriveMapping();
+
+ bool
+ Init();
+ bool
+ ChangeDriveLetter();
+ wchar_t
+ GetDriveLetter() { return mDriveLetter; }
+
+private:
+ bool
+ DoMapping();
+ void
+ Disconnect(wchar_t aDriveLetter);
+
+ wchar_t mDriveLetter;
+ nsString mRemoteUNCPath;
+};
+
+DriveMapping::DriveMapping(const nsAString& aRemoteUNCPath)
+ : mDriveLetter(0)
+ , mRemoteUNCPath(aRemoteUNCPath)
+{
+}
+
+bool
+DriveMapping::Init()
+{
+ if (mDriveLetter) {
+ return false;
+ }
+ return DoMapping();
+}
+
+bool
+DriveMapping::DoMapping()
+{
+ wchar_t drvTemplate[] = L" :";
+ NETRESOURCEW netRes = {0};
+ netRes.dwType = RESOURCETYPE_DISK;
+ netRes.lpLocalName = drvTemplate;
+ netRes.lpRemoteName = reinterpret_cast<wchar_t*>(mRemoteUNCPath.BeginWriting());
+ wchar_t driveLetter = L'D';
+ DWORD result = NO_ERROR;
+ do {
+ drvTemplate[0] = driveLetter;
+ result = WNetAddConnection2W(&netRes, nullptr, nullptr, CONNECT_TEMPORARY);
+ } while (result == ERROR_ALREADY_ASSIGNED && ++driveLetter <= L'Z');
+ if (result != NO_ERROR) {
+ return false;
+ }
+ mDriveLetter = driveLetter;
+ return true;
+}
+
+bool
+DriveMapping::ChangeDriveLetter()
+{
+ wchar_t prevDriveLetter = mDriveLetter;
+ bool result = DoMapping();
+ MOZ_RELEASE_ASSERT(mDriveLetter != prevDriveLetter);
+ if (result && prevDriveLetter) {
+ Disconnect(prevDriveLetter);
+ }
+ return result;
+}
+
+void
+DriveMapping::Disconnect(wchar_t aDriveLetter)
+{
+ wchar_t drvTemplate[] = {aDriveLetter, L':', L'\0'};
+ DWORD result = WNetCancelConnection2W(drvTemplate, 0, TRUE);
+ MOZ_RELEASE_ASSERT(result == NO_ERROR);
+}
+
+DriveMapping::~DriveMapping()
+{
+ if (mDriveLetter) {
+ Disconnect(mDriveLetter);
+ }
+}
+
+bool
+DriveToNtPath(const wchar_t aDriveLetter, nsAString& aNtPath)
+{
+ const wchar_t drvTpl[] = {aDriveLetter, L':', L'\0'};
+ aNtPath.SetLength(MAX_PATH);
+ DWORD pathLen;
+ while (true) {
+ pathLen = QueryDosDeviceW(drvTpl, reinterpret_cast<wchar_t*>(aNtPath.BeginWriting()), aNtPath.Length());
+ if (pathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ break;
+ }
+ aNtPath.SetLength(aNtPath.Length() * 2);
+ }
+ if (!pathLen) {
+ return false;
+ }
+ // aNtPath contains embedded NULLs, so we need to figure out the real length
+ // via wcslen.
+ aNtPath.SetLength(NS_strlen(aNtPath.BeginReading()));
+ return true;
+}
+
+bool
+TestNtPathToDosPath(const wchar_t* aNtPath,
+ const wchar_t* aExpectedDosPath)
+{
+ nsAutoString output;
+ bool result = mozilla::NtPathToDosPath(nsDependentString(aNtPath), output);
+ return result && output == reinterpret_cast<const nsAString::char_type *>(aExpectedDosPath);
+}
+
+TEST(NtPathToDosPath, Tests)
+{
+ nsAutoString cDrive;
+ ASSERT_TRUE(DriveToNtPath(L'C', cDrive));
+
+ // empty string
+ EXPECT_TRUE(TestNtPathToDosPath(L"", L""));
+
+ // non-existent device, must fail
+ EXPECT_FALSE(TestNtPathToDosPath(L"\\Device\\ThisDeviceDoesNotExist\\Foo", nullptr));
+
+ // base case
+ nsAutoString testPath(cDrive);
+ testPath.Append(L"\\Program Files");
+ EXPECT_TRUE(TestNtPathToDosPath(testPath.get(), L"C:\\Program Files"));
+
+ // short filename
+ nsAutoString ntShortName(cDrive);
+ ntShortName.Append(L"\\progra~1");
+ EXPECT_TRUE(TestNtPathToDosPath(ntShortName.get(), L"C:\\Program Files"));
+
+ // drive letters as symbolic links (NtCreateFile uses these)
+ EXPECT_TRUE(TestNtPathToDosPath(L"\\??\\C:\\Foo", L"C:\\Foo"));
+
+ // other symbolic links (should fail)
+ EXPECT_FALSE(TestNtPathToDosPath(L"\\??\\MountPointManager", nullptr));
+
+ // socket (should fail)
+ EXPECT_FALSE(TestNtPathToDosPath(L"\\Device\\Afd\\Endpoint", nullptr));
+
+ // UNC path (using MUP)
+ EXPECT_TRUE(TestNtPathToDosPath(L"\\Device\\Mup\\127.0.0.1\\C$",
+ L"\\\\127.0.0.1\\C$"));
+
+ // UNC path (using LanmanRedirector)
+ EXPECT_TRUE(TestNtPathToDosPath(L"\\Device\\LanmanRedirector\\127.0.0.1\\C$",
+ L"\\\\127.0.0.1\\C$"));
+
+ DriveMapping drvMapping(NS_LITERAL_STRING("\\\\127.0.0.1\\C$"));
+ // Only run these tests if we were able to map; some machines don't have perms
+ if (drvMapping.Init()) {
+ wchar_t expected[] = L" :\\";
+ expected[0] = drvMapping.GetDriveLetter();
+ nsAutoString networkPath;
+ ASSERT_TRUE(DriveToNtPath(drvMapping.GetDriveLetter(), networkPath));
+
+ networkPath += u"\\";
+ EXPECT_TRUE(TestNtPathToDosPath(networkPath.get(), expected));
+
+ // NtPathToDosPath must correctly handle paths whose drive letter mapping has
+ // changed. We need to test this because the APIs called by NtPathToDosPath
+ // return different info if this has happened.
+ ASSERT_TRUE(drvMapping.ChangeDriveLetter());
+
+ expected[0] = drvMapping.GetDriveLetter();
+ ASSERT_TRUE(DriveToNtPath(drvMapping.GetDriveLetter(), networkPath));
+
+ networkPath += u"\\";
+ EXPECT_TRUE(TestNtPathToDosPath(networkPath.get(), expected));
+ }
+}
diff --git a/xpcom/tests/windows/TestWinFileAttribs.cpp b/xpcom/tests/windows/TestWinFileAttribs.cpp
new file mode 100644
index 000000000..56fbcbdea
--- /dev/null
+++ b/xpcom/tests/windows/TestWinFileAttribs.cpp
@@ -0,0 +1,173 @@
+/* -*- 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/. */
+
+/*
+ * Test:
+ */
+
+#include "../TestHarness.h"
+#include "nsEmbedString.h"
+#include "nsILocalFileWin.h"
+#include <windows.h>
+
+#define BUFFSIZE 512
+
+nsresult TestWinAttribs()
+{
+
+ nsresult rv;
+
+ // File variables
+ HANDLE hIndexed;
+ nsCOMPtr<nsIFile> localFile;
+ WCHAR filePath[MAX_PATH];
+
+ // Create and open temporary file
+ hIndexed = CreateFileW(L".\\indexbit.txt",
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ nullptr,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, //FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, not supported by cf
+ nullptr);
+
+ if(hIndexed == INVALID_HANDLE_VALUE)
+ {
+ fail("Test Win Attribs: Creating Test File");
+ return NS_ERROR_FAILURE;
+ }
+
+ CloseHandle(hIndexed);
+
+ GetFullPathNameW((LPCWSTR)L".\\indexbit.txt",
+ MAX_PATH, filePath, nullptr);
+
+ //wprintf(filePath);
+ //wprintf(L"\n");
+
+ rv = NS_NewLocalFile(nsEmbedString(filePath), false,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv))
+ {
+ fail("Test Win Attribs: Opening Test File");
+ DeleteFileW(filePath);
+ return rv;
+ }
+
+ nsCOMPtr<nsILocalFileWin> localFileWin(do_QueryInterface(localFile));
+
+ DWORD dwAttrs = GetFileAttributesW(filePath);
+ if (dwAttrs == INVALID_FILE_ATTRIBUTES)
+ {
+ fail("Test Win Attribs: GetFileAttributesW - couldn't find our temp file.");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ dwAttrs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ SetFileAttributesW(filePath, dwAttrs);
+
+ uint32_t attribs = 0;
+ rv = localFileWin->GetFileAttributesWin(&attribs);
+
+ if (NS_FAILED(rv))
+ {
+ fail("Test Win Attribs: GetFileAttributesWin failed to GET attributes. (1)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (attribs & nsILocalFileWin::WFA_SEARCH_INDEXED)
+ {
+ fail("Test Win Attribs: GetFileAttributesWin attributed did not match. (2)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ dwAttrs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ SetFileAttributesW(filePath, dwAttrs);
+
+ rv = localFileWin->GetFileAttributesWin(&attribs);
+
+ if (NS_FAILED(rv))
+ {
+ fail("Test Win Attribs: GetFileAttributesWin failed to GET attributes. (3)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!(attribs & nsILocalFileWin::WFA_SEARCH_INDEXED))
+ {
+ fail("Test Win Attribs: GetFileAttributesWin attributed did not match. (4)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ dwAttrs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ SetFileAttributesW(filePath, dwAttrs);
+
+ attribs = nsILocalFileWin::WFA_SEARCH_INDEXED;
+ rv = localFileWin->SetFileAttributesWin(attribs);
+
+ dwAttrs = GetFileAttributesW(filePath);
+
+ if (NS_FAILED(rv))
+ {
+ fail("Test Win Attribs: GetFileAttributesWin failed to SET attributes. (5)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ {
+ fail("Test Win Attribs: SetFileAttributesWin attributed did not match. (6)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ dwAttrs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ SetFileAttributesW(filePath, dwAttrs);
+
+ attribs = 0;
+ rv = localFileWin->SetFileAttributesWin(attribs);
+
+ dwAttrs = GetFileAttributesW(filePath);
+
+ if (NS_FAILED(rv))
+ {
+ fail("Test Win Attribs: GetFileAttributesWin failed to SET attributes. (7)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!(dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
+ {
+ fail("Test Win Attribs: SetFileAttributesWin attributed did not match. (8)");
+ DeleteFileW(filePath);
+ return NS_ERROR_FAILURE;
+ }
+
+ DeleteFileW(filePath);
+
+ passed("Test Win Attribs: passed tests.");
+
+ return NS_OK;
+}
+
+int main(int argc, char** argv)
+{
+ ScopedXPCOM xpcom("WinFileAttributes");
+ if (xpcom.failed())
+ return 1;
+
+ int rv = 0;
+
+ if(NS_FAILED(TestWinAttribs()))
+ rv = 1;
+
+ return rv;
+
+}
+
diff --git a/xpcom/tests/windows/moz.build b/xpcom/tests/windows/moz.build
new file mode 100644
index 000000000..21b5eb2f7
--- /dev/null
+++ b/xpcom/tests/windows/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'TestCOM.cpp',
+ 'TestNtPathToDosPath.cpp',
+]
+
+OS_LIBS += [
+ 'mpr',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
diff --git a/xpcom/threads/AbstractThread.cpp b/xpcom/threads/AbstractThread.cpp
new file mode 100644
index 000000000..451b317d8
--- /dev/null
+++ b/xpcom/threads/AbstractThread.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/AbstractThread.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MozPromise.h" // We initialize the MozPromise logging in this file.
+#include "mozilla/StaticPtr.h"
+#include "mozilla/StateWatching.h" // We initialize the StateWatching logging in this file.
+#include "mozilla/TaskQueue.h"
+#include "mozilla/TaskDispatcher.h"
+#include "mozilla/Unused.h"
+
+#include "nsThreadUtils.h"
+#include "nsContentUtils.h"
+#include "nsServiceManagerUtils.h"
+
+
+namespace mozilla {
+
+LazyLogModule gMozPromiseLog("MozPromise");
+LazyLogModule gStateWatchingLog("StateWatching");
+
+StaticRefPtr<AbstractThread> sMainThread;
+MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;
+
+class XPCOMThreadWrapper : public AbstractThread
+{
+public:
+ explicit XPCOMThreadWrapper(nsIThread* aTarget, bool aRequireTailDispatch)
+ : AbstractThread(aRequireTailDispatch)
+ , mTarget(aTarget)
+ {
+ // Our current mechanism of implementing tail dispatch is appshell-specific.
+ // This is because a very similar mechanism already exists on the main
+ // thread, and we want to avoid making event dispatch on the main thread
+ // more complicated than it already is.
+ //
+ // If you need to use tail dispatch on other XPCOM threads, you'll need to
+ // implement an nsIThreadObserver to fire the tail dispatcher at the
+ // appropriate times.
+ MOZ_ASSERT_IF(aRequireTailDispatch,
+ NS_IsMainThread() && NS_GetCurrentThread() == aTarget);
+ }
+
+ virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+ DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
+ DispatchReason aReason = NormalDispatch) override
+ {
+ nsCOMPtr<nsIRunnable> r = aRunnable;
+ AbstractThread* currentThread;
+ if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
+ currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
+ return;
+ }
+
+ nsresult rv = mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
+ MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
+ Unused << rv;
+ }
+
+ virtual bool IsCurrentThreadIn() override
+ {
+ // Compare NSPR threads so that this works after shutdown when
+ // NS_GetCurrentThread starts returning null.
+ PRThread* thread = nullptr;
+ mTarget->GetPRThread(&thread);
+ bool in = PR_GetCurrentThread() == thread;
+ return in;
+ }
+
+ void FireTailDispatcher()
+ {
+ MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
+ mTailDispatcher.ref().DrainDirectTasks();
+ mTailDispatcher.reset();
+ }
+
+ virtual TaskDispatcher& TailDispatcher() override
+ {
+ MOZ_ASSERT(this == sMainThread); // See the comment in the constructor.
+ MOZ_ASSERT(IsCurrentThreadIn());
+ if (!mTailDispatcher.isSome()) {
+ mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
+
+ nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher);
+ nsContentUtils::RunInStableState(event.forget());
+ }
+
+ return mTailDispatcher.ref();
+ }
+
+ virtual bool MightHaveTailTasks() override
+ {
+ return mTailDispatcher.isSome();
+ }
+
+ virtual nsIThread* AsXPCOMThread() override { return mTarget; }
+
+private:
+ RefPtr<nsIThread> mTarget;
+ Maybe<AutoTaskDispatcher> mTailDispatcher;
+};
+
+void
+AbstractThread::TailDispatchTasksFor(AbstractThread* aThread)
+{
+ if (MightHaveTailTasks()) {
+ TailDispatcher().DispatchTasksFor(aThread);
+ }
+}
+
+bool
+AbstractThread::HasTailTasksFor(AbstractThread* aThread)
+{
+ if (!MightHaveTailTasks()) {
+ return false;
+ }
+ return TailDispatcher().HasTasksFor(aThread);
+}
+
+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()
+{
+ MOZ_ASSERT(sMainThread);
+ return sMainThread;
+}
+
+void
+AbstractThread::InitStatics()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sMainThread);
+ nsCOMPtr<nsIThread> mainThread;
+ NS_GetMainThread(getter_AddRefs(mainThread));
+ MOZ_DIAGNOSTIC_ASSERT(mainThread);
+ sMainThread = new XPCOMThreadWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
+ ClearOnShutdown(&sMainThread);
+
+ if (!sCurrentThreadTLS.init()) {
+ MOZ_CRASH();
+ }
+ sCurrentThreadTLS.set(sMainThread);
+}
+
+void
+AbstractThread::DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable)
+{
+ GetCurrent()->TailDispatcher().AddStateChangeTask(this, Move(aRunnable));
+}
+
+/* static */ void
+AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)
+{
+ GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
+}
+
+/* static */
+already_AddRefed<AbstractThread>
+AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
+{
+ RefPtr<XPCOMThreadWrapper> wrapper = new XPCOMThreadWrapper(aThread, aRequireTailDispatch);
+ // Set the thread-local sCurrentThreadTLS to point to the wrapper on the
+ // target thread. This ensures that sCurrentThreadTLS is as expected by
+ // AbstractThread::GetCurrent() on the target thread.
+ nsCOMPtr<nsIRunnable> r =
+ NS_NewRunnableFunction([wrapper]() { sCurrentThreadTLS.set(wrapper); });
+ aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+ return wrapper.forget();
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/AbstractThread.h b/xpcom/threads/AbstractThread.h
new file mode 100644
index 000000000..ca6ec1b84
--- /dev/null
+++ b/xpcom/threads/AbstractThread.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(AbstractThread_h_)
+#define AbstractThread_h_
+
+#include "nscore.h"
+#include "nsIRunnable.h"
+#include "nsISupportsImpl.h"
+#include "nsIThread.h"
+#include "mozilla/RefPtr.h"
+
+#include "mozilla/ThreadLocal.h"
+
+namespace mozilla {
+
+class TaskQueue;
+class TaskDispatcher;
+
+/*
+ * We often want to run tasks on a target that guarantees that events will never
+ * run in parallel. There are various target types that achieve this - namely
+ * nsIThread and TaskQueue. Note that nsIThreadPool (which implements
+ * nsIEventTarget) does not have this property, so we do not want to use
+ * nsIEventTarget for this purpose. This class encapsulates the specifics of
+ * the structures we might use here and provides a consistent interface.
+ *
+ * At present, the supported AbstractThread implementations are TaskQueue
+ * and AbstractThread::MainThread. If you add support for another thread that is
+ * not the MainThread, you'll need to figure out how to make it unique such that
+ * comparing AbstractThread pointers is equivalent to comparing nsIThread pointers.
+ */
+class AbstractThread
+{
+public:
+ // Returns the AbstractThread that the caller is currently running in, or null
+ // if the caller is not running in an AbstractThread.
+ static AbstractThread* GetCurrent() { return sCurrentThreadTLS.get(); }
+
+ AbstractThread(bool aSupportsTailDispatch) : mSupportsTailDispatch(aSupportsTailDispatch) {}
+
+ static already_AddRefed<AbstractThread>
+ CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractThread);
+
+ enum DispatchFailureHandling { AssertDispatchSuccess, DontAssertDispatchSuccess };
+ enum DispatchReason { NormalDispatch, TailDispatch };
+ virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+ DispatchFailureHandling aHandling = AssertDispatchSuccess,
+ DispatchReason aReason = NormalDispatch) = 0;
+
+ virtual bool IsCurrentThreadIn() = 0;
+
+ // Returns true if dispatch is generally reliable. This is used to guard
+ // against FlushableTaskQueues, which should go away.
+ virtual bool IsDispatchReliable() { return true; }
+
+ // Returns a TaskDispatcher that will dispatch its tasks when the currently-
+ // running tasks pops off the stack.
+ //
+ // May only be called when running within the it is invoked up, and only on
+ // threads which support it.
+ virtual TaskDispatcher& TailDispatcher() = 0;
+
+ // Returns true if we have tail tasks scheduled, or if this isn't known.
+ // Returns false if we definitely don't have any tail tasks.
+ virtual bool MightHaveTailTasks() { return true; }
+
+ // Helper functions for methods on the tail TasklDispatcher. These check
+ // HasTailTasks to avoid allocating a TailDispatcher if it isn't
+ // needed.
+ void TailDispatchTasksFor(AbstractThread* aThread);
+ bool HasTailTasksFor(AbstractThread* aThread);
+
+ // Returns true if this supports the tail dispatcher.
+ bool SupportsTailDispatch() const { return mSupportsTailDispatch; }
+
+ // 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!"); }
+
+ // Convenience method for getting an AbstractThread for the main thread.
+ static AbstractThread* MainThread();
+
+ // Must be called exactly once during startup.
+ static void InitStatics();
+
+ void DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable);
+
+ static void DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable);
+
+protected:
+ virtual ~AbstractThread() {}
+ static MOZ_THREAD_LOCAL(AbstractThread*) sCurrentThreadTLS;
+
+ // True if we want to require that every task dispatched from tasks running in
+ // this queue go through our queue's tail dispatcher.
+ const bool mSupportsTailDispatch;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/threads/BackgroundHangMonitor.cpp b/xpcom/threads/BackgroundHangMonitor.cpp
new file mode 100644
index 000000000..ac65d9f37
--- /dev/null
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -0,0 +1,734 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Move.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/ThreadHangStats.h"
+#include "mozilla/ThreadLocal.h"
+
+#include "prinrval.h"
+#include "prthread.h"
+#include "ThreadStackHelper.h"
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "mozilla/Services.h"
+#include "nsXULAppAPI.h"
+
+#include <algorithm>
+
+// Activate BHR only for one every BHR_BETA_MOD users.
+// This is now 100% of Beta population for the Beta 45/46 e10s A/B trials
+// It can be scaled back again in the future
+#define BHR_BETA_MOD 1;
+
+// Maximum depth of the call stack in the reported thread hangs. This value represents
+// the 99.9th percentile of the thread hangs stack depths reported by Telemetry.
+static const size_t kMaxThreadHangStackDepth = 30;
+
+// An utility comparator function used by std::unique to collapse "(* script)" entries in
+// a vector representing a call stack.
+bool StackScriptEntriesCollapser(const char* aStackEntry, const char *aAnotherStackEntry)
+{
+ return !strcmp(aStackEntry, aAnotherStackEntry) &&
+ (!strcmp(aStackEntry, "(chrome script)") || !strcmp(aStackEntry, "(content script)"));
+}
+
+namespace mozilla {
+
+/**
+ * BackgroundHangManager is the global object that
+ * manages all instances of BackgroundHangThread.
+ */
+class BackgroundHangManager : public nsIObserver
+{
+private:
+ // Background hang monitor thread function
+ static void MonitorThread(void* aData)
+ {
+ PR_SetCurrentThreadName("BgHangManager");
+
+ /* We do not hold a reference to BackgroundHangManager here
+ because the monitor thread only exists as long as the
+ BackgroundHangManager instance exists. We stop the monitor
+ thread in the BackgroundHangManager destructor, and we can
+ only get to the destructor if we don't hold a reference here. */
+ static_cast<BackgroundHangManager*>(aData)->RunMonitorThread();
+ }
+
+ // Hang monitor thread
+ PRThread* mHangMonitorThread;
+ // Stop hang monitoring
+ bool mShutdown;
+
+ BackgroundHangManager(const BackgroundHangManager&);
+ BackgroundHangManager& operator=(const BackgroundHangManager&);
+ void RunMonitorThread();
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ static StaticRefPtr<BackgroundHangManager> sInstance;
+ static bool sDisabled;
+
+ // Lock for access to members of this class
+ Monitor mLock;
+ // Current time as seen by hang monitors
+ PRIntervalTime mIntervalNow;
+ // List of BackgroundHangThread instances associated with each thread
+ LinkedList<BackgroundHangThread> mHangThreads;
+
+ void Shutdown()
+ {
+ MonitorAutoLock autoLock(mLock);
+ mShutdown = true;
+ autoLock.Notify();
+ }
+
+ void Wakeup()
+ {
+ // PR_CreateThread could have failed earlier
+ if (mHangMonitorThread) {
+ // Use PR_Interrupt to avoid potentially taking a lock
+ PR_Interrupt(mHangMonitorThread);
+ }
+ }
+
+ BackgroundHangManager();
+private:
+ virtual ~BackgroundHangManager();
+};
+
+NS_IMPL_ISUPPORTS(BackgroundHangManager, nsIObserver)
+
+NS_IMETHODIMP
+BackgroundHangManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) {
+ NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
+ BackgroundHangMonitor::DisableOnBeta();
+
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ MOZ_ASSERT(observerService);
+ observerService->RemoveObserver(this, "profile-after-change");
+
+ return NS_OK;
+}
+
+/**
+ * BackgroundHangThread is a per-thread object that is used
+ * by all instances of BackgroundHangMonitor to monitor hangs.
+ */
+class BackgroundHangThread : public LinkedListElement<BackgroundHangThread>
+{
+private:
+ static MOZ_THREAD_LOCAL(BackgroundHangThread*) sTlsKey;
+ static bool sTlsKeyInitialized;
+
+ BackgroundHangThread(const BackgroundHangThread&);
+ BackgroundHangThread& operator=(const BackgroundHangThread&);
+ ~BackgroundHangThread();
+
+ /* Keep a reference to the manager, so we can keep going even
+ after BackgroundHangManager::Shutdown is called. */
+ const RefPtr<BackgroundHangManager> mManager;
+ // Unique thread ID for identification
+ const PRThread* mThreadID;
+
+ void Update();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(BackgroundHangThread)
+ /**
+ * Returns the BackgroundHangThread associated with the
+ * running thread. Note that this will not find private
+ * BackgroundHangThread threads.
+ *
+ * @return BackgroundHangThread*, or nullptr if no thread
+ * is found.
+ */
+ static BackgroundHangThread* FindThread();
+
+ static void Startup()
+ {
+ /* We can tolerate init() failing. */
+ sTlsKeyInitialized = sTlsKey.init();
+ }
+
+ // Hang timeout in ticks
+ const PRIntervalTime mTimeout;
+ // PermaHang timeout in ticks
+ const PRIntervalTime mMaxTimeout;
+ // Time at last activity
+ PRIntervalTime mInterval;
+ // Time when a hang started
+ PRIntervalTime mHangStart;
+ // Is the thread in a hang
+ bool mHanging;
+ // Is the thread in a waiting state
+ bool mWaiting;
+ // Is the thread dedicated to a single BackgroundHangMonitor
+ BackgroundHangMonitor::ThreadType mThreadType;
+ // Platform-specific helper to get hang stacks
+ ThreadStackHelper mStackHelper;
+ // Stack of current hang
+ Telemetry::HangStack mHangStack;
+ // Statistics for telemetry
+ Telemetry::ThreadHangStats mStats;
+ // Annotations for the current hang
+ UniquePtr<HangMonitor::HangAnnotations> mAnnotations;
+ // Annotators registered for this thread
+ HangMonitor::Observer::Annotators mAnnotators;
+
+ BackgroundHangThread(const char* aName,
+ uint32_t aTimeoutMs,
+ uint32_t aMaxTimeoutMs,
+ BackgroundHangMonitor::ThreadType aThreadType = BackgroundHangMonitor::THREAD_SHARED);
+
+ // Report a hang; aManager->mLock IS locked
+ Telemetry::HangHistogram& ReportHang(PRIntervalTime aHangTime);
+ // Report a permanent hang; aManager->mLock IS locked
+ void ReportPermaHang();
+ // Called by BackgroundHangMonitor::NotifyActivity
+ void NotifyActivity()
+ {
+ MonitorAutoLock autoLock(mManager->mLock);
+ Update();
+ }
+ // Called by BackgroundHangMonitor::NotifyWait
+ void NotifyWait()
+ {
+ MonitorAutoLock autoLock(mManager->mLock);
+
+ if (mWaiting) {
+ return;
+ }
+
+ Update();
+ mWaiting = true;
+ }
+
+ // Returns true if this thread is (or might be) shared between other
+ // BackgroundHangMonitors for the monitored thread.
+ bool IsShared() {
+ return mThreadType == BackgroundHangMonitor::THREAD_SHARED;
+ }
+};
+
+
+StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
+bool BackgroundHangManager::sDisabled = false;
+
+MOZ_THREAD_LOCAL(BackgroundHangThread*) BackgroundHangThread::sTlsKey;
+bool BackgroundHangThread::sTlsKeyInitialized;
+
+BackgroundHangManager::BackgroundHangManager()
+ : mShutdown(false)
+ , mLock("BackgroundHangManager")
+ , mIntervalNow(0)
+{
+ // Lock so we don't race against the new monitor thread
+ MonitorAutoLock autoLock(mLock);
+ mHangMonitorThread = PR_CreateThread(
+ PR_USER_THREAD, MonitorThread, this,
+ PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ MOZ_ASSERT(mHangMonitorThread, "Failed to create monitor thread");
+}
+
+BackgroundHangManager::~BackgroundHangManager()
+{
+ MOZ_ASSERT(mShutdown, "Destruction without Shutdown call");
+ MOZ_ASSERT(mHangThreads.isEmpty(), "Destruction with outstanding monitors");
+ MOZ_ASSERT(mHangMonitorThread, "No monitor thread");
+
+ // PR_CreateThread could have failed above due to resource limitation
+ if (mHangMonitorThread) {
+ // The monitor thread can only live as long as the instance lives
+ PR_JoinThread(mHangMonitorThread);
+ }
+}
+
+void
+BackgroundHangManager::RunMonitorThread()
+{
+ // Keep us locked except when waiting
+ MonitorAutoLock autoLock(mLock);
+
+ /* mIntervalNow is updated at various intervals determined by waitTime.
+ However, if an update latency is too long (due to CPU scheduling, system
+ sleep, etc.), we don't update mIntervalNow at all. This is done so that
+ long latencies in our timing are not detected as hangs. systemTime is
+ used to track PR_IntervalNow() and determine our latency. */
+
+ PRIntervalTime systemTime = PR_IntervalNow();
+ // Default values for the first iteration of thread loop
+ PRIntervalTime waitTime = PR_INTERVAL_NO_WAIT;
+ PRIntervalTime recheckTimeout = PR_INTERVAL_NO_WAIT;
+
+ while (!mShutdown) {
+
+ PR_ClearInterrupt();
+ nsresult rv = autoLock.Wait(waitTime);
+
+ PRIntervalTime newTime = PR_IntervalNow();
+ PRIntervalTime systemInterval = newTime - systemTime;
+ systemTime = newTime;
+
+ /* waitTime is a quarter of the shortest timeout value; If our timing
+ latency is low enough (less than half the shortest timeout value),
+ we can update mIntervalNow. */
+ if (MOZ_LIKELY(waitTime != PR_INTERVAL_NO_TIMEOUT &&
+ systemInterval < 2 * waitTime)) {
+ mIntervalNow += systemInterval;
+ }
+
+ /* If it's before the next recheck timeout, and our wait did not
+ get interrupted (either through Notify or PR_Interrupt), we can
+ keep the current waitTime and skip iterating through hang monitors. */
+ if (MOZ_LIKELY(systemInterval < recheckTimeout &&
+ systemInterval >= waitTime &&
+ rv == NS_OK)) {
+ recheckTimeout -= systemInterval;
+ continue;
+ }
+
+ /* We are in one of the following scenarios,
+ - Hang or permahang recheck timeout
+ - Thread added/removed
+ - Thread wait or hang ended
+ In all cases, we want to go through our list of hang
+ monitors and update waitTime and recheckTimeout. */
+ waitTime = PR_INTERVAL_NO_TIMEOUT;
+ recheckTimeout = PR_INTERVAL_NO_TIMEOUT;
+
+ // Locally hold mIntervalNow
+ PRIntervalTime intervalNow = mIntervalNow;
+
+ // iterate through hang monitors
+ for (BackgroundHangThread* currentThread = mHangThreads.getFirst();
+ currentThread; currentThread = currentThread->getNext()) {
+
+ if (currentThread->mWaiting) {
+ // Thread is waiting, not hanging
+ continue;
+ }
+ PRIntervalTime interval = currentThread->mInterval;
+ PRIntervalTime hangTime = intervalNow - interval;
+ if (MOZ_UNLIKELY(hangTime >= currentThread->mMaxTimeout)) {
+ // A permahang started
+ // Skip subsequent iterations and tolerate a race on mWaiting here
+ currentThread->mWaiting = true;
+ currentThread->mHanging = false;
+ currentThread->ReportPermaHang();
+ continue;
+ }
+
+ if (MOZ_LIKELY(!currentThread->mHanging)) {
+ if (MOZ_UNLIKELY(hangTime >= currentThread->mTimeout)) {
+ // A hang started
+ currentThread->mStackHelper.GetStack(currentThread->mHangStack);
+ currentThread->mHangStart = interval;
+ currentThread->mHanging = true;
+ currentThread->mAnnotations =
+ currentThread->mAnnotators.GatherAnnotations();
+ }
+ } else {
+ if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
+ // A hang ended
+ currentThread->ReportHang(intervalNow - currentThread->mHangStart);
+ currentThread->mHanging = false;
+ }
+ }
+
+ /* If we are hanging, the next time we check for hang status is when
+ the hang turns into a permahang. If we're not hanging, the next
+ recheck timeout is when we may be entering a hang. */
+ PRIntervalTime nextRecheck;
+ if (currentThread->mHanging) {
+ nextRecheck = currentThread->mMaxTimeout;
+ } else {
+ nextRecheck = currentThread->mTimeout;
+ }
+ recheckTimeout = std::min(recheckTimeout, nextRecheck - hangTime);
+
+ if (currentThread->mTimeout != PR_INTERVAL_NO_TIMEOUT) {
+ /* We wait for a quarter of the shortest timeout
+ value to give mIntervalNow enough granularity. */
+ waitTime = std::min(waitTime, currentThread->mTimeout / 4);
+ }
+ }
+ }
+
+ /* We are shutting down now.
+ Wait for all outstanding monitors to unregister. */
+ while (!mHangThreads.isEmpty()) {
+ autoLock.Wait(PR_INTERVAL_NO_TIMEOUT);
+ }
+}
+
+
+BackgroundHangThread::BackgroundHangThread(const char* aName,
+ uint32_t aTimeoutMs,
+ uint32_t aMaxTimeoutMs,
+ BackgroundHangMonitor::ThreadType aThreadType)
+ : mManager(BackgroundHangManager::sInstance)
+ , mThreadID(PR_GetCurrentThread())
+ , mTimeout(aTimeoutMs == BackgroundHangMonitor::kNoTimeout
+ ? PR_INTERVAL_NO_TIMEOUT
+ : PR_MillisecondsToInterval(aTimeoutMs))
+ , mMaxTimeout(aMaxTimeoutMs == BackgroundHangMonitor::kNoTimeout
+ ? PR_INTERVAL_NO_TIMEOUT
+ : PR_MillisecondsToInterval(aMaxTimeoutMs))
+ , mInterval(mManager->mIntervalNow)
+ , mHangStart(mInterval)
+ , mHanging(false)
+ , mWaiting(true)
+ , mThreadType(aThreadType)
+ , mStats(aName)
+{
+ if (sTlsKeyInitialized && IsShared()) {
+ sTlsKey.set(this);
+ }
+ // Lock here because LinkedList is not thread-safe
+ MonitorAutoLock autoLock(mManager->mLock);
+ // Add to thread list
+ mManager->mHangThreads.insertBack(this);
+ // Wake up monitor thread to process new thread
+ autoLock.Notify();
+}
+
+BackgroundHangThread::~BackgroundHangThread()
+{
+ // Lock here because LinkedList is not thread-safe
+ MonitorAutoLock autoLock(mManager->mLock);
+ // Remove from thread list
+ remove();
+ // Wake up monitor thread to process removed thread
+ autoLock.Notify();
+
+ // We no longer have a thread
+ if (sTlsKeyInitialized && IsShared()) {
+ sTlsKey.set(nullptr);
+ }
+
+ // Move our copy of ThreadHangStats to Telemetry storage
+ Telemetry::RecordThreadHangStats(mStats);
+}
+
+Telemetry::HangHistogram&
+BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
+{
+ // Recovered from a hang; called on the monitor thread
+ // mManager->mLock IS locked
+
+ // Remove unwanted "js::RunScript" frame from the stack
+ for (size_t i = 0; i < mHangStack.length(); ) {
+ const char** f = mHangStack.begin() + i;
+ if (!mHangStack.IsInBuffer(*f) && !strcmp(*f, "js::RunScript")) {
+ mHangStack.erase(f);
+ } else {
+ i++;
+ }
+ }
+
+ // Collapse duplicated "(chrome script)" and "(content script)" entries in the stack.
+ auto it = std::unique(mHangStack.begin(), mHangStack.end(), StackScriptEntriesCollapser);
+ mHangStack.erase(it, mHangStack.end());
+
+ // Limit the depth of the reported stack if greater than our limit. Only keep its
+ // last entries, since the most recent frames are at the end of the vector.
+ if (mHangStack.length() > kMaxThreadHangStackDepth) {
+ const int elementsToRemove = mHangStack.length() - kMaxThreadHangStackDepth;
+ // Replace the oldest frame with a known label so that we can tell this stack
+ // was limited.
+ mHangStack[0] = "(reduced stack)";
+ mHangStack.erase(mHangStack.begin() + 1, mHangStack.begin() + elementsToRemove);
+ }
+
+ Telemetry::HangHistogram newHistogram(Move(mHangStack));
+ for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
+ oldHistogram != mStats.mHangs.end(); oldHistogram++) {
+ if (newHistogram == *oldHistogram) {
+ // New histogram matches old one
+ oldHistogram->Add(aHangTime, Move(mAnnotations));
+ return *oldHistogram;
+ }
+ }
+ // Add new histogram
+ newHistogram.Add(aHangTime, Move(mAnnotations));
+ if (!mStats.mHangs.append(Move(newHistogram))) {
+ MOZ_CRASH();
+ }
+ return mStats.mHangs.back();
+}
+
+void
+BackgroundHangThread::ReportPermaHang()
+{
+ // Permanently hanged; called on the monitor thread
+ // mManager->mLock IS locked
+
+ Telemetry::HangHistogram& hang = ReportHang(mMaxTimeout);
+ Telemetry::HangStack& stack = hang.GetNativeStack();
+ if (stack.empty()) {
+ mStackHelper.GetNativeStack(stack);
+ }
+}
+
+MOZ_ALWAYS_INLINE void
+BackgroundHangThread::Update()
+{
+ PRIntervalTime intervalNow = mManager->mIntervalNow;
+ if (mWaiting) {
+ mInterval = intervalNow;
+ mWaiting = false;
+ /* We have to wake up the manager thread because when all threads
+ are waiting, the manager thread waits indefinitely as well. */
+ mManager->Wakeup();
+ } else {
+ PRIntervalTime duration = intervalNow - mInterval;
+ mStats.mActivity.Add(duration);
+ if (MOZ_UNLIKELY(duration >= mTimeout)) {
+ /* Wake up the manager thread to tell it that a hang ended */
+ mManager->Wakeup();
+ }
+ mInterval = intervalNow;
+ }
+}
+
+BackgroundHangThread*
+BackgroundHangThread::FindThread()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ if (BackgroundHangManager::sInstance == nullptr) {
+ MOZ_ASSERT(BackgroundHangManager::sDisabled,
+ "BackgroundHandleManager is not initialized");
+ return nullptr;
+ }
+
+ if (sTlsKeyInitialized) {
+ // Use TLS if available
+ return sTlsKey.get();
+ }
+ // If TLS is unavailable, we can search through the thread list
+ RefPtr<BackgroundHangManager> manager(BackgroundHangManager::sInstance);
+ MOZ_ASSERT(manager, "Creating BackgroundHangMonitor after shutdown");
+
+ PRThread* threadID = PR_GetCurrentThread();
+ // Lock thread list for traversal
+ MonitorAutoLock autoLock(manager->mLock);
+ for (BackgroundHangThread* thread = manager->mHangThreads.getFirst();
+ thread; thread = thread->getNext()) {
+ if (thread->mThreadID == threadID && thread->IsShared()) {
+ return thread;
+ }
+ }
+#endif
+ // Current thread is not initialized
+ return nullptr;
+}
+
+bool
+BackgroundHangMonitor::ShouldDisableOnBeta(const nsCString &clientID) {
+ MOZ_ASSERT(clientID.Length() == 36, "clientID is invalid");
+ const char *suffix = clientID.get() + clientID.Length() - 4;
+ return strtol(suffix, NULL, 16) % BHR_BETA_MOD;
+}
+
+bool
+BackgroundHangMonitor::IsDisabled() {
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ return BackgroundHangManager::sDisabled;
+#else
+ return true;
+#endif
+}
+
+bool
+BackgroundHangMonitor::DisableOnBeta() {
+ nsAdoptingCString clientID = Preferences::GetCString("toolkit.telemetry.cachedClientID");
+ bool telemetryEnabled = Preferences::GetBool("toolkit.telemetry.enabled");
+
+ if (!telemetryEnabled || !clientID || BackgroundHangMonitor::ShouldDisableOnBeta(clientID)) {
+ if (XRE_IsParentProcess()) {
+ BackgroundHangMonitor::Shutdown();
+ } else {
+ BackgroundHangManager::sDisabled = true;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void
+BackgroundHangMonitor::Startup()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized");
+
+ if (!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "beta")) {
+ if (XRE_IsParentProcess()) { // cached ClientID hasn't been read yet
+ ThreadStackHelper::Startup();
+ BackgroundHangThread::Startup();
+ BackgroundHangManager::sInstance = new BackgroundHangManager();
+
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ MOZ_ASSERT(observerService);
+
+ observerService->AddObserver(BackgroundHangManager::sInstance, "profile-after-change", false);
+ return;
+ } else if(DisableOnBeta()){
+ return;
+ }
+ }
+
+ ThreadStackHelper::Startup();
+ BackgroundHangThread::Startup();
+ BackgroundHangManager::sInstance = new BackgroundHangManager();
+#endif
+}
+
+void
+BackgroundHangMonitor::Shutdown()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ if (BackgroundHangManager::sDisabled) {
+ MOZ_ASSERT(!BackgroundHangManager::sInstance, "Initialized");
+ return;
+ }
+
+ MOZ_ASSERT(BackgroundHangManager::sInstance, "Not initialized");
+ /* Scope our lock inside Shutdown() because the sInstance object can
+ be destroyed as soon as we set sInstance to nullptr below, and
+ we don't want to hold the lock when it's being destroyed. */
+ BackgroundHangManager::sInstance->Shutdown();
+ BackgroundHangManager::sInstance = nullptr;
+ ThreadStackHelper::Shutdown();
+ BackgroundHangManager::sDisabled = true;
+#endif
+}
+
+BackgroundHangMonitor::BackgroundHangMonitor(const char* aName,
+ uint32_t aTimeoutMs,
+ uint32_t aMaxTimeoutMs,
+ ThreadType aThreadType)
+ : mThread(aThreadType == THREAD_SHARED ? BackgroundHangThread::FindThread() : nullptr)
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ if (!BackgroundHangManager::sDisabled && !mThread) {
+ mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs,
+ aThreadType);
+ }
+#endif
+}
+
+BackgroundHangMonitor::BackgroundHangMonitor()
+ : mThread(BackgroundHangThread::FindThread())
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ if (BackgroundHangManager::sDisabled) {
+ return;
+ }
+#endif
+}
+
+BackgroundHangMonitor::~BackgroundHangMonitor()
+{
+}
+
+void
+BackgroundHangMonitor::NotifyActivity()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ if (mThread == nullptr) {
+ MOZ_ASSERT(BackgroundHangManager::sDisabled,
+ "This thread is not initialized for hang monitoring");
+ return;
+ }
+
+ if (Telemetry::CanRecordExtended()) {
+ mThread->NotifyActivity();
+ }
+#endif
+}
+
+void
+BackgroundHangMonitor::NotifyWait()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ if (mThread == nullptr) {
+ MOZ_ASSERT(BackgroundHangManager::sDisabled,
+ "This thread is not initialized for hang monitoring");
+ return;
+ }
+
+ if (Telemetry::CanRecordExtended()) {
+ mThread->NotifyWait();
+ }
+#endif
+}
+
+bool
+BackgroundHangMonitor::RegisterAnnotator(HangMonitor::Annotator& aAnnotator)
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ BackgroundHangThread* thisThread = BackgroundHangThread::FindThread();
+ if (!thisThread) {
+ return false;
+ }
+ return thisThread->mAnnotators.Register(aAnnotator);
+#else
+ return false;
+#endif
+}
+
+bool
+BackgroundHangMonitor::UnregisterAnnotator(HangMonitor::Annotator& aAnnotator)
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ BackgroundHangThread* thisThread = BackgroundHangThread::FindThread();
+ if (!thisThread) {
+ return false;
+ }
+ return thisThread->mAnnotators.Unregister(aAnnotator);
+#else
+ return false;
+#endif
+}
+
+/* Because we are iterating through the BackgroundHangThread linked list,
+ we need to take a lock. Using MonitorAutoLock as a base class makes
+ sure all of that is taken care of for us. */
+BackgroundHangMonitor::ThreadHangStatsIterator::ThreadHangStatsIterator()
+ : MonitorAutoLock(BackgroundHangManager::sInstance->mLock)
+ , mThread(BackgroundHangManager::sInstance ?
+ BackgroundHangManager::sInstance->mHangThreads.getFirst() :
+ nullptr)
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+ MOZ_ASSERT(BackgroundHangManager::sInstance ||
+ BackgroundHangManager::sDisabled,
+ "Inconsistent state");
+#endif
+}
+
+Telemetry::ThreadHangStats*
+BackgroundHangMonitor::ThreadHangStatsIterator::GetNext()
+{
+ if (!mThread) {
+ return nullptr;
+ }
+ Telemetry::ThreadHangStats* stats = &mThread->mStats;
+ mThread = mThread->getNext();
+ return stats;
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/BackgroundHangMonitor.h b/xpcom/threads/BackgroundHangMonitor.h
new file mode 100644
index 000000000..698cf2305
--- /dev/null
+++ b/xpcom/threads/BackgroundHangMonitor.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BackgroundHangMonitor_h
+#define mozilla_BackgroundHangMonitor_h
+
+#include "mozilla/HangAnnotations.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+
+#include "nsString.h"
+
+#include <stdint.h>
+
+namespace mozilla {
+
+namespace Telemetry {
+class ThreadHangStats;
+} // namespace Telemetry
+
+class BackgroundHangThread;
+class BackgroundHangManager;
+
+/**
+ * The background hang monitor is responsible for detecting and reporting
+ * hangs in main and background threads. A thread registers itself using
+ * the BackgroundHangMonitor object and periodically calls its methods to
+ * inform the hang monitor of the thread's activity. Each thread is given
+ * a thread name, a timeout, and a maximum timeout. If one of the thread's
+ * tasks runs for longer than the timeout duration but shorter than the
+ * maximum timeout, a (transient) hang is reported. On the other hand, if
+ * a task runs for longer than the maximum timeout duration or never
+ * finishes (e.g. in a deadlock), a permahang is reported.
+ *
+ * Tasks are defined arbitrarily, but are typically represented by events
+ * in an event loop -- processing one event is equivalent to running one
+ * task. To ensure responsiveness, tasks in a thread often have a target
+ * running time. This is a good starting point for determining the timeout
+ * and maximum timeout values. For example, the Compositor thread has a
+ * responsiveness goal of 60Hz or 17ms, so a starting timeout could be
+ * 100ms. Considering some platforms (e.g. Android) can terminate the app
+ * when a critical thread hangs for longer than a few seconds, a good
+ * starting maximum timeout is 4 or 5 seconds.
+ *
+ * A thread registers itself through the BackgroundHangMonitor constructor.
+ * Multiple BackgroundHangMonitor objects can be used in one thread. The
+ * constructor without arguments can be used when it is known that the thread
+ * already has a BackgroundHangMonitor registered. When all instances of
+ * BackgroundHangMonitor are destroyed, the thread is unregistered.
+ *
+ * The thread then uses two methods to inform BackgroundHangMonitor of the
+ * thread's activity:
+ *
+ * > BackgroundHangMonitor::NotifyActivity should be called *before*
+ * starting a task. The task run time is determined by the interval
+ * between this call and the next NotifyActivity call.
+ *
+ * > BackgroundHangMonitor::NotifyWait should be called *before* the
+ * thread enters a wait state (e.g. to wait for a new event). This
+ * prevents a waiting thread from being detected as hanging. The wait
+ * state is automatically cleared at the next NotifyActivity call.
+ *
+ * The following example shows hang monitoring in a simple event loop:
+ *
+ * void thread_main()
+ * {
+ * mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000);
+ * while (!exiting) {
+ * hangMonitor.NotifyActivity();
+ * process_next_event();
+ * hangMonitor.NotifyWait();
+ * wait_for_next_event();
+ * }
+ * }
+ *
+ * The following example shows reentrancy in nested event loops:
+ *
+ * void thread_main()
+ * {
+ * mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000);
+ * while (!exiting) {
+ * hangMonitor.NotifyActivity();
+ * process_next_event();
+ * hangMonitor.NotifyWait();
+ * wait_for_next_event();
+ * }
+ * }
+ *
+ * void process_next_event()
+ * {
+ * mozilla::BackgroundHangMonitor hangMonitor();
+ * if (is_sync_event) {
+ * while (!finished_event) {
+ * hangMonitor.NotifyActivity();
+ * process_next_event();
+ * hangMonitor.NotifyWait();
+ * wait_for_next_event();
+ * }
+ * } else {
+ * process_nonsync_event();
+ * }
+ * }
+ */
+class BackgroundHangMonitor
+{
+private:
+ friend BackgroundHangManager;
+
+ RefPtr<BackgroundHangThread> mThread;
+
+ static bool ShouldDisableOnBeta(const nsCString &);
+ static bool DisableOnBeta();
+
+public:
+ static const uint32_t kNoTimeout = 0;
+ enum ThreadType {
+ // For a new BackgroundHangMonitor for thread T, only create a new
+ // monitoring thread for T if one doesn't already exist. If one does,
+ // share that pre-existing monitoring thread.
+ THREAD_SHARED,
+ // For a new BackgroundHangMonitor for thread T, create a new
+ // monitoring thread for T even if there are other, pre-existing
+ // monitoring threads for T.
+ THREAD_PRIVATE
+ };
+
+ /**
+ * ThreadHangStatsIterator is used to iterate through the ThreadHangStats
+ * associated with each active monitored thread. Because of an internal
+ * lock while this object is alive, a thread must use only one instance
+ * of this class at a time and must iterate through the list as fast as
+ * possible. The following example shows using the iterator:
+ *
+ * {
+ * // Scope the iter variable so it's destroyed as soon as we're done
+ * BackgroundHangMonitor::ThreadHangStatsIterator iter;
+ * for (ThreadHangStats* histogram = iter.GetNext();
+ * histogram; histogram = iter.GetNext()) {
+ * // Process histogram
+ * }
+ * }
+ */
+ class ThreadHangStatsIterator : public MonitorAutoLock
+ {
+ private:
+ BackgroundHangThread* mThread;
+
+ ThreadHangStatsIterator(const ThreadHangStatsIterator&);
+ ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);
+
+ public:
+ /**
+ * Create an ThreadHangStatsIterator instance and take the internal lock.
+ * Internal lock is released on destruction.
+ */
+ ThreadHangStatsIterator();
+
+ /**
+ * Get the next item in the list; the first call returns the first item.
+ * Returns nullptr at the end of the list.
+ */
+ Telemetry::ThreadHangStats* GetNext();
+ };
+
+ /**
+ * Enable hang monitoring.
+ * Must return before using BackgroundHangMonitor.
+ */
+ static void Startup();
+
+ /**
+ * Disable hang monitoring.
+ * Can be called without destroying all BackgroundHangMonitors first.
+ */
+ static void Shutdown();
+
+ /**
+ * Returns true if BHR is disabled.
+ */
+ static bool IsDisabled();
+
+ /**
+ * Start monitoring hangs for the current thread.
+ *
+ * @param aName Name to identify the thread with
+ * @param aTimeoutMs Amount of time in milliseconds without
+ * activity before registering a hang
+ * @param aMaxTimeoutMs Amount of time in milliseconds without
+ * activity before registering a permanent hang
+ * @param aThreadType
+ * The ThreadType type of monitoring thread that should be created
+ * for this monitor. See the documentation for ThreadType.
+ */
+ BackgroundHangMonitor(const char* aName,
+ uint32_t aTimeoutMs,
+ uint32_t aMaxTimeoutMs,
+ ThreadType aThreadType = THREAD_SHARED);
+
+ /**
+ * Monitor hangs using an existing monitor
+ * associated with the current thread.
+ */
+ BackgroundHangMonitor();
+
+ /**
+ * Destroys the hang monitor; hang monitoring for a thread stops
+ * when all monitors associated with the thread are destroyed.
+ */
+ ~BackgroundHangMonitor();
+
+ /**
+ * Notify the hang monitor of pending current thread activity.
+ * Call this method before starting an "activity" or after
+ * exiting from a wait state.
+ */
+ void NotifyActivity();
+
+ /**
+ * Notify the hang monitor of current thread wait.
+ * Call this method before entering a wait state; call
+ * NotifyActivity when subsequently exiting the wait state.
+ */
+ void NotifyWait();
+
+ /**
+ * Register an annotator with BHR for the current thread.
+ * @param aAnnotator annotator to register
+ * @return true if the annotator was registered, otherwise false.
+ */
+ static bool RegisterAnnotator(HangMonitor::Annotator& aAnnotator);
+
+ /**
+ * Unregister an annotator that was previously registered via
+ * RegisterAnnotator.
+ * @param aAnnotator annotator to unregister
+ * @return true if there are still remaining annotators registered
+ */
+ static bool UnregisterAnnotator(HangMonitor::Annotator& aAnnotator);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_BackgroundHangMonitor_h
diff --git a/xpcom/threads/HangAnnotations.cpp b/xpcom/threads/HangAnnotations.cpp
new file mode 100644
index 000000000..529b57b8e
--- /dev/null
+++ b/xpcom/threads/HangAnnotations.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/HangAnnotations.h"
+
+#include <vector>
+
+#include "MainThreadUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace HangMonitor {
+
+// Chrome hang annotators. This can go away once BHR has completely replaced
+// ChromeHangs.
+static StaticAutoPtr<Observer::Annotators> gChromehangAnnotators;
+
+class BrowserHangAnnotations : public HangAnnotations
+{
+public:
+ BrowserHangAnnotations();
+ ~BrowserHangAnnotations();
+
+ void AddAnnotation(const nsAString& aName, const int32_t aData) override;
+ void AddAnnotation(const nsAString& aName, const double aData) override;
+ void AddAnnotation(const nsAString& aName, const nsAString& aData) override;
+ void AddAnnotation(const nsAString& aName, const nsACString& aData) override;
+ void AddAnnotation(const nsAString& aName, const bool aData) override;
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+ bool IsEmpty() const override;
+ UniquePtr<Enumerator> GetEnumerator() override;
+
+ typedef std::pair<nsString, nsString> AnnotationType;
+ typedef std::vector<AnnotationType> VectorType;
+ typedef VectorType::const_iterator IteratorType;
+
+private:
+ VectorType mAnnotations;
+};
+
+BrowserHangAnnotations::BrowserHangAnnotations()
+{
+ MOZ_COUNT_CTOR(BrowserHangAnnotations);
+}
+
+BrowserHangAnnotations::~BrowserHangAnnotations()
+{
+ MOZ_COUNT_DTOR(BrowserHangAnnotations);
+}
+
+void
+BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData)
+{
+ nsString dataString;
+ dataString.AppendInt(aData);
+ AnnotationType annotation = std::make_pair(nsString(aName), dataString);
+ mAnnotations.push_back(annotation);
+}
+
+void
+BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const double aData)
+{
+ nsString dataString;
+ dataString.AppendFloat(aData);
+ AnnotationType annotation = std::make_pair(nsString(aName), dataString);
+ mAnnotations.push_back(annotation);
+}
+
+void
+BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData)
+{
+ AnnotationType annotation = std::make_pair(nsString(aName), nsString(aData));
+ mAnnotations.push_back(annotation);
+}
+
+void
+BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData)
+{
+ nsString dataString;
+ AppendUTF8toUTF16(aData, dataString);
+ AnnotationType annotation = std::make_pair(nsString(aName), dataString);
+ mAnnotations.push_back(annotation);
+}
+
+void
+BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const bool aData)
+{
+ nsString dataString;
+ dataString += aData ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false");
+ AnnotationType annotation = std::make_pair(nsString(aName), dataString);
+ mAnnotations.push_back(annotation);
+}
+
+/**
+ * This class itself does not use synchronization but it (and its parent object)
+ * should be protected by mutual exclusion in some way. In Telemetry the chrome
+ * hang data is protected via TelemetryImpl::mHangReportsMutex.
+ */
+class ChromeHangAnnotationEnumerator : public HangAnnotations::Enumerator
+{
+public:
+ explicit ChromeHangAnnotationEnumerator(const BrowserHangAnnotations::VectorType& aAnnotations);
+ ~ChromeHangAnnotationEnumerator();
+
+ virtual bool Next(nsAString& aOutName, nsAString& aOutValue);
+
+private:
+ BrowserHangAnnotations::IteratorType mIterator;
+ BrowserHangAnnotations::IteratorType mEnd;
+};
+
+ChromeHangAnnotationEnumerator::ChromeHangAnnotationEnumerator(
+ const BrowserHangAnnotations::VectorType& aAnnotations)
+ : mIterator(aAnnotations.begin())
+ , mEnd(aAnnotations.end())
+{
+ MOZ_COUNT_CTOR(ChromeHangAnnotationEnumerator);
+}
+
+ChromeHangAnnotationEnumerator::~ChromeHangAnnotationEnumerator()
+{
+ MOZ_COUNT_DTOR(ChromeHangAnnotationEnumerator);
+}
+
+bool
+ChromeHangAnnotationEnumerator::Next(nsAString& aOutName, nsAString& aOutValue)
+{
+ aOutName.Truncate();
+ aOutValue.Truncate();
+ if (mIterator == mEnd) {
+ return false;
+ }
+ aOutName = mIterator->first;
+ aOutValue = mIterator->second;
+ ++mIterator;
+ return true;
+}
+
+bool
+BrowserHangAnnotations::IsEmpty() const
+{
+ return mAnnotations.empty();
+}
+
+size_t
+BrowserHangAnnotations::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ size_t result = sizeof(mAnnotations) +
+ mAnnotations.capacity() * sizeof(AnnotationType);
+ for (IteratorType i = mAnnotations.begin(), e = mAnnotations.end(); i != e;
+ ++i) {
+ result += i->first.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ result += i->second.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ return result;
+}
+
+UniquePtr<HangAnnotations::Enumerator>
+BrowserHangAnnotations::GetEnumerator()
+{
+ if (mAnnotations.empty()) {
+ return nullptr;
+ }
+ return MakeUnique<ChromeHangAnnotationEnumerator>(mAnnotations);
+}
+
+namespace Observer {
+
+Annotators::Annotators()
+ : mMutex("HangMonitor::Annotators::mMutex")
+{
+ MOZ_COUNT_CTOR(Annotators);
+}
+
+Annotators::~Annotators()
+{
+ MOZ_ASSERT(mAnnotators.empty());
+ MOZ_COUNT_DTOR(Annotators);
+}
+
+bool
+Annotators::Register(Annotator& aAnnotator)
+{
+ MutexAutoLock lock(mMutex);
+ auto result = mAnnotators.insert(&aAnnotator);
+ return result.second;
+}
+
+bool
+Annotators::Unregister(Annotator& aAnnotator)
+{
+ MutexAutoLock lock(mMutex);
+ DebugOnly<std::set<Annotator*>::size_type> numErased;
+ numErased = mAnnotators.erase(&aAnnotator);
+ MOZ_ASSERT(numErased == 1);
+ return mAnnotators.empty();
+}
+
+UniquePtr<HangAnnotations>
+Annotators::GatherAnnotations()
+{
+ auto annotations = MakeUnique<BrowserHangAnnotations>();
+ { // Scope for lock
+ MutexAutoLock lock(mMutex);
+ for (std::set<Annotator*>::iterator i = mAnnotators.begin(),
+ e = mAnnotators.end();
+ i != e; ++i) {
+ (*i)->AnnotateHang(*annotations);
+ }
+ }
+ if (annotations->IsEmpty()) {
+ return nullptr;
+ }
+ return Move(annotations);
+}
+
+} // namespace Observer
+
+void
+RegisterAnnotator(Annotator& aAnnotator)
+{
+ BackgroundHangMonitor::RegisterAnnotator(aAnnotator);
+ // We still register annotators for ChromeHangs
+ if (NS_IsMainThread() &&
+ GeckoProcessType_Default == XRE_GetProcessType()) {
+ if (!gChromehangAnnotators) {
+ gChromehangAnnotators = new Observer::Annotators();
+ }
+ gChromehangAnnotators->Register(aAnnotator);
+ }
+}
+
+void
+UnregisterAnnotator(Annotator& aAnnotator)
+{
+ BackgroundHangMonitor::UnregisterAnnotator(aAnnotator);
+ // We still register annotators for ChromeHangs
+ if (NS_IsMainThread() &&
+ GeckoProcessType_Default == XRE_GetProcessType()) {
+ if (gChromehangAnnotators->Unregister(aAnnotator)) {
+ gChromehangAnnotators = nullptr;
+ }
+ }
+}
+
+UniquePtr<HangAnnotations>
+ChromeHangAnnotatorCallout()
+{
+ if (!gChromehangAnnotators) {
+ return nullptr;
+ }
+ return gChromehangAnnotators->GatherAnnotations();
+}
+
+} // namespace HangMonitor
+} // namespace mozilla
diff --git a/xpcom/threads/HangAnnotations.h b/xpcom/threads/HangAnnotations.h
new file mode 100644
index 000000000..6dddbf4bb
--- /dev/null
+++ b/xpcom/threads/HangAnnotations.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_HangAnnotations_h
+#define mozilla_HangAnnotations_h
+
+#include <set>
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace HangMonitor {
+
+/**
+ * This class declares an abstraction for a data type that encapsulates all
+ * of the annotations being reported by a registered hang Annotator.
+ */
+class HangAnnotations
+{
+public:
+ virtual ~HangAnnotations() {}
+
+ virtual void AddAnnotation(const nsAString& aName, const int32_t aData) = 0;
+ virtual void AddAnnotation(const nsAString& aName, const double aData) = 0;
+ virtual void AddAnnotation(const nsAString& aName, const nsAString& aData) = 0;
+ virtual void AddAnnotation(const nsAString& aName, const nsACString& aData) = 0;
+ virtual void AddAnnotation(const nsAString& aName, const bool aData) = 0;
+
+ class Enumerator
+ {
+ public:
+ virtual ~Enumerator() {}
+ virtual bool Next(nsAString& aOutName, nsAString& aOutValue) = 0;
+ };
+
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+ virtual bool IsEmpty() const = 0;
+ virtual UniquePtr<Enumerator> GetEnumerator() = 0;
+};
+
+typedef UniquePtr<HangAnnotations> HangAnnotationsPtr;
+typedef Vector<HangAnnotationsPtr> HangAnnotationsVector;
+
+class Annotator
+{
+public:
+ /**
+ * NB: This function is always called by the HangMonitor thread.
+ * Plan accordingly.
+ */
+ virtual void AnnotateHang(HangAnnotations& aAnnotations) = 0;
+};
+
+/**
+ * Registers an Annotator to be called when a hang is detected.
+ * @param aAnnotator Reference to an object that implements the
+ * HangMonitor::Annotator interface.
+ */
+void RegisterAnnotator(Annotator& aAnnotator);
+
+/**
+ * Registers an Annotator that was previously registered via RegisterAnnotator.
+ * @param aAnnotator Reference to an object that implements the
+ * HangMonitor::Annotator interface.
+ */
+void UnregisterAnnotator(Annotator& aAnnotator);
+
+/**
+ * Gathers annotations. This function should be called by ChromeHangs.
+ * @return UniquePtr to HangAnnotations object or nullptr if none.
+ */
+HangAnnotationsPtr ChromeHangAnnotatorCallout();
+
+namespace Observer {
+
+class Annotators
+{
+public:
+ Annotators();
+ ~Annotators();
+
+ bool Register(Annotator& aAnnotator);
+ bool Unregister(Annotator& aAnnotator);
+
+ HangAnnotationsPtr GatherAnnotations();
+
+private:
+ Mutex mMutex;
+ std::set<Annotator*> mAnnotators;
+};
+
+} // namespace Observer
+
+} // namespace HangMonitor
+} // namespace mozilla
+
+#endif // mozilla_HangAnnotations_h
diff --git a/xpcom/threads/HangMonitor.cpp b/xpcom/threads/HangMonitor.cpp
new file mode 100644
index 000000000..71cc67ca4
--- /dev/null
+++ b/xpcom/threads/HangMonitor.cpp
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/HangMonitor.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsReadableUtils.h"
+#include "mozilla/StackWalk.h"
+#ifdef _WIN64
+#include "mozilla/StackWalk_windows.h"
+#endif
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#if defined(MOZ_ENABLE_PROFILER_SPS) && defined(MOZ_PROFILING) && defined(XP_WIN)
+ #define REPORT_CHROME_HANGS
+#endif
+
+namespace mozilla {
+namespace HangMonitor {
+
+/**
+ * A flag which may be set from within a debugger to disable the hang
+ * monitor.
+ */
+volatile bool gDebugDisableHangMonitor = false;
+
+const char kHangMonitorPrefName[] = "hangmonitor.timeout";
+
+#ifdef REPORT_CHROME_HANGS
+const char kTelemetryPrefName[] = "toolkit.telemetry.enabled";
+#endif
+
+// Monitor protects gShutdown and gTimeout, but not gTimestamp which rely on
+// being atomically set by the processor; synchronization doesn't really matter
+// in this use case.
+Monitor* gMonitor;
+
+// The timeout preference, in seconds.
+int32_t gTimeout;
+
+PRThread* gThread;
+
+// Set when shutdown begins to signal the thread to exit immediately.
+bool gShutdown;
+
+// The timestamp of the last event notification, or PR_INTERVAL_NO_WAIT if
+// we're currently not processing events.
+Atomic<PRIntervalTime> gTimestamp(PR_INTERVAL_NO_WAIT);
+
+#ifdef REPORT_CHROME_HANGS
+// Main thread ID used in reporting chrome hangs under Windows
+static HANDLE winMainThreadHandle = nullptr;
+
+// Default timeout for reporting chrome hangs to Telemetry (5 seconds)
+static const int32_t DEFAULT_CHROME_HANG_INTERVAL = 5;
+
+// Maximum number of PCs to gather from the stack
+static const int32_t MAX_CALL_STACK_PCS = 400;
+#endif
+
+// PrefChangedFunc
+void
+PrefChanged(const char*, void*)
+{
+ int32_t newval = Preferences::GetInt(kHangMonitorPrefName);
+#ifdef REPORT_CHROME_HANGS
+ // Monitor chrome hangs on the profiling branch if Telemetry enabled
+ if (newval == 0) {
+ bool telemetryEnabled = Preferences::GetBool(kTelemetryPrefName);
+ if (telemetryEnabled) {
+ newval = DEFAULT_CHROME_HANG_INTERVAL;
+ }
+ }
+#endif
+ MonitorAutoLock lock(*gMonitor);
+ if (newval != gTimeout) {
+ gTimeout = newval;
+ lock.Notify();
+ }
+}
+
+void
+Crash()
+{
+ if (gDebugDisableHangMonitor) {
+ return;
+ }
+
+#ifdef XP_WIN
+ if (::IsDebuggerPresent()) {
+ return;
+ }
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+ // If you change this, you must also deal with the threadsafety of AnnotateCrashReport in
+ // non-chrome processes!
+ if (GeckoProcessType_Default == XRE_GetProcessType()) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Hang"),
+ NS_LITERAL_CSTRING("1"));
+ }
+#endif
+
+ NS_RUNTIMEABORT("HangMonitor triggered");
+}
+
+#ifdef REPORT_CHROME_HANGS
+
+static void
+ChromeStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+ MOZ_ASSERT(aClosure);
+ std::vector<uintptr_t>* stack =
+ static_cast<std::vector<uintptr_t>*>(aClosure);
+ if (stack->size() == MAX_CALL_STACK_PCS) {
+ return;
+ }
+ MOZ_ASSERT(stack->size() < MAX_CALL_STACK_PCS);
+ stack->push_back(reinterpret_cast<uintptr_t>(aPC));
+}
+
+static void
+GetChromeHangReport(Telemetry::ProcessedStack& aStack,
+ int32_t& aSystemUptime,
+ int32_t& aFirefoxUptime)
+{
+ MOZ_ASSERT(winMainThreadHandle);
+
+ // The thread we're about to suspend might have the alloc lock
+ // so allocate ahead of time
+ std::vector<uintptr_t> rawStack;
+ rawStack.reserve(MAX_CALL_STACK_PCS);
+
+ // Workaround possible deadlock where the main thread is running a
+ // long-standing JS job, and happens to be in the JIT allocator when we
+ // suspend it. Since, on win 64, this requires holding a process lock that
+ // MozStackWalk requires, take this "workaround lock" to avoid deadlock.
+#ifdef _WIN64
+ AcquireStackWalkWorkaroundLock();
+#endif
+ DWORD ret = ::SuspendThread(winMainThreadHandle);
+ bool suspended = false;
+ if (ret != -1) {
+ // SuspendThread is asynchronous, so the thread may still be running. Use
+ // GetThreadContext to ensure it's really suspended.
+ // See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_CONTROL;
+ if (::GetThreadContext(winMainThreadHandle, &context)) {
+ suspended = true;
+ }
+ }
+
+#ifdef _WIN64
+ ReleaseStackWalkWorkaroundLock();
+#endif
+
+ if (!suspended) {
+ if (ret != -1) {
+ MOZ_ALWAYS_TRUE(::ResumeThread(winMainThreadHandle) != DWORD(-1));
+ }
+ return;
+ }
+
+ MozStackWalk(ChromeStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+ reinterpret_cast<void*>(&rawStack),
+ reinterpret_cast<uintptr_t>(winMainThreadHandle), nullptr);
+ ret = ::ResumeThread(winMainThreadHandle);
+ if (ret == -1) {
+ return;
+ }
+ aStack = Telemetry::GetStackAndModules(rawStack);
+
+ // Record system uptime (in minutes) at the time of the hang
+ aSystemUptime = ((GetTickCount() / 1000) - (gTimeout * 2)) / 60;
+
+ // Record Firefox uptime (in minutes) at the time of the hang
+ bool error;
+ TimeStamp processCreation = TimeStamp::ProcessCreation(error);
+ if (!error) {
+ TimeDuration td = TimeStamp::Now() - processCreation;
+ aFirefoxUptime = (static_cast<int32_t>(td.ToSeconds()) - (gTimeout * 2)) / 60;
+ } else {
+ aFirefoxUptime = -1;
+ }
+}
+
+#endif
+
+void
+ThreadMain(void*)
+{
+ PR_SetCurrentThreadName("Hang Monitor");
+
+ MonitorAutoLock lock(*gMonitor);
+
+ // In order to avoid issues with the hang monitor incorrectly triggering
+ // during a general system stop such as sleeping, the monitor thread must
+ // run twice to trigger hang protection.
+ PRIntervalTime lastTimestamp = 0;
+ int waitCount = 0;
+
+#ifdef REPORT_CHROME_HANGS
+ Telemetry::ProcessedStack stack;
+ int32_t systemUptime = -1;
+ int32_t firefoxUptime = -1;
+ UniquePtr<HangAnnotations> annotations;
+#endif
+
+ while (true) {
+ if (gShutdown) {
+ return; // Exit the thread
+ }
+
+ // avoid rereading the volatile value in this loop
+ PRIntervalTime timestamp = gTimestamp;
+
+ PRIntervalTime now = PR_IntervalNow();
+
+ if (timestamp != PR_INTERVAL_NO_WAIT &&
+ now < timestamp) {
+ // 32-bit overflow, reset for another waiting period
+ timestamp = 1; // lowest legal PRInterval value
+ }
+
+ if (timestamp != PR_INTERVAL_NO_WAIT &&
+ timestamp == lastTimestamp &&
+ gTimeout > 0) {
+ ++waitCount;
+#ifdef REPORT_CHROME_HANGS
+ // Capture the chrome-hang stack + Firefox & system uptimes after
+ // the minimum hang duration has been reached (not when the hang ends)
+ if (waitCount == 2) {
+ GetChromeHangReport(stack, systemUptime, firefoxUptime);
+ annotations = ChromeHangAnnotatorCallout();
+ }
+#else
+ // This is the crash-on-hang feature.
+ // See bug 867313 for the quirk in the waitCount comparison
+ if (waitCount >= 2) {
+ int32_t delay =
+ int32_t(PR_IntervalToSeconds(now - timestamp));
+ if (delay >= gTimeout) {
+ MonitorAutoUnlock unlock(*gMonitor);
+ Crash();
+ }
+ }
+#endif
+ } else {
+#ifdef REPORT_CHROME_HANGS
+ if (waitCount >= 2) {
+ uint32_t hangDuration = PR_IntervalToSeconds(now - lastTimestamp);
+ Telemetry::RecordChromeHang(hangDuration, stack, systemUptime,
+ firefoxUptime, Move(annotations));
+ stack.Clear();
+ }
+#endif
+ lastTimestamp = timestamp;
+ waitCount = 0;
+ }
+
+ PRIntervalTime timeout;
+ if (gTimeout <= 0) {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ timeout = PR_MillisecondsToInterval(gTimeout * 500);
+ }
+ lock.Wait(timeout);
+ }
+}
+
+void
+Startup()
+{
+ if (GeckoProcessType_Default != XRE_GetProcessType() &&
+ GeckoProcessType_Content != XRE_GetProcessType()) {
+ return;
+ }
+
+ MOZ_ASSERT(!gMonitor, "Hang monitor already initialized");
+ gMonitor = new Monitor("HangMonitor");
+
+ Preferences::RegisterCallback(PrefChanged, kHangMonitorPrefName, nullptr);
+ PrefChanged(nullptr, nullptr);
+
+#ifdef REPORT_CHROME_HANGS
+ Preferences::RegisterCallback(PrefChanged, kTelemetryPrefName, nullptr);
+ winMainThreadHandle =
+ OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
+ if (!winMainThreadHandle) {
+ return;
+ }
+#endif
+
+ // Don't actually start measuring hangs until we hit the main event loop.
+ // This potentially misses a small class of really early startup hangs,
+ // but avoids dealing with some xpcshell tests and other situations which
+ // start XPCOM but don't ever start the event loop.
+ Suspend();
+
+ gThread = PR_CreateThread(PR_USER_THREAD,
+ ThreadMain,
+ nullptr, PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+}
+
+void
+Shutdown()
+{
+ if (GeckoProcessType_Default != XRE_GetProcessType() &&
+ GeckoProcessType_Content != XRE_GetProcessType()) {
+ return;
+ }
+
+ MOZ_ASSERT(gMonitor, "Hang monitor not started");
+
+ {
+ // Scope the lock we're going to delete later
+ MonitorAutoLock lock(*gMonitor);
+ gShutdown = true;
+ lock.Notify();
+ }
+
+ // thread creation could theoretically fail
+ if (gThread) {
+ PR_JoinThread(gThread);
+ gThread = nullptr;
+ }
+
+ delete gMonitor;
+ gMonitor = nullptr;
+}
+
+static bool
+IsUIMessageWaiting()
+{
+#ifndef XP_WIN
+ return false;
+#else
+#define NS_WM_IMEFIRST WM_IME_SETCONTEXT
+#define NS_WM_IMELAST WM_IME_KEYUP
+ BOOL haveUIMessageWaiting = FALSE;
+ MSG msg;
+ haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, WM_KEYFIRST,
+ WM_IME_KEYLAST, PM_NOREMOVE);
+ haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, NS_WM_IMEFIRST,
+ NS_WM_IMELAST, PM_NOREMOVE);
+ haveUIMessageWaiting |= ::PeekMessageW(&msg, nullptr, WM_MOUSEFIRST,
+ WM_MOUSELAST, PM_NOREMOVE);
+ return haveUIMessageWaiting;
+#endif
+}
+
+void
+NotifyActivity(ActivityType aActivityType)
+{
+ MOZ_ASSERT(NS_IsMainThread(),
+ "HangMonitor::Notify called from off the main thread.");
+
+ // Determine the activity type more specifically
+ if (aActivityType == kGeneralActivity) {
+ aActivityType = IsUIMessageWaiting() ? kActivityUIAVail :
+ kActivityNoUIAVail;
+ }
+
+ // Calculate the cumulative amount of lag time since the last UI message
+ static uint32_t cumulativeUILagMS = 0;
+ switch (aActivityType) {
+ case kActivityNoUIAVail:
+ cumulativeUILagMS = 0;
+ break;
+ case kActivityUIAVail:
+ case kUIActivity:
+ if (gTimestamp != PR_INTERVAL_NO_WAIT) {
+ cumulativeUILagMS += PR_IntervalToMilliseconds(PR_IntervalNow() -
+ gTimestamp);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // This is not a locked activity because PRTimeStamp is a 32-bit quantity
+ // which can be read/written atomically, and we don't want to pay locking
+ // penalties here.
+ gTimestamp = PR_IntervalNow();
+
+ // If we have UI activity we should reset the timer and report it
+ if (aActivityType == kUIActivity) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::EVENTLOOP_UI_ACTIVITY_EXP_MS,
+ cumulativeUILagMS);
+ cumulativeUILagMS = 0;
+ }
+
+ if (gThread && !gShutdown) {
+ mozilla::BackgroundHangMonitor().NotifyActivity();
+ }
+}
+
+void
+Suspend()
+{
+ MOZ_ASSERT(NS_IsMainThread(),
+ "HangMonitor::Suspend called from off the main thread.");
+
+ // Because gTimestamp changes this resets the wait count.
+ gTimestamp = PR_INTERVAL_NO_WAIT;
+
+ if (gThread && !gShutdown) {
+ mozilla::BackgroundHangMonitor().NotifyWait();
+ }
+}
+
+} // namespace HangMonitor
+} // namespace mozilla
diff --git a/xpcom/threads/HangMonitor.h b/xpcom/threads/HangMonitor.h
new file mode 100644
index 000000000..fd0e6ff83
--- /dev/null
+++ b/xpcom/threads/HangMonitor.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_HangMonitor_h
+#define mozilla_HangMonitor_h
+
+namespace mozilla {
+namespace HangMonitor {
+
+/**
+ * Signifies the type of activity in question
+*/
+enum ActivityType
+{
+ /* There is activity and it is known to be UI related activity. */
+ kUIActivity,
+
+ /* There is non UI activity and no UI activity is pending */
+ kActivityNoUIAVail,
+
+ /* There is non UI activity and UI activity is known to be pending */
+ kActivityUIAVail,
+
+ /* There is non UI activity and UI activity pending is unknown */
+ kGeneralActivity
+};
+
+/**
+ * Start monitoring hangs. Should be called by the XPCOM startup process only.
+ */
+void Startup();
+
+/**
+ * Stop monitoring hangs and join the thread.
+ */
+void Shutdown();
+
+/**
+ * Notify the hang monitor of activity which will reset its internal timer.
+ *
+ * @param activityType The type of activity being reported.
+ * @see ActivityType
+ */
+void NotifyActivity(ActivityType activityType = kGeneralActivity);
+
+/*
+ * Notify the hang monitor that the browser is now idle and no detection should
+ * be done.
+ */
+void Suspend();
+
+} // namespace HangMonitor
+} // namespace mozilla
+
+#endif // mozilla_HangMonitor_h
diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp
new file mode 100644
index 000000000..527cc6819
--- /dev/null
+++ b/xpcom/threads/LazyIdleThread.cpp
@@ -0,0 +1,624 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LazyIdleThread.h"
+
+#include "nsIObserverService.h"
+
+#include "GeckoProfiler.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIIdlePeriod.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+
+#ifdef DEBUG
+#define ASSERT_OWNING_THREAD() \
+ PR_BEGIN_MACRO \
+ nsIThread* currentThread = NS_GetCurrentThread(); \
+ if (currentThread) { \
+ nsCOMPtr<nsISupports> current(do_QueryInterface(currentThread)); \
+ nsCOMPtr<nsISupports> test(do_QueryInterface(mOwningThread)); \
+ MOZ_ASSERT(current == test, "Wrong thread!"); \
+ } \
+ PR_END_MACRO
+#else
+#define ASSERT_OWNING_THREAD() /* nothing */
+#endif
+
+namespace mozilla {
+
+LazyIdleThread::LazyIdleThread(uint32_t aIdleTimeoutMS,
+ const nsCSubstring& aName,
+ ShutdownMethod aShutdownMethod,
+ nsIObserver* aIdleObserver)
+ : mMutex("LazyIdleThread::mMutex")
+ , mOwningThread(NS_GetCurrentThread())
+ , mIdleObserver(aIdleObserver)
+ , mQueuedRunnables(nullptr)
+ , mIdleTimeoutMS(aIdleTimeoutMS)
+ , mPendingEventCount(0)
+ , mIdleNotificationCount(0)
+ , mShutdownMethod(aShutdownMethod)
+ , mShutdown(false)
+ , mThreadIsShuttingDown(false)
+ , mIdleTimeoutEnabled(true)
+ , mName(aName)
+{
+ MOZ_ASSERT(mOwningThread, "Need owning thread!");
+}
+
+LazyIdleThread::~LazyIdleThread()
+{
+ ASSERT_OWNING_THREAD();
+
+ Shutdown();
+}
+
+void
+LazyIdleThread::SetWeakIdleObserver(nsIObserver* aObserver)
+{
+ ASSERT_OWNING_THREAD();
+
+ if (mShutdown) {
+ NS_WARNING_ASSERTION(!aObserver,
+ "Setting an observer after Shutdown was called!");
+ return;
+ }
+
+ mIdleObserver = aObserver;
+}
+
+void
+LazyIdleThread::DisableIdleTimeout()
+{
+ ASSERT_OWNING_THREAD();
+ if (!mIdleTimeoutEnabled) {
+ return;
+ }
+ mIdleTimeoutEnabled = false;
+
+ if (mIdleTimer && NS_FAILED(mIdleTimer->Cancel())) {
+ NS_WARNING("Failed to cancel timer!");
+ }
+
+ MutexAutoLock lock(mMutex);
+
+ // Pretend we have a pending event to keep the idle timer from firing.
+ MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
+ mPendingEventCount++;
+}
+
+void
+LazyIdleThread::EnableIdleTimeout()
+{
+ ASSERT_OWNING_THREAD();
+ if (mIdleTimeoutEnabled) {
+ return;
+ }
+ mIdleTimeoutEnabled = true;
+
+ {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
+ --mPendingEventCount;
+ }
+
+ if (mThread) {
+ nsCOMPtr<nsIRunnable> runnable(new Runnable());
+ if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch!");
+ }
+ }
+}
+
+void
+LazyIdleThread::PreDispatch()
+{
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mPendingEventCount < UINT32_MAX, "Way too many!");
+ mPendingEventCount++;
+}
+
+nsresult
+LazyIdleThread::EnsureThread()
+{
+ ASSERT_OWNING_THREAD();
+
+ if (mShutdown) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (mThread) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(!mPendingEventCount, "Shouldn't have events yet!");
+ MOZ_ASSERT(!mIdleNotificationCount, "Shouldn't have idle events yet!");
+ MOZ_ASSERT(!mIdleTimer, "Should have killed this long ago!");
+ MOZ_ASSERT(!mThreadIsShuttingDown, "Should have cleared that!");
+
+ nsresult rv;
+
+ if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> obs =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = obs->AddObserver(this, "xpcom-shutdown-threads", false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ if (NS_WARN_IF(!mIdleTimer)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod(this, &LazyIdleThread::InitThread);
+ if (NS_WARN_IF(!runnable)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = NS_NewThread(getter_AddRefs(mThread), runnable);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+LazyIdleThread::InitThread()
+{
+ char aLocal;
+ profiler_register_thread(mName.get(), &aLocal);
+
+ PR_SetCurrentThreadName(mName.get());
+
+ // Happens on mThread but mThread may not be set yet...
+
+ nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
+ MOZ_ASSERT(thread, "This should always succeed!");
+
+ if (NS_FAILED(thread->SetObserver(this))) {
+ NS_WARNING("Failed to set thread observer!");
+ }
+}
+
+void
+LazyIdleThread::CleanupThread()
+{
+ nsCOMPtr<nsIThreadInternal> thread(do_QueryInterface(NS_GetCurrentThread()));
+ MOZ_ASSERT(thread, "This should always succeed!");
+
+ if (NS_FAILED(thread->SetObserver(nullptr))) {
+ NS_WARNING("Failed to set thread observer!");
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(!mThreadIsShuttingDown, "Shouldn't be true ever!");
+ mThreadIsShuttingDown = true;
+ }
+
+ profiler_unregister_thread();
+}
+
+void
+LazyIdleThread::ScheduleTimer()
+{
+ ASSERT_OWNING_THREAD();
+
+ bool shouldSchedule;
+ {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mIdleNotificationCount, "Should have at least one!");
+ --mIdleNotificationCount;
+
+ shouldSchedule = !mIdleNotificationCount && !mPendingEventCount;
+ }
+
+ if (mIdleTimer) {
+ if (NS_FAILED(mIdleTimer->Cancel())) {
+ NS_WARNING("Failed to cancel timer!");
+ }
+
+ if (shouldSchedule &&
+ NS_FAILED(mIdleTimer->InitWithCallback(this, mIdleTimeoutMS,
+ nsITimer::TYPE_ONE_SHOT))) {
+ NS_WARNING("Failed to schedule timer!");
+ }
+ }
+}
+
+nsresult
+LazyIdleThread::ShutdownThread()
+{
+ ASSERT_OWNING_THREAD();
+
+ // Before calling Shutdown() on the real thread we need to put a queue in
+ // place in case a runnable is posted to the thread while it's in the
+ // process of shutting down. This will be our queue.
+ AutoTArray<nsCOMPtr<nsIRunnable>, 10> queuedRunnables;
+
+ nsresult rv;
+
+ // Make sure to cancel the shutdown timer before spinning the event loop
+ // during |mThread->Shutdown()| below. Otherwise the timer might fire and we
+ // could reenter here.
+ if (mIdleTimer) {
+ rv = mIdleTimer->Cancel();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mIdleTimer = nullptr;
+ }
+
+ if (mThread) {
+ if (mShutdownMethod == AutomaticShutdown && NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService();
+ NS_WARNING_ASSERTION(obs, "Failed to get observer service!");
+
+ if (obs &&
+ NS_FAILED(obs->RemoveObserver(this, "xpcom-shutdown-threads"))) {
+ NS_WARNING("Failed to remove observer!");
+ }
+ }
+
+ if (mIdleObserver) {
+ mIdleObserver->Observe(static_cast<nsIThread*>(this), IDLE_THREAD_TOPIC,
+ nullptr);
+ }
+
+#ifdef DEBUG
+ {
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(!mThreadIsShuttingDown, "Huh?!");
+ }
+#endif
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod(this, &LazyIdleThread::CleanupThread);
+ if (NS_WARN_IF(!runnable)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ PreDispatch();
+
+ rv = mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Put the temporary queue in place before calling Shutdown().
+ mQueuedRunnables = &queuedRunnables;
+
+ if (NS_FAILED(mThread->Shutdown())) {
+ NS_ERROR("Failed to shutdown the thread!");
+ }
+
+ // Now unset the queue.
+ mQueuedRunnables = nullptr;
+
+ mThread = nullptr;
+
+ {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(!mPendingEventCount, "Huh?!");
+ MOZ_ASSERT(!mIdleNotificationCount, "Huh?!");
+ MOZ_ASSERT(mThreadIsShuttingDown, "Huh?!");
+ mThreadIsShuttingDown = false;
+ }
+ }
+
+ // If our temporary queue has any runnables then we need to dispatch them.
+ if (queuedRunnables.Length()) {
+ // If the thread manager has gone away then these runnables will never run.
+ if (mShutdown) {
+ NS_ERROR("Runnables dispatched to LazyIdleThread will never run!");
+ return NS_OK;
+ }
+
+ // Re-dispatch the queued runnables.
+ for (uint32_t index = 0; index < queuedRunnables.Length(); index++) {
+ nsCOMPtr<nsIRunnable> runnable;
+ runnable.swap(queuedRunnables[index]);
+ MOZ_ASSERT(runnable, "Null runnable?!");
+
+ if (NS_FAILED(Dispatch(runnable.forget(), NS_DISPATCH_NORMAL))) {
+ NS_ERROR("Failed to re-dispatch queued runnable!");
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+LazyIdleThread::SelfDestruct()
+{
+ MOZ_ASSERT(mRefCnt == 1, "Bad refcount!");
+ delete this;
+}
+
+NS_IMPL_ADDREF(LazyIdleThread)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+LazyIdleThread::Release()
+{
+ nsrefcnt count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "LazyIdleThread");
+
+ if (!count) {
+ // Stabilize refcount.
+ mRefCnt = 1;
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NewNonOwningRunnableMethod(this, &LazyIdleThread::SelfDestruct);
+ NS_WARNING_ASSERTION(runnable, "Couldn't make runnable!");
+
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+ // The only way this could fail is if we're in shutdown, and in that case
+ // threads should have been joined already. Deleting here isn't dangerous
+ // anymore because we won't spin the event loop waiting to join the
+ // thread.
+ SelfDestruct();
+ }
+ }
+
+ return count;
+}
+
+NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread,
+ nsIEventTarget,
+ nsITimerCallback,
+ nsIThreadObserver,
+ nsIObserver)
+
+NS_IMETHODIMP
+LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+LazyIdleThread::Dispatch(already_AddRefed<nsIRunnable> aEvent,
+ uint32_t aFlags)
+{
+ ASSERT_OWNING_THREAD();
+ nsCOMPtr<nsIRunnable> event(aEvent); // avoid leaks
+
+ // LazyIdleThread can't always support synchronous dispatch currently.
+ if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ if (NS_WARN_IF(mShutdown)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // If our thread is shutting down then we can't actually dispatch right now.
+ // Queue this runnable for later.
+ if (UseRunnableQueue()) {
+ mQueuedRunnables->AppendElement(event);
+ return NS_OK;
+ }
+
+ nsresult rv = EnsureThread();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ PreDispatch();
+
+ return mThread->Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+LazyIdleThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::IsOnCurrentThread(bool* aIsOnCurrentThread)
+{
+ if (mThread) {
+ return mThread->IsOnCurrentThread(aIsOnCurrentThread);
+ }
+
+ *aIsOnCurrentThread = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::GetPRThread(PRThread** aPRThread)
+{
+ if (mThread) {
+ return mThread->GetPRThread(aPRThread);
+ }
+
+ *aPRThread = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::GetCanInvokeJS(bool* aCanInvokeJS)
+{
+ *aCanInvokeJS = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::SetCanInvokeJS(bool aCanInvokeJS)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::AsyncShutdown()
+{
+ ASSERT_OWNING_THREAD();
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::Shutdown()
+{
+ ASSERT_OWNING_THREAD();
+
+ mShutdown = true;
+
+ nsresult rv = ShutdownThread();
+ MOZ_ASSERT(!mThread, "Should have destroyed this by now!");
+
+ mIdleObserver = nullptr;
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::HasPendingEvents(bool* aHasPendingEvents)
+{
+ // This is only supposed to be called from the thread itself so it's not
+ // implemented here.
+ NS_NOTREACHED("Shouldn't ever call this!");
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::ProcessNextEvent(bool aMayWait,
+ bool* aEventWasProcessed)
+{
+ // This is only supposed to be called from the thread itself so it's not
+ // implemented here.
+ NS_NOTREACHED("Shouldn't ever call this!");
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::Notify(nsITimer* aTimer)
+{
+ ASSERT_OWNING_THREAD();
+
+ {
+ MutexAutoLock lock(mMutex);
+
+ if (mPendingEventCount || mIdleNotificationCount) {
+ // Another event was scheduled since this timer was set. Don't do
+ // anything and wait for the timer to fire again.
+ return NS_OK;
+ }
+ }
+
+ nsresult rv = ShutdownThread();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */)
+{
+ MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread, "Wrong thread!");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
+ bool /* aMayWait */)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
+ bool aEventWasProcessed)
+{
+ bool shouldNotifyIdle;
+ {
+ MutexAutoLock lock(mMutex);
+
+ if (aEventWasProcessed) {
+ MOZ_ASSERT(mPendingEventCount, "Mismatched calls to observer methods!");
+ --mPendingEventCount;
+ }
+
+ if (mThreadIsShuttingDown) {
+ // We're shutting down, no need to fire any timer.
+ return NS_OK;
+ }
+
+ shouldNotifyIdle = !mPendingEventCount;
+ if (shouldNotifyIdle) {
+ MOZ_ASSERT(mIdleNotificationCount < UINT32_MAX, "Way too many!");
+ mIdleNotificationCount++;
+ }
+ }
+
+ if (shouldNotifyIdle) {
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod(this, &LazyIdleThread::ScheduleTimer);
+ if (NS_WARN_IF(!runnable)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = mOwningThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::Observe(nsISupports* /* aSubject */,
+ const char* aTopic,
+ const char16_t* /* aData */)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+ MOZ_ASSERT(mShutdownMethod == AutomaticShutdown,
+ "Should not receive notifications if not AutomaticShutdown!");
+ MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!");
+
+ Shutdown();
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/LazyIdleThread.h b/xpcom/threads/LazyIdleThread.h
new file mode 100644
index 000000000..6bf8e8e81
--- /dev/null
+++ b/xpcom/threads/LazyIdleThread.h
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_lazyidlethread_h__
+#define mozilla_lazyidlethread_h__
+
+#ifndef MOZILLA_INTERNAL_API
+#error "This header is only usable from within libxul (MOZILLA_INTERNAL_API)."
+#endif
+
+#include "nsIObserver.h"
+#include "nsIThreadInternal.h"
+#include "nsITimer.h"
+
+#include "mozilla/Mutex.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+
+#define IDLE_THREAD_TOPIC "thread-shutting-down"
+
+namespace mozilla {
+
+/**
+ * This class provides a basic event target that creates its thread lazily and
+ * destroys its thread after a period of inactivity. It may be created on any
+ * thread but it may only be used from the thread on which it is created. If it
+ * is created on the main thread then it will automatically join its thread on
+ * XPCOM shutdown using the Observer Service.
+ */
+class LazyIdleThread final
+ : public nsIThread
+ , public nsITimerCallback
+ , public nsIThreadObserver
+ , public nsIObserver
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEVENTTARGET
+ NS_DECL_NSITHREAD
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSITHREADOBSERVER
+ NS_DECL_NSIOBSERVER
+ using nsIEventTarget::Dispatch;
+
+ enum ShutdownMethod
+ {
+ AutomaticShutdown = 0,
+ ManualShutdown
+ };
+
+ /**
+ * Create a new LazyIdleThread that will destroy its thread after the given
+ * number of milliseconds.
+ */
+ LazyIdleThread(uint32_t aIdleTimeoutMS,
+ const nsCSubstring& aName,
+ ShutdownMethod aShutdownMethod = AutomaticShutdown,
+ nsIObserver* aIdleObserver = nullptr);
+
+ /**
+ * Add an observer that will be notified when the thread is idle and about to
+ * be shut down. The aSubject argument can be QueryInterface'd to an nsIThread
+ * that can be used to post cleanup events. The aTopic argument will be
+ * IDLE_THREAD_TOPIC, and aData will be null. The LazyIdleThread does not add
+ * a reference to the observer to avoid circular references as it is assumed
+ * to be the owner. It is the caller's responsibility to clear this observer
+ * if the pointer becomes invalid.
+ */
+ void SetWeakIdleObserver(nsIObserver* aObserver);
+
+ /**
+ * Disable the idle timeout for this thread. No effect if the timeout is
+ * already disabled.
+ */
+ void DisableIdleTimeout();
+
+ /**
+ * Enable the idle timeout. No effect if the timeout is already enabled.
+ */
+ void EnableIdleTimeout();
+
+private:
+ /**
+ * Calls Shutdown().
+ */
+ ~LazyIdleThread();
+
+ /**
+ * Called just before dispatching to mThread.
+ */
+ void PreDispatch();
+
+ /**
+ * Makes sure a valid thread lives in mThread.
+ */
+ nsresult EnsureThread();
+
+ /**
+ * Called on mThread to set up the thread observer.
+ */
+ void InitThread();
+
+ /**
+ * Called on mThread to clean up the thread observer.
+ */
+ void CleanupThread();
+
+ /**
+ * Called on the main thread when mThread believes itself to be idle. Sets up
+ * the idle timer.
+ */
+ void ScheduleTimer();
+
+ /**
+ * Called when we are shutting down mThread.
+ */
+ nsresult ShutdownThread();
+
+ /**
+ * Deletes this object. Used to delay calling mThread->Shutdown() during the
+ * final release (during a GC, for instance).
+ */
+ void SelfDestruct();
+
+ /**
+ * Returns true if events should be queued rather than immediately dispatched
+ * to mThread. Currently only happens when the thread is shutting down.
+ */
+ bool UseRunnableQueue()
+ {
+ return !!mQueuedRunnables;
+ }
+
+ /**
+ * Protects data that is accessed on both threads.
+ */
+ mozilla::Mutex mMutex;
+
+ /**
+ * Touched on both threads but set before mThread is created. Used to direct
+ * timer events to the owning thread.
+ */
+ nsCOMPtr<nsIThread> mOwningThread;
+
+ /**
+ * Only accessed on the owning thread. Set by EnsureThread().
+ */
+ nsCOMPtr<nsIThread> mThread;
+
+ /**
+ * Protected by mMutex. Created when mThread has no pending events and fired
+ * at mOwningThread. Any thread that dispatches to mThread will take ownership
+ * of the timer and fire a separate cancel event to the owning thread.
+ */
+ nsCOMPtr<nsITimer> mIdleTimer;
+
+ /**
+ * Idle observer. Called when the thread is about to be shut down. Released
+ * only when Shutdown() is called.
+ */
+ nsIObserver* MOZ_UNSAFE_REF("See the documentation for SetWeakIdleObserver for "
+ "how the owner of LazyIdleThread should manage the "
+ "lifetime information of this field") mIdleObserver;
+
+ /**
+ * Temporary storage for events that happen to be dispatched while we're in
+ * the process of shutting down our real thread.
+ */
+ nsTArray<nsCOMPtr<nsIRunnable>>* mQueuedRunnables;
+
+ /**
+ * The number of milliseconds a thread should be idle before dying.
+ */
+ const uint32_t mIdleTimeoutMS;
+
+ /**
+ * The number of events that are pending on mThread. A nonzero value means
+ * that the thread cannot be cleaned up.
+ */
+ uint32_t mPendingEventCount;
+
+ /**
+ * The number of times that mThread has dispatched an idle notification. Any
+ * timer that fires while this count is nonzero can safely be ignored as
+ * another timer will be on the way.
+ */
+ uint32_t mIdleNotificationCount;
+
+ /**
+ * Whether or not the thread should automatically shutdown. If the owner
+ * specified ManualShutdown at construction time then the owner should take
+ * care to call Shutdown() manually when appropriate.
+ */
+ ShutdownMethod mShutdownMethod;
+
+ /**
+ * Only accessed on the owning thread. Set to true when Shutdown() has been
+ * called and prevents EnsureThread() from recreating mThread.
+ */
+ bool mShutdown;
+
+ /**
+ * Set from CleanupThread and lasting until the thread has shut down. Prevents
+ * further idle notifications during the shutdown process.
+ */
+ bool mThreadIsShuttingDown;
+
+ /**
+ * Whether or not the idle timeout is enabled.
+ */
+ bool mIdleTimeoutEnabled;
+
+ /**
+ * Name of the thread, set on the actual thread after it gets created.
+ */
+ nsCString mName;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_lazyidlethread_h__
diff --git a/xpcom/threads/LeakRefPtr.h b/xpcom/threads/LeakRefPtr.h
new file mode 100644
index 000000000..56f5d90af
--- /dev/null
+++ b/xpcom/threads/LeakRefPtr.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Smart pointer which leaks its owning refcounted object by default. */
+
+#ifndef LeakRefPtr_h
+#define LeakRefPtr_h
+
+#include "mozilla/AlreadyAddRefed.h"
+
+namespace mozilla {
+
+/**
+ * Instance of this class behaves like a raw pointer which leaks the
+ * resource it's owning if not explicitly released.
+ */
+template<class T>
+class LeakRefPtr
+{
+public:
+ explicit LeakRefPtr(already_AddRefed<T>&& aPtr)
+ : mRawPtr(aPtr.take()) { }
+
+ explicit operator bool() const { return !!mRawPtr; }
+
+ LeakRefPtr<T>& operator=(already_AddRefed<T>&& aPtr)
+ {
+ mRawPtr = aPtr.take();
+ return *this;
+ }
+
+ T* get() const { return mRawPtr; }
+
+ already_AddRefed<T> take()
+ {
+ T* rawPtr = mRawPtr;
+ mRawPtr = nullptr;
+ return already_AddRefed<T>(rawPtr);
+ }
+
+ void release() { NS_RELEASE(mRawPtr); }
+
+private:
+ T* MOZ_OWNING_REF mRawPtr;
+};
+
+} // namespace mozilla
+
+#endif // LeakRefPtr_h
diff --git a/xpcom/threads/MainThreadIdlePeriod.cpp b/xpcom/threads/MainThreadIdlePeriod.cpp
new file mode 100644
index 000000000..4a5f99dd7
--- /dev/null
+++ b/xpcom/threads/MainThreadIdlePeriod.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MainThreadIdlePeriod.h"
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "nsRefreshDriver.h"
+
+#define DEFAULT_LONG_IDLE_PERIOD 50.0f
+#define DEFAULT_MIN_IDLE_PERIOD 3.0f
+
+namespace mozilla {
+
+NS_IMETHODIMP
+MainThreadIdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aIdleDeadline);
+
+ Maybe<TimeStamp> deadline = nsRefreshDriver::GetIdleDeadlineHint();
+
+ if (deadline.isSome()) {
+ // If the idle period is too small, then just return a null time
+ // to indicate we are busy. Otherwise return the actual deadline.
+ TimeDuration minIdlePeriod =
+ TimeDuration::FromMilliseconds(GetMinIdlePeriod());
+ bool busySoon = deadline.value().IsNull() ||
+ (TimeStamp::Now() >= (deadline.value() - minIdlePeriod));
+ *aIdleDeadline = busySoon ? TimeStamp() : deadline.value();
+ } else {
+ *aIdleDeadline =
+ TimeStamp::Now() + TimeDuration::FromMilliseconds(GetLongIdlePeriod());
+ }
+
+ return NS_OK;
+}
+
+/* static */ float
+MainThreadIdlePeriod::GetLongIdlePeriod()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ static float sLongIdlePeriod = DEFAULT_LONG_IDLE_PERIOD;
+ static bool sInitialized = false;
+
+ if (!sInitialized && Preferences::IsServiceAvailable()) {
+ sInitialized = true;
+ Preferences::AddFloatVarCache(&sLongIdlePeriod, "idle_queue.long_period",
+ DEFAULT_LONG_IDLE_PERIOD);
+ }
+
+ return sLongIdlePeriod;
+}
+
+/* static */ float
+MainThreadIdlePeriod::GetMinIdlePeriod()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ static float sMinIdlePeriod = DEFAULT_MIN_IDLE_PERIOD;
+ static bool sInitialized = false;
+
+ if (!sInitialized && Preferences::IsServiceAvailable()) {
+ sInitialized = true;
+ Preferences::AddFloatVarCache(&sMinIdlePeriod, "idle_queue.min_period",
+ DEFAULT_MIN_IDLE_PERIOD);
+ }
+
+ return sMinIdlePeriod;
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/MainThreadIdlePeriod.h b/xpcom/threads/MainThreadIdlePeriod.h
new file mode 100644
index 000000000..2b773551c
--- /dev/null
+++ b/xpcom/threads/MainThreadIdlePeriod.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_mainthreadidleperiod_h
+#define mozilla_dom_mainthreadidleperiod_h
+
+#include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+class MainThreadIdlePeriod final : public IdlePeriod
+{
+public:
+ NS_DECL_NSIIDLEPERIOD
+
+ static float GetLongIdlePeriod();
+ static float GetMinIdlePeriod();
+private:
+ virtual ~MainThreadIdlePeriod() {}
+};
+
+} // namespace mozilla
+
+#endif // mozilla_dom_mainthreadidleperiod_h
diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h
new file mode 100644
index 000000000..7a2921d2a
--- /dev/null
+++ b/xpcom/threads/MozPromise.h
@@ -0,0 +1,1067 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(MozPromise_h_)
+#define MozPromise_h_
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/IndexSequence.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#define PROMISE_DEBUG
+#endif
+
+#ifdef PROMISE_DEBUG
+#define PROMISE_ASSERT MOZ_RELEASE_ASSERT
+#else
+#define PROMISE_ASSERT(...) do { } while (0)
+#endif
+
+namespace mozilla {
+
+extern LazyLogModule gMozPromiseLog;
+
+#define PROMISE_LOG(x, ...) \
+ MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
+
+namespace detail {
+template<typename ThisType, typename Ret, typename ArgType>
+static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType));
+template<typename ThisType, typename Ret, typename ArgType>
+static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType) const);
+template<typename ThisType, typename Ret>
+static FalseType TakesArgumentHelper(Ret (ThisType::*)());
+template<typename ThisType, typename Ret>
+static FalseType TakesArgumentHelper(Ret (ThisType::*)() const);
+
+template<typename ThisType, typename Ret, typename ArgType>
+static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType));
+template<typename ThisType, typename Ret, typename ArgType>
+static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType) const);
+template<typename ThisType, typename Ret>
+static Ret ReturnTypeHelper(Ret (ThisType::*)());
+template<typename ThisType, typename Ret>
+static Ret ReturnTypeHelper(Ret (ThisType::*)() const);
+
+template<typename MethodType>
+struct ReturnType {
+ typedef decltype(detail::ReturnTypeHelper(DeclVal<MethodType>())) Type;
+};
+
+} // namespace detail
+
+template<typename MethodType>
+struct TakesArgument {
+ static const bool value = decltype(detail::TakesArgumentHelper(DeclVal<MethodType>()))::value;
+};
+
+template<typename MethodType, typename TargetType>
+struct ReturnTypeIs {
+ static const bool value = IsConvertible<typename detail::ReturnType<MethodType>::Type, TargetType>::value;
+};
+
+/*
+ * A promise manages an asynchronous request that may or may not be able to be
+ * fulfilled immediately. When an API returns a promise, the consumer may attach
+ * callbacks to be invoked (asynchronously, on a specified thread) when the
+ * request is either completed (resolved) or cannot be completed (rejected).
+ * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
+ * MozPromises resolution/rejection make a normal round-trip through the event
+ * loop, which simplifies their ordering semantics relative to other native code.
+ *
+ * MozPromises attempt to mirror the spirit of JS Promises to the extent that
+ * is possible (and desirable) in C++. While the intent is that MozPromises
+ * feel familiar to programmers who are accustomed to their JS-implemented cousin,
+ * we don't shy away from imposing restrictions and adding features that make
+ * sense for the use cases we encounter.
+ *
+ * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
+ * call accepts resolve and reject callbacks, and returns a MozPromise::Request.
+ * The Request object serves several purposes for the consumer.
+ *
+ * (1) It allows the caller to cancel the delivery of the resolve/reject value
+ * if it has not already occurred, via Disconnect() (this must be done on
+ * the target thread to avoid racing).
+ *
+ * (2) It provides access to a "Completion Promise", which is roughly analagous
+ * to the Promise returned directly by ->then() calls on JS promises. If
+ * the resolve/reject callback returns a new MozPromise, that promise is
+ * chained to the completion promise, such that its resolve/reject value
+ * will be forwarded along when it arrives. If the resolve/reject callback
+ * returns void, the completion promise is resolved/rejected with the same
+ * value that was passed to the callback.
+ *
+ * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
+ * (rather than already_AddRefed) from various methods. This is done to allow elegant
+ * chaining of calls without cluttering up the code with intermediate variables, and
+ * without introducing separate API variants for callers that want a return value
+ * (from, say, ->Then()) from those that don't.
+ *
+ * When IsExclusive is true, the MozPromise does a release-mode assertion that
+ * there is at most one call to either Then(...) or ChainTo(...).
+ */
+
+class MozPromiseRefcountable
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
+protected:
+ virtual ~MozPromiseRefcountable() {}
+};
+
+template<typename T> class MozPromiseHolder;
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MozPromise : public MozPromiseRefcountable
+{
+ static const uint32_t sMagic = 0xcecace11;
+
+public:
+ typedef ResolveValueT ResolveValueType;
+ typedef RejectValueT RejectValueType;
+ class ResolveOrRejectValue
+ {
+ public:
+ template<typename ResolveValueType_>
+ void SetResolve(ResolveValueType_&& aResolveValue)
+ {
+ MOZ_ASSERT(IsNothing());
+ mResolveValue.emplace(Forward<ResolveValueType_>(aResolveValue));
+ }
+
+ template<typename RejectValueType_>
+ void SetReject(RejectValueType_&& aRejectValue)
+ {
+ MOZ_ASSERT(IsNothing());
+ mRejectValue.emplace(Forward<RejectValueType_>(aRejectValue));
+ }
+
+ template<typename ResolveValueType_>
+ static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue)
+ {
+ ResolveOrRejectValue val;
+ val.SetResolve(Forward<ResolveValueType_>(aResolveValue));
+ return val;
+ }
+
+ template<typename RejectValueType_>
+ static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue)
+ {
+ ResolveOrRejectValue val;
+ val.SetReject(Forward<RejectValueType_>(aRejectValue));
+ return val;
+ }
+
+ bool IsResolve() const { return mResolveValue.isSome(); }
+ bool IsReject() const { return mRejectValue.isSome(); }
+ bool IsNothing() const { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
+
+ const ResolveValueType& ResolveValue() const { return mResolveValue.ref(); }
+ const RejectValueType& RejectValue() const { return mRejectValue.ref(); }
+
+ private:
+ Maybe<ResolveValueType> mResolveValue;
+ Maybe<RejectValueType> mRejectValue;
+ };
+
+protected:
+ // MozPromise is the public type, and never constructed directly. Construct
+ // a MozPromise::Private, defined below.
+ MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
+ : mCreationSite(aCreationSite)
+ , mMutex("MozPromise Mutex")
+ , mHaveRequest(false)
+ , mIsCompletionPromise(aIsCompletionPromise)
+#ifdef PROMISE_DEBUG
+ , mMagic4(mMutex.mLock)
+#endif
+ {
+ PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
+ }
+
+public:
+ // MozPromise::Private allows us to separate the public interface (upon which
+ // consumers of the promise may invoke methods like Then()) from the private
+ // interface (upon which the creator of the promise may invoke Resolve() or
+ // Reject()). APIs should create and store a MozPromise::Private (usually
+ // via a MozPromiseHolder), and return a MozPromise to consumers.
+ //
+ // NB: We can include the definition of this class inline once B2G ICS is gone.
+ class Private;
+
+ template<typename ResolveValueType_>
+ static RefPtr<MozPromise>
+ CreateAndResolve(ResolveValueType_&& aResolveValue, const char* aResolveSite)
+ {
+ RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aResolveSite);
+ p->Resolve(Forward<ResolveValueType_>(aResolveValue), aResolveSite);
+ return p.forget();
+ }
+
+ template<typename RejectValueType_>
+ static RefPtr<MozPromise>
+ CreateAndReject(RejectValueType_&& aRejectValue, const char* aRejectSite)
+ {
+ RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aRejectSite);
+ p->Reject(Forward<RejectValueType_>(aRejectValue), aRejectSite);
+ return p.forget();
+ }
+
+ typedef MozPromise<nsTArray<ResolveValueType>, RejectValueType, IsExclusive> AllPromiseType;
+private:
+ class AllPromiseHolder : public MozPromiseRefcountable
+ {
+ public:
+ explicit AllPromiseHolder(size_t aDependentPromises)
+ : mPromise(new typename AllPromiseType::Private(__func__))
+ , mOutstandingPromises(aDependentPromises)
+ {
+ mResolveValues.SetLength(aDependentPromises);
+ }
+
+ void Resolve(size_t aIndex, const ResolveValueType& aResolveValue)
+ {
+ if (!mPromise) {
+ // Already rejected.
+ return;
+ }
+
+ mResolveValues[aIndex].emplace(aResolveValue);
+ if (--mOutstandingPromises == 0) {
+ nsTArray<ResolveValueType> resolveValues;
+ resolveValues.SetCapacity(mResolveValues.Length());
+ for (size_t i = 0; i < mResolveValues.Length(); ++i) {
+ resolveValues.AppendElement(mResolveValues[i].ref());
+ }
+
+ mPromise->Resolve(resolveValues, __func__);
+ mPromise = nullptr;
+ mResolveValues.Clear();
+ }
+ }
+
+ void Reject(const RejectValueType& aRejectValue)
+ {
+ if (!mPromise) {
+ // Already rejected.
+ return;
+ }
+
+ mPromise->Reject(aRejectValue, __func__);
+ mPromise = nullptr;
+ mResolveValues.Clear();
+ }
+
+ AllPromiseType* Promise() { return mPromise; }
+
+ private:
+ nsTArray<Maybe<ResolveValueType>> mResolveValues;
+ RefPtr<typename AllPromiseType::Private> mPromise;
+ size_t mOutstandingPromises;
+ };
+public:
+
+ static RefPtr<AllPromiseType> All(AbstractThread* aProcessingThread, nsTArray<RefPtr<MozPromise>>& aPromises)
+ {
+ RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
+ for (size_t i = 0; i < aPromises.Length(); ++i) {
+ aPromises[i]->Then(aProcessingThread, __func__,
+ [holder, i] (ResolveValueType aResolveValue) -> void { holder->Resolve(i, aResolveValue); },
+ [holder] (RejectValueType aRejectValue) -> void { holder->Reject(aRejectValue); }
+ );
+ }
+ return holder->Promise();
+ }
+
+ class Request : public MozPromiseRefcountable
+ {
+ public:
+ virtual void Disconnect() = 0;
+
+ virtual MozPromise* CompletionPromise() = 0;
+
+ virtual void AssertIsDead() = 0;
+
+ protected:
+ Request() : mComplete(false), mDisconnected(false) {}
+ virtual ~Request() {}
+
+ bool mComplete;
+ bool mDisconnected;
+ };
+
+protected:
+
+ /*
+ * A ThenValue tracks a single consumer waiting on the promise. When a consumer
+ * invokes promise->Then(...), a ThenValue is created. Once the Promise is
+ * resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
+ * invokes the resolve/reject method and then deletes the ThenValue.
+ */
+ class ThenValueBase : public Request
+ {
+ static const uint32_t sMagic = 0xfadece11;
+
+ public:
+ class ResolveOrRejectRunnable : public Runnable
+ {
+ public:
+ ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
+ : mThenValue(aThenValue)
+ , mPromise(aPromise)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
+ }
+
+ ~ResolveOrRejectRunnable()
+ {
+ if (mThenValue) {
+ mThenValue->AssertIsDead();
+ }
+ }
+
+ NS_IMETHOD Run() override
+ {
+ PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
+ mThenValue->DoResolveOrReject(mPromise->Value());
+ mThenValue = nullptr;
+ mPromise = nullptr;
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<ThenValueBase> mThenValue;
+ RefPtr<MozPromise> mPromise;
+ };
+
+ explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
+ : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
+
+#ifdef PROMISE_DEBUG
+ ~ThenValueBase()
+ {
+ mMagic1 = 0;
+ mMagic2 = 0;
+ }
+#endif
+
+ MozPromise* CompletionPromise() override
+ {
+ MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
+ MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
+ if (!mCompletionPromise) {
+ mCompletionPromise = new MozPromise::Private(
+ "<completion promise>", true /* aIsCompletionPromise */);
+ }
+ return mCompletionPromise;
+ }
+
+ void AssertIsDead() override
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
+ // We want to assert that this ThenValues is dead - that is to say, that
+ // there are no consumers waiting for the result. In the case of a normal
+ // ThenValue, we check that it has been disconnected, which is the way
+ // that the consumer signals that it no longer wishes to hear about the
+ // result. If this ThenValue has a completion promise (which is mutually
+ // exclusive with being disconnectable), we recursively assert that every
+ // ThenValue associated with the completion promise is dead.
+ if (mCompletionPromise) {
+ mCompletionPromise->AssertIsDead();
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
+ }
+ }
+
+ void Dispatch(MozPromise *aPromise)
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
+ aPromise->mMutex.AssertCurrentThreadOwns();
+ MOZ_ASSERT(!aPromise->IsPending());
+
+ RefPtr<Runnable> runnable =
+ static_cast<Runnable*>(new (typename ThenValueBase::ResolveOrRejectRunnable)(this, aPromise));
+ PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
+ aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
+ runnable.get(), aPromise, this);
+
+ // Promise consumers are allowed to disconnect the Request object and
+ // then shut down the thread or task queue that the promise result would
+ // be dispatched on. So we unfortunately can't assert that promise
+ // dispatch succeeds. :-(
+ mResponseTarget->Dispatch(runnable.forget(), AbstractThread::DontAssertDispatchSuccess);
+ }
+
+ virtual void Disconnect() override
+ {
+ MOZ_DIAGNOSTIC_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn());
+ MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
+ Request::mDisconnected = true;
+
+ // We could support rejecting the completion promise on disconnection, but
+ // then we'd need to have some sort of default reject value. The use cases
+ // of disconnection and completion promise chaining seem pretty orthogonal,
+ // so let's use assert against it.
+ MOZ_DIAGNOSTIC_ASSERT(!mCompletionPromise);
+ }
+
+ protected:
+ virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) = 0;
+
+ void DoResolveOrReject(const ResolveOrRejectValue& aValue)
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
+ MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
+ Request::mComplete = true;
+ if (Request::mDisconnected) {
+ PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
+ return;
+ }
+
+ // Invoke the resolve or reject method.
+ RefPtr<MozPromise> p = DoResolveOrRejectInternal(aValue);
+
+ // If there's a completion promise, resolve it appropriately with the
+ // result of the method.
+ //
+ // We jump through some hoops to cast to MozPromise::Private here. This
+ // can go away when we can just declare mCompletionPromise as
+ // MozPromise::Private. See the declaration below.
+ RefPtr<MozPromise::Private> completionPromise =
+ dont_AddRef(static_cast<MozPromise::Private*>(mCompletionPromise.forget().take()));
+ if (completionPromise) {
+ if (p) {
+ p->ChainTo(completionPromise.forget(), "<chained completion promise>");
+ } else {
+ completionPromise->ResolveOrReject(aValue, "<completion of non-promise-returning method>");
+ }
+ }
+ }
+
+ RefPtr<AbstractThread> mResponseTarget; // May be released on any thread.
+#ifdef PROMISE_DEBUG
+ uint32_t mMagic1 = sMagic;
+#endif
+ // Declaring RefPtr<MozPromise::Private> here causes build failures
+ // on MSVC because MozPromise::Private is only forward-declared at this
+ // point. This hack can go away when we inline-declare MozPromise::Private,
+ // which is blocked on the B2G ICS compiler being too old.
+ RefPtr<MozPromise> mCompletionPromise;
+#ifdef PROMISE_DEBUG
+ uint32_t mMagic2 = sMagic;
+#endif
+ const char* mCallSite;
+ };
+
+ /*
+ * We create two overloads for invoking Resolve/Reject Methods so as to
+ * make the resolve/reject value argument "optional".
+ */
+
+ template<typename ThisType, typename MethodType, typename ValueType>
+ static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
+ TakesArgument<MethodType>::value,
+ already_AddRefed<MozPromise>>::Type
+ InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+ {
+ return ((*aThisVal).*aMethod)(Forward<ValueType>(aValue)).forget();
+ }
+
+ template<typename ThisType, typename MethodType, typename ValueType>
+ static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
+ TakesArgument<MethodType>::value,
+ already_AddRefed<MozPromise>>::Type
+ InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+ {
+ ((*aThisVal).*aMethod)(Forward<ValueType>(aValue));
+ return nullptr;
+ }
+
+ template<typename ThisType, typename MethodType, typename ValueType>
+ static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
+ !TakesArgument<MethodType>::value,
+ already_AddRefed<MozPromise>>::Type
+ InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+ {
+ return ((*aThisVal).*aMethod)().forget();
+ }
+
+ template<typename ThisType, typename MethodType, typename ValueType>
+ static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
+ !TakesArgument<MethodType>::value,
+ already_AddRefed<MozPromise>>::Type
+ InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+ {
+ ((*aThisVal).*aMethod)();
+ return nullptr;
+ }
+
+ template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
+ class MethodThenValue : public ThenValueBase
+ {
+ public:
+ MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
+ ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
+ const char* aCallSite)
+ : ThenValueBase(aResponseTarget, aCallSite)
+ , mThisVal(aThisVal)
+ , mResolveMethod(aResolveMethod)
+ , mRejectMethod(aRejectMethod) {}
+
+ virtual void Disconnect() override
+ {
+ ThenValueBase::Disconnect();
+
+ // If a Request has been disconnected, we don't guarantee that the
+ // resolve/reject runnable will be dispatched. Null out our refcounted
+ // this-value now so that it's released predictably on the dispatch thread.
+ mThisVal = nullptr;
+ }
+
+ protected:
+ virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) override
+ {
+ RefPtr<MozPromise> completion;
+ if (aValue.IsResolve()) {
+ completion = InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue());
+ } else {
+ completion = InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue());
+ }
+
+ // Null out mThisVal after invoking the callback so that any references are
+ // released predictably on the dispatch thread. Otherwise, it would be
+ // released on whatever thread last drops its reference to the ThenValue,
+ // which may or may not be ok.
+ mThisVal = nullptr;
+
+ return completion.forget();
+ }
+
+ private:
+ RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
+ ResolveMethodType mResolveMethod;
+ RejectMethodType mRejectMethod;
+ };
+
+ // NB: We could use std::function here instead of a template if it were supported. :-(
+ template<typename ResolveFunction, typename RejectFunction>
+ class FunctionThenValue : public ThenValueBase
+ {
+ public:
+ FunctionThenValue(AbstractThread* aResponseTarget,
+ ResolveFunction&& aResolveFunction,
+ RejectFunction&& aRejectFunction,
+ const char* aCallSite)
+ : ThenValueBase(aResponseTarget, aCallSite)
+ {
+ mResolveFunction.emplace(Move(aResolveFunction));
+ mRejectFunction.emplace(Move(aRejectFunction));
+ }
+
+ virtual void Disconnect() override
+ {
+ ThenValueBase::Disconnect();
+
+ // If a Request has been disconnected, we don't guarantee that the
+ // resolve/reject runnable will be dispatched. Destroy our callbacks
+ // now so that any references in closures are released predictable on
+ // the dispatch thread.
+ mResolveFunction.reset();
+ mRejectFunction.reset();
+ }
+
+ protected:
+ virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) override
+ {
+ // Note: The usage of InvokeCallbackMethod here requires that
+ // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
+ // classes with ::operator()), since it allows us to share code more easily.
+ // We could fix this if need be, though it's quite easy to work around by
+ // just capturing something.
+ RefPtr<MozPromise> completion;
+ if (aValue.IsResolve()) {
+ completion = InvokeCallbackMethod(mResolveFunction.ptr(), &ResolveFunction::operator(), aValue.ResolveValue());
+ } else {
+ completion = InvokeCallbackMethod(mRejectFunction.ptr(), &RejectFunction::operator(), aValue.RejectValue());
+ }
+
+ // Destroy callbacks after invocation so that any references in closures are
+ // released predictably on the dispatch thread. Otherwise, they would be
+ // released on whatever thread last drops its reference to the ThenValue,
+ // which may or may not be ok.
+ mResolveFunction.reset();
+ mRejectFunction.reset();
+
+ return completion.forget();
+ }
+
+ private:
+ Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
+ Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
+ };
+
+public:
+ void ThenInternal(AbstractThread* aResponseThread, ThenValueBase* aThenValue,
+ const char* aCallSite)
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(aResponseThread->IsDispatchReliable());
+ MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
+ mHaveRequest = true;
+ PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
+ aCallSite, this, aThenValue, (int) IsPending());
+ if (!IsPending()) {
+ aThenValue->Dispatch(this);
+ } else {
+ mThenValues.AppendElement(aThenValue);
+ }
+ }
+
+public:
+
+ template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
+ RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
+ ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+ {
+ RefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
+ aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
+ ThenInternal(aResponseThread, thenValue, aCallSite);
+ return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
+ }
+
+ template<typename ResolveFunction, typename RejectFunction>
+ RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
+ ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+ {
+ RefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
+ Move(aResolveFunction), Move(aRejectFunction), aCallSite);
+ ThenInternal(aResponseThread, thenValue, aCallSite);
+ return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
+ }
+
+ void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
+ {
+ MutexAutoLock lock(mMutex);
+ MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
+ mHaveRequest = true;
+ RefPtr<Private> chainedPromise = aChainedPromise;
+ PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
+ aCallSite, this, chainedPromise.get(), (int) IsPending());
+ if (!IsPending()) {
+ ForwardTo(chainedPromise);
+ } else {
+ mChainedPromises.AppendElement(chainedPromise);
+ }
+ }
+
+ // Note we expose the function AssertIsDead() instead of IsDead() since
+ // checking IsDead() is a data race in the situation where the request is not
+ // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
+ // AssertIsDead() only.
+ void AssertIsDead()
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
+ MutexAutoLock lock(mMutex);
+ for (auto&& then : mThenValues) {
+ then->AssertIsDead();
+ }
+ for (auto&& chained : mChainedPromises) {
+ chained->AssertIsDead();
+ }
+ }
+
+protected:
+ bool IsPending() const { return mValue.IsNothing(); }
+ const ResolveOrRejectValue& Value() const
+ {
+ // This method should only be called once the value has stabilized. As
+ // such, we don't need to acquire the lock here.
+ MOZ_DIAGNOSTIC_ASSERT(!IsPending());
+ return mValue;
+ }
+
+ void DispatchAll()
+ {
+ mMutex.AssertCurrentThreadOwns();
+ for (size_t i = 0; i < mThenValues.Length(); ++i) {
+ mThenValues[i]->Dispatch(this);
+ }
+ mThenValues.Clear();
+
+ for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
+ ForwardTo(mChainedPromises[i]);
+ }
+ mChainedPromises.Clear();
+ }
+
+ void ForwardTo(Private* aOther)
+ {
+ MOZ_ASSERT(!IsPending());
+ if (mValue.IsResolve()) {
+ aOther->Resolve(mValue.ResolveValue(), "<chained promise>");
+ } else {
+ aOther->Reject(mValue.RejectValue(), "<chained promise>");
+ }
+ }
+
+ virtual ~MozPromise()
+ {
+ PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
+ AssertIsDead();
+ // We can't guarantee a completion promise will always be revolved or
+ // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
+ if (!mIsCompletionPromise) {
+ MOZ_ASSERT(!IsPending());
+ MOZ_ASSERT(mThenValues.IsEmpty());
+ MOZ_ASSERT(mChainedPromises.IsEmpty());
+ }
+#ifdef PROMISE_DEBUG
+ mMagic1 = 0;
+ mMagic2 = 0;
+ mMagic3 = 0;
+ mMagic4 = nullptr;
+#endif
+ };
+
+ const char* mCreationSite; // For logging
+ Mutex mMutex;
+ ResolveOrRejectValue mValue;
+#ifdef PROMISE_DEBUG
+ uint32_t mMagic1 = sMagic;
+#endif
+ nsTArray<RefPtr<ThenValueBase>> mThenValues;
+#ifdef PROMISE_DEBUG
+ uint32_t mMagic2 = sMagic;
+#endif
+ nsTArray<RefPtr<Private>> mChainedPromises;
+#ifdef PROMISE_DEBUG
+ uint32_t mMagic3 = sMagic;
+#endif
+ bool mHaveRequest;
+ const bool mIsCompletionPromise;
+#ifdef PROMISE_DEBUG
+ void* mMagic4;
+#endif
+};
+
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
+ : public MozPromise<ResolveValueT, RejectValueT, IsExclusive>
+{
+public:
+ explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
+ : MozPromise(aCreationSite, aIsCompletionPromise) {}
+
+ template<typename ResolveValueT_>
+ void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite)
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(IsPending());
+ PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
+ mValue.SetResolve(Forward<ResolveValueT_>(aResolveValue));
+ DispatchAll();
+ }
+
+ template<typename RejectValueT_>
+ void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite)
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(IsPending());
+ PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
+ mValue.SetReject(Forward<RejectValueT_>(aRejectValue));
+ DispatchAll();
+ }
+
+ template<typename ResolveOrRejectValue_>
+ void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite)
+ {
+ PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(IsPending());
+ PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite, this, mCreationSite);
+ mValue = Forward<ResolveOrRejectValue_>(aValue);
+ DispatchAll();
+ }
+};
+
+// A generic promise type that does the trick for simple use cases.
+typedef MozPromise<bool, nsresult, /* IsExclusive = */ false> GenericPromise;
+
+/*
+ * Class to encapsulate a promise for a particular role. Use this as the member
+ * variable for a class whose method returns a promise.
+ */
+template<typename PromiseType>
+class MozPromiseHolder
+{
+public:
+ MozPromiseHolder()
+ : mMonitor(nullptr) {}
+
+ // Move semantics.
+ MozPromiseHolder& operator=(MozPromiseHolder&& aOther)
+ {
+ MOZ_ASSERT(!mMonitor && !aOther.mMonitor);
+ MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+ mPromise = aOther.mPromise;
+ aOther.mPromise = nullptr;
+ return *this;
+ }
+
+ ~MozPromiseHolder() { MOZ_ASSERT(!mPromise); }
+
+ already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
+ if (mMonitor) {
+ mMonitor->AssertCurrentThreadOwns();
+ }
+ if (!mPromise) {
+ mPromise = new (typename PromiseType::Private)(aMethodName);
+ }
+ RefPtr<PromiseType> p = mPromise.get();
+ return p.forget();
+ }
+
+ // Provide a Monitor that should always be held when accessing this instance.
+ void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
+
+ bool IsEmpty() const
+ {
+ if (mMonitor) {
+ mMonitor->AssertCurrentThreadOwns();
+ }
+ return !mPromise;
+ }
+
+ already_AddRefed<typename PromiseType::Private> Steal()
+ {
+ if (mMonitor) {
+ mMonitor->AssertCurrentThreadOwns();
+ }
+
+ RefPtr<typename PromiseType::Private> p = mPromise;
+ mPromise = nullptr;
+ return p.forget();
+ }
+
+ void Resolve(typename PromiseType::ResolveValueType aResolveValue,
+ const char* aMethodName)
+ {
+ if (mMonitor) {
+ mMonitor->AssertCurrentThreadOwns();
+ }
+ MOZ_ASSERT(mPromise);
+ mPromise->Resolve(aResolveValue, aMethodName);
+ mPromise = nullptr;
+ }
+
+
+ void ResolveIfExists(typename PromiseType::ResolveValueType aResolveValue,
+ const char* aMethodName)
+ {
+ if (!IsEmpty()) {
+ Resolve(aResolveValue, aMethodName);
+ }
+ }
+
+ void Reject(typename PromiseType::RejectValueType aRejectValue,
+ const char* aMethodName)
+ {
+ if (mMonitor) {
+ mMonitor->AssertCurrentThreadOwns();
+ }
+ MOZ_ASSERT(mPromise);
+ mPromise->Reject(aRejectValue, aMethodName);
+ mPromise = nullptr;
+ }
+
+
+ void RejectIfExists(typename PromiseType::RejectValueType aRejectValue,
+ const char* aMethodName)
+ {
+ if (!IsEmpty()) {
+ Reject(aRejectValue, aMethodName);
+ }
+ }
+
+private:
+ Monitor* mMonitor;
+ RefPtr<typename PromiseType::Private> mPromise;
+};
+
+/*
+ * Class to encapsulate a MozPromise::Request reference. Use this as the member
+ * variable for a class waiting on a MozPromise.
+ */
+template<typename PromiseType>
+class MozPromiseRequestHolder
+{
+public:
+ MozPromiseRequestHolder() {}
+ ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
+
+ void Begin(RefPtr<typename PromiseType::Request>&& aRequest)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(!Exists());
+ mRequest = Move(aRequest);
+ }
+
+ void Begin(typename PromiseType::Request* aRequest)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(!Exists());
+ mRequest = aRequest;
+ }
+
+ void Complete()
+ {
+ MOZ_DIAGNOSTIC_ASSERT(Exists());
+ mRequest = nullptr;
+ }
+
+ // Disconnects and forgets an outstanding promise. The resolve/reject methods
+ // will never be called.
+ void Disconnect() {
+ MOZ_ASSERT(Exists());
+ mRequest->Disconnect();
+ mRequest = nullptr;
+ }
+
+ void DisconnectIfExists() {
+ if (Exists()) {
+ Disconnect();
+ }
+ }
+
+ bool Exists() const { return !!mRequest; }
+
+private:
+ RefPtr<typename PromiseType::Request> mRequest;
+};
+
+// Asynchronous Potentially-Cross-Thread Method Calls.
+//
+// This machinery allows callers to schedule a promise-returning method to be
+// invoked asynchronously on a given thread, while at the same time receiving
+// a promise upon which to invoke Then() immediately. InvokeAsync dispatches
+// a task to invoke the method on the proper thread and also chain the resulting
+// promise to the one that the caller received, so that resolve/reject values
+// are forwarded through.
+
+namespace detail {
+
+template<typename ReturnType, typename ThisType, typename... ArgTypes, size_t... Indices>
+ReturnType
+MethodCallInvokeHelper(ReturnType(ThisType::*aMethod)(ArgTypes...), ThisType* aThisVal,
+ Tuple<ArgTypes...>& aArgs, IndexSequence<Indices...>)
+{
+ return ((*aThisVal).*aMethod)(Get<Indices>(aArgs)...);
+}
+
+// Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
+// assertions when used on templated types.
+class MethodCallBase
+{
+public:
+ MethodCallBase() { MOZ_COUNT_CTOR(MethodCallBase); }
+ virtual ~MethodCallBase() { MOZ_COUNT_DTOR(MethodCallBase); }
+};
+
+template<typename PromiseType, typename ThisType, typename... ArgTypes>
+class MethodCall : public MethodCallBase
+{
+public:
+ typedef RefPtr<PromiseType>(ThisType::*MethodType)(ArgTypes...);
+ MethodCall(MethodType aMethod, ThisType* aThisVal, ArgTypes... aArgs)
+ : mMethod(aMethod)
+ , mThisVal(aThisVal)
+ , mArgs(Forward<ArgTypes>(aArgs)...)
+ {}
+
+ RefPtr<PromiseType> Invoke()
+ {
+ return MethodCallInvokeHelper(mMethod, mThisVal.get(), mArgs, typename IndexSequenceFor<ArgTypes...>::Type());
+ }
+
+private:
+ MethodType mMethod;
+ RefPtr<ThisType> mThisVal;
+ Tuple<ArgTypes...> mArgs;
+};
+
+template<typename PromiseType, typename ThisType, typename ...ArgTypes>
+class ProxyRunnable : public Runnable
+{
+public:
+ ProxyRunnable(typename PromiseType::Private* aProxyPromise, MethodCall<PromiseType, ThisType, ArgTypes...>* aMethodCall)
+ : mProxyPromise(aProxyPromise), mMethodCall(aMethodCall) {}
+
+ NS_IMETHOD Run() override
+ {
+ RefPtr<PromiseType> p = mMethodCall->Invoke();
+ mMethodCall = nullptr;
+ p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
+ return NS_OK;
+ }
+
+private:
+ RefPtr<typename PromiseType::Private> mProxyPromise;
+ nsAutoPtr<MethodCall<PromiseType, ThisType, ArgTypes...>> mMethodCall;
+};
+
+constexpr bool Any()
+{
+ return false;
+}
+
+template <typename T1>
+constexpr bool Any(T1 a)
+{
+ return static_cast<bool>(a);
+}
+
+template <typename T1, typename... Ts>
+constexpr bool Any(T1 a, Ts... aOthers)
+{
+ return a || Any(aOthers...);
+}
+
+} // namespace detail
+
+template<typename PromiseType, typename ThisType, typename ...ArgTypes, typename ...ActualArgTypes>
+static RefPtr<PromiseType>
+InvokeAsync(AbstractThread* aTarget, ThisType* aThisVal, const char* aCallerName,
+ RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...), ActualArgTypes&&... aArgs)
+{
+ static_assert(!detail::Any(IsReference<ArgTypes>::value...),
+ "Cannot pass reference types through InvokeAsync, see bug 1313497 if you require it");
+ typedef detail::MethodCall<PromiseType, ThisType, ArgTypes...> MethodCallType;
+ typedef detail::ProxyRunnable<PromiseType, ThisType, ArgTypes...> ProxyRunnableType;
+
+ MethodCallType* methodCall = new MethodCallType(aMethod, aThisVal, Forward<ActualArgTypes>(aArgs)...);
+ RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
+ RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
+ MOZ_ASSERT(aTarget->IsDispatchReliable());
+ aTarget->Dispatch(r.forget());
+ return p.forget();
+}
+
+#undef PROMISE_LOG
+#undef PROMISE_ASSERT
+#undef PROMISE_DEBUG
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/threads/SharedThreadPool.cpp b/xpcom/threads/SharedThreadPool.cpp
new file mode 100644
index 000000000..9adf6449e
--- /dev/null
+++ b/xpcom/threads/SharedThreadPool.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsDataHashtable.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#ifdef XP_WIN
+#include "ThreadPoolCOMListener.h"
+#endif
+
+namespace mozilla {
+
+// Created and destroyed on the main thread.
+static StaticAutoPtr<ReentrantMonitor> sMonitor;
+
+// Hashtable, maps thread pool name to SharedThreadPool instance.
+// Modified only on the main thread.
+static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>> sPools;
+
+static already_AddRefed<nsIThreadPool>
+CreateThreadPool(const nsCString& aName);
+
+class SharedThreadPoolShutdownObserver : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+protected:
+ virtual ~SharedThreadPoolShutdownObserver() {}
+};
+
+NS_IMPL_ISUPPORTS(SharedThreadPoolShutdownObserver, nsIObserver, nsISupports)
+
+NS_IMETHODIMP
+SharedThreadPoolShutdownObserver::Observe(nsISupports* aSubject, const char *aTopic,
+ const char16_t *aData)
+{
+ MOZ_RELEASE_ASSERT(!strcmp(aTopic, "xpcom-shutdown-threads"));
+ SharedThreadPool::SpinUntilEmpty();
+ sMonitor = nullptr;
+ sPools = nullptr;
+ return NS_OK;
+}
+
+void
+SharedThreadPool::InitStatics()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sMonitor && !sPools);
+ sMonitor = new ReentrantMonitor("SharedThreadPool");
+ sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>();
+ nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
+ nsCOMPtr<nsIObserver> obs = new SharedThreadPoolShutdownObserver();
+ obsService->AddObserver(obs, "xpcom-shutdown-threads", false);
+}
+
+/* static */
+bool
+SharedThreadPool::IsEmpty()
+{
+ ReentrantMonitorAutoEnter mon(*sMonitor);
+ return !sPools->Count();
+}
+
+/* static */
+void
+SharedThreadPool::SpinUntilEmpty()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ while (!IsEmpty()) {
+ sMonitor->AssertNotCurrentThreadIn();
+ NS_ProcessNextEvent(NS_GetCurrentThread(), true);
+ }
+}
+
+already_AddRefed<SharedThreadPool>
+SharedThreadPool::Get(const nsCString& aName, uint32_t aThreadLimit)
+{
+ MOZ_ASSERT(sMonitor && sPools);
+ ReentrantMonitorAutoEnter mon(*sMonitor);
+ SharedThreadPool* pool = nullptr;
+ nsresult rv;
+ if (!sPools->Get(aName, &pool)) {
+ nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
+ NS_ENSURE_TRUE(threadPool, nullptr);
+ pool = new SharedThreadPool(aName, threadPool);
+
+ // Set the thread and idle limits. Note that we don't rely on the
+ // EnsureThreadLimitIsAtLeast() call below, as the default thread limit
+ // is 4, and if aThreadLimit is less than 4 we'll end up with a pool
+ // with 4 threads rather than what we expected; so we'll have unexpected
+ // behaviour.
+ rv = pool->SetThreadLimit(aThreadLimit);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ rv = pool->SetIdleThreadLimit(aThreadLimit);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ sPools->Put(aName, pool);
+ } else if (NS_FAILED(pool->EnsureThreadLimitIsAtLeast(aThreadLimit))) {
+ NS_WARNING("Failed to set limits on thread pool");
+ }
+
+ MOZ_ASSERT(pool);
+ RefPtr<SharedThreadPool> instance(pool);
+ return instance.forget();
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::AddRef(void)
+{
+ MOZ_ASSERT(sMonitor);
+ ReentrantMonitorAutoEnter mon(*sMonitor);
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
+ nsrefcnt count = ++mRefCnt;
+ NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
+ return count;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void)
+{
+ MOZ_ASSERT(sMonitor);
+ ReentrantMonitorAutoEnter mon(*sMonitor);
+ nsrefcnt count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "SharedThreadPool");
+ if (count) {
+ return count;
+ }
+
+ // Remove SharedThreadPool from table of pools.
+ sPools->Remove(mName);
+ 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,
+ // and when the Runnable releases the nsIThreadPool it will be deleted.
+ NS_DispatchToMainThread(NewRunnableMethod(mPool, &nsIThreadPool::Shutdown));
+
+ // Stabilize refcount, so that if something in the dtor QIs, it won't explode.
+ mRefCnt = 1;
+ delete this;
+ return 0;
+}
+
+NS_IMPL_QUERY_INTERFACE(SharedThreadPool, nsIThreadPool, nsIEventTarget)
+
+SharedThreadPool::SharedThreadPool(const nsCString& aName,
+ nsIThreadPool* aPool)
+ : mName(aName)
+ , mPool(aPool)
+ , mRefCnt(0)
+{
+ MOZ_COUNT_CTOR(SharedThreadPool);
+ mEventTarget = do_QueryInterface(aPool);
+}
+
+SharedThreadPool::~SharedThreadPool()
+{
+ MOZ_COUNT_DTOR(SharedThreadPool);
+}
+
+nsresult
+SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit)
+{
+ // We limit the number of threads that we use. Note that we
+ // set the thread limit to the same as the idle limit so that we're not
+ // constantly creating and destroying threads (see Bug 881954). When the
+ // thread pool threads shutdown they dispatch an event to the main thread
+ // to call nsIThread::Shutdown(), and if we're very busy that can take a
+ // while to run, and we end up with dozens of extra threads. Note that
+ // threads that are idle for 60 seconds are shutdown naturally.
+ uint32_t existingLimit = 0;
+ nsresult rv;
+
+ rv = mPool->GetThreadLimit(&existingLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aLimit > existingLimit) {
+ rv = mPool->SetThreadLimit(aLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = mPool->GetIdleThreadLimit(&existingLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aLimit > existingLimit) {
+ rv = mPool->SetIdleThreadLimit(aLimit);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+static already_AddRefed<nsIThreadPool>
+CreateThreadPool(const nsCString& aName)
+{
+ nsresult rv;
+ nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ rv = pool->SetName(aName);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ rv = pool->SetThreadStackSize(SharedThreadPool::kStackSize);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+#ifdef XP_WIN
+ // Ensure MSCOM is initialized on the thread pools threads.
+ nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
+ rv = pool->SetListener(listener);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+#endif
+
+ return pool.forget();
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/SharedThreadPool.h b/xpcom/threads/SharedThreadPool.h
new file mode 100644
index 000000000..185b9e76f
--- /dev/null
+++ b/xpcom/threads/SharedThreadPool.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SharedThreadPool_h_
+#define SharedThreadPool_h_
+
+#include <queue>
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+#include "nsIThreadManager.h"
+#include "nsIThreadPool.h"
+#include "nsISupports.h"
+#include "nsISupportsImpl.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+
+// Wrapper that makes an nsIThreadPool a singleton, and provides a
+// consistent threadsafe interface to get instances. Callers simply get a
+// SharedThreadPool by the name of its nsIThreadPool. All get requests of
+// the same name get the same SharedThreadPool. Users must store a reference
+// to the pool, and when the last reference to a SharedThreadPool is dropped
+// the pool is shutdown and deleted. Users aren't required to manually
+// shutdown the pool, and can release references on any thread. This can make
+// it significantly easier to use thread pools, because the caller doesn't need
+// to worry about joining and tearing it down.
+//
+// On Windows all threads in the pool have MSCOM initialized with
+// COINIT_MULTITHREADED. Note that not all users of MSCOM use this mode see [1],
+// and mixing MSCOM objects between the two is terrible for performance, and can
+// cause some functions to fail. So be careful when using Win32 APIs on a
+// SharedThreadPool, and avoid sharing objects if at all possible.
+//
+// [1] https://dxr.mozilla.org/mozilla-central/search?q=coinitialize&redirect=false
+class SharedThreadPool : public nsIThreadPool
+{
+public:
+
+ // Gets (possibly creating) the shared thread pool singleton instance with
+ // thread pool named aName.
+ static already_AddRefed<SharedThreadPool> Get(const nsCString& aName,
+ uint32_t aThreadLimit = 4);
+
+ // We implement custom threadsafe AddRef/Release pair, that destroys the
+ // the shared pool singleton when the refcount drops to 0. The addref/release
+ // are implemented using locking, so it's not recommended that you use them
+ // in a tight loop.
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
+
+ // Forward behaviour to wrapped thread pool implementation.
+ NS_FORWARD_SAFE_NSITHREADPOOL(mPool);
+
+ // Call this when dispatching from an event on the same
+ // threadpool that is about to complete. We should not create a new thread
+ // in that case since a thread is about to become idle.
+ nsresult DispatchFromEndOfTaskInThisPool(nsIRunnable *event)
+ {
+ return Dispatch(event, NS_DISPATCH_AT_END);
+ }
+
+ NS_IMETHOD DispatchFromScript(nsIRunnable *event, uint32_t flags) override {
+ return Dispatch(event, flags);
+ }
+
+ NS_IMETHOD Dispatch(already_AddRefed<nsIRunnable> event, uint32_t flags) override
+ { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(Move(event), flags); }
+
+ NS_IMETHOD DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override
+ { return NS_ERROR_NOT_IMPLEMENTED; }
+
+ using nsIEventTarget::Dispatch;
+
+ NS_IMETHOD IsOnCurrentThread(bool *_retval) override { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->IsOnCurrentThread(_retval); }
+
+ // Creates necessary statics. Called once at startup.
+ static void InitStatics();
+
+ // Spins the event loop until all thread pools are shutdown.
+ // *Must* be called on the main thread.
+ static void SpinUntilEmpty();
+
+#if defined(MOZ_ASAN)
+ // Use the system default in ASAN builds, because the default is assumed to be
+ // larger than the size we want to use and is hopefully sufficient for ASAN.
+ static const uint32_t kStackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
+#elif defined(XP_WIN) || defined(XP_MACOSX) || defined(LINUX)
+ static const uint32_t kStackSize = (256 * 1024);
+#else
+ // All other platforms use their system defaults.
+ static const uint32_t kStackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
+#endif
+
+private:
+
+ // Returns whether there are no pools in existence at the moment.
+ static bool IsEmpty();
+
+ // Creates a singleton SharedThreadPool wrapper around aPool.
+ // aName is the name of the aPool, and is used to lookup the
+ // SharedThreadPool in the hash table of all created pools.
+ SharedThreadPool(const nsCString& aName,
+ nsIThreadPool* aPool);
+ virtual ~SharedThreadPool();
+
+ nsresult EnsureThreadLimitIsAtLeast(uint32_t aThreadLimit);
+
+ // Name of mPool.
+ const nsCString mName;
+
+ // Thread pool being wrapped.
+ nsCOMPtr<nsIThreadPool> mPool;
+
+ // Refcount. We implement custom ref counting so that the thread pool is
+ // shutdown in a threadsafe manner and singletonness is preserved.
+ nsrefcnt mRefCnt;
+
+ // mPool QI'd to nsIEventTarget. We cache this, so that we can use
+ // NS_FORWARD_SAFE_NSIEVENTTARGET above.
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+};
+
+} // namespace mozilla
+
+#endif // SharedThreadPool_h_
diff --git a/xpcom/threads/StateMirroring.h b/xpcom/threads/StateMirroring.h
new file mode 100644
index 000000000..87d94ba74
--- /dev/null
+++ b/xpcom/threads/StateMirroring.h
@@ -0,0 +1,378 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(StateMirroring_h_)
+#define StateMirroring_h_
+
+#include "mozilla/Maybe.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/StateWatching.h"
+#include "mozilla/TaskDispatcher.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+#include "mozilla/Logging.h"
+#include "nsISupportsImpl.h"
+
+/*
+ * The state-mirroring machinery allows pieces of interesting state to be
+ * observed on multiple thread without locking. The basic strategy is to track
+ * changes in a canonical value and post updates to other threads that hold
+ * mirrors for that value.
+ *
+ * One problem with the naive implementation of such a system is that some pieces
+ * of state need to be updated atomically, and certain other operations need to
+ * wait for these atomic updates to complete before executing. The state-mirroring
+ * machinery solves this problem by requiring that its owner thread uses tail
+ * dispatch, and posting state update events (which should always be run first by
+ * TaskDispatcher implementations) to that tail dispatcher. This ensures that
+ * state changes are always atomic from the perspective of observing threads.
+ *
+ * Given that state-mirroring is an automatic background process, we try to avoid
+ * burdening the caller with worrying too much about teardown. To that end, we
+ * don't assert dispatch success for any of the notifications, and assume that
+ * any canonical or mirror owned by a thread for whom dispatch fails will soon
+ * be disconnected by its holder anyway.
+ *
+ * Given that semantics may change and comments tend to go out of date, we
+ * deliberately don't provide usage examples here. Grep around to find them.
+ */
+
+namespace mozilla {
+
+// Mirror<T> and Canonical<T> inherit WatchTarget, so we piggy-back on the
+// logging that WatchTarget already does. Given that, it makes sense to share
+// the same log module.
+#define MIRROR_LOG(x, ...) \
+ MOZ_ASSERT(gStateWatchingLog); \
+ MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
+
+template<typename T> class AbstractMirror;
+
+/*
+ * AbstractCanonical is a superclass from which all Canonical values must
+ * inherit. It serves as the interface of operations which may be performed (via
+ * asynchronous dispatch) by other threads, in particular by the corresponding
+ * Mirror value.
+ */
+template<typename T>
+class AbstractCanonical
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractCanonical)
+ AbstractCanonical(AbstractThread* aThread) : mOwnerThread(aThread) {}
+ virtual void AddMirror(AbstractMirror<T>* aMirror) = 0;
+ virtual void RemoveMirror(AbstractMirror<T>* aMirror) = 0;
+
+ AbstractThread* OwnerThread() const { return mOwnerThread; }
+protected:
+ virtual ~AbstractCanonical() {}
+ RefPtr<AbstractThread> mOwnerThread;
+};
+
+/*
+ * AbstractMirror is a superclass from which all Mirror values must
+ * inherit. It serves as the interface of operations which may be performed (via
+ * asynchronous dispatch) by other threads, in particular by the corresponding
+ * Canonical value.
+ */
+template<typename T>
+class AbstractMirror
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractMirror)
+ AbstractMirror(AbstractThread* aThread) : mOwnerThread(aThread) {}
+ virtual void UpdateValue(const T& aNewValue) = 0;
+ virtual void NotifyDisconnected() = 0;
+
+ AbstractThread* OwnerThread() const { return mOwnerThread; }
+protected:
+ virtual ~AbstractMirror() {}
+ RefPtr<AbstractThread> mOwnerThread;
+};
+
+/*
+ * Canonical<T> is a wrapper class that allows a given value to be mirrored by other
+ * threads. It maintains a list of active mirrors, and queues updates for them
+ * when the internal value changes. When changing the value, the caller needs to
+ * pass a TaskDispatcher object, which fires the updates at the appropriate time.
+ * Canonical<T> is also a WatchTarget, and may be set up to trigger other routines
+ * (on the same thread) when the canonical value changes.
+ *
+ * Canonical<T> is intended to be used as a member variable, so it doesn't actually
+ * inherit AbstractCanonical<T> (a refcounted type). Rather, it contains an inner
+ * class called |Impl| that implements most of the interesting logic.
+ */
+template<typename T>
+class Canonical
+{
+public:
+ Canonical(AbstractThread* aThread, const T& aInitialValue, const char* aName)
+ {
+ mImpl = new Impl(aThread, aInitialValue, aName);
+ }
+
+
+ ~Canonical() {}
+
+private:
+ class Impl : public AbstractCanonical<T>, public WatchTarget
+ {
+ public:
+ using AbstractCanonical<T>::OwnerThread;
+
+ Impl(AbstractThread* aThread, const T& aInitialValue, const char* aName)
+ : AbstractCanonical<T>(aThread), WatchTarget(aName), mValue(aInitialValue)
+ {
+ MIRROR_LOG("%s [%p] initialized", mName, this);
+ MOZ_ASSERT(aThread->SupportsTailDispatch(), "Can't get coherency without tail dispatch");
+ }
+
+ void AddMirror(AbstractMirror<T>* aMirror) override
+ {
+ MIRROR_LOG("%s [%p] adding mirror %p", mName, this, aMirror);
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ MOZ_ASSERT(!mMirrors.Contains(aMirror));
+ mMirrors.AppendElement(aMirror);
+ aMirror->OwnerThread()->DispatchStateChange(MakeNotifier(aMirror));
+ }
+
+ void RemoveMirror(AbstractMirror<T>* aMirror) override
+ {
+ MIRROR_LOG("%s [%p] removing mirror %p", mName, this, aMirror);
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ MOZ_ASSERT(mMirrors.Contains(aMirror));
+ mMirrors.RemoveElement(aMirror);
+ }
+
+ void DisconnectAll()
+ {
+ MIRROR_LOG("%s [%p] Disconnecting all mirrors", mName, this);
+ for (size_t i = 0; i < mMirrors.Length(); ++i) {
+ mMirrors[i]->OwnerThread()->Dispatch(NewRunnableMethod(mMirrors[i],
+ &AbstractMirror<T>::NotifyDisconnected),
+ AbstractThread::DontAssertDispatchSuccess);
+ }
+ mMirrors.Clear();
+ }
+
+ operator const T&()
+ {
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ return mValue;
+ }
+
+ void Set(const T& aNewValue)
+ {
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+
+ if (aNewValue == mValue) {
+ return;
+ }
+
+ // Notify same-thread watchers. The state watching machinery will make sure
+ // that notifications run at the right time.
+ NotifyWatchers();
+
+ // Check if we've already got a pending update. If so we won't schedule another
+ // one.
+ bool alreadyNotifying = mInitialValue.isSome();
+
+ // Stash the initial value if needed, then update to the new value.
+ if (mInitialValue.isNothing()) {
+ mInitialValue.emplace(mValue);
+ }
+ mValue = aNewValue;
+
+ // We wait until things have stablized before sending state updates so that
+ // we can avoid sending multiple updates, and possibly avoid sending any
+ // updates at all if the value ends up where it started.
+ if (!alreadyNotifying) {
+ AbstractThread::DispatchDirectTask(NewRunnableMethod(this, &Impl::DoNotify));
+ }
+ }
+
+ Impl& operator=(const T& aNewValue) { Set(aNewValue); return *this; }
+ Impl& operator=(const Impl& aOther) { Set(aOther); return *this; }
+ Impl(const Impl& aOther) = delete;
+
+ protected:
+ ~Impl() { MOZ_DIAGNOSTIC_ASSERT(mMirrors.IsEmpty()); }
+
+ private:
+ void DoNotify()
+ {
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ MOZ_ASSERT(mInitialValue.isSome());
+ bool same = mInitialValue.ref() == mValue;
+ mInitialValue.reset();
+
+ if (same) {
+ MIRROR_LOG("%s [%p] unchanged - not sending update", mName, this);
+ return;
+ }
+
+ for (size_t i = 0; i < mMirrors.Length(); ++i) {
+ mMirrors[i]->OwnerThread()->DispatchStateChange(MakeNotifier(mMirrors[i]));
+ }
+ }
+
+ already_AddRefed<nsIRunnable> MakeNotifier(AbstractMirror<T>* aMirror)
+ {
+ return NewRunnableMethod<T>(aMirror, &AbstractMirror<T>::UpdateValue, mValue);;
+ }
+
+ T mValue;
+ Maybe<T> mInitialValue;
+ nsTArray<RefPtr<AbstractMirror<T>>> mMirrors;
+ };
+public:
+
+ // NB: Because mirror-initiated disconnection can race with canonical-
+ // initiated disconnection, a canonical should never be reinitialized.
+ // Forward control operations to the Impl.
+ void DisconnectAll() { return mImpl->DisconnectAll(); }
+
+ // Access to the Impl.
+ operator Impl&() { return *mImpl; }
+ Impl* operator&() { return mImpl; }
+
+ // Access to the T.
+ const T& Ref() const { return *mImpl; }
+ operator const T&() const { return Ref(); }
+ void Set(const T& aNewValue) { mImpl->Set(aNewValue); }
+ Canonical& operator=(const T& aNewValue) { Set(aNewValue); return *this; }
+ Canonical& operator=(const Canonical& aOther) { Set(aOther); return *this; }
+ Canonical(const Canonical& aOther) = delete;
+
+private:
+ RefPtr<Impl> mImpl;
+};
+
+/*
+ * Mirror<T> is a wrapper class that allows a given value to mirror that of a
+ * Canonical<T> owned by another thread. It registers itself with a Canonical<T>,
+ * and is periodically updated with new values. Mirror<T> is also a WatchTarget,
+ * and may be set up to trigger other routines (on the same thread) when the
+ * mirrored value changes.
+ *
+ * Mirror<T> is intended to be used as a member variable, so it doesn't actually
+ * inherit AbstractMirror<T> (a refcounted type). Rather, it contains an inner
+ * class called |Impl| that implements most of the interesting logic.
+ */
+template<typename T>
+class Mirror
+{
+public:
+ Mirror(AbstractThread* aThread, const T& aInitialValue, const char* aName)
+ {
+ mImpl = new Impl(aThread, aInitialValue, aName);
+ }
+
+ ~Mirror()
+ {
+ // As a member of complex objects, a Mirror<T> may be destroyed on a
+ // different thread than its owner, or late in shutdown during CC. Given
+ // that, we require manual disconnection so that callers can put things in
+ // the right place.
+ MOZ_DIAGNOSTIC_ASSERT(!mImpl->IsConnected());
+ }
+
+private:
+ class Impl : public AbstractMirror<T>, public WatchTarget
+ {
+ public:
+ using AbstractMirror<T>::OwnerThread;
+
+ Impl(AbstractThread* aThread, const T& aInitialValue, const char* aName)
+ : AbstractMirror<T>(aThread), WatchTarget(aName), mValue(aInitialValue)
+ {
+ MIRROR_LOG("%s [%p] initialized", mName, this);
+ MOZ_ASSERT(aThread->SupportsTailDispatch(), "Can't get coherency without tail dispatch");
+ }
+
+ operator const T&()
+ {
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ return mValue;
+ }
+
+ virtual void UpdateValue(const T& aNewValue) override
+ {
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ if (mValue != aNewValue) {
+ mValue = aNewValue;
+ WatchTarget::NotifyWatchers();
+ }
+ }
+
+ virtual void NotifyDisconnected() override
+ {
+ MIRROR_LOG("%s [%p] Notifed of disconnection from %p", mName, this, mCanonical.get());
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ mCanonical = nullptr;
+ }
+
+ bool IsConnected() const { return !!mCanonical; }
+
+ void Connect(AbstractCanonical<T>* aCanonical)
+ {
+ MIRROR_LOG("%s [%p] Connecting to %p", mName, this, aCanonical);
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ MOZ_ASSERT(!IsConnected());
+ MOZ_ASSERT(OwnerThread()->RequiresTailDispatch(aCanonical->OwnerThread()), "Can't get coherency without tail dispatch");
+
+ nsCOMPtr<nsIRunnable> r = NewRunnableMethod<StorensRefPtrPassByPtr<AbstractMirror<T>>>
+ (aCanonical, &AbstractCanonical<T>::AddMirror, this);
+ aCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
+ mCanonical = aCanonical;
+ }
+ public:
+
+ void DisconnectIfConnected()
+ {
+ MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
+ if (!IsConnected()) {
+ return;
+ }
+
+ MIRROR_LOG("%s [%p] Disconnecting from %p", mName, this, mCanonical.get());
+ nsCOMPtr<nsIRunnable> r = NewRunnableMethod<StorensRefPtrPassByPtr<AbstractMirror<T>>>
+ (mCanonical, &AbstractCanonical<T>::RemoveMirror, this);
+ mCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
+ mCanonical = nullptr;
+ }
+
+ protected:
+ ~Impl() { MOZ_DIAGNOSTIC_ASSERT(!IsConnected()); }
+
+ private:
+ T mValue;
+ RefPtr<AbstractCanonical<T>> mCanonical;
+ };
+public:
+
+ // Forward control operations to the Impl<T>.
+ void Connect(AbstractCanonical<T>* aCanonical) { mImpl->Connect(aCanonical); }
+ void DisconnectIfConnected() { mImpl->DisconnectIfConnected(); }
+
+ // Access to the Impl<T>.
+ operator Impl&() { return *mImpl; }
+ Impl* operator&() { return mImpl; }
+
+ // Access to the T.
+ const T& Ref() const { return *mImpl; }
+ operator const T&() const { return Ref(); }
+
+private:
+ RefPtr<Impl> mImpl;
+};
+
+#undef MIRROR_LOG
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/threads/StateWatching.h b/xpcom/threads/StateWatching.h
new file mode 100644
index 000000000..99d521603
--- /dev/null
+++ b/xpcom/threads/StateWatching.h
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(StateWatching_h_)
+#define StateWatching_h_
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Logging.h"
+#include "mozilla/TaskDispatcher.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+#include "nsISupportsImpl.h"
+
+/*
+ * The state-watching machinery automates the process of responding to changes
+ * in various pieces of state.
+ *
+ * A standard programming pattern is as follows:
+ *
+ * mFoo = ...;
+ * NotifyStuffChanged();
+ * ...
+ * mBar = ...;
+ * NotifyStuffChanged();
+ *
+ * This pattern is error-prone and difficult to audit because it requires the
+ * programmer to manually trigger the update routine. This can be especially
+ * problematic when the update routine depends on numerous pieces of state, and
+ * when that state is modified across a variety of helper methods. In these
+ * cases the responsibility for invoking the routine is often unclear, causing
+ * developers to scatter calls to it like pixie dust. This can result in
+ * duplicate invocations (which is wasteful) and missing invocations in corner-
+ * cases (which is a source of bugs).
+ *
+ * This file provides a set of primitives that automatically handle updates and
+ * allow the programmers to explicitly construct a graph of state dependencies.
+ * When used correctly, it eliminates the guess-work and wasted cycles described
+ * above.
+ *
+ * There are two basic pieces:
+ * (1) Objects that can be watched for updates. These inherit WatchTarget.
+ * (2) Objects that receive objects and trigger processing. These inherit
+ * AbstractWatcher. In the current machinery, these exist only internally
+ * within the WatchManager, though that could change.
+ *
+ * Note that none of this machinery is thread-safe - it must all happen on the
+ * same owning thread. To solve multi-threaded use-cases, use state mirroring
+ * and watch the mirrored value.
+ *
+ * Given that semantics may change and comments tend to go out of date, we
+ * deliberately don't provide usage examples here. Grep around to find them.
+ */
+
+namespace mozilla {
+
+extern LazyLogModule gStateWatchingLog;
+
+#define WATCH_LOG(x, ...) \
+ MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
+
+/*
+ * AbstractWatcher is a superclass from which all watchers must inherit.
+ */
+class AbstractWatcher
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
+ AbstractWatcher() : mDestroyed(false) {}
+ bool IsDestroyed() { return mDestroyed; }
+ virtual void Notify() = 0;
+
+protected:
+ virtual ~AbstractWatcher() { MOZ_ASSERT(mDestroyed); }
+ bool mDestroyed;
+};
+
+/*
+ * WatchTarget is a superclass from which all watchable things must inherit.
+ * Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass
+ * needs only to invoke NotifyWatchers when something changes.
+ *
+ * The functionality that this class provides is not threadsafe, and should only
+ * be used on the thread that owns that WatchTarget.
+ */
+class WatchTarget
+{
+public:
+ explicit WatchTarget(const char* aName) : mName(aName) {}
+
+ void AddWatcher(AbstractWatcher* aWatcher)
+ {
+ MOZ_ASSERT(!mWatchers.Contains(aWatcher));
+ mWatchers.AppendElement(aWatcher);
+ }
+
+ void RemoveWatcher(AbstractWatcher* aWatcher)
+ {
+ MOZ_ASSERT(mWatchers.Contains(aWatcher));
+ mWatchers.RemoveElement(aWatcher);
+ }
+
+protected:
+ void NotifyWatchers()
+ {
+ WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
+ PruneWatchers();
+ for (size_t i = 0; i < mWatchers.Length(); ++i) {
+ mWatchers[i]->Notify();
+ }
+ }
+
+private:
+ // We don't have Watchers explicitly unregister themselves when they die,
+ // because then they'd need back-references to all the WatchTargets they're
+ // subscribed to, and WatchTargets aren't reference-counted. So instead we
+ // just prune dead ones at appropriate times, which works just fine.
+ void PruneWatchers()
+ {
+ for (int i = mWatchers.Length() - 1; i >= 0; --i) {
+ if (mWatchers[i]->IsDestroyed()) {
+ mWatchers.RemoveElementAt(i);
+ }
+ }
+ }
+
+ nsTArray<RefPtr<AbstractWatcher>> mWatchers;
+
+protected:
+ const char* mName;
+};
+
+/*
+ * Watchable is a wrapper class that turns any primitive into a WatchTarget.
+ */
+template<typename T>
+class Watchable : public WatchTarget
+{
+public:
+ Watchable(const T& aInitialValue, const char* aName)
+ : WatchTarget(aName), mValue(aInitialValue) {}
+
+ const T& Ref() const { return mValue; }
+ operator const T&() const { return Ref(); }
+ Watchable& operator=(const T& aNewValue)
+ {
+ if (aNewValue != mValue) {
+ mValue = aNewValue;
+ NotifyWatchers();
+ }
+
+ return *this;
+ }
+
+private:
+ Watchable(const Watchable& aOther); // Not implemented
+ Watchable& operator=(const Watchable& aOther); // Not implemented
+
+ T mValue;
+};
+
+// Manager class for state-watching. Declare one of these in any class for which
+// you want to invoke method callbacks.
+//
+// Internally, WatchManager maintains one AbstractWatcher per callback method.
+// Consumers invoke Watch/Unwatch on a particular (WatchTarget, Callback) tuple.
+// This causes an AbstractWatcher for |Callback| to be instantiated if it doesn't
+// already exist, and registers it with |WatchTarget|.
+//
+// Using Direct Tasks on the TailDispatcher, WatchManager ensures that we fire
+// watch callbacks no more than once per task, once all other operations for that
+// task have been completed.
+//
+// WatchManager<OwnerType> is intended to be declared as a member of |OwnerType|
+// objects. Given that, it and its owned objects can't hold permanent strong refs to
+// the owner, since that would keep the owner alive indefinitely. Instead, it
+// _only_ holds strong refs while waiting for Direct Tasks to fire. This ensures
+// that everything is kept alive just long enough.
+template <typename OwnerType>
+class WatchManager
+{
+public:
+ typedef void(OwnerType::*CallbackMethod)();
+ explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
+ : mOwner(aOwner), mOwnerThread(aOwnerThread) {}
+
+ ~WatchManager()
+ {
+ if (!IsShutdown()) {
+ Shutdown();
+ }
+ }
+
+ bool IsShutdown() const { return !mOwner; }
+
+ // Shutdown needs to happen on mOwnerThread. If the WatchManager will be
+ // destroyed on a different thread, Shutdown() must be called manually.
+ void Shutdown()
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ for (size_t i = 0; i < mWatchers.Length(); ++i) {
+ mWatchers[i]->Destroy();
+ }
+ mWatchers.Clear();
+ mOwner = nullptr;
+ }
+
+ void Watch(WatchTarget& aTarget, CallbackMethod aMethod)
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ aTarget.AddWatcher(&EnsureWatcher(aMethod));
+ }
+
+ void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod)
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ PerCallbackWatcher* watcher = GetWatcher(aMethod);
+ MOZ_ASSERT(watcher);
+ aTarget.RemoveWatcher(watcher);
+ }
+
+ void ManualNotify(CallbackMethod aMethod)
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ PerCallbackWatcher* watcher = GetWatcher(aMethod);
+ MOZ_ASSERT(watcher);
+ watcher->Notify();
+ }
+
+private:
+ class PerCallbackWatcher : public AbstractWatcher
+ {
+ public:
+ PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread, CallbackMethod aMethod)
+ : mOwner(aOwner), mOwnerThread(aOwnerThread), mCallbackMethod(aMethod) {}
+
+ void Destroy()
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ mDestroyed = true;
+ mOwner = nullptr;
+ }
+
+ void Notify() override
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ MOZ_DIAGNOSTIC_ASSERT(mOwner, "mOwner is only null after destruction, "
+ "at which point we shouldn't be notified");
+ if (mStrongRef) {
+ // We've already got a notification job in the pipe.
+ return;
+ }
+ mStrongRef = mOwner; // Hold the owner alive while notifying.
+
+ // Queue up our notification jobs to run in a stable state.
+ mOwnerThread->TailDispatcher().AddDirectTask(NewRunnableMethod(this, &PerCallbackWatcher::DoNotify));
+ }
+
+ bool CallbackMethodIs(CallbackMethod aMethod) const
+ {
+ return mCallbackMethod == aMethod;
+ }
+
+ private:
+ ~PerCallbackWatcher() {}
+
+ void DoNotify()
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ MOZ_ASSERT(mStrongRef);
+ RefPtr<OwnerType> ref = mStrongRef.forget();
+ if (!mDestroyed) {
+ ((*ref).*mCallbackMethod)();
+ }
+ }
+
+ OwnerType* mOwner; // Never null.
+ RefPtr<OwnerType> mStrongRef; // Only non-null when notifying.
+ RefPtr<AbstractThread> mOwnerThread;
+ CallbackMethod mCallbackMethod;
+ };
+
+ PerCallbackWatcher* GetWatcher(CallbackMethod aMethod)
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ for (size_t i = 0; i < mWatchers.Length(); ++i) {
+ if (mWatchers[i]->CallbackMethodIs(aMethod)) {
+ return mWatchers[i];
+ }
+ }
+ return nullptr;
+ }
+
+ PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod)
+ {
+ MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+ PerCallbackWatcher* watcher = GetWatcher(aMethod);
+ if (watcher) {
+ return *watcher;
+ }
+ watcher = mWatchers.AppendElement(new PerCallbackWatcher(mOwner, mOwnerThread, aMethod))->get();
+ return *watcher;
+ }
+
+ nsTArray<RefPtr<PerCallbackWatcher>> mWatchers;
+ OwnerType* mOwner;
+ RefPtr<AbstractThread> mOwnerThread;
+};
+
+#undef WATCH_LOG
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/threads/SyncRunnable.h b/xpcom/threads/SyncRunnable.h
new file mode 100644
index 000000000..d96bac7ba
--- /dev/null
+++ b/xpcom/threads/SyncRunnable.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SyncRunnable_h
+#define mozilla_SyncRunnable_h
+
+#include "nsThreadUtils.h"
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+
+/**
+ * This class will wrap a nsIRunnable and dispatch it to the main thread
+ * synchronously. This is different from nsIEventTarget.DISPATCH_SYNC:
+ * this class does not spin the event loop waiting for the event to be
+ * dispatched. This means that you don't risk reentrance from pending
+ * messages, but you must be sure that the target thread does not ever block
+ * on this thread, or else you will deadlock.
+ *
+ * Typical usage:
+ * RefPtr<SyncRunnable> sr = new SyncRunnable(new myrunnable...());
+ * sr->DispatchToThread(t);
+ *
+ * We also provide a convenience wrapper:
+ * SyncRunnable::DispatchToThread(new myrunnable...());
+ *
+ */
+class SyncRunnable : public Runnable
+{
+public:
+ explicit SyncRunnable(nsIRunnable* aRunnable)
+ : mRunnable(aRunnable)
+ , mMonitor("SyncRunnable")
+ , mDone(false)
+ {
+ }
+
+ explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
+ : mRunnable(Move(aRunnable))
+ , mMonitor("SyncRunnable")
+ , mDone(false)
+ {
+ }
+
+ void DispatchToThread(nsIEventTarget* aThread, bool aForceDispatch = false)
+ {
+ nsresult rv;
+ bool on;
+
+ if (!aForceDispatch) {
+ rv = aThread->IsOnCurrentThread(&on);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (NS_SUCCEEDED(rv) && on) {
+ mRunnable->Run();
+ return;
+ }
+ }
+
+ rv = aThread->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_SUCCEEDED(rv)) {
+ mozilla::MonitorAutoLock lock(mMonitor);
+ while (!mDone) {
+ lock.Wait();
+ }
+ }
+ }
+
+ 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<nsIRunnable>(this).forget());
+ mozilla::MonitorAutoLock lock(mMonitor);
+ while (!mDone) {
+ lock.Wait();
+ }
+ }
+
+ static void DispatchToThread(nsIEventTarget* aThread,
+ nsIRunnable* aRunnable,
+ bool aForceDispatch = false)
+ {
+ RefPtr<SyncRunnable> s(new SyncRunnable(aRunnable));
+ s->DispatchToThread(aThread, aForceDispatch);
+ }
+
+ static void DispatchToThread(AbstractThread* aThread,
+ nsIRunnable* aRunnable,
+ bool aForceDispatch = false)
+ {
+ RefPtr<SyncRunnable> s(new SyncRunnable(aRunnable));
+ s->DispatchToThread(aThread, aForceDispatch);
+ }
+
+protected:
+ NS_IMETHOD Run() override
+ {
+ mRunnable->Run();
+
+ mozilla::MonitorAutoLock lock(mMonitor);
+ MOZ_ASSERT(!mDone);
+
+ mDone = true;
+ mMonitor.Notify();
+
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIRunnable> mRunnable;
+ mozilla::Monitor mMonitor;
+ bool mDone;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SyncRunnable_h
diff --git a/xpcom/threads/TaskDispatcher.h b/xpcom/threads/TaskDispatcher.h
new file mode 100644
index 000000000..405c3acfe
--- /dev/null
+++ b/xpcom/threads/TaskDispatcher.h
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(TaskDispatcher_h_)
+#define TaskDispatcher_h_
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+#include <queue>
+
+namespace mozilla {
+
+/*
+ * A classic approach to cross-thread communication is to dispatch asynchronous
+ * runnables to perform updates on other threads. This generally works well, but
+ * there are sometimes reasons why we might want to delay the actual dispatch of
+ * these tasks until a specified moment. At present, this is primarily useful to
+ * ensure that mirrored state gets updated atomically - but there may be other
+ * applications as well.
+ *
+ * TaskDispatcher is a general abstract class that accepts tasks and dispatches
+ * them at some later point. These groups of tasks are per-target-thread, and
+ * contain separate queues for several kinds of tasks (see comments below). - "state change tasks" (which
+ * run first, and are intended to be used to update the value held by mirrors),
+ * and regular tasks, which are other arbitrary operations that the are gated
+ * to run after all the state changes have completed.
+ */
+class TaskDispatcher
+{
+public:
+ TaskDispatcher() {}
+ virtual ~TaskDispatcher() {}
+
+ // Direct tasks are run directly (rather than dispatched asynchronously) when
+ // the tail dispatcher fires. A direct task may cause other tasks to be added
+ // to the tail dispatcher.
+ virtual void AddDirectTask(already_AddRefed<nsIRunnable> aRunnable) = 0;
+
+ // State change tasks are dispatched asynchronously always run before regular
+ // tasks. They are intended to be used to update the value held by mirrors
+ // before any other dispatched tasks are run on the target thread.
+ virtual void AddStateChangeTask(AbstractThread* aThread,
+ already_AddRefed<nsIRunnable> aRunnable) = 0;
+
+ // Regular tasks are dispatched asynchronously, and run after state change
+ // tasks.
+ virtual void AddTask(AbstractThread* aThread,
+ already_AddRefed<nsIRunnable> aRunnable,
+ AbstractThread::DispatchFailureHandling aFailureHandling = AbstractThread::AssertDispatchSuccess) = 0;
+
+ virtual void DispatchTasksFor(AbstractThread* aThread) = 0;
+ virtual bool HasTasksFor(AbstractThread* aThread) = 0;
+ virtual void DrainDirectTasks() = 0;
+};
+
+/*
+ * AutoTaskDispatcher is a stack-scoped TaskDispatcher implementation that fires
+ * its queued tasks when it is popped off the stack.
+ */
+class AutoTaskDispatcher : public TaskDispatcher
+{
+public:
+ explicit AutoTaskDispatcher(bool aIsTailDispatcher = false)
+ : mIsTailDispatcher(aIsTailDispatcher)
+ {}
+
+ ~AutoTaskDispatcher()
+ {
+ // Given that direct tasks may trigger other code that uses the tail
+ // dispatcher, it's better to avoid processing them in the tail dispatcher's
+ // destructor. So we require TailDispatchers to manually invoke
+ // DrainDirectTasks before the AutoTaskDispatcher gets destroyed. In truth,
+ // this is only necessary in the case where this AutoTaskDispatcher can be
+ // accessed by the direct tasks it dispatches (true for TailDispatchers, but
+ // potentially not true for other hypothetical AutoTaskDispatchers). Feel
+ // free to loosen this restriction to apply only to mIsTailDispatcher if a
+ // use-case requires it.
+ MOZ_ASSERT(!HaveDirectTasks());
+
+ for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
+ DispatchTaskGroup(Move(mTaskGroups[i]));
+ }
+ }
+
+ bool HaveDirectTasks() const
+ {
+ return mDirectTasks.isSome() && !mDirectTasks->empty();
+ }
+
+ void DrainDirectTasks() override
+ {
+ while (HaveDirectTasks()) {
+ nsCOMPtr<nsIRunnable> r = mDirectTasks->front();
+ mDirectTasks->pop();
+ r->Run();
+ }
+ }
+
+ void AddDirectTask(already_AddRefed<nsIRunnable> aRunnable) override
+ {
+ if (mDirectTasks.isNothing()) {
+ mDirectTasks.emplace();
+ }
+ mDirectTasks->push(Move(aRunnable));
+ }
+
+ void AddStateChangeTask(AbstractThread* aThread,
+ already_AddRefed<nsIRunnable> aRunnable) override
+ {
+ EnsureTaskGroup(aThread).mStateChangeTasks.AppendElement(aRunnable);
+ }
+
+ void AddTask(AbstractThread* aThread,
+ already_AddRefed<nsIRunnable> aRunnable,
+ AbstractThread::DispatchFailureHandling aFailureHandling) override
+ {
+ PerThreadTaskGroup& group = EnsureTaskGroup(aThread);
+ group.mRegularTasks.AppendElement(aRunnable);
+
+ // The task group needs to assert dispatch success if any of the runnables
+ // it's dispatching want to assert it.
+ if (aFailureHandling == AbstractThread::AssertDispatchSuccess) {
+ group.mFailureHandling = AbstractThread::AssertDispatchSuccess;
+ }
+ }
+
+ bool HasTasksFor(AbstractThread* aThread) override
+ {
+ return !!GetTaskGroup(aThread) ||
+ (aThread == AbstractThread::GetCurrent() && HaveDirectTasks());
+ }
+
+ void DispatchTasksFor(AbstractThread* aThread) override
+ {
+ for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
+ if (mTaskGroups[i]->mThread == aThread) {
+ DispatchTaskGroup(Move(mTaskGroups[i]));
+ mTaskGroups.RemoveElementAt(i);
+ return;
+ }
+ }
+ }
+
+private:
+
+ struct PerThreadTaskGroup
+ {
+ public:
+ explicit PerThreadTaskGroup(AbstractThread* aThread)
+ : mThread(aThread), mFailureHandling(AbstractThread::DontAssertDispatchSuccess)
+ {
+ MOZ_COUNT_CTOR(PerThreadTaskGroup);
+ }
+
+ ~PerThreadTaskGroup() { MOZ_COUNT_DTOR(PerThreadTaskGroup); }
+
+ RefPtr<AbstractThread> mThread;
+ nsTArray<nsCOMPtr<nsIRunnable>> mStateChangeTasks;
+ nsTArray<nsCOMPtr<nsIRunnable>> mRegularTasks;
+ AbstractThread::DispatchFailureHandling mFailureHandling;
+ };
+
+ class TaskGroupRunnable : public Runnable
+ {
+ public:
+ explicit TaskGroupRunnable(UniquePtr<PerThreadTaskGroup>&& aTasks) : mTasks(Move(aTasks)) {}
+
+ NS_IMETHOD Run() override
+ {
+ // State change tasks get run all together before any code is run, so
+ // that all state changes are made in an atomic unit.
+ for (size_t i = 0; i < mTasks->mStateChangeTasks.Length(); ++i) {
+ mTasks->mStateChangeTasks[i]->Run();
+ }
+
+ // Once the state changes have completed, drain any direct tasks
+ // generated by those state changes (i.e. watcher notification tasks).
+ // This needs to be outside the loop because we don't want to run code
+ // that might observe intermediate states.
+ MaybeDrainDirectTasks();
+
+ for (size_t i = 0; i < mTasks->mRegularTasks.Length(); ++i) {
+ mTasks->mRegularTasks[i]->Run();
+
+ // Scope direct tasks tightly to the task that generated them.
+ MaybeDrainDirectTasks();
+ }
+
+ return NS_OK;
+ }
+
+ private:
+ void MaybeDrainDirectTasks()
+ {
+ AbstractThread* currentThread = AbstractThread::GetCurrent();
+ if (currentThread) {
+ currentThread->TailDispatcher().DrainDirectTasks();
+ }
+ }
+
+ UniquePtr<PerThreadTaskGroup> mTasks;
+ };
+
+ PerThreadTaskGroup& EnsureTaskGroup(AbstractThread* aThread)
+ {
+ PerThreadTaskGroup* existing = GetTaskGroup(aThread);
+ if (existing) {
+ return *existing;
+ }
+
+ mTaskGroups.AppendElement(new PerThreadTaskGroup(aThread));
+ return *mTaskGroups.LastElement();
+ }
+
+ PerThreadTaskGroup* GetTaskGroup(AbstractThread* aThread)
+ {
+ for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
+ if (mTaskGroups[i]->mThread == aThread) {
+ return mTaskGroups[i].get();
+ }
+ }
+
+ // Not found.
+ return nullptr;
+ }
+
+ void DispatchTaskGroup(UniquePtr<PerThreadTaskGroup> aGroup)
+ {
+ RefPtr<AbstractThread> thread = aGroup->mThread;
+
+ AbstractThread::DispatchFailureHandling failureHandling = aGroup->mFailureHandling;
+ AbstractThread::DispatchReason reason = mIsTailDispatcher ? AbstractThread::TailDispatch
+ : AbstractThread::NormalDispatch;
+ nsCOMPtr<nsIRunnable> r = new TaskGroupRunnable(Move(aGroup));
+ thread->Dispatch(r.forget(), failureHandling, reason);
+ }
+
+ // Direct tasks. We use a Maybe<> because (a) this class is hot, (b)
+ // mDirectTasks often doesn't get anything put into it, and (c) the
+ // std::queue implementation in GNU libstdc++ does two largish heap
+ // allocations when creating a new std::queue.
+ mozilla::Maybe<std::queue<nsCOMPtr<nsIRunnable>>> mDirectTasks;
+
+ // Task groups, organized by thread.
+ nsTArray<UniquePtr<PerThreadTaskGroup>> mTaskGroups;
+
+ // True if this TaskDispatcher represents the tail dispatcher for the thread
+ // upon which it runs.
+ const bool mIsTailDispatcher;
+};
+
+// Little utility class to allow declaring AutoTaskDispatcher as a default
+// parameter for methods that take a TaskDispatcher&.
+template<typename T>
+class PassByRef
+{
+public:
+ PassByRef() {}
+ operator T&() { return mVal; }
+private:
+ T mVal;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/threads/TaskQueue.cpp b/xpcom/threads/TaskQueue.cpp
new file mode 100644
index 000000000..2e593a773
--- /dev/null
+++ b/xpcom/threads/TaskQueue.cpp
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/TaskQueue.h"
+
+#include "nsIEventTarget.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+class TaskQueue::EventTargetWrapper final : public nsIEventTarget
+{
+ RefPtr<TaskQueue> mTaskQueue;
+
+ ~EventTargetWrapper()
+ {
+ }
+
+public:
+ explicit EventTargetWrapper(TaskQueue* aTaskQueue)
+ : mTaskQueue(aTaskQueue)
+ {
+ MOZ_ASSERT(mTaskQueue);
+ }
+
+ NS_IMETHOD
+ DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) override
+ {
+ nsCOMPtr<nsIRunnable> ref = aEvent;
+ return Dispatch(ref.forget(), aFlags);
+ }
+
+ NS_IMETHOD
+ Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) override
+ {
+ nsCOMPtr<nsIRunnable> runnable = aEvent;
+ MonitorAutoLock mon(mTaskQueue->mQueueMonitor);
+ return mTaskQueue->DispatchLocked(/* passed by ref */runnable,
+ DontAssertDispatchSuccess,
+ NormalDispatch);
+ }
+
+ NS_IMETHOD
+ DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t aFlags) override
+ {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD
+ IsOnCurrentThread(bool* aResult) override
+ {
+ *aResult = mTaskQueue->IsCurrentThreadIn();
+ return NS_OK;
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper, nsIEventTarget)
+
+TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
+ bool aRequireTailDispatch)
+ : AbstractThread(aRequireTailDispatch)
+ , mTarget(aTarget)
+ , mQueueMonitor("TaskQueue::Queue")
+ , mTailDispatcher(nullptr)
+ , mIsRunning(false)
+ , mIsShutdown(false)
+{
+ MOZ_COUNT_CTOR(TaskQueue);
+}
+
+TaskQueue::~TaskQueue()
+{
+ MonitorAutoLock mon(mQueueMonitor);
+ MOZ_ASSERT(mIsShutdown);
+ MOZ_COUNT_DTOR(TaskQueue);
+}
+
+TaskDispatcher&
+TaskQueue::TailDispatcher()
+{
+ MOZ_ASSERT(IsCurrentThreadIn());
+ MOZ_ASSERT(mTailDispatcher);
+ return *mTailDispatcher;
+}
+
+// Note aRunnable is passed by ref to support conditional ownership transfer.
+// See Dispatch() in TaskQueue.h for more details.
+nsresult
+TaskQueue::DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable,
+ DispatchFailureHandling aFailureHandling,
+ DispatchReason aReason)
+{
+ mQueueMonitor.AssertCurrentThreadOwns();
+ if (mIsShutdown) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AbstractThread* currentThread;
+ if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
+ currentThread->TailDispatcher().AddTask(this, aRunnable.forget(), aFailureHandling);
+ return NS_OK;
+ }
+
+ mTasks.push(aRunnable.forget());
+ if (mIsRunning) {
+ return NS_OK;
+ }
+ RefPtr<nsIRunnable> runner(new Runner(this));
+ nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch runnable to run TaskQueue");
+ return rv;
+ }
+ mIsRunning = true;
+
+ return NS_OK;
+}
+
+void
+TaskQueue::AwaitIdle()
+{
+ MonitorAutoLock mon(mQueueMonitor);
+ AwaitIdleLocked();
+}
+
+void
+TaskQueue::AwaitIdleLocked()
+{
+ // Make sure there are no tasks for this queue waiting in the caller's tail
+ // dispatcher.
+ MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
+ !AbstractThread::GetCurrent()->HasTailTasksFor(this));
+
+ mQueueMonitor.AssertCurrentThreadOwns();
+ MOZ_ASSERT(mIsRunning || mTasks.empty());
+ while (mIsRunning) {
+ mQueueMonitor.Wait();
+ }
+}
+
+void
+TaskQueue::AwaitShutdownAndIdle()
+{
+ MOZ_ASSERT(!IsCurrentThreadIn());
+ // Make sure there are no tasks for this queue waiting in the caller's tail
+ // dispatcher.
+ MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
+ !AbstractThread::GetCurrent()->HasTailTasksFor(this));
+
+ MonitorAutoLock mon(mQueueMonitor);
+ while (!mIsShutdown) {
+ mQueueMonitor.Wait();
+ }
+ AwaitIdleLocked();
+}
+
+RefPtr<ShutdownPromise>
+TaskQueue::BeginShutdown()
+{
+ // Dispatch any tasks for this queue waiting in the caller's tail dispatcher,
+ // since this is the last opportunity to do so.
+ if (AbstractThread* currentThread = AbstractThread::GetCurrent()) {
+ currentThread->TailDispatchTasksFor(this);
+ }
+
+ MonitorAutoLock mon(mQueueMonitor);
+ mIsShutdown = true;
+ RefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
+ MaybeResolveShutdown();
+ mon.NotifyAll();
+ return p;
+}
+
+bool
+TaskQueue::IsEmpty()
+{
+ MonitorAutoLock mon(mQueueMonitor);
+ return mTasks.empty();
+}
+
+uint32_t
+TaskQueue::ImpreciseLengthForHeuristics()
+{
+ MonitorAutoLock mon(mQueueMonitor);
+ return mTasks.size();
+}
+
+bool
+TaskQueue::IsCurrentThreadIn()
+{
+ bool in = NS_GetCurrentThread() == mRunningThread;
+ return in;
+}
+
+already_AddRefed<nsIEventTarget>
+TaskQueue::WrapAsEventTarget()
+{
+ nsCOMPtr<nsIEventTarget> ref = new EventTargetWrapper(this);
+ return ref.forget();
+}
+
+nsresult
+TaskQueue::Runner::Run()
+{
+ RefPtr<nsIRunnable> event;
+ {
+ MonitorAutoLock mon(mQueue->mQueueMonitor);
+ MOZ_ASSERT(mQueue->mIsRunning);
+ if (mQueue->mTasks.size() == 0) {
+ mQueue->mIsRunning = false;
+ mQueue->MaybeResolveShutdown();
+ mon.NotifyAll();
+ return NS_OK;
+ }
+ event = mQueue->mTasks.front().forget();
+ mQueue->mTasks.pop();
+ }
+ MOZ_ASSERT(event);
+
+ // Note that dropping the queue monitor before running the task, and
+ // taking the monitor again after the task has run ensures we have memory
+ // fences enforced. This means that if the object we're calling wasn't
+ // designed to be threadsafe, it will be, provided we're only calling it
+ // in this task queue.
+ {
+ AutoTaskGuard g(mQueue);
+ event->Run();
+ }
+
+ // Drop the reference to event. The event will hold a reference to the
+ // object it's calling, and we don't want to keep it alive, it may be
+ // making assumptions what holds references to it. This is especially
+ // the case if the object is waiting for us to shutdown, so that it
+ // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
+ event = nullptr;
+
+ {
+ MonitorAutoLock mon(mQueue->mQueueMonitor);
+ if (mQueue->mTasks.size() == 0) {
+ // No more events to run. Exit the task runner.
+ mQueue->mIsRunning = false;
+ mQueue->MaybeResolveShutdown();
+ mon.NotifyAll();
+ return NS_OK;
+ }
+ }
+
+ // There's at least one more event that we can run. Dispatch this Runner
+ // to the target again to ensure it runs again. Note that we don't just
+ // run in a loop here so that we don't hog the target. This means we may
+ // run on another thread next time, but we rely on the memory fences from
+ // mQueueMonitor for thread safety of non-threadsafe tasks.
+ nsresult rv = mQueue->mTarget->Dispatch(this, NS_DISPATCH_AT_END);
+ if (NS_FAILED(rv)) {
+ // Failed to dispatch, shutdown!
+ MonitorAutoLock mon(mQueue->mQueueMonitor);
+ mQueue->mIsRunning = false;
+ mQueue->mIsShutdown = true;
+ mQueue->MaybeResolveShutdown();
+ mon.NotifyAll();
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/TaskQueue.h b/xpcom/threads/TaskQueue.h
new file mode 100644
index 000000000..aafd206a7
--- /dev/null
+++ b/xpcom/threads/TaskQueue.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TaskQueue_h_
+#define TaskQueue_h_
+
+#include "mozilla/Monitor.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TaskDispatcher.h"
+#include "mozilla/Unused.h"
+
+#include <queue>
+
+#include "nsThreadUtils.h"
+
+class nsIEventTarget;
+class nsIRunnable;
+
+namespace mozilla {
+
+typedef MozPromise<bool, bool, false> ShutdownPromise;
+
+// Abstracts executing runnables in order on an arbitrary event target. The
+// runnables dispatched to the TaskQueue will be executed in the order in which
+// they're received, and are guaranteed to not be executed concurrently.
+// They may be executed on different threads, and a memory barrier is used
+// to make this threadsafe for objects that aren't already threadsafe.
+//
+// Note, since a TaskQueue can also be converted to an nsIEventTarget using
+// WrapAsEventTarget() its possible to construct a hierarchy of TaskQueues.
+// Consider these three TaskQueues:
+//
+// TQ1 dispatches to the main thread
+// TQ2 dispatches to TQ1
+// TQ3 dispatches to TQ1
+//
+// This ensures there is only ever a single runnable from the entire chain on
+// the main thread. It also ensures that TQ2 and TQ3 only have a single runnable
+// in TQ1 at any time.
+//
+// This arrangement lets you prioritize work by dispatching runnables directly
+// to TQ1. You can issue many runnables for important work. Meanwhile the TQ2
+// and TQ3 work will always execute at most one runnable and then yield.
+class TaskQueue : public AbstractThread
+{
+ class EventTargetWrapper;
+
+public:
+ explicit TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
+ bool aSupportsTailDispatch = false);
+
+ TaskDispatcher& TailDispatcher() override;
+
+ TaskQueue* AsTaskQueue() override { return this; }
+
+ void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+ DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
+ DispatchReason aReason = NormalDispatch) override
+ {
+ nsCOMPtr<nsIRunnable> r = aRunnable;
+ {
+ MonitorAutoLock mon(mQueueMonitor);
+ nsresult rv = DispatchLocked(/* passed by ref */r, aFailureHandling, aReason);
+ MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
+ Unused << rv;
+ }
+ // If the ownership of |r| is not transferred in DispatchLocked() due to
+ // dispatch failure, it will be deleted here outside the lock. We do so
+ // since the destructor of the runnable might access TaskQueue and result
+ // in deadlocks.
+ }
+
+ // Puts the queue in a shutdown state and returns immediately. The queue will
+ // remain alive at least until all the events are drained, because the Runners
+ // hold a strong reference to the task queue, and one of them is always held
+ // by the target event queue when the task queue is non-empty.
+ //
+ // The returned promise is resolved when the queue goes empty.
+ RefPtr<ShutdownPromise> BeginShutdown();
+
+ // Blocks until all task finish executing.
+ void AwaitIdle();
+
+ // Blocks until the queue is flagged for shutdown and all tasks have finished
+ // executing.
+ void AwaitShutdownAndIdle();
+
+ bool IsEmpty();
+ uint32_t ImpreciseLengthForHeuristics();
+
+ // Returns true if the current thread is currently running a Runnable in
+ // the task queue.
+ bool IsCurrentThreadIn() override;
+
+ // Create a new nsIEventTarget wrapper object that dispatches to this
+ // TaskQueue.
+ already_AddRefed<nsIEventTarget> WrapAsEventTarget();
+
+protected:
+ virtual ~TaskQueue();
+
+
+ // Blocks until all task finish executing. Called internally by methods
+ // that need to wait until the task queue is idle.
+ // mQueueMonitor must be held.
+ void AwaitIdleLocked();
+
+ nsresult DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable,
+ DispatchFailureHandling aFailureHandling,
+ DispatchReason aReason = NormalDispatch);
+
+ void MaybeResolveShutdown()
+ {
+ mQueueMonitor.AssertCurrentThreadOwns();
+ if (mIsShutdown && !mIsRunning) {
+ mShutdownPromise.ResolveIfExists(true, __func__);
+ mTarget = nullptr;
+ }
+ }
+
+ nsCOMPtr<nsIEventTarget> mTarget;
+
+ // Monitor that protects the queue and mIsRunning;
+ Monitor mQueueMonitor;
+
+ // Queue of tasks to run.
+ std::queue<nsCOMPtr<nsIRunnable>> mTasks;
+
+ // The thread currently running the task queue. We store a reference
+ // to this so that IsCurrentThreadIn() can tell if the current thread
+ // is the thread currently running in the task queue.
+ //
+ // This may be read on any thread, but may only be written on mRunningThread.
+ // The thread can't die while we're running in it, and we only use it for
+ // pointer-comparison with the current thread anyway - so we make it atomic
+ // and don't refcount it.
+ Atomic<nsIThread*> mRunningThread;
+
+ // RAII class that gets instantiated for each dispatched task.
+ class AutoTaskGuard : public AutoTaskDispatcher
+ {
+ public:
+ explicit AutoTaskGuard(TaskQueue* aQueue)
+ : AutoTaskDispatcher(/* aIsTailDispatcher = */ true), mQueue(aQueue)
+ , mLastCurrentThread(nullptr)
+ {
+ // NB: We don't hold the lock to aQueue here. Don't do anything that
+ // might require it.
+ MOZ_ASSERT(!mQueue->mTailDispatcher);
+ mQueue->mTailDispatcher = this;
+
+ mLastCurrentThread = sCurrentThreadTLS.get();
+ sCurrentThreadTLS.set(aQueue);
+
+ MOZ_ASSERT(mQueue->mRunningThread == nullptr);
+ mQueue->mRunningThread = NS_GetCurrentThread();
+ }
+
+ ~AutoTaskGuard()
+ {
+ DrainDirectTasks();
+
+ MOZ_ASSERT(mQueue->mRunningThread == NS_GetCurrentThread());
+ mQueue->mRunningThread = nullptr;
+
+ sCurrentThreadTLS.set(mLastCurrentThread);
+ mQueue->mTailDispatcher = nullptr;
+ }
+
+ private:
+ TaskQueue* mQueue;
+ AbstractThread* mLastCurrentThread;
+ };
+
+ TaskDispatcher* mTailDispatcher;
+
+ // True if we've dispatched an event to the target to execute events from
+ // the queue.
+ bool mIsRunning;
+
+ // True if we've started our shutdown process.
+ bool mIsShutdown;
+ MozPromiseHolder<ShutdownPromise> mShutdownPromise;
+
+ class Runner : public Runnable {
+ public:
+ explicit Runner(TaskQueue* aQueue)
+ : mQueue(aQueue)
+ {
+ }
+ NS_IMETHOD Run() override;
+ private:
+ RefPtr<TaskQueue> mQueue;
+ };
+};
+
+} // namespace mozilla
+
+#endif // TaskQueue_h_
diff --git a/xpcom/threads/ThreadStackHelper.cpp b/xpcom/threads/ThreadStackHelper.cpp
new file mode 100644
index 000000000..d31bf6359
--- /dev/null
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -0,0 +1,726 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ThreadStackHelper.h"
+#include "MainThreadUtils.h"
+#include "nsJSPrincipals.h"
+#include "nsScriptSecurityManager.h"
+#include "jsfriendapi.h"
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+#include "shared-libraries.h"
+#endif
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Move.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/MemoryChecking.h"
+#include "mozilla/Sprintf.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#if defined(MOZ_VALGRIND)
+# include <valgrind/valgrind.h>
+#endif
+
+#include <string.h>
+#include <vector>
+#include <cstdlib>
+
+#ifdef XP_LINUX
+#include <ucontext.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#endif
+
+#ifdef __GNUC__
+# pragma GCC diagnostic pop // -Wshadow
+#endif
+
+#if defined(XP_LINUX) || defined(XP_MACOSX)
+#include <pthread.h>
+#endif
+
+#ifdef ANDROID
+#ifndef SYS_gettid
+#define SYS_gettid __NR_gettid
+#endif
+#if defined(__arm__) && !defined(__NR_rt_tgsigqueueinfo)
+// Some NDKs don't define this constant even though the kernel supports it.
+#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
+#endif
+#ifndef SYS_rt_tgsigqueueinfo
+#define SYS_rt_tgsigqueueinfo __NR_rt_tgsigqueueinfo
+#endif
+#endif
+
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+#if defined(MOZ_THREADSTACKHELPER_X86) || \
+ defined(MOZ_THREADSTACKHELPER_X64) || \
+ defined(MOZ_THREADSTACKHELPER_ARM)
+// On these architectures, the stack grows downwards (toward lower addresses).
+#define MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN
+#else
+#error "Unsupported architecture"
+#endif
+#endif // MOZ_THREADSTACKHELPER_NATIVE
+
+namespace mozilla {
+
+void
+ThreadStackHelper::Startup()
+{
+#if defined(XP_LINUX)
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sInitialized) {
+ // TODO: centralize signal number allocation
+ sFillStackSignum = SIGRTMIN + 4;
+ if (sFillStackSignum > SIGRTMAX) {
+ // Leave uninitialized
+ MOZ_ASSERT(false);
+ return;
+ }
+ struct sigaction sigact = {};
+ sigact.sa_sigaction = FillStackHandler;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = SA_SIGINFO | SA_RESTART;
+ MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr));
+ }
+ sInitialized++;
+#endif
+}
+
+void
+ThreadStackHelper::Shutdown()
+{
+#if defined(XP_LINUX)
+ MOZ_ASSERT(NS_IsMainThread());
+ if (sInitialized == 1) {
+ struct sigaction sigact = {};
+ sigact.sa_handler = SIG_DFL;
+ MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr));
+ }
+ sInitialized--;
+#endif
+}
+
+ThreadStackHelper::ThreadStackHelper()
+ : mStackToFill(nullptr)
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+ , mPseudoStack(mozilla_get_pseudo_stack())
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ , mContextToFill(nullptr)
+#endif
+ , mMaxStackSize(Stack::sMaxInlineStorage)
+ , mMaxBufferSize(512)
+#endif
+{
+#if defined(XP_LINUX)
+ MOZ_ALWAYS_TRUE(!::sem_init(&mSem, 0, 0));
+ mThreadID = ::syscall(SYS_gettid);
+#elif defined(XP_WIN)
+ mInitialized = !!::DuplicateHandle(
+ ::GetCurrentProcess(), ::GetCurrentThread(),
+ ::GetCurrentProcess(), &mThreadID,
+ THREAD_SUSPEND_RESUME
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION
+#endif
+ , FALSE, 0);
+ MOZ_ASSERT(mInitialized);
+#elif defined(XP_MACOSX)
+ mThreadID = mach_thread_self();
+#endif
+
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ GetThreadStackBase();
+#endif
+}
+
+ThreadStackHelper::~ThreadStackHelper()
+{
+#if defined(XP_LINUX)
+ MOZ_ALWAYS_TRUE(!::sem_destroy(&mSem));
+#elif defined(XP_WIN)
+ if (mInitialized) {
+ MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID));
+ }
+#endif
+}
+
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+void ThreadStackHelper::GetThreadStackBase()
+{
+ mThreadStackBase = 0;
+
+#if defined(XP_LINUX)
+ void* stackAddr;
+ size_t stackSize;
+ ::pthread_t pthr = ::pthread_self();
+ ::pthread_attr_t pthr_attr;
+ NS_ENSURE_TRUE_VOID(!::pthread_getattr_np(pthr, &pthr_attr));
+ if (!::pthread_attr_getstack(&pthr_attr, &stackAddr, &stackSize)) {
+#ifdef MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN
+ mThreadStackBase = intptr_t(stackAddr) + stackSize;
+#else
+ mThreadStackBase = intptr_t(stackAddr);
+#endif
+ }
+ MOZ_ALWAYS_TRUE(!::pthread_attr_destroy(&pthr_attr));
+
+#elif defined(XP_WIN)
+ ::MEMORY_BASIC_INFORMATION meminfo = {};
+ NS_ENSURE_TRUE_VOID(::VirtualQuery(&meminfo, &meminfo, sizeof(meminfo)));
+#ifdef MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN
+ mThreadStackBase = intptr_t(meminfo.BaseAddress) + meminfo.RegionSize;
+#else
+ mThreadStackBase = intptr_t(meminfo.AllocationBase);
+#endif
+
+#elif defined(XP_MACOSX)
+ ::pthread_t pthr = ::pthread_self();
+ mThreadStackBase = intptr_t(::pthread_get_stackaddr_np(pthr));
+
+#else
+ #error "Unsupported platform"
+#endif // platform
+}
+#endif // MOZ_THREADSTACKHELPER_NATIVE
+
+namespace {
+template<typename T>
+class ScopedSetPtr
+{
+private:
+ T*& mPtr;
+public:
+ ScopedSetPtr(T*& p, T* val) : mPtr(p) { mPtr = val; }
+ ~ScopedSetPtr() { mPtr = nullptr; }
+};
+} // namespace
+
+void
+ThreadStackHelper::GetStack(Stack& aStack)
+{
+ // Always run PrepareStackBuffer first to clear aStack
+ if (!PrepareStackBuffer(aStack)) {
+ // Skip and return empty aStack
+ return;
+ }
+
+ ScopedSetPtr<Stack> stackPtr(mStackToFill, &aStack);
+
+#if defined(XP_LINUX)
+ if (!sInitialized) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ siginfo_t uinfo = {};
+ uinfo.si_signo = sFillStackSignum;
+ uinfo.si_code = SI_QUEUE;
+ uinfo.si_pid = getpid();
+ uinfo.si_uid = getuid();
+ uinfo.si_value.sival_ptr = this;
+ if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid,
+ mThreadID, sFillStackSignum, &uinfo)) {
+ // rt_tgsigqueueinfo was added in Linux 2.6.31.
+ // Could have failed because the syscall did not exist.
+ return;
+ }
+ MOZ_ALWAYS_TRUE(!::sem_wait(&mSem));
+
+#elif defined(XP_WIN)
+ if (!mInitialized) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ if (::SuspendThread(mThreadID) == DWORD(-1)) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ // SuspendThread is asynchronous, so the thread may still be running. Use
+ // GetThreadContext to ensure it's really suspended.
+ // See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_CONTROL;
+ if (::GetThreadContext(mThreadID, &context)) {
+ FillStackBuffer();
+ FillThreadContext();
+ }
+
+ MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
+
+#elif defined(XP_MACOSX)
+# if defined(MOZ_VALGRIND) && defined(RUNNING_ON_VALGRIND)
+ if (RUNNING_ON_VALGRIND) {
+ /* thread_suspend and thread_resume sometimes hang runs on Valgrind,
+ for unknown reasons. So, just avoid them. See bug 1100911. */
+ return;
+ }
+# endif
+
+ if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ FillStackBuffer();
+ FillThreadContext();
+
+ MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
+
+#endif
+}
+
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+class ThreadStackHelper::ThreadContext final
+{
+public:
+ // TODO: provide per-platform definition of Context.
+ typedef struct {} Context;
+
+ // Limit copied stack to 4kB
+ static const size_t kMaxStackSize = 0x1000;
+ // Limit unwound stack to 32 frames
+ static const unsigned int kMaxStackFrames = 32;
+ // Whether this structure contains valid data
+ bool mValid;
+ // Processor context
+ Context mContext;
+ // Stack area
+ UniquePtr<uint8_t[]> mStack;
+ // Start of stack area
+ uintptr_t mStackBase;
+ // Size of stack area
+ size_t mStackSize;
+ // End of stack area
+ const void* mStackEnd;
+
+ ThreadContext()
+ : mValid(false)
+ , mStackBase(0)
+ , mStackSize(0)
+ , mStackEnd(nullptr) {}
+};
+#endif // MOZ_THREADSTACKHELPER_NATIVE
+
+void
+ThreadStackHelper::GetNativeStack(Stack& aStack)
+{
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ ThreadContext context;
+ context.mStack = MakeUnique<uint8_t[]>(ThreadContext::kMaxStackSize);
+
+ ScopedSetPtr<ThreadContext> contextPtr(mContextToFill, &context);
+
+ // Get pseudostack first and fill the thread context.
+ GetStack(aStack);
+ NS_ENSURE_TRUE_VOID(context.mValid);
+
+ // TODO: walk the saved stack frames.
+#endif // MOZ_THREADSTACKHELPER_NATIVE
+}
+
+#ifdef XP_LINUX
+
+int ThreadStackHelper::sInitialized;
+int ThreadStackHelper::sFillStackSignum;
+
+void
+ThreadStackHelper::FillStackHandler(int aSignal, siginfo_t* aInfo,
+ void* aContext)
+{
+ ThreadStackHelper* const helper =
+ reinterpret_cast<ThreadStackHelper*>(aInfo->si_value.sival_ptr);
+ helper->FillStackBuffer();
+ helper->FillThreadContext(aContext);
+ ::sem_post(&helper->mSem);
+}
+
+#endif // XP_LINUX
+
+bool
+ThreadStackHelper::PrepareStackBuffer(Stack& aStack)
+{
+ // Return false to skip getting the stack and return an empty stack
+ aStack.clear();
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+ /* Normally, provided the profiler is enabled, it would be an error if we
+ don't have a pseudostack here (the thread probably forgot to call
+ profiler_register_thread). However, on B2G, profiling secondary threads
+ may be disabled despite profiler being enabled. This is by-design and
+ is not an error. */
+#ifdef MOZ_WIDGET_GONK
+ if (!mPseudoStack) {
+ return false;
+ }
+#endif
+ MOZ_ASSERT(mPseudoStack);
+ if (!aStack.reserve(mMaxStackSize) ||
+ !aStack.reserve(aStack.capacity()) || // reserve up to the capacity
+ !aStack.EnsureBufferCapacity(mMaxBufferSize)) {
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+
+namespace {
+
+bool
+IsChromeJSScript(JSScript* aScript)
+{
+ // May be called from another thread or inside a signal handler.
+ // We assume querying the script is safe but we must not manipulate it.
+
+ nsIScriptSecurityManager* const secman =
+ nsScriptSecurityManager::GetScriptSecurityManager();
+ NS_ENSURE_TRUE(secman, false);
+
+ JSPrincipals* const principals = JS_GetScriptPrincipals(aScript);
+ return secman->IsSystemPrincipal(nsJSPrincipals::get(principals));
+}
+
+// Get the full path after the URI scheme, if the URI matches the scheme.
+// For example, GetFullPathForScheme("a://b/c/d/e", "a://") returns "b/c/d/e".
+template <size_t LEN>
+const char*
+GetFullPathForScheme(const char* filename, const char (&scheme)[LEN]) {
+ // Account for the null terminator included in LEN.
+ if (!strncmp(filename, scheme, LEN - 1)) {
+ return filename + LEN - 1;
+ }
+ return nullptr;
+}
+
+// Get the full path after a URI component, if the URI contains the component.
+// For example, GetPathAfterComponent("a://b/c/d/e", "/c/") returns "d/e".
+template <size_t LEN>
+const char*
+GetPathAfterComponent(const char* filename, const char (&component)[LEN]) {
+ const char* found = nullptr;
+ const char* next = strstr(filename, component);
+ while (next) {
+ // Move 'found' to end of the component, after the separator '/'.
+ // 'LEN - 1' accounts for the null terminator included in LEN,
+ found = next + LEN - 1;
+ // Resume searching before the separator '/'.
+ next = strstr(found - 1, component);
+ }
+ return found;
+}
+
+} // namespace
+
+const char*
+ThreadStackHelper::AppendJSEntry(const volatile StackEntry* aEntry,
+ intptr_t& aAvailableBufferSize,
+ const char* aPrevLabel)
+{
+ // May be called from another thread or inside a signal handler.
+ // We assume querying the script is safe but we must not manupulate it.
+ // Also we must not allocate any memory from heap.
+ MOZ_ASSERT(aEntry->isJs());
+
+ const char* label;
+ JSScript* script = aEntry->script();
+ if (!script) {
+ label = "(profiling suppressed)";
+ } else if (IsChromeJSScript(aEntry->script())) {
+ const char* filename = JS_GetScriptFilename(aEntry->script());
+ const unsigned lineno = JS_PCToLineNumber(aEntry->script(), aEntry->pc());
+ MOZ_ASSERT(filename);
+
+ char buffer[128]; // Enough to fit longest js file name from the tree
+
+ // Some script names are in the form "foo -> bar -> baz".
+ // Here we find the origin of these redirected scripts.
+ const char* basename = GetPathAfterComponent(filename, " -> ");
+ if (basename) {
+ filename = basename;
+ }
+
+ basename = GetFullPathForScheme(filename, "chrome://");
+ if (!basename) {
+ basename = GetFullPathForScheme(filename, "resource://");
+ }
+ if (!basename) {
+ // If the (add-on) script is located under the {profile}/extensions
+ // directory, extract the path after the /extensions/ part.
+ basename = GetPathAfterComponent(filename, "/extensions/");
+ }
+ if (!basename) {
+ // Only keep the file base name for paths outside the above formats.
+ basename = strrchr(filename, '/');
+ basename = basename ? basename + 1 : filename;
+ // Look for Windows path separator as well.
+ filename = strrchr(basename, '\\');
+ if (filename) {
+ basename = filename + 1;
+ }
+ }
+
+ size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno);
+ if (len < sizeof(buffer)) {
+ if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) {
+ return aPrevLabel;
+ }
+
+ // Keep track of the required buffer size
+ aAvailableBufferSize -= (len + 1);
+ if (aAvailableBufferSize >= 0) {
+ // Buffer is big enough.
+ return mStackToFill->InfallibleAppendViaBuffer(buffer, len);
+ }
+ // Buffer is not big enough; fall through to using static label below.
+ }
+ // snprintf failed or buffer is not big enough.
+ label = "(chrome script)";
+ } else {
+ label = "(content script)";
+ }
+
+ if (mStackToFill->IsSameAsEntry(aPrevLabel, label)) {
+ return aPrevLabel;
+ }
+ mStackToFill->infallibleAppend(label);
+ return label;
+}
+
+#endif // MOZ_THREADSTACKHELPER_PSEUDO
+
+void
+ThreadStackHelper::FillStackBuffer()
+{
+ MOZ_ASSERT(mStackToFill->empty());
+
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+ size_t reservedSize = mStackToFill->capacity();
+ size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
+ intptr_t availableBufferSize = intptr_t(reservedBufferSize);
+
+ // Go from front to back
+ const volatile StackEntry* entry = mPseudoStack->mStack;
+ const volatile StackEntry* end = entry + mPseudoStack->stackSize();
+ // Deduplicate identical, consecutive frames
+ const char* prevLabel = nullptr;
+ for (; reservedSize-- && entry != end; entry++) {
+ /* We only accept non-copy labels, including js::RunScript,
+ because we only want static labels in the hang stack. */
+ if (entry->isCopyLabel()) {
+ continue;
+ }
+ if (entry->isJs()) {
+ prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel);
+ continue;
+ }
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ if (mContextToFill) {
+ mContextToFill->mStackEnd = entry->stackAddress();
+ }
+#endif
+ const char* const label = entry->label();
+ if (mStackToFill->IsSameAsEntry(prevLabel, label)) {
+ // Avoid duplicate labels to save space in the stack.
+ continue;
+ }
+ mStackToFill->infallibleAppend(label);
+ prevLabel = label;
+ }
+
+ // end != entry if we exited early due to not enough reserved frames.
+ // Expand the number of reserved frames for next time.
+ mMaxStackSize = mStackToFill->capacity() + (end - entry);
+
+ // availableBufferSize < 0 if we needed a larger buffer than we reserved.
+ // Calculate a new reserve size for next time.
+ if (availableBufferSize < 0) {
+ mMaxBufferSize = reservedBufferSize - availableBufferSize;
+ }
+#endif
+}
+
+MOZ_ASAN_BLACKLIST void
+ThreadStackHelper::FillThreadContext(void* aContext)
+{
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ if (!mContextToFill) {
+ return;
+ }
+
+#if 0 // TODO: remove dependency on Breakpad structs.
+#if defined(XP_LINUX)
+ const ucontext_t& context = *reinterpret_cast<ucontext_t*>(aContext);
+#if defined(MOZ_THREADSTACKHELPER_X86)
+ mContextToFill->mContext.context_flags = MD_CONTEXT_X86_FULL;
+ mContextToFill->mContext.edi = context.uc_mcontext.gregs[REG_EDI];
+ mContextToFill->mContext.esi = context.uc_mcontext.gregs[REG_ESI];
+ mContextToFill->mContext.ebx = context.uc_mcontext.gregs[REG_EBX];
+ mContextToFill->mContext.edx = context.uc_mcontext.gregs[REG_EDX];
+ mContextToFill->mContext.ecx = context.uc_mcontext.gregs[REG_ECX];
+ mContextToFill->mContext.eax = context.uc_mcontext.gregs[REG_EAX];
+ mContextToFill->mContext.ebp = context.uc_mcontext.gregs[REG_EBP];
+ mContextToFill->mContext.eip = context.uc_mcontext.gregs[REG_EIP];
+ mContextToFill->mContext.eflags = context.uc_mcontext.gregs[REG_EFL];
+ mContextToFill->mContext.esp = context.uc_mcontext.gregs[REG_ESP];
+#elif defined(MOZ_THREADSTACKHELPER_X64)
+ mContextToFill->mContext.context_flags = MD_CONTEXT_AMD64_FULL;
+ mContextToFill->mContext.eflags = uint32_t(context.uc_mcontext.gregs[REG_EFL]);
+ mContextToFill->mContext.rax = context.uc_mcontext.gregs[REG_RAX];
+ mContextToFill->mContext.rcx = context.uc_mcontext.gregs[REG_RCX];
+ mContextToFill->mContext.rdx = context.uc_mcontext.gregs[REG_RDX];
+ mContextToFill->mContext.rbx = context.uc_mcontext.gregs[REG_RBX];
+ mContextToFill->mContext.rsp = context.uc_mcontext.gregs[REG_RSP];
+ mContextToFill->mContext.rbp = context.uc_mcontext.gregs[REG_RBP];
+ mContextToFill->mContext.rsi = context.uc_mcontext.gregs[REG_RSI];
+ mContextToFill->mContext.rdi = context.uc_mcontext.gregs[REG_RDI];
+ memcpy(&mContextToFill->mContext.r8,
+ &context.uc_mcontext.gregs[REG_R8], 8 * sizeof(int64_t));
+ mContextToFill->mContext.rip = context.uc_mcontext.gregs[REG_RIP];
+#elif defined(MOZ_THREADSTACKHELPER_ARM)
+ mContextToFill->mContext.context_flags = MD_CONTEXT_ARM_FULL;
+ memcpy(&mContextToFill->mContext.iregs[0],
+ &context.uc_mcontext.arm_r0, 17 * sizeof(int32_t));
+#else
+ #error "Unsupported architecture"
+#endif // architecture
+
+#elif defined(XP_WIN)
+ // Breakpad context struct is based off of the Windows CONTEXT struct,
+ // so we assume they are the same; do some sanity checks to make sure.
+ static_assert(sizeof(ThreadContext::Context) == sizeof(::CONTEXT),
+ "Context struct mismatch");
+ static_assert(offsetof(ThreadContext::Context, context_flags) ==
+ offsetof(::CONTEXT, ContextFlags),
+ "Context struct mismatch");
+ mContextToFill->mContext.context_flags = CONTEXT_FULL;
+ NS_ENSURE_TRUE_VOID(::GetThreadContext(mThreadID,
+ reinterpret_cast<::CONTEXT*>(&mContextToFill->mContext)));
+
+#elif defined(XP_MACOSX)
+#if defined(MOZ_THREADSTACKHELPER_X86)
+ const thread_state_flavor_t flavor = x86_THREAD_STATE32;
+ x86_thread_state32_t state = {};
+ mach_msg_type_number_t count = x86_THREAD_STATE32_COUNT;
+#elif defined(MOZ_THREADSTACKHELPER_X64)
+ const thread_state_flavor_t flavor = x86_THREAD_STATE64;
+ x86_thread_state64_t state = {};
+ mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
+#elif defined(MOZ_THREADSTACKHELPER_ARM)
+ const thread_state_flavor_t flavor = ARM_THREAD_STATE;
+ arm_thread_state_t state = {};
+ mach_msg_type_number_t count = ARM_THREAD_STATE_COUNT;
+#endif
+ NS_ENSURE_TRUE_VOID(KERN_SUCCESS == ::thread_get_state(
+ mThreadID, flavor, reinterpret_cast<thread_state_t>(&state), &count));
+#if __DARWIN_UNIX03
+#define GET_REGISTER(s, r) ((s).__##r)
+#else
+#define GET_REGISTER(s, r) ((s).r)
+#endif
+#if defined(MOZ_THREADSTACKHELPER_X86)
+ mContextToFill->mContext.context_flags = MD_CONTEXT_X86_FULL;
+ mContextToFill->mContext.edi = GET_REGISTER(state, edi);
+ mContextToFill->mContext.esi = GET_REGISTER(state, esi);
+ mContextToFill->mContext.ebx = GET_REGISTER(state, ebx);
+ mContextToFill->mContext.edx = GET_REGISTER(state, edx);
+ mContextToFill->mContext.ecx = GET_REGISTER(state, ecx);
+ mContextToFill->mContext.eax = GET_REGISTER(state, eax);
+ mContextToFill->mContext.ebp = GET_REGISTER(state, ebp);
+ mContextToFill->mContext.eip = GET_REGISTER(state, eip);
+ mContextToFill->mContext.eflags = GET_REGISTER(state, eflags);
+ mContextToFill->mContext.esp = GET_REGISTER(state, esp);
+#elif defined(MOZ_THREADSTACKHELPER_X64)
+ mContextToFill->mContext.context_flags = MD_CONTEXT_AMD64_FULL;
+ mContextToFill->mContext.eflags = uint32_t(GET_REGISTER(state, rflags));
+ mContextToFill->mContext.rax = GET_REGISTER(state, rax);
+ mContextToFill->mContext.rcx = GET_REGISTER(state, rcx);
+ mContextToFill->mContext.rdx = GET_REGISTER(state, rdx);
+ mContextToFill->mContext.rbx = GET_REGISTER(state, rbx);
+ mContextToFill->mContext.rsp = GET_REGISTER(state, rsp);
+ mContextToFill->mContext.rbp = GET_REGISTER(state, rbp);
+ mContextToFill->mContext.rsi = GET_REGISTER(state, rsi);
+ mContextToFill->mContext.rdi = GET_REGISTER(state, rdi);
+ memcpy(&mContextToFill->mContext.r8,
+ &GET_REGISTER(state, r8), 8 * sizeof(int64_t));
+ mContextToFill->mContext.rip = GET_REGISTER(state, rip);
+#elif defined(MOZ_THREADSTACKHELPER_ARM)
+ mContextToFill->mContext.context_flags = MD_CONTEXT_ARM_FULL;
+ memcpy(mContextToFill->mContext.iregs,
+ GET_REGISTER(state, r), 17 * sizeof(int32_t));
+#else
+ #error "Unsupported architecture"
+#endif // architecture
+#undef GET_REGISTER
+
+#else
+ #error "Unsupported platform"
+#endif // platform
+
+ intptr_t sp = 0;
+#if defined(MOZ_THREADSTACKHELPER_X86)
+ sp = mContextToFill->mContext.esp;
+#elif defined(MOZ_THREADSTACKHELPER_X64)
+ sp = mContextToFill->mContext.rsp;
+#elif defined(MOZ_THREADSTACKHELPER_ARM)
+ sp = mContextToFill->mContext.iregs[13];
+#else
+ #error "Unsupported architecture"
+#endif // architecture
+ NS_ENSURE_TRUE_VOID(sp);
+ NS_ENSURE_TRUE_VOID(mThreadStackBase);
+
+ size_t stackSize = std::min(intptr_t(ThreadContext::kMaxStackSize),
+ std::abs(sp - mThreadStackBase));
+
+ if (mContextToFill->mStackEnd) {
+ // Limit the start of stack to a certain location if specified.
+ stackSize = std::min(intptr_t(stackSize),
+ std::abs(sp - intptr_t(mContextToFill->mStackEnd)));
+ }
+
+#ifndef MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN
+ // If if the stack grows upwards, and we need to recalculate our
+ // stack copy's base address. Subtract sizeof(void*) so that the
+ // location pointed to by sp is included.
+ sp -= stackSize - sizeof(void*);
+#endif
+
+#ifndef MOZ_ASAN
+ memcpy(mContextToFill->mStack.get(), reinterpret_cast<void*>(sp), stackSize);
+ // Valgrind doesn't care about the access outside the stack frame, but
+ // the presence of uninitialised values on the stack does cause it to
+ // later report a lot of false errors when Breakpad comes to unwind it.
+ // So mark the extracted data as defined.
+ MOZ_MAKE_MEM_DEFINED(mContextToFill->mStack.get(), stackSize);
+#else
+ // ASan will flag memcpy for access outside of stack frames,
+ // so roll our own memcpy here.
+ intptr_t* dst = reinterpret_cast<intptr_t*>(&mContextToFill->mStack[0]);
+ const intptr_t* src = reinterpret_cast<intptr_t*>(sp);
+ for (intptr_t len = stackSize; len > 0; len -= sizeof(*src)) {
+ *(dst++) = *(src++);
+ }
+#endif
+
+ mContextToFill->mStackBase = uintptr_t(sp);
+ mContextToFill->mStackSize = stackSize;
+ mContextToFill->mValid = true;
+#endif
+#endif // MOZ_THREADSTACKHELPER_NATIVE
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/ThreadStackHelper.h b/xpcom/threads/ThreadStackHelper.h
new file mode 100644
index 000000000..9c40ad5e2
--- /dev/null
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ThreadStackHelper_h
+#define mozilla_ThreadStackHelper_h
+
+#include "mozilla/ThreadHangStats.h"
+
+#include "GeckoProfiler.h"
+
+#include <stddef.h>
+
+#if defined(XP_LINUX)
+#include <signal.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#elif defined(XP_MACOSX)
+#include <mach/mach.h>
+#endif
+
+// Support pseudostack on these platforms.
+#if defined(XP_LINUX) || defined(XP_WIN) || defined(XP_MACOSX)
+# ifdef MOZ_ENABLE_PROFILER_SPS
+# define MOZ_THREADSTACKHELPER_PSEUDO
+# endif
+#endif
+
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+# define MOZ_THREADSTACKHELPER_NATIVE
+# if defined(__i386__) || defined(_M_IX86)
+# define MOZ_THREADSTACKHELPER_X86
+# elif defined(__x86_64__) || defined(_M_X64)
+# define MOZ_THREADSTACKHELPER_X64
+# elif defined(__arm__) || defined(_M_ARM)
+# define MOZ_THREADSTACKHELPER_ARM
+# else
+ // Unsupported architecture
+# undef MOZ_THREADSTACKHELPER_NATIVE
+# endif
+#endif
+
+namespace mozilla {
+
+/**
+ * ThreadStackHelper is used to retrieve the profiler pseudo-stack of a
+ * thread, as an alternative of using the profiler to take a profile.
+ * The target thread first declares an ThreadStackHelper instance;
+ * then another thread can call ThreadStackHelper::GetStack to retrieve
+ * the pseudo-stack of the target thread at that instant.
+ *
+ * Only non-copying labels are included in the stack, which means labels
+ * with custom text and markers are not included.
+ */
+class ThreadStackHelper
+{
+public:
+ typedef Telemetry::HangStack Stack;
+
+private:
+ Stack* mStackToFill;
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+ const PseudoStack* const mPseudoStack;
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ class ThreadContext;
+ // Set to non-null if GetStack should get the thread context.
+ ThreadContext* mContextToFill;
+ intptr_t mThreadStackBase;
+#endif
+ size_t mMaxStackSize;
+ size_t mMaxBufferSize;
+#endif
+
+ bool PrepareStackBuffer(Stack& aStack);
+ void FillStackBuffer();
+ void FillThreadContext(void* aContext = nullptr);
+#ifdef MOZ_THREADSTACKHELPER_PSEUDO
+ const char* AppendJSEntry(const volatile StackEntry* aEntry,
+ intptr_t& aAvailableBufferSize,
+ const char* aPrevLabel);
+#endif
+#ifdef MOZ_THREADSTACKHELPER_NATIVE
+ void GetThreadStackBase();
+#endif
+
+public:
+ /**
+ * Initialize ThreadStackHelper. Must be called from main thread.
+ */
+ static void Startup();
+ /**
+ * Uninitialize ThreadStackHelper. Must be called from main thread.
+ */
+ static void Shutdown();
+
+ /**
+ * Create a ThreadStackHelper instance targeting the current thread.
+ */
+ ThreadStackHelper();
+
+ ~ThreadStackHelper();
+
+ /**
+ * Retrieve the current pseudostack of the thread associated
+ * with this ThreadStackHelper.
+ *
+ * @param aStack Stack instance to be filled.
+ */
+ void GetStack(Stack& aStack);
+
+ /**
+ * Retrieve the current native stack of the thread associated
+ * with this ThreadStackHelper.
+ *
+ * @param aNativeStack Stack instance to be filled.
+ */
+ void GetNativeStack(Stack& aStack);
+
+#if defined(XP_LINUX)
+private:
+ static int sInitialized;
+ static int sFillStackSignum;
+
+ static void FillStackHandler(int aSignal, siginfo_t* aInfo, void* aContext);
+
+ sem_t mSem;
+ pid_t mThreadID;
+
+#elif defined(XP_WIN)
+private:
+ bool mInitialized;
+ HANDLE mThreadID;
+
+#elif defined(XP_MACOSX)
+private:
+ thread_act_t mThreadID;
+
+#endif
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ThreadStackHelper_h
diff --git a/xpcom/threads/ThrottledEventQueue.cpp b/xpcom/threads/ThrottledEventQueue.cpp
new file mode 100644
index 000000000..941566ef2
--- /dev/null
+++ b/xpcom/threads/ThrottledEventQueue.cpp
@@ -0,0 +1,446 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ThrottledEventQueue.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Unused.h"
+#include "nsEventQueue.h"
+
+namespace mozilla {
+
+using mozilla::services::GetObserverService;
+
+namespace {
+
+static const char kShutdownTopic[] = "xpcom-shutdown";
+
+} // anonymous namespace
+
+// The ThrottledEventQueue is designed with inner and outer objects:
+//
+// XPCOM code nsObserverService
+// | |
+// | |
+// v |
+// +-------+ |
+// | Outer | |
+// +-------+ |
+// | |
+// | +-------+ |
+// +-->| Inner |<--+
+// +-------+
+//
+// Client code references the outer nsIEventTarget which in turn references
+// an inner object. The inner object is also held alive by the observer
+// service.
+//
+// If the outer object is dereferenced and destroyed, it will trigger a
+// shutdown operation on the inner object. Similarly if the observer
+// service notifies that the browser is shutting down, then the inner
+// object also starts shutting down.
+//
+// Once the queue has drained we unregister from the observer service. If
+// the outer object is already gone, then the inner object is free'd at this
+// point. If the outer object still exists then calls fall back to the
+// ThrottledEventQueue's base target. We just don't queue things
+// any more. The inner is then released once the outer object is released.
+//
+// Note, we must keep the inner object alive and attached to the observer
+// service until the TaskQueue is fully shutdown and idle. We must delay
+// xpcom shutdown if the TaskQueue is in the middle of draining.
+class ThrottledEventQueue::Inner final : public nsIObserver
+{
+ // The runnable which is dispatched to the underlying base target. Since
+ // we only execute one event at a time we just re-use a single instance
+ // of this class while there are events left in the queue.
+ class Executor final : public Runnable
+ {
+ RefPtr<Inner> mInner;
+
+ public:
+ explicit Executor(Inner* aInner)
+ : mInner(aInner)
+ { }
+
+ NS_IMETHODIMP
+ Run()
+ {
+ mInner->ExecuteRunnable();
+ return NS_OK;
+ }
+ };
+
+ mutable Mutex mMutex;
+ mutable CondVar mIdleCondVar;
+
+ mozilla::CondVar mEventsAvailable;
+
+ // any thread, protected by mutex
+ nsEventQueue mEventQueue;
+
+ // written on main thread, read on any thread
+ nsCOMPtr<nsIEventTarget> mBaseTarget;
+
+ // any thread, protected by mutex
+ nsCOMPtr<nsIRunnable> mExecutor;
+
+ // any thread, atomic
+ Atomic<uint32_t> mExecutionDepth;
+
+ // any thread, protected by mutex
+ bool mShutdownStarted;
+
+ explicit Inner(nsIEventTarget* aBaseTarget)
+ : mMutex("ThrottledEventQueue")
+ , mIdleCondVar(mMutex, "ThrottledEventQueue:Idle")
+ , mEventsAvailable(mMutex, "[ThrottledEventQueue::Inner.mEventsAvailable]")
+ , mEventQueue(mEventsAvailable, nsEventQueue::eNormalQueue)
+ , mBaseTarget(aBaseTarget)
+ , mExecutionDepth(0)
+ , mShutdownStarted(false)
+ {
+ }
+
+ ~Inner()
+ {
+ MOZ_ASSERT(!mExecutor);
+ MOZ_ASSERT(mShutdownStarted);
+ }
+
+ void
+ ExecuteRunnable()
+ {
+ // Any thread
+ nsCOMPtr<nsIRunnable> event;
+ bool shouldShutdown = false;
+
+#ifdef DEBUG
+ bool currentThread = false;
+ mBaseTarget->IsOnCurrentThread(&currentThread);
+ MOZ_ASSERT(currentThread);
+#endif
+
+ {
+ MutexAutoLock lock(mMutex);
+
+ // We only dispatch an executor runnable when we know there is something
+ // in the queue, so this should never fail.
+ MOZ_ALWAYS_TRUE(mEventQueue.GetPendingEvent(getter_AddRefs(event), lock));
+
+ // If there are more events in the queue, then dispatch the next
+ // executor. We do this now, before running the event, because
+ // the event might spin the event loop and we don't want to stall
+ // the queue.
+ if (mEventQueue.HasPendingEvent(lock)) {
+ // Dispatch the next base target runnable to attempt to execute
+ // the next throttled event. We must do this before executing
+ // the event in case the event spins the event loop.
+ MOZ_ALWAYS_SUCCEEDS(
+ mBaseTarget->Dispatch(mExecutor, NS_DISPATCH_NORMAL));
+ }
+
+ // Otherwise the queue is empty and we can stop dispatching the
+ // executor. We might also need to shutdown after running the
+ // last event.
+ else {
+ shouldShutdown = mShutdownStarted;
+ // Note, this breaks a ref cycle.
+ mExecutor = nullptr;
+ mIdleCondVar.NotifyAll();
+ }
+ }
+
+ // Execute the event now that we have unlocked.
+ ++mExecutionDepth;
+ Unused << event->Run();
+ --mExecutionDepth;
+
+ // If shutdown was started and the queue is now empty we can now
+ // finalize the shutdown. This is performed separately at the end
+ // of the method in order to wait for the event to finish running.
+ if (shouldShutdown) {
+ MOZ_ASSERT(IsEmpty());
+ NS_DispatchToMainThread(NewRunnableMethod(this, &Inner::ShutdownComplete));
+ }
+ }
+
+ void
+ ShutdownComplete()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsEmpty());
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ obs->RemoveObserver(this, kShutdownTopic);
+ }
+
+public:
+ static already_AddRefed<Inner>
+ Create(nsIEventTarget* aBaseTarget)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (ClearOnShutdown_Internal::sCurrentShutdownPhase != ShutdownPhase::NotInShutdown) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (NS_WARN_IF(!obs)) {
+ return nullptr;
+ }
+
+ RefPtr<Inner> ref = new Inner(aBaseTarget);
+
+ nsresult rv = obs->AddObserver(ref, kShutdownTopic,
+ false /* means OS will hold a strong ref */);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ ref->MaybeStartShutdown();
+ MOZ_ASSERT(ref->IsEmpty());
+ return nullptr;
+ }
+
+ return ref.forget();
+ }
+
+ NS_IMETHOD
+ Observe(nsISupports*, const char* aTopic, const char16_t*) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!strcmp(aTopic, kShutdownTopic));
+
+ MaybeStartShutdown();
+
+ // Once shutdown begins we set the Atomic<bool> mShutdownStarted flag.
+ // This prevents any new runnables from being dispatched into the
+ // TaskQueue. Therefore this loop should be finite.
+ while (!IsEmpty()) {
+ MOZ_ALWAYS_TRUE(NS_ProcessNextEvent());
+ }
+
+ return NS_OK;
+ }
+
+ void
+ MaybeStartShutdown()
+ {
+ // Any thread
+ MutexAutoLock lock(mMutex);
+
+ if (mShutdownStarted) {
+ return;
+ }
+ mShutdownStarted = true;
+
+ // We are marked for shutdown now, but we are still processing runnables.
+ // Return for now. The shutdown will be completed once the queue is
+ // drained.
+ if (mExecutor) {
+ return;
+ }
+
+ // The queue is empty, so we can complete immediately.
+ NS_DispatchToMainThread(NewRunnableMethod(this, &Inner::ShutdownComplete));
+ }
+
+ bool
+ IsEmpty() const
+ {
+ // Any thread
+ return Length() == 0;
+ }
+
+ uint32_t
+ Length() const
+ {
+ // Any thread
+ MutexAutoLock lock(mMutex);
+ return mEventQueue.Count(lock);
+ }
+
+ void
+ AwaitIdle() const
+ {
+ // Any thread, except the main thread or our base target. Blocking the
+ // main thread is forbidden. Blocking the base target is guaranteed to
+ // produce a deadlock.
+ MOZ_ASSERT(!NS_IsMainThread());
+#ifdef DEBUG
+ bool onBaseTarget = false;
+ Unused << mBaseTarget->IsOnCurrentThread(&onBaseTarget);
+ MOZ_ASSERT(!onBaseTarget);
+#endif
+
+ MutexAutoLock lock(mMutex);
+ while (mExecutor) {
+ mIdleCondVar.Wait();
+ }
+ }
+
+ nsresult
+ DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+ {
+ // Any thread
+ nsCOMPtr<nsIRunnable> r = aEvent;
+ return Dispatch(r.forget(), aFlags);
+ }
+
+ nsresult
+ Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
+ {
+ MOZ_ASSERT(aFlags == NS_DISPATCH_NORMAL ||
+ aFlags == NS_DISPATCH_AT_END);
+
+ // Any thread
+ MutexAutoLock lock(mMutex);
+
+ // If we are shutting down, just fall back to our base target
+ // directly.
+ if (mShutdownStarted) {
+ return mBaseTarget->Dispatch(Move(aEvent), aFlags);
+ }
+
+ // We are not currently processing events, so we must start
+ // operating on our base target. This is fallible, so do
+ // it first. Our lock will prevent the executor from accessing
+ // the event queue before we add the event below.
+ if (!mExecutor) {
+ // Note, this creates a ref cycle keeping the inner alive
+ // until the queue is drained.
+ mExecutor = new Executor(this);
+ nsresult rv = mBaseTarget->Dispatch(mExecutor, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ mExecutor = nullptr;
+ return rv;
+ }
+ }
+
+ // Only add the event to the underlying queue if are able to
+ // dispatch to our base target.
+ mEventQueue.PutEvent(Move(aEvent), lock);
+ return NS_OK;
+ }
+
+ nsresult
+ DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelay)
+ {
+ // The base target may implement this, but we don't. Always fail
+ // to provide consistent behavior.
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsresult
+ IsOnCurrentThread(bool* aResult)
+ {
+ // Any thread
+
+ bool shutdownAndIdle = false;
+ {
+ MutexAutoLock lock(mMutex);
+ shutdownAndIdle = mShutdownStarted && mEventQueue.Count(lock) == 0;
+ }
+
+ bool onBaseTarget = false;
+ nsresult rv = mBaseTarget->IsOnCurrentThread(&onBaseTarget);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We consider the current stack on this event target if are on
+ // the base target and one of the following is true
+ // 1) We are currently running an event OR
+ // 2) We are both shutting down and the queue is idle
+ *aResult = onBaseTarget && (mExecutionDepth || shutdownAndIdle);
+
+ return NS_OK;
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(ThrottledEventQueue::Inner, nsIObserver);
+
+NS_IMPL_ISUPPORTS(ThrottledEventQueue, nsIEventTarget);
+
+ThrottledEventQueue::ThrottledEventQueue(already_AddRefed<Inner> aInner)
+ : mInner(aInner)
+{
+ MOZ_ASSERT(mInner);
+}
+
+ThrottledEventQueue::~ThrottledEventQueue()
+{
+ mInner->MaybeStartShutdown();
+}
+
+void
+ThrottledEventQueue::MaybeStartShutdown()
+{
+ return mInner->MaybeStartShutdown();
+}
+
+already_AddRefed<ThrottledEventQueue>
+ThrottledEventQueue::Create(nsIEventTarget* aBaseTarget)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBaseTarget);
+
+ RefPtr<Inner> inner = Inner::Create(aBaseTarget);
+ if (NS_WARN_IF(!inner)) {
+ return nullptr;
+ }
+
+ RefPtr<ThrottledEventQueue> ref =
+ new ThrottledEventQueue(inner.forget());
+ return ref.forget();
+}
+
+bool
+ThrottledEventQueue::IsEmpty() const
+{
+ return mInner->IsEmpty();
+}
+
+uint32_t
+ThrottledEventQueue::Length() const
+{
+ return mInner->Length();
+}
+
+void
+ThrottledEventQueue::AwaitIdle() const
+{
+ return mInner->AwaitIdle();
+}
+
+NS_IMETHODIMP
+ThrottledEventQueue::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+ return mInner->DispatchFromScript(aEvent, aFlags);
+}
+
+NS_IMETHODIMP
+ThrottledEventQueue::Dispatch(already_AddRefed<nsIRunnable> aEvent,
+ uint32_t aFlags)
+{
+ return mInner->Dispatch(Move(aEvent), aFlags);
+}
+
+NS_IMETHODIMP
+ThrottledEventQueue::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
+ uint32_t aFlags)
+{
+ return mInner->DelayedDispatch(Move(aEvent), aFlags);
+}
+
+NS_IMETHODIMP
+ThrottledEventQueue::IsOnCurrentThread(bool* aResult)
+{
+ return mInner->IsOnCurrentThread(aResult);
+}
+
+} // namespace mozilla
diff --git a/xpcom/threads/ThrottledEventQueue.h b/xpcom/threads/ThrottledEventQueue.h
new file mode 100644
index 000000000..e0762bcce
--- /dev/null
+++ b/xpcom/threads/ThrottledEventQueue.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// nsIEventTarget wrapper for throttling event dispatch.
+
+#ifndef mozilla_ThrottledEventQueue_h
+#define mozilla_ThrottledEventQueue_h
+
+#include "nsIEventTarget.h"
+
+namespace mozilla {
+
+// A ThrottledEventQueue is an event target that can be used to throttle
+// events being dispatched to another base target. It maintains its
+// own queue of events and only dispatches one at a time to the wrapped
+// target. This can be used to avoid flooding the base target.
+//
+// Flooding is avoided via a very simply principal. Runnables dispatched
+// to the ThrottledEventQueue are only dispatched to the base target
+// one at a time. Only once that runnable has executed will we dispatch
+// the next runnable to the base target. This in effect makes all
+// runnables passing through the ThrottledEventQueue yield to other work
+// on the base target.
+//
+// ThrottledEventQueue keeps runnables waiting to be dispatched to the
+// base in its own internal queue. Code can query the length of this
+// queue using IsEmpty() and Length(). Further, code implement back
+// pressure by checking the depth of the queue and deciding to stop
+// issuing runnables if they see the ThrottledEventQueue is backed up.
+// Code running on other threads could even use AwaitIdle() to block
+// all operation until the ThrottledEventQueue drains.
+//
+// Note, this class is similar to TaskQueue, but also differs in a few
+// ways. First, it is a very simple nsIEventTarget implementation. It
+// does not use the AbstractThread API.
+//
+// In addition, ThrottledEventQueue currently dispatches its next
+// runnable to the base target *before* running the current event. This
+// allows the event code to spin the event loop without stalling the
+// ThrottledEventQueue. In contrast, TaskQueue only dispatches its next
+// runnable after running the current event. That approach is necessary
+// for TaskQueue in order to work with thread pool targets.
+//
+// So, if you are targeting a thread pool you probably want a TaskQueue.
+// If you are targeting a single thread or other non-concurrent event
+// target, you probably want a ThrottledEventQueue.
+//
+// ThrottledEventQueue also implements an automatic shutdown mechanism.
+// De-referencing the queue or browser shutdown will automatically begin
+// shutdown.
+//
+// Once shutdown begins all events will bypass the queue and be dispatched
+// straight to the underlying base target.
+class ThrottledEventQueue final : public nsIEventTarget
+{
+ class Inner;
+ RefPtr<Inner> mInner;
+
+ explicit ThrottledEventQueue(already_AddRefed<Inner> aInner);
+ ~ThrottledEventQueue();
+
+ // Begin shutdown of the event queue. This has no effect if shutdown
+ // is already in process. After this is called nsIEventTarget methods
+ // will bypass the queue and operate directly on the base target.
+ // Note, this could be made public if code needs to explicitly shutdown
+ // for some reason.
+ void MaybeStartShutdown();
+
+public:
+ // Attempt to create a ThrottledEventQueue for the given target. This
+ // may return nullptr if the browser is already shutting down.
+ static already_AddRefed<ThrottledEventQueue>
+ Create(nsIEventTarget* aBaseTarget);
+
+ // Determine if there are any events pending in the queue.
+ bool IsEmpty() const;
+
+ // Determine how many events are pending in the queue.
+ uint32_t Length() const;
+
+ // Block the current thread until the queue is empty. This may not
+ // be called on the main thread or the base target.
+ void AwaitIdle() const;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEVENTTARGET
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ThrottledEventQueue_h
diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp
new file mode 100644
index 000000000..0127e2dd1
--- /dev/null
+++ b/xpcom/threads/TimerThread.cpp
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTimerImpl.h"
+#include "TimerThread.h"
+
+#include "nsThreadUtils.h"
+#include "plarena.h"
+#include "pratom.h"
+
+#include "nsIObserverService.h"
+#include "nsIServiceManager.h"
+#include "mozilla/Services.h"
+#include "mozilla/ChaosMode.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/BinarySearch.h"
+
+#include <math.h>
+
+using namespace mozilla;
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracerImpl.h"
+using namespace mozilla::tasktracer;
+#endif
+
+NS_IMPL_ISUPPORTS(TimerThread, nsIRunnable, nsIObserver)
+
+TimerThread::TimerThread() :
+ mInitInProgress(false),
+ mInitialized(false),
+ mMonitor("TimerThread.mMonitor"),
+ mShutdown(false),
+ mWaiting(false),
+ mNotified(false),
+ mSleeping(false)
+{
+}
+
+TimerThread::~TimerThread()
+{
+ mThread = nullptr;
+
+ NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
+}
+
+nsresult
+TimerThread::InitLocks()
+{
+ return NS_OK;
+}
+
+namespace {
+
+class TimerObserverRunnable : public Runnable
+{
+public:
+ explicit TimerObserverRunnable(nsIObserver* aObserver)
+ : mObserver(aObserver)
+ {
+ }
+
+ NS_DECL_NSIRUNNABLE
+
+private:
+ nsCOMPtr<nsIObserver> mObserver;
+};
+
+NS_IMETHODIMP
+TimerObserverRunnable::Run()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->AddObserver(mObserver, "sleep_notification", false);
+ observerService->AddObserver(mObserver, "wake_notification", false);
+ observerService->AddObserver(mObserver, "suspend_process_notification", false);
+ observerService->AddObserver(mObserver, "resume_process_notification", false);
+ }
+ return NS_OK;
+}
+
+} // namespace
+
+namespace {
+
+// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
+// It's needed to avoid contention over the default allocator lock when
+// firing timer events (see bug 733277). The thread-safety is required because
+// nsTimerEvent objects are allocated on the timer thread, and freed on another
+// thread. Because TimerEventAllocator has its own lock, contention over that
+// lock is limited to the allocation and deallocation of nsTimerEvent objects.
+//
+// Because this allocator is layered over PLArenaPool, it never shrinks -- even
+// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
+// for later recycling. So the amount of memory consumed will always be equal
+// to the high-water mark consumption. But nsTimerEvents are small and it's
+// unusual to have more than a few hundred of them, so this shouldn't be a
+// problem in practice.
+
+class TimerEventAllocator
+{
+private:
+ struct FreeEntry
+ {
+ FreeEntry* mNext;
+ };
+
+ PLArenaPool mPool;
+ FreeEntry* mFirstFree;
+ mozilla::Monitor mMonitor;
+
+public:
+ TimerEventAllocator()
+ : mFirstFree(nullptr)
+ , mMonitor("TimerEventAllocator")
+ {
+ PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
+ }
+
+ ~TimerEventAllocator()
+ {
+ PL_FinishArenaPool(&mPool);
+ }
+
+ void* Alloc(size_t aSize);
+ void Free(void* aPtr);
+};
+
+} // namespace
+
+// This is a nsICancelableRunnable because we can dispatch it to Workers and
+// those can be shut down at any time, and in these cases, Cancel() is called
+// instead of Run().
+class nsTimerEvent final : public CancelableRunnable
+{
+public:
+ NS_IMETHOD Run() override;
+
+ nsresult Cancel() override
+ {
+ // Since nsTimerImpl is not thread-safe, we should release |mTimer|
+ // here in the target thread to avoid race condition. Otherwise,
+ // ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
+ // timer thread and result in race condition.
+ mTimer = nullptr;
+ return NS_OK;
+ }
+
+ nsTimerEvent()
+ : mTimer()
+ , mGeneration(0)
+ {
+ // Note: We override operator new for this class, and the override is
+ // fallible!
+ sAllocatorUsers++;
+ }
+
+ TimeStamp mInitTime;
+
+ static void Init();
+ static void Shutdown();
+ static void DeleteAllocatorIfNeeded();
+
+ static void* operator new(size_t aSize) CPP_THROW_NEW
+ {
+ return sAllocator->Alloc(aSize);
+ }
+ void operator delete(void* aPtr)
+ {
+ sAllocator->Free(aPtr);
+ DeleteAllocatorIfNeeded();
+ }
+
+ already_AddRefed<nsTimerImpl> ForgetTimer()
+ {
+ return mTimer.forget();
+ }
+
+ void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
+ {
+ mTimer = aTimer;
+ mGeneration = mTimer->GetGeneration();
+ }
+
+private:
+ nsTimerEvent(const nsTimerEvent&) = delete;
+ nsTimerEvent& operator=(const nsTimerEvent&) = delete;
+ nsTimerEvent& operator=(const nsTimerEvent&&) = delete;
+
+ ~nsTimerEvent()
+ {
+ MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
+ "This will result in us attempting to deallocate the nsTimerEvent allocator twice");
+ sAllocatorUsers--;
+ }
+
+ RefPtr<nsTimerImpl> mTimer;
+ int32_t mGeneration;
+
+ static TimerEventAllocator* sAllocator;
+ static Atomic<int32_t> sAllocatorUsers;
+ static bool sCanDeleteAllocator;
+};
+
+TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
+Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
+bool nsTimerEvent::sCanDeleteAllocator = false;
+
+namespace {
+
+void*
+TimerEventAllocator::Alloc(size_t aSize)
+{
+ MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
+
+ mozilla::MonitorAutoLock lock(mMonitor);
+
+ void* p;
+ if (mFirstFree) {
+ p = mFirstFree;
+ mFirstFree = mFirstFree->mNext;
+ } else {
+ PL_ARENA_ALLOCATE(p, &mPool, aSize);
+ if (!p) {
+ return nullptr;
+ }
+ }
+
+ return p;
+}
+
+void
+TimerEventAllocator::Free(void* aPtr)
+{
+ mozilla::MonitorAutoLock lock(mMonitor);
+
+ FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
+
+ entry->mNext = mFirstFree;
+ mFirstFree = entry;
+}
+
+} // namespace
+
+void
+nsTimerEvent::Init()
+{
+ sAllocator = new TimerEventAllocator();
+}
+
+void
+nsTimerEvent::Shutdown()
+{
+ sCanDeleteAllocator = true;
+ DeleteAllocatorIfNeeded();
+}
+
+void
+nsTimerEvent::DeleteAllocatorIfNeeded()
+{
+ if (sCanDeleteAllocator && sAllocatorUsers == 0) {
+ delete sAllocator;
+ sAllocator = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+nsTimerEvent::Run()
+{
+ if (!mTimer) {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+
+ if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
+ TimeStamp now = TimeStamp::Now();
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
+ this, (now - mInitTime).ToMilliseconds()));
+ }
+
+ mTimer->Fire(mGeneration);
+
+ // We call Cancel() to correctly release mTimer.
+ // Read more in the Cancel() implementation.
+ return Cancel();
+}
+
+nsresult
+TimerThread::Init()
+{
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("TimerThread::Init [%d]\n", mInitialized));
+
+ if (mInitialized) {
+ if (!mThread) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ nsTimerEvent::Init();
+
+ if (mInitInProgress.exchange(true) == false) {
+ // We hold on to mThread to keep the thread alive.
+ nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
+ if (NS_FAILED(rv)) {
+ mThread = nullptr;
+ } else {
+ RefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
+ if (NS_IsMainThread()) {
+ r->Run();
+ } else {
+ NS_DispatchToMainThread(r);
+ }
+ }
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ mInitialized = true;
+ mMonitor.NotifyAll();
+ }
+ } else {
+ MonitorAutoLock lock(mMonitor);
+ while (!mInitialized) {
+ mMonitor.Wait();
+ }
+ }
+
+ if (!mThread) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+TimerThread::Shutdown()
+{
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown begin\n"));
+
+ if (!mThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsTArray<nsTimerImpl*> timers;
+ {
+ // lock scope
+ MonitorAutoLock lock(mMonitor);
+
+ mShutdown = true;
+
+ // notify the cond var so that Run() can return
+ if (mWaiting) {
+ mNotified = true;
+ mMonitor.Notify();
+ }
+
+ // Need to copy content of mTimers array to a local array
+ // because call to timers' Cancel() (and release its self)
+ // must not be done under the lock. Destructor of a callback
+ // might potentially call some code reentering the same lock
+ // that leads to unexpected behavior or deadlock.
+ // See bug 422472.
+ timers.AppendElements(mTimers);
+ mTimers.Clear();
+ }
+
+ uint32_t timersCount = timers.Length();
+ for (uint32_t i = 0; i < timersCount; i++) {
+ nsTimerImpl* timer = timers[i];
+ timer->Cancel();
+ ReleaseTimerInternal(timer);
+ }
+
+ mThread->Shutdown(); // wait for the thread to die
+
+ nsTimerEvent::Shutdown();
+
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown end\n"));
+ return NS_OK;
+}
+
+namespace {
+
+struct MicrosecondsToInterval
+{
+ PRIntervalTime operator[](size_t aMs) const {
+ return PR_MicrosecondsToInterval(aMs);
+ }
+};
+
+struct IntervalComparator
+{
+ int operator()(PRIntervalTime aInterval) const {
+ return (0 < aInterval) ? -1 : 1;
+ }
+};
+
+} // namespace
+
+NS_IMETHODIMP
+TimerThread::Run()
+{
+ PR_SetCurrentThreadName("Timer");
+
+ MonitorAutoLock lock(mMonitor);
+
+ // We need to know how many microseconds give a positive PRIntervalTime. This
+ // is platform-dependent and we calculate it at runtime, finding a value |v|
+ // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
+ // the range [0, v) to find the ms-to-interval scale.
+ uint32_t usForPosInterval = 1;
+ while (PR_MicrosecondsToInterval(usForPosInterval) == 0) {
+ usForPosInterval <<= 1;
+ }
+
+ size_t usIntervalResolution;
+ BinarySearchIf(MicrosecondsToInterval(), 0, usForPosInterval, IntervalComparator(), &usIntervalResolution);
+ MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution - 1) == 0);
+ MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution) == 1);
+
+ // Half of the amount of microseconds needed to get positive PRIntervalTime.
+ // We use this to decide how to round our wait times later
+ int32_t halfMicrosecondsIntervalResolution = usIntervalResolution / 2;
+ bool forceRunNextTimer = false;
+
+ while (!mShutdown) {
+ // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
+ PRIntervalTime waitFor;
+ bool forceRunThisTimer = forceRunNextTimer;
+ forceRunNextTimer = false;
+
+ if (mSleeping) {
+ // Sleep for 0.1 seconds while not firing timers.
+ uint32_t milliseconds = 100;
+ if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
+ milliseconds = ChaosMode::randomUint32LessThan(200);
+ }
+ waitFor = PR_MillisecondsToInterval(milliseconds);
+ } else {
+ waitFor = PR_INTERVAL_NO_TIMEOUT;
+ TimeStamp now = TimeStamp::Now();
+ nsTimerImpl* timer = nullptr;
+
+ if (!mTimers.IsEmpty()) {
+ timer = mTimers[0];
+
+ if (now >= timer->mTimeout || forceRunThisTimer) {
+ next:
+ // NB: AddRef before the Release under RemoveTimerInternal to avoid
+ // mRefCnt passing through zero, in case all other refs than the one
+ // from mTimers have gone away (the last non-mTimers[i]-ref's Release
+ // must be racing with us, blocked in gThread->RemoveTimer waiting
+ // for TimerThread::mMonitor, under nsTimerImpl::Release.
+
+ RefPtr<nsTimerImpl> timerRef(timer);
+ RemoveTimerInternal(timer);
+ timer = nullptr;
+
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("Timer thread woke up %fms from when it was supposed to\n",
+ fabs((now - timerRef->mTimeout).ToMilliseconds())));
+
+ // We are going to let the call to PostTimerEvent here handle the
+ // release of the timer so that we don't end up releasing the timer
+ // on the TimerThread instead of on the thread it targets.
+ timerRef = PostTimerEvent(timerRef.forget());
+
+ if (timerRef) {
+ // We got our reference back due to an error.
+ // Unhook the nsRefPtr, and release manually so we can get the
+ // refcount.
+ nsrefcnt rc = timerRef.forget().take()->Release();
+ (void)rc;
+
+ // The nsITimer interface requires that its users keep a reference
+ // to the timers they use while those timers are initialized but
+ // have not yet fired. If this ever happens, it is a bug in the
+ // code that created and used the timer.
+ //
+ // Further, note that this should never happen even with a
+ // misbehaving user, because nsTimerImpl::Release checks for a
+ // refcount of 1 with an armed timer (a timer whose only reference
+ // is from the timer thread) and when it hits this will remove the
+ // timer from the timer thread and thus destroy the last reference,
+ // preventing this situation from occurring.
+ MOZ_ASSERT(rc != 0, "destroyed timer off its target thread!");
+ }
+
+ if (mShutdown) {
+ break;
+ }
+
+ // Update now, as PostTimerEvent plus the locking may have taken a
+ // tick or two, and we may goto next below.
+ now = TimeStamp::Now();
+ }
+ }
+
+ if (!mTimers.IsEmpty()) {
+ timer = mTimers[0];
+
+ TimeStamp timeout = timer->mTimeout;
+
+ // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
+ // is due now or overdue.
+ //
+ // Note that we can only sleep for integer values of a certain
+ // resolution. We use halfMicrosecondsIntervalResolution, calculated
+ // before, to do the optimal rounding (i.e., of how to decide what
+ // interval is so small we should not wait at all).
+ double microseconds = (timeout - now).ToMilliseconds() * 1000;
+
+ if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
+ // The mean value of sFractions must be 1 to ensure that
+ // the average of a long sequence of timeouts converges to the
+ // actual sum of their times.
+ static const float sFractions[] = {
+ 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
+ };
+ microseconds *=
+ sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
+ forceRunNextTimer = true;
+ }
+
+ if (microseconds < halfMicrosecondsIntervalResolution) {
+ forceRunNextTimer = false;
+ goto next; // round down; execute event now
+ }
+ waitFor = PR_MicrosecondsToInterval(
+ static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
+ if (waitFor == 0) {
+ waitFor = 1; // round up, wait the minimum time we can wait
+ }
+ }
+
+ if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
+ if (waitFor == PR_INTERVAL_NO_TIMEOUT)
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
+ else
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
+ }
+ }
+
+ mWaiting = true;
+ mNotified = false;
+ mMonitor.Wait(waitFor);
+ if (mNotified) {
+ forceRunNextTimer = false;
+ }
+ mWaiting = false;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+TimerThread::AddTimer(nsTimerImpl* aTimer)
+{
+ MonitorAutoLock lock(mMonitor);
+
+ if (!aTimer->mEventTarget) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // Add the timer to our list.
+ int32_t i = AddTimerInternal(aTimer);
+ if (i < 0) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Awaken the timer thread.
+ if (mWaiting && i == 0) {
+ mNotified = true;
+ mMonitor.Notify();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+TimerThread::RemoveTimer(nsTimerImpl* aTimer)
+{
+ MonitorAutoLock lock(mMonitor);
+
+ // Remove the timer from our array. Tell callers that aTimer was not found
+ // by returning NS_ERROR_NOT_AVAILABLE.
+
+ if (!RemoveTimerInternal(aTimer)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Awaken the timer thread.
+ if (mWaiting) {
+ mNotified = true;
+ mMonitor.Notify();
+ }
+
+ return NS_OK;
+}
+
+// This function must be called from within a lock
+int32_t
+TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
+{
+ mMonitor.AssertCurrentThreadOwns();
+ if (mShutdown) {
+ return -1;
+ }
+
+ TimeStamp now = TimeStamp::Now();
+
+ TimerAdditionComparator c(now, aTimer);
+ nsTimerImpl** insertSlot = mTimers.InsertElementSorted(aTimer, c);
+
+ if (!insertSlot) {
+ return -1;
+ }
+
+ NS_ADDREF(aTimer);
+
+#ifdef MOZ_TASK_TRACER
+ // Caller of AddTimer is the parent task of its timer event, so we store the
+ // TraceInfo here for later used.
+ aTimer->GetTLSTraceInfo();
+#endif
+
+ return insertSlot - mTimers.Elements();
+}
+
+bool
+TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
+{
+ mMonitor.AssertCurrentThreadOwns();
+ if (!mTimers.RemoveElement(aTimer)) {
+ return false;
+ }
+
+ ReleaseTimerInternal(aTimer);
+ return true;
+}
+
+void
+TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
+{
+ if (!mShutdown) {
+ // copied to a local array before releasing in shutdown
+ mMonitor.AssertCurrentThreadOwns();
+ }
+ NS_RELEASE(aTimer);
+}
+
+already_AddRefed<nsTimerImpl>
+TimerThread::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
+{
+ mMonitor.AssertCurrentThreadOwns();
+
+ RefPtr<nsTimerImpl> timer(aTimerRef);
+ if (!timer->mEventTarget) {
+ NS_ERROR("Attempt to post timer event to NULL event target");
+ return timer.forget();
+ }
+
+ // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
+
+ // Since we already addref'd 'timer', we don't need to addref here.
+ // We will release either in ~nsTimerEvent(), or pass the reference back to
+ // the caller. We need to copy the generation number from this timer into the
+ // event, so we can avoid firing a timer that was re-initialized after being
+ // canceled.
+
+ RefPtr<nsTimerEvent> event = new nsTimerEvent;
+ if (!event) {
+ return timer.forget();
+ }
+
+ if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
+ event->mInitTime = TimeStamp::Now();
+ }
+
+#ifdef MOZ_TASK_TRACER
+ // During the dispatch of TimerEvent, we overwrite the current TraceInfo
+ // partially with the info saved in timer earlier, and restore it back by
+ // AutoSaveCurTraceInfo.
+ AutoSaveCurTraceInfo saveCurTraceInfo;
+ (timer->GetTracedTask()).SetTLSTraceInfo();
+#endif
+
+ nsCOMPtr<nsIEventTarget> target = timer->mEventTarget;
+ event->SetTimer(timer.forget());
+
+ nsresult rv;
+ {
+ // We release mMonitor around the Dispatch because if this timer is targeted
+ // at the TimerThread we'll deadlock.
+ MonitorAutoUnlock unlock(mMonitor);
+ rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
+ }
+
+ if (NS_FAILED(rv)) {
+ timer = event->ForgetTimer();
+ RemoveTimerInternal(timer);
+ return timer.forget();
+ }
+
+ return nullptr;
+}
+
+void
+TimerThread::DoBeforeSleep()
+{
+ // Mainthread
+ MonitorAutoLock lock(mMonitor);
+ mSleeping = true;
+}
+
+// Note: wake may be notified without preceding sleep notification
+void
+TimerThread::DoAfterSleep()
+{
+ // Mainthread
+ MonitorAutoLock lock(mMonitor);
+ mSleeping = false;
+
+ // Wake up the timer thread to re-process the array to ensure the sleep delay is correct,
+ // and fire any expired timers (perhaps quite a few)
+ mNotified = true;
+ mMonitor.Notify();
+}
+
+
+NS_IMETHODIMP
+TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
+ const char16_t* /* aData */)
+{
+ if (strcmp(aTopic, "sleep_notification") == 0 ||
+ strcmp(aTopic, "suspend_process_notification") == 0) {
+ DoBeforeSleep();
+ } else if (strcmp(aTopic, "wake_notification") == 0 ||
+ strcmp(aTopic, "resume_process_notification") == 0) {
+ DoAfterSleep();
+ }
+
+ return NS_OK;
+}
diff --git a/xpcom/threads/TimerThread.h b/xpcom/threads/TimerThread.h
new file mode 100644
index 000000000..a7204810a
--- /dev/null
+++ b/xpcom/threads/TimerThread.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TimerThread_h___
+#define TimerThread_h___
+
+#include "nsIObserver.h"
+#include "nsIRunnable.h"
+#include "nsIThread.h"
+
+#include "nsTimerImpl.h"
+#include "nsThreadUtils.h"
+
+#include "nsTArray.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Monitor.h"
+
+namespace mozilla {
+class TimeStamp;
+} // namespace mozilla
+
+class TimerThread final
+ : public nsIRunnable
+ , public nsIObserver
+{
+public:
+ typedef mozilla::Monitor Monitor;
+ typedef mozilla::TimeStamp TimeStamp;
+ typedef mozilla::TimeDuration TimeDuration;
+
+ TimerThread();
+ nsresult InitLocks();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+ NS_DECL_NSIOBSERVER
+
+ nsresult Init();
+ nsresult Shutdown();
+
+ nsresult AddTimer(nsTimerImpl* aTimer);
+ nsresult RemoveTimer(nsTimerImpl* aTimer);
+
+ void DoBeforeSleep();
+ void DoAfterSleep();
+
+ bool IsOnTimerThread() const
+ {
+ return mThread == NS_GetCurrentThread();
+ }
+
+private:
+ ~TimerThread();
+
+ mozilla::Atomic<bool> mInitInProgress;
+ bool mInitialized;
+
+ // These two internal helper methods must be called while mMonitor is held.
+ // AddTimerInternal returns the position where the timer was added in the
+ // list, or -1 if it failed.
+ int32_t AddTimerInternal(nsTimerImpl* aTimer);
+ bool RemoveTimerInternal(nsTimerImpl* aTimer);
+ void ReleaseTimerInternal(nsTimerImpl* aTimer);
+
+ already_AddRefed<nsTimerImpl> PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef);
+
+ nsCOMPtr<nsIThread> mThread;
+ Monitor mMonitor;
+
+ bool mShutdown;
+ bool mWaiting;
+ bool mNotified;
+ bool mSleeping;
+
+ nsTArray<nsTimerImpl*> mTimers;
+};
+
+struct TimerAdditionComparator
+{
+ TimerAdditionComparator(const mozilla::TimeStamp& aNow,
+ nsTimerImpl* aTimerToInsert) :
+ now(aNow)
+#ifdef DEBUG
+ , timerToInsert(aTimerToInsert)
+#endif
+ {
+ }
+
+ bool LessThan(nsTimerImpl* aFromArray, nsTimerImpl* aNewTimer) const
+ {
+ MOZ_ASSERT(aNewTimer == timerToInsert, "Unexpected timer ordering");
+
+ // Skip any overdue timers.
+ return aFromArray->mTimeout <= now ||
+ aFromArray->mTimeout <= aNewTimer->mTimeout;
+ }
+
+ bool Equals(nsTimerImpl* aFromArray, nsTimerImpl* aNewTimer) const
+ {
+ return false;
+ }
+
+private:
+ const mozilla::TimeStamp& now;
+#ifdef DEBUG
+ const nsTimerImpl* const timerToInsert;
+#endif
+};
+
+#endif /* TimerThread_h___ */
diff --git a/xpcom/threads/moz.build b/xpcom/threads/moz.build
new file mode 100644
index 000000000..5d54a4bf4
--- /dev/null
+++ b/xpcom/threads/moz.build
@@ -0,0 +1,89 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIEnvironment.idl',
+ 'nsIEventTarget.idl',
+ 'nsIIdlePeriod.idl',
+ 'nsIProcess.idl',
+ 'nsIRunnable.idl',
+ 'nsISupportsPriority.idl',
+ 'nsIThread.idl',
+ 'nsIThreadInternal.idl',
+ 'nsIThreadManager.idl',
+ 'nsIThreadPool.idl',
+ 'nsITimer.idl',
+]
+
+XPIDL_MODULE = 'xpcom_threads'
+
+EXPORTS += [
+ 'nsEventQueue.h',
+ 'nsICancelableRunnable.h',
+ 'nsIIncrementalRunnable.h',
+ 'nsMemoryPressure.h',
+ 'nsProcess.h',
+ 'nsThread.h',
+]
+
+EXPORTS.mozilla += [
+ 'AbstractThread.h',
+ 'BackgroundHangMonitor.h',
+ 'HangAnnotations.h',
+ 'HangMonitor.h',
+ 'LazyIdleThread.h',
+ 'MainThreadIdlePeriod.h',
+ 'MozPromise.h',
+ 'SharedThreadPool.h',
+ 'StateMirroring.h',
+ 'StateWatching.h',
+ 'SyncRunnable.h',
+ 'TaskDispatcher.h',
+ 'TaskQueue.h',
+ 'ThrottledEventQueue.h',
+]
+
+UNIFIED_SOURCES += [
+ 'AbstractThread.cpp',
+ 'BackgroundHangMonitor.cpp',
+ 'HangAnnotations.cpp',
+ 'HangMonitor.cpp',
+ 'LazyIdleThread.cpp',
+ 'MainThreadIdlePeriod.cpp',
+ 'nsEnvironment.cpp',
+ 'nsEventQueue.cpp',
+ 'nsMemoryPressure.cpp',
+ 'nsProcessCommon.cpp',
+ 'nsThread.cpp',
+ 'nsThreadManager.cpp',
+ 'nsThreadPool.cpp',
+ 'nsTimerImpl.cpp',
+ 'SharedThreadPool.cpp',
+ 'TaskQueue.cpp',
+ 'ThreadStackHelper.cpp',
+ 'ThrottledEventQueue.cpp',
+ 'TimerThread.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '../build',
+ '/caps',
+ '/tools/profiler',
+]
+
+# BHR disabled for Release builds because of bug 965392.
+# BHR disabled for debug builds because of bug 979069.
+# BHR disabled on gonk because of bug 1180533
+# BHR disabled for TSan builds because of bug 1121216.
+if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('release') and \
+ not CONFIG['MOZ_DEBUG'] and \
+ not CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and \
+ not CONFIG['MOZ_TSAN']:
+ DEFINES['MOZ_ENABLE_BACKGROUND_HANG_MONITOR'] = 1
+
+FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild')
diff --git a/xpcom/threads/nsEnvironment.cpp b/xpcom/threads/nsEnvironment.cpp
new file mode 100644
index 000000000..0de56675e
--- /dev/null
+++ b/xpcom/threads/nsEnvironment.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsEnvironment.h"
+#include "prenv.h"
+#include "prprf.h"
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsPromiseFlatString.h"
+#include "nsDependentString.h"
+#include "nsNativeCharsetUtils.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsEnvironment, nsIEnvironment)
+
+nsresult
+nsEnvironment::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ nsresult rv;
+ *aResult = nullptr;
+
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsEnvironment* obj = new nsEnvironment();
+
+ rv = obj->QueryInterface(aIID, aResult);
+ if (NS_FAILED(rv)) {
+ delete obj;
+ }
+ return rv;
+}
+
+nsEnvironment::~nsEnvironment()
+{
+}
+
+NS_IMETHODIMP
+nsEnvironment::Exists(const nsAString& aName, bool* aOutValue)
+{
+ nsAutoCString nativeName;
+ nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoCString nativeVal;
+#if defined(XP_UNIX)
+ /* For Unix/Linux platforms we follow the Unix definition:
+ * An environment variable exists when |getenv()| returns a non-nullptr
+ * value. An environment variable does not exist when |getenv()| returns
+ * nullptr.
+ */
+ const char* value = PR_GetEnv(nativeName.get());
+ *aOutValue = value && *value;
+#else
+ /* For non-Unix/Linux platforms we have to fall back to a
+ * "portable" definition (which is incorrect for Unix/Linux!!!!)
+ * which simply checks whether the string returned by |Get()| is empty
+ * or not.
+ */
+ nsAutoString value;
+ Get(aName, value);
+ *aOutValue = !value.IsEmpty();
+#endif /* XP_UNIX */
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEnvironment::Get(const nsAString& aName, nsAString& aOutValue)
+{
+ nsAutoCString nativeName;
+ nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoCString nativeVal;
+ const char* value = PR_GetEnv(nativeName.get());
+ if (value && *value) {
+ rv = NS_CopyNativeToUnicode(nsDependentCString(value), aOutValue);
+ } else {
+ aOutValue.Truncate();
+ rv = NS_OK;
+ }
+
+ return rv;
+}
+
+/* Environment strings must have static duration; We're gonna leak all of this
+ * at shutdown: this is by design, caused how Unix/Linux implement environment
+ * vars.
+ */
+
+typedef nsBaseHashtableET<nsCharPtrHashKey, char*> EnvEntryType;
+typedef nsTHashtable<EnvEntryType> EnvHashType;
+
+static EnvHashType* gEnvHash = nullptr;
+
+static bool
+EnsureEnvHash()
+{
+ if (gEnvHash) {
+ return true;
+ }
+
+ gEnvHash = new EnvHashType;
+ if (!gEnvHash) {
+ return false;
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsEnvironment::Set(const nsAString& aName, const nsAString& aValue)
+{
+ nsAutoCString nativeName;
+ nsAutoCString nativeVal;
+
+ nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = NS_CopyUnicodeToNative(aValue, nativeVal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MutexAutoLock lock(mLock);
+
+ if (!EnsureEnvHash()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ EnvEntryType* entry = gEnvHash->PutEntry(nativeName.get());
+ if (!entry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char* newData = PR_smprintf("%s=%s",
+ nativeName.get(),
+ nativeVal.get());
+ if (!newData) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ PR_SetEnv(newData);
+ if (entry->mData) {
+ PR_smprintf_free(entry->mData);
+ }
+ entry->mData = newData;
+ return NS_OK;
+}
+
+
diff --git a/xpcom/threads/nsEnvironment.h b/xpcom/threads/nsEnvironment.h
new file mode 100644
index 000000000..234055a07
--- /dev/null
+++ b/xpcom/threads/nsEnvironment.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsEnvironment_h__
+#define nsEnvironment_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "nsIEnvironment.h"
+
+#define NS_ENVIRONMENT_CID \
+ { 0X3D68F92UL, 0X9513, 0X4E25, \
+ { 0X9B, 0XE9, 0X7C, 0XB2, 0X39, 0X87, 0X41, 0X72 } }
+#define NS_ENVIRONMENT_CONTRACTID "@mozilla.org/process/environment;1"
+
+class nsEnvironment final : public nsIEnvironment
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIENVIRONMENT
+
+ static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+private:
+ nsEnvironment() : mLock("nsEnvironment.mLock")
+ {
+ }
+ ~nsEnvironment();
+
+ mozilla::Mutex mLock;
+};
+
+#endif /* !nsEnvironment_h__ */
diff --git a/xpcom/threads/nsEventQueue.cpp b/xpcom/threads/nsEventQueue.cpp
new file mode 100644
index 000000000..4ca2f11ea
--- /dev/null
+++ b/xpcom/threads/nsEventQueue.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsEventQueue.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Logging.h"
+#include "nsThreadUtils.h"
+#include "prthread.h"
+#include "mozilla/ChaosMode.h"
+
+using namespace mozilla;
+
+static LazyLogModule sEventQueueLog("nsEventQueue");
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(args) MOZ_LOG(sEventQueueLog, mozilla::LogLevel::Debug, args)
+
+nsEventQueue::nsEventQueue(mozilla::CondVar& aCondVar, EventQueueType aType)
+ : mHead(nullptr)
+ , mTail(nullptr)
+ , mOffsetHead(0)
+ , mOffsetTail(0)
+ , mEventsAvailable(aCondVar)
+ , mType(aType)
+{
+}
+
+nsEventQueue::~nsEventQueue()
+{
+ // It'd be nice to be able to assert that no one else is holding the lock,
+ // but NSPR doesn't really expose APIs for it.
+ NS_ASSERTION(IsEmpty(),
+ "Non-empty event queue being destroyed; events being leaked.");
+
+ if (mHead) {
+ FreePage(mHead);
+ }
+}
+
+bool
+nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult,
+ MutexAutoLock& aProofOfLock)
+{
+ if (aResult) {
+ *aResult = nullptr;
+ }
+
+ while (IsEmpty()) {
+ if (!aMayWait) {
+ return false;
+ }
+ LOG(("EVENTQ(%p): wait begin\n", this));
+ mEventsAvailable.Wait();
+ LOG(("EVENTQ(%p): wait end\n", this));
+
+ if (mType == eSharedCondVarQueue) {
+ if (IsEmpty()) {
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (aResult) {
+ MOZ_ASSERT(mOffsetHead < EVENTS_PER_PAGE);
+ MOZ_ASSERT_IF(mHead == mTail, mOffsetHead <= mOffsetTail);
+ *aResult = mHead->mEvents[mOffsetHead++];
+
+ MOZ_ASSERT(*aResult);
+ MOZ_ASSERT(mOffsetHead <= EVENTS_PER_PAGE);
+
+ // Check if mHead points to empty Page
+ if (mOffsetHead == EVENTS_PER_PAGE) {
+ Page* dead = mHead;
+ mHead = mHead->mNext;
+ FreePage(dead);
+ mOffsetHead = 0;
+ }
+ }
+
+ return true;
+}
+
+void
+nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable,
+ MutexAutoLock& aProofOfLock)
+{
+ if (!mHead) {
+ mHead = NewPage();
+ MOZ_ASSERT(mHead);
+
+ mTail = mHead;
+ mOffsetHead = 0;
+ mOffsetTail = 0;
+ } else if (mOffsetTail == EVENTS_PER_PAGE) {
+ Page* page = NewPage();
+ MOZ_ASSERT(page);
+
+ mTail->mNext = page;
+ mTail = page;
+ mOffsetTail = 0;
+ }
+
+ nsIRunnable*& queueLocation = mTail->mEvents[mOffsetTail];
+ MOZ_ASSERT(!queueLocation);
+ queueLocation = aRunnable.take();
+ ++mOffsetTail;
+ LOG(("EVENTQ(%p): notify\n", this));
+ mEventsAvailable.Notify();
+}
+
+void
+nsEventQueue::PutEvent(nsIRunnable* aRunnable, MutexAutoLock& aProofOfLock)
+{
+ nsCOMPtr<nsIRunnable> event(aRunnable);
+ PutEvent(event.forget(), aProofOfLock);
+}
+
+size_t
+nsEventQueue::Count(MutexAutoLock& aProofOfLock) const
+{
+ // It is obvious count is 0 when the queue is empty.
+ if (!mHead) {
+ return 0;
+ }
+
+ /* How we count the number of events in the queue:
+ * 1. Let pageCount(x, y) denote the number of pages excluding the tail page
+ * where x is the index of head page and y is the index of the tail page.
+ * 2. Then we have pageCount(x, y) = y - x.
+ *
+ * Ex: pageCount(0, 0) = 0 where both head and tail pages point to page 0.
+ * pageCount(0, 1) = 1 where head points to page 0 and tail points page 1.
+ *
+ * 3. number of events = (EVENTS_PER_PAGE * pageCount(x, y))
+ * - (empty slots in head page) + (non-empty slots in tail page)
+ * = (EVENTS_PER_PAGE * pageCount(x, y)) - mOffsetHead + mOffsetTail
+ */
+
+ int count = -mOffsetHead;
+
+ // Compute (EVENTS_PER_PAGE * pageCount(x, y))
+ for (Page* page = mHead; page != mTail; page = page->mNext) {
+ count += EVENTS_PER_PAGE;
+ }
+
+ count += mOffsetTail;
+ MOZ_ASSERT(count >= 0);
+
+ return count;
+}
diff --git a/xpcom/threads/nsEventQueue.h b/xpcom/threads/nsEventQueue.h
new file mode 100644
index 000000000..23b55e63d
--- /dev/null
+++ b/xpcom/threads/nsEventQueue.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsEventQueue_h__
+#define nsEventQueue_h__
+
+#include <stdlib.h>
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+#include "nsIRunnable.h"
+#include "nsCOMPtr.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/UniquePtr.h"
+
+class nsThreadPool;
+
+// A threadsafe FIFO event queue...
+class nsEventQueue
+{
+public:
+ typedef mozilla::MutexAutoLock MutexAutoLock;
+
+ enum EventQueueType
+ {
+ eNormalQueue,
+ eSharedCondVarQueue
+ };
+
+ nsEventQueue(mozilla::CondVar& aCondVar, EventQueueType aType);
+ ~nsEventQueue();
+
+ // This method adds a new event to the pending event queue. The queue holds
+ // a strong reference to the event after this method returns. This method
+ // cannot fail.
+ void PutEvent(nsIRunnable* aEvent, MutexAutoLock& aProofOfLock);
+ void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
+ MutexAutoLock& aProofOfLock);
+
+ // This method gets an event from the event queue. If mayWait is true, then
+ // the method will block the calling thread until an event is available. If
+ // the event is null, then the method returns immediately indicating whether
+ // or not an event is pending. When the resulting event is non-null, the
+ // caller is responsible for releasing the event object. This method does
+ // not alter the reference count of the resulting event.
+ bool GetEvent(bool aMayWait, nsIRunnable** aEvent,
+ MutexAutoLock& aProofOfLock);
+
+ // This method returns true if there is a pending event.
+ bool HasPendingEvent(MutexAutoLock& aProofOfLock)
+ {
+ return GetEvent(false, nullptr, aProofOfLock);
+ }
+
+ // This method returns the next pending event or null.
+ bool GetPendingEvent(nsIRunnable** aRunnable, MutexAutoLock& aProofOfLock)
+ {
+ return GetEvent(false, aRunnable, aProofOfLock);
+ }
+
+ size_t Count(MutexAutoLock&) const;
+
+private:
+ bool IsEmpty()
+ {
+ return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail);
+ }
+
+ enum
+ {
+ EVENTS_PER_PAGE = 255
+ };
+
+ // Page objects are linked together to form a simple deque.
+
+ struct Page
+ {
+ struct Page* mNext;
+ nsIRunnable* mEvents[EVENTS_PER_PAGE];
+ };
+
+ static_assert((sizeof(Page) & (sizeof(Page) - 1)) == 0,
+ "sizeof(Page) should be a power of two to avoid heap slop.");
+
+ static Page* NewPage()
+ {
+ return static_cast<Page*>(moz_xcalloc(1, sizeof(Page)));
+ }
+
+ static void FreePage(Page* aPage)
+ {
+ free(aPage);
+ }
+
+ Page* mHead;
+ Page* mTail;
+
+ uint16_t mOffsetHead; // offset into mHead where next item is removed
+ uint16_t mOffsetTail; // offset into mTail where next item is added
+ mozilla::CondVar& mEventsAvailable;
+
+ EventQueueType mType;
+
+ // These methods are made available to nsThreadPool as a hack, since
+ // nsThreadPool needs to have its threads sleep for fixed amounts of
+ // time as well as being able to wake up all threads when thread
+ // limits change.
+ friend class nsThreadPool;
+ void Wait(PRIntervalTime aInterval)
+ {
+ MOZ_ASSERT(mType == eNormalQueue);
+ mEventsAvailable.Wait(aInterval);
+ }
+ void NotifyAll()
+ {
+ MOZ_ASSERT(mType == eNormalQueue);
+ mEventsAvailable.NotifyAll();
+ }
+};
+
+#endif // nsEventQueue_h__
diff --git a/xpcom/threads/nsICancelableRunnable.h b/xpcom/threads/nsICancelableRunnable.h
new file mode 100644
index 000000000..5ae9f5b14
--- /dev/null
+++ b/xpcom/threads/nsICancelableRunnable.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsICancelableRunnable_h__
+#define nsICancelableRunnable_h__
+
+#include "nsISupports.h"
+
+#define NS_ICANCELABLERUNNABLE_IID \
+{ 0xde93dc4c, 0x5eea, 0x4eb7, \
+{ 0xb6, 0xd1, 0xdb, 0xf1, 0xe0, 0xce, 0xf6, 0x5c } }
+
+class nsICancelableRunnable : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANCELABLERUNNABLE_IID)
+
+ /*
+ * Cancels a pending task. If the task has already been executed this will
+ * be a no-op. Calling this method twice is considered an error.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that the runnable has already been canceled.
+ */
+ virtual nsresult Cancel() = 0;
+
+protected:
+ nsICancelableRunnable() { }
+ virtual ~nsICancelableRunnable() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsICancelableRunnable,
+ NS_ICANCELABLERUNNABLE_IID)
+
+#endif // nsICancelableRunnable_h__
diff --git a/xpcom/threads/nsIEnvironment.idl b/xpcom/threads/nsIEnvironment.idl
new file mode 100644
index 000000000..afbc3eb7c
--- /dev/null
+++ b/xpcom/threads/nsIEnvironment.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 access to the current process environment.
+ *
+ */
+[scriptable, uuid(101d5941-d820-4e85-a266-9a3469940807)]
+interface nsIEnvironment : nsISupports
+{
+ /**
+ * Set the value of an environment variable.
+ *
+ * @param aName the variable name to set.
+ * @param aValue the value to set.
+ */
+ void set(in AString aName, in AString aValue);
+
+ /**
+ * Get the value of an environment variable.
+ *
+ * @param aName the variable name to retrieve.
+ * @return returns the value of the env variable. An empty string
+ * will be returned when the env variable does not exist or
+ * when the value itself is an empty string - please use
+ * |exists()| to probe whether the env variable exists
+ * or not.
+ */
+ AString get(in AString aName);
+
+ /**
+ * Check the existence of an environment variable.
+ * This method checks whether an environment variable is present in
+ * the environment or not.
+ *
+ * - For Unix/Linux platforms we follow the Unix definition:
+ * An environment variable exists when |getenv()| returns a non-NULL value.
+ * An environment variable does not exist when |getenv()| returns NULL.
+ * - For non-Unix/Linux platforms we have to fall back to a
+ * "portable" definition (which is incorrect for Unix/Linux!!!!)
+ * which simply checks whether the string returned by |Get()| is empty
+ * or not.
+ *
+ * @param aName the variable name to probe.
+ * @return if the variable has been set, the value returned is
+ * PR_TRUE. If the variable was not defined in the
+ * environment PR_FALSE will be returned.
+ */
+ boolean exists(in AString aName);
+};
+
diff --git a/xpcom/threads/nsIEventTarget.idl b/xpcom/threads/nsIEventTarget.idl
new file mode 100644
index 000000000..a6f9068dc
--- /dev/null
+++ b/xpcom/threads/nsIEventTarget.idl
@@ -0,0 +1,127 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIRunnable.idl"
+%{C++
+#include "nsCOMPtr.h"
+#include "mozilla/AlreadyAddRefed.h"
+%}
+
+native alreadyAddRefed_nsIRunnable(already_AddRefed<nsIRunnable>);
+
+[scriptable, uuid(88145945-3278-424e-9f37-d874cbdd9f6f)]
+interface nsIEventTarget : nsISupports
+{
+ /* until we can get rid of all uses, keep the non-alreadyAddRefed<> version */
+%{C++
+ nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) {
+ return Dispatch(nsCOMPtr<nsIRunnable>(aEvent).forget(), aFlags);
+ }
+%}
+
+ /**
+ * This flag specifies the default mode of event dispatch, whereby the event
+ * is simply queued for later processing. When this flag is specified,
+ * dispatch returns immediately after the event is queued.
+ */
+ const unsigned long DISPATCH_NORMAL = 0;
+
+ /**
+ * This flag specifies the synchronous mode of event dispatch, in which the
+ * dispatch method does not return until the event has been processed.
+ *
+ * NOTE: passing this flag to dispatch may have the side-effect of causing
+ * other events on the current thread to be processed while waiting for the
+ * given event to be processed.
+ */
+ const unsigned long DISPATCH_SYNC = 1;
+
+ /**
+ * This flag specifies that the dispatch is occurring from a running event
+ * that was dispatched to the same event target, and that event is about to
+ * finish.
+ *
+ * A thread pool can use this as an optimization hint to not spin up
+ * another thread, since the current thread is about to become idle.
+ *
+ * These events are always async.
+ */
+ const unsigned long DISPATCH_AT_END = 2;
+
+ /**
+ * Check to see if this event target is associated with the current thread.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that events dispatched to this
+ * event target will run on the current thread (i.e., the thread calling
+ * this method).
+ */
+ boolean isOnCurrentThread();
+
+ /**
+ * Dispatch an event to this event target. This function may be called from
+ * any thread, and it may be called re-entrantly.
+ *
+ * @param event
+ * The alreadyAddRefed<> event to dispatch.
+ * NOTE that the event will be leaked if it fails to dispatch.
+ * @param flags
+ * The flags modifying event dispatch. The flags are described in detail
+ * below.
+ *
+ * @throws NS_ERROR_INVALID_ARG
+ * Indicates that event is null.
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that the thread is shutting down and has finished processing
+ * events, so this event would never run and has not been dispatched.
+ */
+ [noscript, binaryname(Dispatch)] void dispatchFromC(in alreadyAddRefed_nsIRunnable event, in unsigned long flags);
+ /**
+ * Version of Dispatch to expose to JS, which doesn't require an alreadyAddRefed<>
+ * (it will be converted to that internally)
+ *
+ * @param event
+ * The (raw) event to dispatch.
+ * @param flags
+ * The flags modifying event dispatch. The flags are described in detail
+ * below.
+ *
+ * @throws NS_ERROR_INVALID_ARG
+ * Indicates that event is null.
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that the thread is shutting down and has finished processing
+ * events, so this event would never run and has not been dispatched.
+ */
+ [binaryname(DispatchFromScript)] void dispatch(in nsIRunnable event, in unsigned long flags);
+ /**
+ * Dispatch an event to this event target, but do not run it before delay
+ * milliseconds have passed. This function may be called from any thread.
+ *
+ * @param event
+ * The alreadyAddrefed<> event to dispatch.
+ * @param delay
+ * The delay (in ms) before running the event. If event does not rise to
+ * the top of the event queue before the delay has passed, it will be set
+ * aside to execute once the delay has passed. Otherwise, it will be
+ * executed immediately.
+ *
+ * @throws NS_ERROR_INVALID_ARG
+ * Indicates that event is null.
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that the thread is shutting down and has finished processing
+ * events, so this event would never run and has not been dispatched, or
+ * that delay is zero.
+ */
+ [noscript] void delayedDispatch(in alreadyAddRefed_nsIRunnable event, in unsigned long delay);
+};
+
+%{C++
+// convenient aliases:
+#define NS_DISPATCH_NORMAL nsIEventTarget::DISPATCH_NORMAL
+#define NS_DISPATCH_SYNC nsIEventTarget::DISPATCH_SYNC
+#define NS_DISPATCH_AT_END nsIEventTarget::DISPATCH_AT_END
+%}
diff --git a/xpcom/threads/nsIIdlePeriod.idl b/xpcom/threads/nsIIdlePeriod.idl
new file mode 100644
index 000000000..aa72b2171
--- /dev/null
+++ b/xpcom/threads/nsIIdlePeriod.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+%{C++
+namespace mozilla {
+class TimeStamp;
+}
+%}
+
+native TimeStamp(mozilla::TimeStamp);
+
+/**
+ * An instance implementing nsIIdlePeriod is used by an associated
+ * nsIThread to estimate when it is likely that it will receive an
+ * event.
+ */
+[builtinclass, uuid(21dd35a2-eae9-4bd8-b470-0dfa35a0e3b9)]
+interface nsIIdlePeriod : nsISupports
+{
+ /**
+ * Return an estimate of a point in time in the future when we
+ * think that the associated thread will become busy. Should
+ * return TimeStamp() (i.e. the null time) or a time less than
+ * TimeStamp::Now() if the thread is currently busy or will become
+ * busy very soon.
+ */
+ TimeStamp getIdlePeriodHint();
+};
diff --git a/xpcom/threads/nsIIncrementalRunnable.h b/xpcom/threads/nsIIncrementalRunnable.h
new file mode 100644
index 000000000..526bc165e
--- /dev/null
+++ b/xpcom/threads/nsIIncrementalRunnable.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIIncrementalRunnable_h__
+#define nsIIncrementalRunnable_h__
+
+#include "nsISupports.h"
+#include "mozilla/TimeStamp.h"
+
+#define NS_IINCREMENTALRUNNABLE_IID \
+{ 0x688be92e, 0x7ade, 0x4fdc, \
+{ 0x9d, 0x83, 0x74, 0xcb, 0xef, 0xf4, 0xa5, 0x2c } }
+
+
+/**
+ * A task interface for tasks that can schedule their work to happen
+ * in increments bounded by a deadline.
+ */
+class nsIIncrementalRunnable : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IINCREMENTALRUNNABLE_IID)
+
+ /**
+ * Notify the task of a point in time in the future when the task
+ * should stop executing.
+ */
+ virtual void SetDeadline(mozilla::TimeStamp aDeadline) = 0;
+
+protected:
+ nsIIncrementalRunnable() { }
+ virtual ~nsIIncrementalRunnable() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIIncrementalRunnable,
+ NS_IINCREMENTALRUNNABLE_IID)
+
+#endif // nsIIncrementalRunnable_h__
diff --git a/xpcom/threads/nsIProcess.idl b/xpcom/threads/nsIProcess.idl
new file mode 100644
index 000000000..2c7dcd55e
--- /dev/null
+++ b/xpcom/threads/nsIProcess.idl
@@ -0,0 +1,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 "nsISupports.idl"
+
+interface nsIFile;
+interface nsIObserver;
+
+[scriptable, uuid(609610de-9954-4a63-8a7c-346350a86403)]
+interface nsIProcess : nsISupports
+{
+ /**
+ * Initialises the process with an executable to be run. Call the run method
+ * to run the executable.
+ * @param executable The executable to run.
+ */
+ void init(in nsIFile executable);
+
+ /**
+ * Kills the running process. After exiting the process will either have
+ * been killed or a failure will have been returned.
+ */
+ void kill();
+
+ /**
+ * Executes the file this object was initialized with
+ * @param blocking Whether to wait until the process terminates before
+ returning or not.
+ * @param args An array of arguments to pass to the process in the
+ * native character set.
+ * @param count The length of the args array.
+ */
+ void run(in boolean blocking, [array, size_is(count)] in string args,
+ in unsigned long count);
+
+ /**
+ * Executes the file this object was initialized with optionally calling
+ * an observer after the process has finished running.
+ * @param args An array of arguments to pass to the process in the
+ * native character set.
+ * @param count The length of the args array.
+ * @param observer An observer to notify when the process has completed. It
+ * will receive this process instance as the subject and
+ * "process-finished" or "process-failed" as the topic. The
+ * observer will be notified on the main thread.
+ * @param holdWeak Whether to use a weak reference to hold the observer.
+ */
+ void runAsync([array, size_is(count)] in string args, in unsigned long count,
+ [optional] in nsIObserver observer, [optional] in boolean holdWeak);
+
+ /**
+ * Executes the file this object was initialized with
+ * @param blocking Whether to wait until the process terminates before
+ returning or not.
+ * @param args An array of arguments to pass to the process in UTF-16
+ * @param count The length of the args array.
+ */
+ void runw(in boolean blocking, [array, size_is(count)] in wstring args,
+ in unsigned long count);
+
+ /**
+ * Executes the file this object was initialized with optionally calling
+ * an observer after the process has finished running.
+ * @param args An array of arguments to pass to the process in UTF-16
+ * @param count The length of the args array.
+ * @param observer An observer to notify when the process has completed. It
+ * will receive this process instance as the subject and
+ * "process-finished" or "process-failed" as the topic. The
+ * observer will be notified on the main thread.
+ * @param holdWeak Whether to use a weak reference to hold the observer.
+ */
+ void runwAsync([array, size_is(count)] in wstring args,
+ in unsigned long count,
+ [optional] in nsIObserver observer, [optional] in boolean holdWeak);
+
+ /**
+ * The process identifier of the currently running process. This will only
+ * be available after the process has started and may not be available on
+ * some platforms.
+ */
+ readonly attribute unsigned long pid;
+
+ /**
+ * The exit value of the process. This is only valid after the process has
+ * exited.
+ */
+ readonly attribute long exitValue;
+
+ /**
+ * Returns whether the process is currently running or not.
+ */
+ readonly attribute boolean isRunning;
+};
+
+%{C++
+
+#define NS_PROCESS_CONTRACTID "@mozilla.org/process/util;1"
+%}
diff --git a/xpcom/threads/nsIRunnable.idl b/xpcom/threads/nsIRunnable.idl
new file mode 100644
index 000000000..4d26f72d9
--- /dev/null
+++ b/xpcom/threads/nsIRunnable.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * Represents a task which can be dispatched to a thread for execution.
+ */
+
+[scriptable, function, uuid(4a2abaf0-6886-11d3-9382-00104ba0fd40)]
+interface nsIRunnable : nsISupports
+{
+ /**
+ * The function implementing the task to be run.
+ */
+ void run();
+};
+
+[uuid(e75aa42a-80a9-11e6-afb5-e89d87348e2c)]
+interface nsIRunnablePriority : nsISupports
+{
+ const unsigned short PRIORITY_NORMAL = 0;
+ const unsigned short PRIORITY_HIGH = 1;
+ readonly attribute unsigned long priority;
+};
diff --git a/xpcom/threads/nsISupportsPriority.idl b/xpcom/threads/nsISupportsPriority.idl
new file mode 100644
index 000000000..579c280cf
--- /dev/null
+++ b/xpcom/threads/nsISupportsPriority.idl
@@ -0,0 +1,45 @@
+/* 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"
+
+/**
+ * This interface exposes the general notion of a scheduled object with a
+ * integral priority value. Following UNIX conventions, smaller (and possibly
+ * negative) values have higher priority.
+ *
+ * This interface does not strictly define what happens when the priority of an
+ * object is changed. An implementation of this interface is free to define
+ * the side-effects of changing the priority of an object. In some cases,
+ * changing the priority of an object may be disallowed (resulting in an
+ * exception being thrown) or may simply be ignored.
+ */
+[scriptable, uuid(aa578b44-abd5-4c19-8b14-36d4de6fdc36)]
+interface nsISupportsPriority : nsISupports
+{
+ /**
+ * Typical priority values.
+ */
+ const long PRIORITY_HIGHEST = -20;
+ const long PRIORITY_HIGH = -10;
+ const long PRIORITY_NORMAL = 0;
+ const long PRIORITY_LOW = 10;
+ const long PRIORITY_LOWEST = 20;
+
+ /**
+ * This attribute may be modified to change the priority of this object. The
+ * implementation of this interface is free to truncate a given priority
+ * value to whatever limits are appropriate. Typically, this attribute is
+ * initialized to PRIORITY_NORMAL, but implementations may choose to assign a
+ * different initial value.
+ */
+ attribute long priority;
+
+ /**
+ * This method adjusts the priority attribute by a given delta. It helps
+ * reduce the amount of coding required to increment or decrement the value
+ * of the priority attribute.
+ */
+ void adjustPriority(in long delta);
+};
diff --git a/xpcom/threads/nsIThread.idl b/xpcom/threads/nsIThread.idl
new file mode 100644
index 000000000..fbfc8d4fb
--- /dev/null
+++ b/xpcom/threads/nsIThread.idl
@@ -0,0 +1,149 @@
+/* -*- 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 "nsIEventTarget.idl"
+#include "nsIIdlePeriod.idl"
+
+%{C++
+#include "mozilla/AlreadyAddRefed.h"
+%}
+
+[ptr] native PRThread(PRThread);
+
+native alreadyAddRefed_nsIIdlePeriod(already_AddRefed<nsIIdlePeriod>);
+
+/**
+ * This interface provides a high-level abstraction for an operating system
+ * thread.
+ *
+ * Threads have a built-in event queue, and a thread is an event target that
+ * can receive nsIRunnable objects (events) to be processed on the thread.
+ *
+ * See nsIThreadManager for the API used to create and locate threads.
+ */
+[scriptable, uuid(5801d193-29d1-4964-a6b7-70eb697ddf2b)]
+interface nsIThread : nsIEventTarget
+{
+ /**
+ * @returns
+ * The NSPR thread object corresponding to this nsIThread.
+ */
+ [noscript] readonly attribute PRThread PRThread;
+
+ /**
+ * @returns
+ * Whether or not this thread may call into JS. Used in the profiler
+ * to avoid some unnecessary locking.
+ */
+ [noscript] attribute boolean CanInvokeJS;
+
+
+ /**
+ * Shutdown the thread. This method prevents further dispatch of events to
+ * the thread, and it causes any pending events to run to completion before
+ * the thread joins (see PR_JoinThread) with the current thread. During this
+ * method call, events for the current thread may be processed.
+ *
+ * This method MAY NOT be executed from the thread itself. Instead, it is
+ * meant to be executed from another thread (usually the thread that created
+ * this thread or the main application thread). When this function returns,
+ * the thread will be shutdown, and it will no longer be possible to dispatch
+ * events to the thread.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that this method was erroneously called when this thread was
+ * the current thread, that this thread was not created with a call to
+ * nsIThreadManager::NewThread, or if this method was called more than once
+ * on the thread object.
+ */
+ void shutdown();
+
+ /**
+ * This method may be called to determine if there are any events ready to be
+ * processed. It may only be called when this thread is the current thread.
+ *
+ * Because events may be added to this thread by another thread, a "false"
+ * result does not mean that this thread has no pending events. It only
+ * means that there were no pending events when this method was called.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that this thread has one or
+ * more pending events.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that this method was erroneously called when this thread was
+ * not the current thread.
+ */
+ boolean hasPendingEvents();
+
+ /**
+ * Process the next event. If there are no pending events, then this method
+ * may wait -- depending on the value of the mayWait parameter -- until an
+ * event is dispatched to this thread. This method is re-entrant but may
+ * only be called if this thread is the current thread.
+ *
+ * @param mayWait
+ * A boolean parameter that if "true" indicates that the method may block
+ * the calling thread to wait for a pending event.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that an event was processed.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that this method was erroneously called when this thread was
+ * not the current thread.
+ */
+ boolean processNextEvent(in boolean mayWait);
+
+ /**
+ * Shutdown the thread asynchronously. This method immediately prevents
+ * further dispatch of events to the thread, and it causes any pending events
+ * to run to completion before this thread joins with the current thread.
+ *
+ * UNLIKE shutdown() this does not process events on the current thread.
+ * Instead it merely ensures that the current thread continues running until
+ * this thread has shut down.
+ *
+ * This method MAY NOT be executed from the thread itself. Instead, it is
+ * meant to be executed from another thread (usually the thread that created
+ * this thread or the main application thread). When this function returns,
+ * the thread will continue running until it exhausts its event queue.
+ *
+ * @throws NS_ERROR_UNEXPECTED
+ * Indicates that this method was erroneously called when this thread was
+ * the current thread, that this thread was not created with a call to
+ * nsIThreadManager::NewThread, or if this method was called more than once
+ * on the thread object.
+ */
+ void asyncShutdown();
+
+ /**
+ * Register an instance of nsIIdlePeriod which works as a facade of
+ * the abstract notion of a "current idle period". The
+ * nsIIdlePeriod should always represent the "current" idle period
+ * with regard to expected work for the thread. The thread is free
+ * to use this when there are no higher prioritized tasks to process
+ * to determine if it is reasonable to schedule tasks that could run
+ * when the thread is idle. The responsibility of the registered
+ * nsIIdlePeriod is to answer with an estimated deadline at which
+ * the thread should expect that it could receive new higher
+ * priority tasks.
+ */
+ [noscript] void registerIdlePeriod(in alreadyAddRefed_nsIIdlePeriod aIdlePeriod);
+
+ /**
+ * Dispatch an event to the thread's idle queue. This function may be called
+ * from any thread, and it may be called re-entrantly.
+ *
+ * @param event
+ * The alreadyAddRefed<> event to dispatch.
+ * NOTE that the event will be leaked if it fails to dispatch.
+ *
+ * @throws NS_ERROR_INVALID_ARG
+ * Indicates that event is null.
+ */
+ [noscript] void idleDispatch(in alreadyAddRefed_nsIRunnable event);
+};
diff --git a/xpcom/threads/nsIThreadInternal.idl b/xpcom/threads/nsIThreadInternal.idl
new file mode 100644
index 000000000..e5d7cc83f
--- /dev/null
+++ b/xpcom/threads/nsIThreadInternal.idl
@@ -0,0 +1,135 @@
+/* -*- 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 "nsIThread.idl"
+
+interface nsIRunnable;
+interface nsIThreadObserver;
+
+/**
+ * The XPCOM thread object implements this interface, which allows a consumer
+ * to observe dispatch activity on the thread.
+ */
+[scriptable, uuid(a3a72e5f-71d9-4add-8f30-59a78fb6d5eb)]
+interface nsIThreadInternal : nsIThread
+{
+ /**
+ * Get/set the current thread observer (may be null). This attribute may be
+ * read from any thread, but must only be set on the thread corresponding to
+ * this thread object. The observer will be released on the thread
+ * corresponding to this thread object after all other events have been
+ * processed during a call to Shutdown.
+ */
+ attribute nsIThreadObserver observer;
+
+ /**
+ * Add an observer that will *only* receive onProcessNextEvent,
+ * beforeProcessNextEvent. and afterProcessNextEvent callbacks. Always called
+ * on the target thread, and the implementation does not have to be
+ * threadsafe. Order of callbacks is not guaranteed (i.e.
+ * afterProcessNextEvent may be called first depending on whether or not the
+ * observer is added in a nested loop). Holds a strong ref.
+ */
+ void addObserver(in nsIThreadObserver observer);
+
+ /**
+ * Remove an observer added via the addObserver call. Once removed the
+ * observer will never be called again by the thread.
+ */
+ void removeObserver(in nsIThreadObserver observer);
+
+ /**
+ * This method causes any events currently enqueued on the thread to be
+ * suppressed until PopEventQueue is called, and any event dispatched to this
+ * thread's nsIEventTarget will queue as well. Calls to PushEventQueue may be
+ * nested and must each be paired with a call to PopEventQueue in order to
+ * restore the original state of the thread. The returned nsIEventTarget may
+ * be used to push events onto the nested queue. Dispatching will be disabled
+ * once the event queue is popped. The thread will only ever process pending
+ * events for the innermost event queue. Must only be called on the target
+ * thread.
+ */
+ [noscript] nsIEventTarget pushEventQueue();
+
+ /**
+ * Revert a call to PushEventQueue. When an event queue is popped, any events
+ * remaining in the queue are appended to the elder queue. This also causes
+ * the nsIEventTarget returned from PushEventQueue to stop dispatching events.
+ * Must only be called on the target thread, and with the innermost event
+ * queue.
+ */
+ [noscript] void popEventQueue(in nsIEventTarget aInnermostTarget);
+};
+
+/**
+ * This interface provides the observer with hooks to implement a layered
+ * event queue. For example, it is possible to overlay processing events
+ * for a GUI toolkit on top of the events for a thread:
+ *
+ * var NativeQueue;
+ * Observer = {
+ * onDispatchedEvent(thread) {
+ * NativeQueue.signal();
+ * }
+ * onProcessNextEvent(thread, mayWait) {
+ * if (NativeQueue.hasNextEvent())
+ * NativeQueue.processNextEvent();
+ * while (mayWait && !thread.hasPendingEvent()) {
+ * NativeQueue.wait();
+ * NativeQueue.processNextEvent();
+ * }
+ * }
+ * };
+ *
+ * NOTE: The implementation of this interface must be threadsafe.
+ *
+ * NOTE: It is valid to change the thread's observer during a call to an
+ * observer method.
+ *
+ * NOTE: Will be split into two interfaces soon: one for onProcessNextEvent and
+ * afterProcessNextEvent, then another that inherits the first and adds
+ * onDispatchedEvent.
+ */
+[uuid(cc8da053-1776-44c2-9199-b5a629d0a19d)]
+interface nsIThreadObserver : nsISupports
+{
+ /**
+ * This method is called after an event has been dispatched to the thread.
+ * This method may be called from any thread.
+ *
+ * @param thread
+ * The thread where the event is being dispatched.
+ */
+ void onDispatchedEvent(in nsIThreadInternal thread);
+
+ /**
+ * This method is called when nsIThread::ProcessNextEvent is called. It does
+ * not guarantee that an event is actually going to be processed. This method
+ * is only called on the target thread.
+ *
+ * @param thread
+ * The thread being asked to process another event.
+ * @param mayWait
+ * Indicates whether or not the method is allowed to block the calling
+ * thread. For example, this parameter is false during thread shutdown.
+ */
+ void onProcessNextEvent(in nsIThreadInternal thread, in boolean mayWait);
+
+ /**
+ * This method is called (from nsIThread::ProcessNextEvent) after an event
+ * is processed. It does not guarantee that an event was actually processed
+ * (depends on the value of |eventWasProcessed|. This method is only called
+ * on the target thread. DO NOT EVER RUN SCRIPT FROM THIS CALLBACK!!!
+ *
+ * @param thread
+ * The thread that processed another event.
+ * @param eventWasProcessed
+ * Indicates whether an event was actually processed. May be false if the
+ * |mayWait| flag was false when calling nsIThread::ProcessNextEvent().
+ */
+ void afterProcessNextEvent(in nsIThreadInternal thread,
+ in bool eventWasProcessed);
+};
diff --git a/xpcom/threads/nsIThreadManager.idl b/xpcom/threads/nsIThreadManager.idl
new file mode 100644
index 000000000..9b4fc126f
--- /dev/null
+++ b/xpcom/threads/nsIThreadManager.idl
@@ -0,0 +1,68 @@
+/* -*- 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 "nsISupports.idl"
+
+[ptr] native PRThread(PRThread);
+
+interface nsIThread;
+
+/**
+ * An interface for creating and locating nsIThread instances.
+ */
+[scriptable, uuid(1be89eca-e2f7-453b-8d38-c11ba247f6f3)]
+interface nsIThreadManager : nsISupports
+{
+ /**
+ * Default number of bytes reserved for a thread's stack, if no stack size
+ * is specified in newThread(). 0 means use platform default.
+ */
+ const unsigned long DEFAULT_STACK_SIZE = 0;
+
+ /**
+ * Create a new thread (a global, user PRThread).
+ *
+ * @param creationFlags
+ * Reserved for future use. Pass 0.
+ * @param stackSize
+ * Number of bytes to reserve for the thread's stack.
+ *
+ * @returns
+ * The newly created nsIThread object.
+ */
+ nsIThread newThread(in unsigned long creationFlags, [optional] in unsigned long stackSize);
+
+ /**
+ * Get the nsIThread object (if any) corresponding to the given PRThread.
+ * This method returns null if there is no corresponding nsIThread.
+ *
+ * @param prthread
+ * The PRThread of the nsIThread being requested.
+ *
+ * @returns
+ * The nsIThread object corresponding to the given PRThread or null if no
+ * such nsIThread exists.
+ */
+ [noscript] nsIThread getThreadFromPRThread(in PRThread prthread);
+
+ /**
+ * Get the main thread.
+ */
+ readonly attribute nsIThread mainThread;
+
+ /**
+ * Get the current thread. If the calling thread does not already have a
+ * nsIThread associated with it, then a new nsIThread will be created and
+ * associated with the current PRThread.
+ */
+ readonly attribute nsIThread currentThread;
+
+ /**
+ * This attribute is true if the calling thread is the main thread of the
+ * application process.
+ */
+ readonly attribute boolean isMainThread;
+};
diff --git a/xpcom/threads/nsIThreadPool.idl b/xpcom/threads/nsIThreadPool.idl
new file mode 100644
index 000000000..d04e8504a
--- /dev/null
+++ b/xpcom/threads/nsIThreadPool.idl
@@ -0,0 +1,88 @@
+/* -*- 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 "nsIEventTarget.idl"
+
+[scriptable, uuid(ef194cab-3f86-4b61-b132-e5e96a79e5d1)]
+interface nsIThreadPoolListener : nsISupports
+{
+ /**
+ * Called when a new thread is created by the thread pool. The notification
+ * happens on the newly-created thread.
+ */
+ void onThreadCreated();
+
+ /**
+ * Called when a thread is about to be destroyed by the thread pool. The
+ * notification happens on the thread that is about to be destroyed.
+ */
+ void onThreadShuttingDown();
+};
+
+/**
+ * An interface to a thread pool. A thread pool creates a limited number of
+ * anonymous (unnamed) worker threads. An event dispatched to the thread pool
+ * will be run on the next available worker thread.
+ */
+[scriptable, uuid(76ce99c9-8e43-489a-9789-f27cc4424965)]
+interface nsIThreadPool : nsIEventTarget
+{
+ /**
+ * Shutdown the thread pool. This method may not be executed from any thread
+ * in the thread pool. Instead, it is meant to be executed from another
+ * thread (usually the thread that created this thread pool). When this
+ * function returns, the thread pool and all of its threads will be shutdown,
+ * and it will no longer be possible to dispatch tasks to the thread pool.
+ *
+ * As a side effect, events on the current thread will be processed.
+ */
+ void shutdown();
+
+ /**
+ * Get/set the maximum number of threads allowed at one time in this pool.
+ */
+ attribute unsigned long threadLimit;
+
+ /**
+ * Get/set the maximum number of idle threads kept alive.
+ */
+ attribute unsigned long idleThreadLimit;
+
+ /**
+ * Get/set the amount of time in milliseconds before an idle thread is
+ * destroyed.
+ */
+ attribute unsigned long idleThreadTimeout;
+
+ /**
+ * Get/set the number of bytes reserved for the stack of all threads in
+ * the pool. By default this is nsIThreadManager::DEFAULT_STACK_SIZE.
+ */
+ attribute unsigned long threadStackSize;
+
+ /**
+ * An optional listener that will be notified when a thread is created or
+ * destroyed in the course of the thread pool's operation.
+ *
+ * A listener will only receive notifications about threads created after the
+ * listener is set so it is recommended that the consumer set the listener
+ * before dispatching the first event. A listener that receives an
+ * onThreadCreated() notification is guaranteed to always receive the
+ * corresponding onThreadShuttingDown() notification.
+ *
+ * The thread pool takes ownership of the listener and releases it when the
+ * shutdown() method is called. Threads created after the listener is set will
+ * also take ownership of the listener so that the listener will be kept alive
+ * long enough to receive the guaranteed onThreadShuttingDown() notification.
+ */
+ attribute nsIThreadPoolListener listener;
+
+ /**
+ * Set the label for threads in the pool. All threads will be named
+ * "<aName> #<n>", where <n> is a serial number.
+ */
+ void setName(in ACString aName);
+};
diff --git a/xpcom/threads/nsITimer.idl b/xpcom/threads/nsITimer.idl
new file mode 100644
index 000000000..ade2168f2
--- /dev/null
+++ b/xpcom/threads/nsITimer.idl
@@ -0,0 +1,244 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsIObserver;
+interface nsIEventTarget;
+
+%{C++
+#include "mozilla/MemoryReporting.h"
+
+/**
+ * The signature of the timer callback function passed to initWithFuncCallback.
+ * This is the function that will get called when the timer expires if the
+ * timer is initialized via initWithFuncCallback.
+ *
+ * @param aTimer the timer which has expired
+ * @param aClosure opaque parameter passed to initWithFuncCallback
+ */
+class nsITimer;
+typedef void (*nsTimerCallbackFunc) (nsITimer *aTimer, void *aClosure);
+
+/**
+ * The signature of the timer name callback function passed to
+ * initWithNameableFuncCallback.
+ * This is the function that will get called when timer profiling is enabled
+ * via the "TimerFirings" log module.
+ *
+ * @param aTimer the timer which has expired
+ * @param aClosure opaque parameter passed to initWithFuncCallback
+ * @param aBuf a buffer in which to put the name
+ * @param aLen the length of the buffer
+ */
+typedef void (*nsTimerNameCallbackFunc) (nsITimer *aTimer, void *aClosure,
+ char *aBuf, size_t aLen);
+%}
+
+native nsTimerCallbackFunc(nsTimerCallbackFunc);
+native nsTimerNameCallbackFunc(nsTimerNameCallbackFunc);
+
+/**
+ * The callback interface for timers.
+ */
+interface nsITimer;
+
+[function, scriptable, uuid(a796816d-7d47-4348-9ab8-c7aeb3216a7d)]
+interface nsITimerCallback : nsISupports
+{
+ /**
+ * @param aTimer the timer which has expired
+ */
+ void notify(in nsITimer timer);
+};
+
+%{C++
+// Two timer deadlines must differ by less than half the PRIntervalTime domain.
+#define DELAY_INTERVAL_LIMIT PR_BIT(8 * sizeof(PRIntervalTime) - 1)
+%}
+
+/**
+ * nsITimer instances must be initialized by calling one of the "init" methods
+ * documented below. You may also re-initialize (using one of the init()
+ * methods) an existing instance to avoid the overhead of destroying and
+ * creating a timer. It is not necessary to cancel the timer in that case.
+ *
+ * By default a timer will fire on the thread that created it. Set the .target
+ * attribute to fire on a different thread. Once you have set a timer's .target
+ * and called one of its init functions, any further interactions with the timer
+ * (calling cancel(), changing member fields, etc) should only be done by the
+ * target thread, or races may occur with bad results like timers firing after
+ * they've been canceled, and/or not firing after re-initiatization.
+ */
+[scriptable, uuid(3de4b105-363c-482c-a409-baac83a01bfc)]
+interface nsITimer : nsISupports
+{
+ /* Timer types */
+
+ /**
+ * Type of a timer that fires once only.
+ */
+ const short TYPE_ONE_SHOT = 0;
+
+ /**
+ * After firing, a TYPE_REPEATING_SLACK timer is stopped and not restarted
+ * until its callback completes. Specified timer period will be at least
+ * the time between when processing for last firing the callback completes
+ * and when the next firing occurs.
+ *
+ * This is the preferable repeating type for most situations.
+ */
+ const short TYPE_REPEATING_SLACK = 1;
+
+ /**
+ * TYPE_REPEATING_PRECISE is just a synonym for
+ * TYPE_REPEATING_PRECISE_CAN_SKIP. They used to be distinct, but the old
+ * TYPE_REPEATING_PRECISE kind was similar to TYPE_REPEATING_PRECISE_CAN_SKIP
+ * while also being less useful. So the distinction was removed.
+ */
+ const short TYPE_REPEATING_PRECISE = 2;
+
+ /**
+ * A TYPE_REPEATING_PRECISE_CAN_SKIP repeating timer aims to have constant
+ * period between firings. The processing time for each timer callback
+ * should not influence the timer period. However this timer type
+ * guarantees that it will not queue up new events to fire the callback
+ * until the previous callback event finishes firing. If the callback
+ * takes a long time, then the next callback will be scheduled immediately
+ * afterward, but only once. This is the only non-slack timer available.
+ */
+ const short TYPE_REPEATING_PRECISE_CAN_SKIP = 3;
+
+ /**
+ * Initialize a timer that will fire after the said delay.
+ * A user must keep a reference to this timer till it is
+ * is no longer needed or has been cancelled.
+ *
+ * @param aObserver the callback object that observes the
+ * ``timer-callback'' topic with the subject being
+ * the timer itself when the timer fires:
+ *
+ * observe(nsISupports aSubject, => nsITimer
+ * string aTopic, => ``timer-callback''
+ * wstring data => null
+ *
+ * @param aDelay delay in milliseconds for timer to fire
+ * @param aType timer type per TYPE* consts defined above
+ */
+ void init(in nsIObserver aObserver, in unsigned long aDelay,
+ in unsigned long aType);
+
+
+ /**
+ * Initialize a timer to fire after the given millisecond interval.
+ * This version takes a function to call and a closure to pass to
+ * that function.
+ *
+ * @param aFunc The function to invoke
+ * @param aClosure An opaque pointer to pass to that function
+ * @param aDelay The millisecond interval
+ * @param aType Timer type per TYPE* consts defined above
+ */
+ [noscript] void initWithFuncCallback(in nsTimerCallbackFunc aCallback,
+ in voidPtr aClosure,
+ in unsigned long aDelay,
+ in unsigned long aType);
+
+ /**
+ * Initialize a timer to fire after the given millisecond interval.
+ * This version takes a function to call.
+ *
+ * @param aFunc nsITimerCallback interface to call when timer expires
+ * @param aDelay The millisecond interval
+ * @param aType Timer type per TYPE* consts defined above
+ */
+ void initWithCallback(in nsITimerCallback aCallback,
+ in unsigned long aDelay,
+ in unsigned long aType);
+
+ /**
+ * Cancel the timer. This method works on all types, not just on repeating
+ * timers -- you might want to cancel a TYPE_ONE_SHOT timer, and even reuse
+ * it by re-initializing it (to avoid object destruction and creation costs
+ * by conserving one timer instance).
+ */
+ void cancel();
+
+ /**
+ * Like initWithFuncCallback, but also takes a name for the timer; the name
+ * will be used when timer profiling is enabled via the "TimerFirings" log
+ * module.
+ *
+ * @param aFunc The function to invoke
+ * @param aClosure An opaque pointer to pass to that function
+ * @param aDelay The millisecond interval
+ * @param aType Timer type per TYPE* consts defined above
+ * @param aName The timer's name
+ */
+ [noscript] void initWithNamedFuncCallback(in nsTimerCallbackFunc aCallback,
+ in voidPtr aClosure,
+ in unsigned long aDelay,
+ in unsigned long aType,
+ in string aName);
+
+ /**
+ * Like initWithNamedFuncCallback, but instead of a timer name it takes a
+ * callback that will provide a name when the timer fires.
+ *
+ * @param aFunc The function to invoke
+ * @param aClosure An opaque pointer to pass to that function
+ * @param aDelay The millisecond interval
+ * @param aType Timer type per TYPE* consts defined above
+ * @param aNameCallback The callback function
+ */
+ [noscript] void initWithNameableFuncCallback(
+ in nsTimerCallbackFunc aCallback,
+ in voidPtr aClosure,
+ in unsigned long aDelay,
+ in unsigned long aType,
+ in nsTimerNameCallbackFunc aNameCallback);
+
+ /**
+ * The millisecond delay of the timeout.
+ *
+ * NOTE: Re-setting the delay on a one-shot timer that has already fired
+ * doesn't restart the timer. Call one of the init() methods to restart
+ * a one-shot timer.
+ */
+ attribute unsigned long delay;
+
+ /**
+ * The timer type - one of the above TYPE_* constants.
+ */
+ attribute unsigned long type;
+
+ /**
+ * The opaque pointer pass to initWithFuncCallback.
+ */
+ [noscript] readonly attribute voidPtr closure;
+
+ /**
+ * The nsITimerCallback object passed to initWithCallback.
+ */
+ readonly attribute nsITimerCallback callback;
+
+ /**
+ * The nsIEventTarget where the callback will be dispatched. Note that this
+ * target may only be set before the call to one of the init methods above.
+ *
+ * By default the target is the thread that created the timer.
+ */
+ attribute nsIEventTarget target;
+
+%{C++
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+%}
+};
+
+%{C++
+#define NS_TIMER_CONTRACTID "@mozilla.org/timer;1"
+#define NS_TIMER_CALLBACK_TOPIC "timer-callback"
+%}
+
diff --git a/xpcom/threads/nsMemoryPressure.cpp b/xpcom/threads/nsMemoryPressure.cpp
new file mode 100644
index 000000000..fea9b0437
--- /dev/null
+++ b/xpcom/threads/nsMemoryPressure.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMemoryPressure.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+static Atomic<int32_t, Relaxed> sMemoryPressurePending;
+static_assert(MemPressure_None == 0,
+ "Bad static initialization with the default constructor.");
+
+MemoryPressureState
+NS_GetPendingMemoryPressure()
+{
+ int32_t value = sMemoryPressurePending.exchange(MemPressure_None);
+ return MemoryPressureState(value);
+}
+
+void
+NS_DispatchEventualMemoryPressure(MemoryPressureState aState)
+{
+ /*
+ * A new memory pressure event erases an ongoing memory pressure, but an
+ * existing "new" memory pressure event takes precedence over a new "ongoing"
+ * memory pressure event.
+ */
+ switch (aState) {
+ case MemPressure_None:
+ sMemoryPressurePending = MemPressure_None;
+ break;
+ case MemPressure_New:
+ sMemoryPressurePending = MemPressure_New;
+ break;
+ case MemPressure_Ongoing:
+ sMemoryPressurePending.compareExchange(MemPressure_None,
+ MemPressure_Ongoing);
+ break;
+ }
+}
+
+nsresult
+NS_DispatchMemoryPressure(MemoryPressureState aState)
+{
+ NS_DispatchEventualMemoryPressure(aState);
+ nsCOMPtr<nsIRunnable> event = new Runnable;
+ return NS_DispatchToMainThread(event);
+}
diff --git a/xpcom/threads/nsMemoryPressure.h b/xpcom/threads/nsMemoryPressure.h
new file mode 100644
index 000000000..a05728c61
--- /dev/null
+++ b/xpcom/threads/nsMemoryPressure.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMemoryPressure_h__
+#define nsMemoryPressure_h__
+
+#include "nscore.h"
+
+enum MemoryPressureState
+{
+ /*
+ * No memory pressure.
+ */
+ MemPressure_None = 0,
+
+ /*
+ * New memory pressure deteced.
+ *
+ * On a new memory pressure, we stop everything to start cleaning
+ * aggresively the memory used, in order to free as much memory as
+ * possible.
+ */
+ MemPressure_New,
+
+ /*
+ * Repeated memory pressure.
+ *
+ * A repeated memory pressure implies to clean softly recent allocations.
+ * It is supposed to happen after a new memory pressure which already
+ * cleaned aggressivley. So there is no need to damage the reactivity of
+ * Gecko by stopping the world again.
+ *
+ * In case of conflict with an new memory pressue, the new memory pressure
+ * takes precedence over an ongoing memory pressure. The reason being
+ * that if no events are processed between 2 notifications (new followed
+ * by ongoing, or ongoing followed by a new) we want to be as aggresive as
+ * possible on the clean-up of the memory. After all, we are trying to
+ * keep Gecko alive as long as possible.
+ */
+ MemPressure_Ongoing
+};
+
+/**
+ * Return and erase the latest state of the memory pressure event set by any of
+ * the corresponding dispatch function.
+ */
+MemoryPressureState
+NS_GetPendingMemoryPressure();
+
+/**
+ * This function causes the main thread to fire a memory pressure event
+ * before processing the next event, but if there are no events pending in
+ * the main thread's event queue, the memory pressure event would not be
+ * dispatched until one is enqueued. It is infallible and does not allocate
+ * any memory.
+ *
+ * You may call this function from any thread.
+ */
+void
+NS_DispatchEventualMemoryPressure(MemoryPressureState aState);
+
+/**
+ * This function causes the main thread to fire a memory pressure event
+ * before processing the next event. We wake up the main thread by adding a
+ * dummy event to its event loop, so, unlike with
+ * NS_DispatchEventualMemoryPressure, this memory-pressure event is always
+ * fired relatively quickly, even if the event loop is otherwise empty.
+ *
+ * You may call this function from any thread.
+ */
+nsresult
+NS_DispatchMemoryPressure(MemoryPressureState aState);
+
+#endif // nsMemoryPressure_h__
diff --git a/xpcom/threads/nsProcess.h b/xpcom/threads/nsProcess.h
new file mode 100644
index 000000000..140944415
--- /dev/null
+++ b/xpcom/threads/nsProcess.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsPROCESSWIN_H_
+#define _nsPROCESSWIN_H_
+
+#if defined(XP_WIN)
+#define PROCESSMODEL_WINAPI
+#endif
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "nsIProcess.h"
+#include "nsIFile.h"
+#include "nsIThread.h"
+#include "nsIObserver.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+#ifndef XP_MACOSX
+#include "prproces.h"
+#endif
+#if defined(PROCESSMODEL_WINAPI)
+#include <windows.h>
+#include <shellapi.h>
+#endif
+
+#define NS_PROCESS_CID \
+{0x7b4eeb20, 0xd781, 0x11d4, \
+ {0x8A, 0x83, 0x00, 0x10, 0xa4, 0xe0, 0xc9, 0xca}}
+
+class nsProcess final
+ : public nsIProcess
+ , public nsIObserver
+{
+public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPROCESS
+ NS_DECL_NSIOBSERVER
+
+ nsProcess();
+
+private:
+ ~nsProcess();
+ static void Monitor(void* aArg);
+ void ProcessComplete();
+ nsresult CopyArgsAndRunProcess(bool aBlocking, const char** aArgs,
+ uint32_t aCount, nsIObserver* aObserver,
+ bool aHoldWeak);
+ nsresult CopyArgsAndRunProcessw(bool aBlocking, const char16_t** aArgs,
+ uint32_t aCount, nsIObserver* aObserver,
+ bool aHoldWeak);
+ // The 'args' array is null-terminated.
+ nsresult RunProcess(bool aBlocking, char** aArgs, nsIObserver* aObserver,
+ bool aHoldWeak, bool aArgsUTF8);
+
+ PRThread* mThread;
+ mozilla::Mutex mLock;
+ bool mShutdown;
+ bool mBlocking;
+
+ nsCOMPtr<nsIFile> mExecutable;
+ nsString mTargetPath;
+ int32_t mPid;
+ nsCOMPtr<nsIObserver> mObserver;
+ nsWeakPtr mWeakObserver;
+
+ // These members are modified by multiple threads, any accesses should be
+ // protected with mLock.
+ int32_t mExitValue;
+#if defined(PROCESSMODEL_WINAPI)
+ HANDLE mProcess;
+#elif !defined(XP_MACOSX)
+ PRProcess* mProcess;
+#endif
+};
+
+#endif
diff --git a/xpcom/threads/nsProcessCommon.cpp b/xpcom/threads/nsProcessCommon.cpp
new file mode 100644
index 000000000..709865a09
--- /dev/null
+++ b/xpcom/threads/nsProcessCommon.cpp
@@ -0,0 +1,663 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*****************************************************************************
+ *
+ * nsProcess is used to execute new processes and specify if you want to
+ * wait (blocking) or continue (non-blocking).
+ *
+ *****************************************************************************
+ */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsMemory.h"
+#include "nsProcess.h"
+#include "prio.h"
+#include "prenv.h"
+#include "nsCRT.h"
+#include "nsThreadUtils.h"
+#include "nsIObserverService.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/Services.h"
+
+#include <stdlib.h>
+
+#if defined(PROCESSMODEL_WINAPI)
+#include "prmem.h"
+#include "nsString.h"
+#include "nsLiteralString.h"
+#include "nsReadableUtils.h"
+#else
+#ifdef XP_MACOSX
+#include <crt_externs.h>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#endif
+#include <sys/types.h>
+#include <signal.h>
+#endif
+
+using namespace mozilla;
+
+#ifdef XP_MACOSX
+cpu_type_t pref_cpu_types[2] = {
+#if defined(__i386__)
+ CPU_TYPE_X86,
+#elif defined(__x86_64__)
+ CPU_TYPE_X86_64,
+#elif defined(__ppc__)
+ CPU_TYPE_POWERPC,
+#endif
+ CPU_TYPE_ANY
+};
+#endif
+
+//-------------------------------------------------------------------//
+// nsIProcess implementation
+//-------------------------------------------------------------------//
+NS_IMPL_ISUPPORTS(nsProcess, nsIProcess,
+ nsIObserver)
+
+//Constructor
+nsProcess::nsProcess()
+ : mThread(nullptr)
+ , mLock("nsProcess.mLock")
+ , mShutdown(false)
+ , mBlocking(false)
+ , mPid(-1)
+ , mObserver(nullptr)
+ , mWeakObserver(nullptr)
+ , mExitValue(-1)
+#if !defined(XP_MACOSX)
+ , mProcess(nullptr)
+#endif
+{
+}
+
+//Destructor
+nsProcess::~nsProcess()
+{
+}
+
+NS_IMETHODIMP
+nsProcess::Init(nsIFile* aExecutable)
+{
+ if (mExecutable) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ if (NS_WARN_IF(!aExecutable)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ bool isFile;
+
+ //First make sure the file exists
+ nsresult rv = aExecutable->IsFile(&isFile);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!isFile) {
+ return NS_ERROR_FAILURE;
+ }
+
+ //Store the nsIFile in mExecutable
+ mExecutable = aExecutable;
+ //Get the path because it is needed by the NSPR process creation
+#ifdef XP_WIN
+ rv = mExecutable->GetTarget(mTargetPath);
+ if (NS_FAILED(rv) || mTargetPath.IsEmpty())
+#endif
+ rv = mExecutable->GetPath(mTargetPath);
+
+ return rv;
+}
+
+
+#if defined(XP_WIN)
+// Out param `aWideCmdLine` must be PR_Freed by the caller.
+static int
+assembleCmdLine(char* const* aArgv, wchar_t** aWideCmdLine, UINT aCodePage)
+{
+ char* const* arg;
+ char* p;
+ char* q;
+ char* cmdLine;
+ int cmdLineSize;
+ int numBackslashes;
+ int i;
+ int argNeedQuotes;
+
+ /*
+ * Find out how large the command line buffer should be.
+ */
+ cmdLineSize = 0;
+ for (arg = aArgv; *arg; ++arg) {
+ /*
+ * \ and " need to be escaped by a \. In the worst case,
+ * every character is a \ or ", so the string of length
+ * may double. If we quote an argument, that needs two ".
+ * Finally, we need a space between arguments, and
+ * a null byte at the end of command line.
+ */
+ cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
+ + 2 /* we quote every argument */
+ + 1; /* space in between, or final null */
+ }
+ p = cmdLine = (char*)PR_MALLOC(cmdLineSize * sizeof(char));
+ if (!p) {
+ return -1;
+ }
+
+ for (arg = aArgv; *arg; ++arg) {
+ /* Add a space to separates the arguments */
+ if (arg != aArgv) {
+ *p++ = ' ';
+ }
+ q = *arg;
+ numBackslashes = 0;
+ argNeedQuotes = 0;
+
+ /* If the argument contains white space, it needs to be quoted. */
+ if (strpbrk(*arg, " \f\n\r\t\v")) {
+ argNeedQuotes = 1;
+ }
+
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ while (*q) {
+ if (*q == '\\') {
+ numBackslashes++;
+ q++;
+ } else if (*q == '"') {
+ if (numBackslashes) {
+ /*
+ * Double the backslashes since they are followed
+ * by a quote
+ */
+ for (i = 0; i < 2 * numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ /* To escape the quote */
+ *p++ = '\\';
+ *p++ = *q++;
+ } else {
+ if (numBackslashes) {
+ /*
+ * Backslashes are not followed by a quote, so
+ * don't need to double the backslashes.
+ */
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ *p++ = *q++;
+ }
+ }
+
+ /* Now we are at the end of this argument */
+ if (numBackslashes) {
+ /*
+ * Double the backslashes if we have a quote string
+ * delimiter at the end.
+ */
+ if (argNeedQuotes) {
+ numBackslashes *= 2;
+ }
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ }
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ }
+
+ *p = '\0';
+ int32_t numChars = MultiByteToWideChar(aCodePage, 0, cmdLine, -1, nullptr, 0);
+ *aWideCmdLine = (wchar_t*)PR_MALLOC(numChars * sizeof(wchar_t));
+ MultiByteToWideChar(aCodePage, 0, cmdLine, -1, *aWideCmdLine, numChars);
+ PR_Free(cmdLine);
+ return 0;
+}
+#endif
+
+void
+nsProcess::Monitor(void* aArg)
+{
+ RefPtr<nsProcess> process = dont_AddRef(static_cast<nsProcess*>(aArg));
+
+ if (!process->mBlocking) {
+ PR_SetCurrentThreadName("RunProcess");
+ }
+
+#if defined(PROCESSMODEL_WINAPI)
+ DWORD dwRetVal;
+ unsigned long exitCode = -1;
+
+ dwRetVal = WaitForSingleObject(process->mProcess, INFINITE);
+ if (dwRetVal != WAIT_FAILED) {
+ if (GetExitCodeProcess(process->mProcess, &exitCode) == FALSE) {
+ exitCode = -1;
+ }
+ }
+
+ // Lock in case Kill or GetExitCode are called during this
+ {
+ MutexAutoLock lock(process->mLock);
+ CloseHandle(process->mProcess);
+ process->mProcess = nullptr;
+ process->mExitValue = exitCode;
+ if (process->mShutdown) {
+ return;
+ }
+ }
+#else
+#ifdef XP_MACOSX
+ int exitCode = -1;
+ int status = 0;
+ pid_t result;
+ do {
+ result = waitpid(process->mPid, &status, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == process->mPid) {
+ if (WIFEXITED(status)) {
+ exitCode = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ exitCode = 256; // match NSPR's signal exit status
+ }
+ }
+#else
+ int32_t exitCode = -1;
+ if (PR_WaitProcess(process->mProcess, &exitCode) != PR_SUCCESS) {
+ exitCode = -1;
+ }
+#endif
+
+ // Lock in case Kill or GetExitCode are called during this
+ {
+ MutexAutoLock lock(process->mLock);
+#if !defined(XP_MACOSX)
+ process->mProcess = nullptr;
+#endif
+ process->mExitValue = exitCode;
+ if (process->mShutdown) {
+ return;
+ }
+ }
+#endif
+
+ // If we ran a background thread for the monitor then notify on the main
+ // thread
+ if (NS_IsMainThread()) {
+ process->ProcessComplete();
+ } else {
+ NS_DispatchToMainThread(NewRunnableMethod(process, &nsProcess::ProcessComplete));
+ }
+}
+
+void
+nsProcess::ProcessComplete()
+{
+ if (mThread) {
+ nsCOMPtr<nsIObserverService> os =
+ mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "xpcom-shutdown");
+ }
+ PR_JoinThread(mThread);
+ mThread = nullptr;
+ }
+
+ const char* topic;
+ if (mExitValue < 0) {
+ topic = "process-failed";
+ } else {
+ topic = "process-finished";
+ }
+
+ mPid = -1;
+ nsCOMPtr<nsIObserver> observer;
+ if (mWeakObserver) {
+ observer = do_QueryReferent(mWeakObserver);
+ } else if (mObserver) {
+ observer = mObserver;
+ }
+ mObserver = nullptr;
+ mWeakObserver = nullptr;
+
+ if (observer) {
+ observer->Observe(NS_ISUPPORTS_CAST(nsIProcess*, this), topic, nullptr);
+ }
+}
+
+// XXXldb |aArgs| has the wrong const-ness
+NS_IMETHODIMP
+nsProcess::Run(bool aBlocking, const char** aArgs, uint32_t aCount)
+{
+ return CopyArgsAndRunProcess(aBlocking, aArgs, aCount, nullptr, false);
+}
+
+// XXXldb |aArgs| has the wrong const-ness
+NS_IMETHODIMP
+nsProcess::RunAsync(const char** aArgs, uint32_t aCount,
+ nsIObserver* aObserver, bool aHoldWeak)
+{
+ return CopyArgsAndRunProcess(false, aArgs, aCount, aObserver, aHoldWeak);
+}
+
+nsresult
+nsProcess::CopyArgsAndRunProcess(bool aBlocking, const char** aArgs,
+ uint32_t aCount, nsIObserver* aObserver,
+ bool aHoldWeak)
+{
+ // Add one to the aCount for the program name and one for null termination.
+ char** my_argv = nullptr;
+ my_argv = (char**)moz_xmalloc(sizeof(char*) * (aCount + 2));
+ if (!my_argv) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ my_argv[0] = ToNewUTF8String(mTargetPath);
+
+ for (uint32_t i = 0; i < aCount; ++i) {
+ my_argv[i + 1] = const_cast<char*>(aArgs[i]);
+ }
+
+ my_argv[aCount + 1] = nullptr;
+
+ nsresult rv = RunProcess(aBlocking, my_argv, aObserver, aHoldWeak, false);
+
+ free(my_argv[0]);
+ free(my_argv);
+ return rv;
+}
+
+// XXXldb |aArgs| has the wrong const-ness
+NS_IMETHODIMP
+nsProcess::Runw(bool aBlocking, const char16_t** aArgs, uint32_t aCount)
+{
+ return CopyArgsAndRunProcessw(aBlocking, aArgs, aCount, nullptr, false);
+}
+
+// XXXldb |aArgs| has the wrong const-ness
+NS_IMETHODIMP
+nsProcess::RunwAsync(const char16_t** aArgs, uint32_t aCount,
+ nsIObserver* aObserver, bool aHoldWeak)
+{
+ return CopyArgsAndRunProcessw(false, aArgs, aCount, aObserver, aHoldWeak);
+}
+
+nsresult
+nsProcess::CopyArgsAndRunProcessw(bool aBlocking, const char16_t** aArgs,
+ uint32_t aCount, nsIObserver* aObserver,
+ bool aHoldWeak)
+{
+ // Add one to the aCount for the program name and one for null termination.
+ char** my_argv = nullptr;
+ my_argv = (char**)moz_xmalloc(sizeof(char*) * (aCount + 2));
+ if (!my_argv) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ my_argv[0] = ToNewUTF8String(mTargetPath);
+
+ for (uint32_t i = 0; i < aCount; i++) {
+ my_argv[i + 1] = ToNewUTF8String(nsDependentString(aArgs[i]));
+ }
+
+ my_argv[aCount + 1] = nullptr;
+
+ nsresult rv = RunProcess(aBlocking, my_argv, aObserver, aHoldWeak, true);
+
+ for (uint32_t i = 0; i <= aCount; ++i) {
+ free(my_argv[i]);
+ }
+ free(my_argv);
+ return rv;
+}
+
+nsresult
+nsProcess::RunProcess(bool aBlocking, char** aMyArgv, nsIObserver* aObserver,
+ bool aHoldWeak, bool aArgsUTF8)
+{
+ NS_WARNING_ASSERTION(!XRE_IsContentProcess(),
+ "No launching of new processes in the content process");
+
+ if (NS_WARN_IF(!mExecutable)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (NS_WARN_IF(mThread)) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ if (aObserver) {
+ if (aHoldWeak) {
+ mWeakObserver = do_GetWeakReference(aObserver);
+ if (!mWeakObserver) {
+ return NS_NOINTERFACE;
+ }
+ } else {
+ mObserver = aObserver;
+ }
+ }
+
+ mExitValue = -1;
+ mPid = -1;
+
+#if defined(PROCESSMODEL_WINAPI)
+ BOOL retVal;
+ wchar_t* cmdLine = nullptr;
+
+ // |aMyArgv| is null-terminated and always starts with the program path. If
+ // the second slot is non-null then arguments are being passed.
+ if (aMyArgv[1] && assembleCmdLine(aMyArgv + 1, &cmdLine,
+ aArgsUTF8 ? CP_UTF8 : CP_ACP) == -1) {
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+
+ /* The SEE_MASK_NO_CONSOLE flag is important to prevent console windows
+ * from appearing. This makes behavior the same on all platforms. The flag
+ * will not have any effect on non-console applications.
+ */
+
+ // The program name in aMyArgv[0] is always UTF-8
+ NS_ConvertUTF8toUTF16 wideFile(aMyArgv[0]);
+
+ SHELLEXECUTEINFOW sinfo;
+ memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW));
+ sinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+ sinfo.hwnd = nullptr;
+ sinfo.lpFile = wideFile.get();
+ sinfo.nShow = SW_SHOWNORMAL;
+ sinfo.fMask = SEE_MASK_FLAG_DDEWAIT |
+ SEE_MASK_NO_CONSOLE |
+ SEE_MASK_NOCLOSEPROCESS;
+
+ if (cmdLine) {
+ sinfo.lpParameters = cmdLine;
+ }
+
+ retVal = ShellExecuteExW(&sinfo);
+ if (!retVal) {
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+
+ mProcess = sinfo.hProcess;
+
+ if (cmdLine) {
+ PR_Free(cmdLine);
+ }
+
+ mPid = GetProcessId(mProcess);
+#elif defined(XP_MACOSX)
+ // Initialize spawn attributes.
+ posix_spawnattr_t spawnattr;
+ if (posix_spawnattr_init(&spawnattr) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set spawn attributes.
+ size_t attr_count = ArrayLength(pref_cpu_types);
+ size_t attr_ocount = 0;
+ if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, pref_cpu_types,
+ &attr_ocount) != 0 ||
+ attr_ocount != attr_count) {
+ posix_spawnattr_destroy(&spawnattr);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Note: |aMyArgv| is already null-terminated as required by posix_spawnp.
+ pid_t newPid = 0;
+ int result = posix_spawnp(&newPid, aMyArgv[0], nullptr, &spawnattr, aMyArgv,
+ *_NSGetEnviron());
+ mPid = static_cast<int32_t>(newPid);
+
+ posix_spawnattr_destroy(&spawnattr);
+
+ if (result != 0) {
+ return NS_ERROR_FAILURE;
+ }
+#else
+ mProcess = PR_CreateProcess(aMyArgv[0], aMyArgv, nullptr, nullptr);
+ if (!mProcess) {
+ return NS_ERROR_FAILURE;
+ }
+ struct MYProcess
+ {
+ uint32_t pid;
+ };
+ MYProcess* ptrProc = (MYProcess*)mProcess;
+ mPid = ptrProc->pid;
+#endif
+
+ NS_ADDREF_THIS();
+ mBlocking = aBlocking;
+ if (aBlocking) {
+ Monitor(this);
+ if (mExitValue < 0) {
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+ } else {
+ mThread = PR_CreateThread(PR_SYSTEM_THREAD, Monitor, this,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (!mThread) {
+ NS_RELEASE_THIS();
+ return NS_ERROR_FAILURE;
+ }
+
+ // It isn't a failure if we just can't watch for shutdown
+ nsCOMPtr<nsIObserverService> os =
+ mozilla::services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "xpcom-shutdown", false);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProcess::GetIsRunning(bool* aIsRunning)
+{
+ if (mThread) {
+ *aIsRunning = true;
+ } else {
+ *aIsRunning = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProcess::GetPid(uint32_t* aPid)
+{
+ if (!mThread) {
+ return NS_ERROR_FAILURE;
+ }
+ if (mPid < 0) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ *aPid = mPid;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProcess::Kill()
+{
+ if (!mThread) {
+ return NS_ERROR_FAILURE;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+#if defined(PROCESSMODEL_WINAPI)
+ if (TerminateProcess(mProcess, 0) == 0) {
+ return NS_ERROR_FAILURE;
+ }
+#elif defined(XP_MACOSX)
+ if (kill(mPid, SIGKILL) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+#else
+ if (!mProcess || (PR_KillProcess(mProcess) != PR_SUCCESS)) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+ }
+
+ // We must null out mThread if we want IsRunning to return false immediately
+ // after this call.
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "xpcom-shutdown");
+ }
+ PR_JoinThread(mThread);
+ mThread = nullptr;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProcess::GetExitValue(int32_t* aExitValue)
+{
+ MutexAutoLock lock(mLock);
+
+ *aExitValue = mExitValue;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsProcess::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ // Shutting down, drop all references
+ if (mThread) {
+ nsCOMPtr<nsIObserverService> os =
+ mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "xpcom-shutdown");
+ }
+ mThread = nullptr;
+ }
+
+ mObserver = nullptr;
+ mWeakObserver = nullptr;
+
+ MutexAutoLock lock(mLock);
+ mShutdown = true;
+
+ return NS_OK;
+}
diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp
new file mode 100644
index 000000000..63bd28ca3
--- /dev/null
+++ b/xpcom/threads/nsThread.cpp
@@ -0,0 +1,1500 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThread.h"
+
+#include "base/message_loop.h"
+
+// Chromium's logging can sometimes leak through...
+#ifdef LOG
+#undef LOG
+#endif
+
+#include "mozilla/ReentrantMonitor.h"
+#include "nsMemoryPressure.h"
+#include "nsThreadManager.h"
+#include "nsIClassInfoImpl.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsQueryObject.h"
+#include "pratom.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/Logging.h"
+#include "nsIObserverService.h"
+#include "mozilla/HangMonitor.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/Services.h"
+#include "nsXPCOMPrivate.h"
+#include "mozilla/ChaosMode.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIIdlePeriod.h"
+#include "nsIIncrementalRunnable.h"
+#include "nsThreadSyncDispatch.h"
+#include "LeakRefPtr.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsServiceManagerUtils.h"
+#include "nsICrashReporter.h"
+#include "mozilla/dom/ContentChild.h"
+#endif
+
+#ifdef XP_LINUX
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sched.h>
+#endif
+
+#define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \
+ _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
+ !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+
+#if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
+#define HAVE_SCHED_SETAFFINITY
+#endif
+
+#ifdef XP_MACOSX
+#include <mach/mach.h>
+#include <mach/thread_policy.h>
+#endif
+
+#ifdef MOZ_CANARY
+# include <unistd.h>
+# include <execinfo.h>
+# include <signal.h>
+# include <fcntl.h>
+# include "nsXULAppAPI.h"
+#endif
+
+#if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
+#include "nsTimerImpl.h"
+#include "mozilla/StackWalk.h"
+#endif
+#ifdef NS_FUNCTION_TIMER
+#include "nsCRT.h"
+#endif
+
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+#include "TracedTaskCommon.h"
+using namespace mozilla::tasktracer;
+#endif
+
+using namespace mozilla;
+
+static LazyLogModule sThreadLog("nsThread");
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
+
+NS_DECL_CI_INTERFACE_GETTER(nsThread)
+
+//-----------------------------------------------------------------------------
+// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
+// somewhat manually.
+
+class nsThreadClassInfo : public nsIClassInfo
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
+ NS_DECL_NSICLASSINFO
+
+ nsThreadClassInfo()
+ {
+ }
+};
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsThreadClassInfo::AddRef()
+{
+ return 2;
+}
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsThreadClassInfo::Release()
+{
+ return 1;
+}
+NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
+{
+ return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
+}
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult)
+{
+ *aResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetContractID(char** aResult)
+{
+ *aResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetClassDescription(char** aResult)
+{
+ *aResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetClassID(nsCID** aResult)
+{
+ *aResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetFlags(uint32_t* aResult)
+{
+ *aResult = THREADSAFE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ADDREF(nsThread)
+NS_IMPL_RELEASE(nsThread)
+NS_INTERFACE_MAP_BEGIN(nsThread)
+ NS_INTERFACE_MAP_ENTRY(nsIThread)
+ NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
+ NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
+ if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
+ static nsThreadClassInfo sThreadClassInfo;
+ foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
+ } else
+NS_INTERFACE_MAP_END
+NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
+ nsIEventTarget, nsISupportsPriority)
+
+//-----------------------------------------------------------------------------
+
+class nsThreadStartupEvent : public Runnable
+{
+public:
+ nsThreadStartupEvent()
+ : mMon("nsThreadStartupEvent.mMon")
+ , mInitialized(false)
+ {
+ }
+
+ // This method does not return until the thread startup object is in the
+ // completion state.
+ void Wait()
+ {
+ ReentrantMonitorAutoEnter mon(mMon);
+ while (!mInitialized) {
+ mon.Wait();
+ }
+ }
+
+ // This method needs to be public to support older compilers (xlC_r on AIX).
+ // It should be called directly as this class type is reference counted.
+ virtual ~nsThreadStartupEvent() {}
+
+private:
+ NS_IMETHOD Run() override
+ {
+ ReentrantMonitorAutoEnter mon(mMon);
+ mInitialized = true;
+ mon.Notify();
+ return NS_OK;
+ }
+
+ ReentrantMonitor mMon;
+ bool mInitialized;
+};
+//-----------------------------------------------------------------------------
+
+namespace {
+class DelayedRunnable : public Runnable,
+ public nsITimerCallback
+{
+public:
+ DelayedRunnable(already_AddRefed<nsIThread> aTargetThread,
+ already_AddRefed<nsIRunnable> aRunnable,
+ uint32_t aDelay)
+ : mTargetThread(aTargetThread),
+ mWrappedRunnable(aRunnable),
+ mDelayedFrom(TimeStamp::NowLoRes()),
+ mDelay(aDelay)
+ { }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsresult Init()
+ {
+ nsresult rv;
+ mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MOZ_ASSERT(mTimer);
+ rv = mTimer->SetTarget(mTargetThread);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
+ }
+
+ nsresult DoRun()
+ {
+ nsCOMPtr<nsIRunnable> r = mWrappedRunnable.forget();
+ return r->Run();
+ }
+
+ NS_IMETHOD Run() override
+ {
+ // Already ran?
+ if (!mWrappedRunnable) {
+ return NS_OK;
+ }
+
+ // Are we too early?
+ if ((TimeStamp::NowLoRes() - mDelayedFrom).ToMilliseconds() < mDelay) {
+ return NS_OK; // Let the nsITimer run us.
+ }
+
+ mTimer->Cancel();
+ return DoRun();
+ }
+
+ NS_IMETHOD Notify(nsITimer* aTimer) override
+ {
+ // If we already ran, the timer should have been canceled.
+ MOZ_ASSERT(mWrappedRunnable);
+ MOZ_ASSERT(aTimer == mTimer);
+
+ return DoRun();
+ }
+
+private:
+ ~DelayedRunnable() {}
+
+ nsCOMPtr<nsIThread> mTargetThread;
+ nsCOMPtr<nsIRunnable> mWrappedRunnable;
+ nsCOMPtr<nsITimer> mTimer;
+ TimeStamp mDelayedFrom;
+ uint32_t mDelay;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(DelayedRunnable, Runnable, nsITimerCallback)
+
+} // anonymous namespace
+
+//-----------------------------------------------------------------------------
+
+struct nsThreadShutdownContext
+{
+ nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
+ NotNull<nsThread*> aJoiningThread,
+ bool aAwaitingShutdownAck)
+ : mTerminatingThread(aTerminatingThread)
+ , mJoiningThread(aJoiningThread)
+ , mAwaitingShutdownAck(aAwaitingShutdownAck)
+ {
+ MOZ_COUNT_CTOR(nsThreadShutdownContext);
+ }
+ ~nsThreadShutdownContext()
+ {
+ MOZ_COUNT_DTOR(nsThreadShutdownContext);
+ }
+
+ // NB: This will be the last reference.
+ NotNull<RefPtr<nsThread>> mTerminatingThread;
+ NotNull<nsThread*> mJoiningThread;
+ bool mAwaitingShutdownAck;
+};
+
+// This event is responsible for notifying nsThread::Shutdown that it is time
+// to call PR_JoinThread. It implements nsICancelableRunnable so that it can
+// run on a DOM Worker thread (where all events must implement
+// nsICancelableRunnable.)
+class nsThreadShutdownAckEvent : public CancelableRunnable
+{
+public:
+ explicit nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext*> aCtx)
+ : mShutdownContext(aCtx)
+ {
+ }
+ NS_IMETHOD Run() override
+ {
+ mShutdownContext->mTerminatingThread->ShutdownComplete(mShutdownContext);
+ return NS_OK;
+ }
+ nsresult Cancel() override
+ {
+ return Run();
+ }
+private:
+ virtual ~nsThreadShutdownAckEvent() { }
+
+ NotNull<nsThreadShutdownContext*> mShutdownContext;
+};
+
+// This event is responsible for setting mShutdownContext
+class nsThreadShutdownEvent : public Runnable
+{
+public:
+ nsThreadShutdownEvent(NotNull<nsThread*> aThr,
+ NotNull<nsThreadShutdownContext*> aCtx)
+ : mThread(aThr)
+ , mShutdownContext(aCtx)
+ {
+ }
+ NS_IMETHOD Run() override
+ {
+ mThread->mShutdownContext = mShutdownContext;
+ MessageLoop::current()->Quit();
+ return NS_OK;
+ }
+private:
+ NotNull<RefPtr<nsThread>> mThread;
+ NotNull<nsThreadShutdownContext*> mShutdownContext;
+};
+
+//-----------------------------------------------------------------------------
+
+static void
+SetThreadAffinity(unsigned int cpu)
+{
+#ifdef HAVE_SCHED_SETAFFINITY
+ cpu_set_t cpus;
+ CPU_ZERO(&cpus);
+ CPU_SET(cpu, &cpus);
+ sched_setaffinity(0, sizeof(cpus), &cpus);
+ // Don't assert sched_setaffinity's return value because it intermittently (?)
+ // fails with EINVAL on Linux x64 try runs.
+#elif defined(XP_MACOSX)
+ // OS X does not provide APIs to pin threads to specific processors, but you
+ // can tag threads as belonging to the same "affinity set" and the OS will try
+ // to run them on the same processor. To run threads on different processors,
+ // tag them as belonging to different affinity sets. Tag 0, the default, means
+ // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
+ thread_affinity_policy_data_t policy;
+ policy.affinity_tag = cpu + 1;
+ MOZ_ALWAYS_TRUE(thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
+ &policy.affinity_tag, 1) == KERN_SUCCESS);
+#elif defined(XP_WIN)
+ MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu) != -1);
+#endif
+}
+
+static void
+SetupCurrentThreadForChaosMode()
+{
+ if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
+ return;
+ }
+
+#ifdef XP_LINUX
+ // PR_SetThreadPriority doesn't really work since priorities >
+ // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
+ // setpriority(2) to set random 'nice values'. In regular Linux this is only
+ // a dynamic adjustment so it still doesn't really do what we want, but tools
+ // like 'rr' can be more aggressive about honoring these values.
+ // Some of these calls may fail due to trying to lower the priority
+ // (e.g. something may have already called setpriority() for this thread).
+ // This makes it hard to have non-main threads with higher priority than the
+ // main thread, but that's hard to fix. Tools like rr can choose to honor the
+ // requested values anyway.
+ // Use just 4 priorities so there's a reasonable chance of any two threads
+ // having equal priority.
+ setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
+#else
+ // We should set the affinity here but NSPR doesn't provide a way to expose it.
+ uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
+ PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
+#endif
+
+ // Force half the threads to CPU 0 so they compete for CPU
+ if (ChaosMode::randomUint32LessThan(2)) {
+ SetThreadAffinity(0);
+ }
+}
+
+/*static*/ void
+nsThread::ThreadFunc(void* aArg)
+{
+ using mozilla::ipc::BackgroundChild;
+
+ nsThread* self = static_cast<nsThread*>(aArg); // strong reference
+ self->mThread = PR_GetCurrentThread();
+ SetupCurrentThreadForChaosMode();
+
+ // Inform the ThreadManager
+ nsThreadManager::get().RegisterCurrentThread(*self);
+
+ mozilla::IOInterposer::RegisterCurrentThread();
+
+ // Wait for and process startup event
+ nsCOMPtr<nsIRunnable> event;
+ {
+ MutexAutoLock lock(self->mLock);
+ if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) {
+ NS_WARNING("failed waiting for thread startup event");
+ return;
+ }
+ }
+ event->Run(); // unblocks nsThread::Init
+ event = nullptr;
+
+ {
+ // Scope for MessageLoop.
+ nsAutoPtr<MessageLoop> loop(
+ new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self));
+
+ // Now, process incoming events...
+ loop->Run();
+
+ BackgroundChild::CloseForCurrentThread();
+
+ // NB: The main thread does not shut down here! It shuts down via
+ // nsThreadManager::Shutdown.
+
+ // Do NS_ProcessPendingEvents but with special handling to set
+ // mEventsAreDoomed atomically with the removal of the last event. The key
+ // invariant here is that we will never permit PutEvent to succeed if the
+ // event would be left in the queue after our final call to
+ // NS_ProcessPendingEvents. We also have to keep processing events as long
+ // as we have outstanding mRequestedShutdownContexts.
+ while (true) {
+ // Check and see if we're waiting on any threads.
+ self->WaitForAllAsynchronousShutdowns();
+
+ {
+ MutexAutoLock lock(self->mLock);
+ if (!self->mEvents->HasPendingEvent(lock)) {
+ // No events in the queue, so we will stop now. Don't let any more
+ // events be added, since they won't be processed. It is critical
+ // that no PutEvent can occur between testing that the event queue is
+ // empty and setting mEventsAreDoomed!
+ self->mEventsAreDoomed = true;
+ break;
+ }
+ }
+ NS_ProcessPendingEvents(self);
+ }
+ }
+
+ mozilla::IOInterposer::UnregisterCurrentThread();
+
+ // Inform the threadmanager that this thread is going away
+ nsThreadManager::get().UnregisterCurrentThread(*self);
+
+ // Dispatch shutdown ACK
+ NotNull<nsThreadShutdownContext*> context =
+ WrapNotNull(self->mShutdownContext);
+ MOZ_ASSERT(context->mTerminatingThread == self);
+ event = do_QueryObject(new nsThreadShutdownAckEvent(context));
+ context->mJoiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
+
+ // Release any observer of the thread here.
+ self->SetObserver(nullptr);
+
+#ifdef MOZ_TASK_TRACER
+ FreeTraceInfo();
+#endif
+
+ NS_RELEASE(self);
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef MOZ_CRASHREPORTER
+// Tell the crash reporter to save a memory report if our heuristics determine
+// that an OOM failure is likely to occur soon.
+// Memory usage will not be checked more than every 30 seconds or saved more
+// than every 3 minutes
+// If |aShouldSave == kForceReport|, a report will be saved regardless of
+// whether the process is low on memory or not. However, it will still not be
+// saved if a report was saved less than 3 minutes ago.
+bool
+nsThread::SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)
+{
+ // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
+ // but save memory reports (expensive, ~75ms) less frequently.
+ const size_t kLowMemoryCheckSeconds = 30;
+ const size_t kLowMemorySaveSeconds = 3 * 60;
+
+ static TimeStamp nextCheck = TimeStamp::NowLoRes()
+ + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
+ static bool recentlySavedReport = false; // Keeps track of whether a report
+ // was saved last time we checked
+
+ // Are we checking again too soon?
+ TimeStamp now = TimeStamp::NowLoRes();
+ if ((aShouldSave == ShouldSaveMemoryReport::kMaybeReport ||
+ recentlySavedReport) && now < nextCheck) {
+ return false;
+ }
+
+ bool needMemoryReport = (aShouldSave == ShouldSaveMemoryReport::kForceReport);
+#ifdef XP_WIN // XXX implement on other platforms as needed
+ // If the report is forced there is no need to check whether it is necessary
+ if (aShouldSave != ShouldSaveMemoryReport::kForceReport) {
+ const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
+ MEMORYSTATUSEX statex;
+ statex.dwLength = sizeof(statex);
+ if (GlobalMemoryStatusEx(&statex)) {
+ if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
+ needMemoryReport = true;
+ }
+ }
+ }
+#endif
+
+ if (needMemoryReport) {
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+ if (cc) {
+ cc->SendNotifyLowMemory();
+ }
+ } else {
+ nsCOMPtr<nsICrashReporter> cr =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (cr) {
+ cr->SaveMemoryReport();
+ }
+ }
+ recentlySavedReport = true;
+ nextCheck = now + TimeDuration::FromSeconds(kLowMemorySaveSeconds);
+ } else {
+ recentlySavedReport = false;
+ nextCheck = now + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
+ }
+
+ return recentlySavedReport;
+}
+#endif
+
+#ifdef MOZ_CANARY
+int sCanaryOutputFD = -1;
+#endif
+
+nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
+ : mLock("nsThread.mLock")
+ , mScriptObserver(nullptr)
+ , mEvents(WrapNotNull(&mEventsRoot))
+ , mEventsRoot(mLock)
+ , mIdleEventsAvailable(mLock, "[nsThread.mEventsAvailable]")
+ , mIdleEvents(mIdleEventsAvailable, nsEventQueue::eNormalQueue)
+ , mPriority(PRIORITY_NORMAL)
+ , mThread(nullptr)
+ , mNestedEventLoopDepth(0)
+ , mStackSize(aStackSize)
+ , mShutdownContext(nullptr)
+ , mShutdownRequired(false)
+ , mEventsAreDoomed(false)
+ , mIsMainThread(aMainThread)
+ , mCanInvokeJS(false)
+{
+}
+
+nsThread::~nsThread()
+{
+ NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
+ "shouldn't be waiting on other threads to shutdown");
+#ifdef DEBUG
+ // We deliberately leak these so they can be tracked by the leak checker.
+ // If you're having nsThreadShutdownContext leaks, you can set:
+ // XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
+ // during a test run and that will at least tell you what thread is
+ // requesting shutdown on another, which can be helpful for diagnosing
+ // the leak.
+ for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
+ Unused << mRequestedShutdownContexts[i].forget();
+ }
+#endif
+}
+
+nsresult
+nsThread::Init()
+{
+ // spawn thread and wait until it is fully setup
+ RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
+
+ NS_ADDREF_THIS();
+
+ mIdlePeriod = new IdlePeriod();
+
+ mShutdownRequired = true;
+
+ // ThreadFunc is responsible for setting mThread
+ if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, mStackSize)) {
+ NS_RELEASE_THIS();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // ThreadFunc will wait for this event to be run before it tries to access
+ // mThread. By delaying insertion of this event into the queue, we ensure
+ // that mThread is set properly.
+ {
+ MutexAutoLock lock(mLock);
+ mEventsRoot.PutEvent(startup, lock); // retain a reference
+ }
+
+ // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
+ // initialization of ThreadFunc.
+ startup->Wait();
+ return NS_OK;
+}
+
+nsresult
+nsThread::InitCurrentThread()
+{
+ mThread = PR_GetCurrentThread();
+ SetupCurrentThreadForChaosMode();
+
+ mIdlePeriod = new IdlePeriod();
+
+ nsThreadManager::get().RegisterCurrentThread(*this);
+ return NS_OK;
+}
+
+nsresult
+nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return PutEvent(event.forget(), aTarget);
+}
+
+nsresult
+nsThread::PutEvent(already_AddRefed<nsIRunnable> aEvent, nsNestedEventTarget* aTarget)
+{
+ // We want to leak the reference when we fail to dispatch it, so that
+ // we won't release the event in a wrong thread.
+ LeakRefPtr<nsIRunnable> event(Move(aEvent));
+ nsCOMPtr<nsIThreadObserver> obs;
+
+ {
+ MutexAutoLock lock(mLock);
+ nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
+ if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
+ NS_WARNING("An event was posted to a thread that will never run it (rejected)");
+ return NS_ERROR_UNEXPECTED;
+ }
+ queue->PutEvent(event.take(), lock);
+
+ // Make sure to grab the observer before dropping the lock, otherwise the
+ // event that we just placed into the queue could run and eventually delete
+ // this nsThread before the calling thread is scheduled again. We would then
+ // crash while trying to access a dead nsThread.
+ obs = mObserver;
+ }
+
+ if (obs) {
+ obs->OnDispatchedEvent(this);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsThread::DispatchInternal(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags,
+ nsNestedEventTarget* aTarget)
+{
+ // We want to leak the reference when we fail to dispatch it, so that
+ // we won't release the event in a wrong thread.
+ LeakRefPtr<nsIRunnable> event(Move(aEvent));
+ if (NS_WARN_IF(!event)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
+ NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
+ return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+ }
+
+#ifdef MOZ_TASK_TRACER
+ nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
+ (static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
+ // XXX tracedRunnable will always leaked when we fail to disptch.
+ event = tracedRunnable.forget();
+#endif
+
+ if (aFlags & DISPATCH_SYNC) {
+ nsThread* thread = nsThreadManager::get().GetCurrentThread();
+ if (NS_WARN_IF(!thread)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // XXX we should be able to do something better here... we should
+ // be able to monitor the slot occupied by this event and use
+ // that to tell us when the event has been processed.
+
+ RefPtr<nsThreadSyncDispatch> wrapper =
+ new nsThreadSyncDispatch(thread, event.take());
+ nsresult rv = PutEvent(wrapper, aTarget); // hold a ref
+ // Don't wait for the event to finish if we didn't dispatch it...
+ if (NS_FAILED(rv)) {
+ // PutEvent leaked the wrapper runnable object on failure, so we
+ // explicitly release this object once for that. Note that this
+ // object will be released again soon because it exits the scope.
+ wrapper.get()->Release();
+ return rv;
+ }
+
+ // Allows waiting; ensure no locks are held that would deadlock us!
+ while (wrapper->IsPending()) {
+ NS_ProcessNextEvent(thread, true);
+ }
+ return NS_OK;
+ }
+
+ NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
+ aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
+ return PutEvent(event.take(), aTarget);
+}
+
+bool
+nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent,
+ mozilla::MutexAutoLock& aProofOfLock)
+{
+ bool retVal = false;
+ do {
+ if (mProcessSecondaryQueueRunnable) {
+ MOZ_ASSERT(mSecondaryQueue->HasPendingEvent(aProofOfLock));
+ retVal = mSecondaryQueue->GetEvent(aMayWait, aEvent, aProofOfLock);
+ MOZ_ASSERT(*aEvent);
+ mProcessSecondaryQueueRunnable = false;
+ return retVal;
+ }
+
+ // We don't want to wait if mSecondaryQueue has some events.
+ bool reallyMayWait =
+ aMayWait && !mSecondaryQueue->HasPendingEvent(aProofOfLock);
+ retVal =
+ mNormalQueue->GetEvent(reallyMayWait, aEvent, aProofOfLock);
+
+ // Let's see if we should next time process an event from the secondary
+ // queue.
+ mProcessSecondaryQueueRunnable =
+ mSecondaryQueue->HasPendingEvent(aProofOfLock);
+
+ if (*aEvent) {
+ // We got an event, return early.
+ return retVal;
+ }
+ } while(aMayWait || mProcessSecondaryQueueRunnable);
+
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// nsIEventTarget
+
+NS_IMETHODIMP
+nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+nsThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
+{
+ LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags));
+
+ return DispatchInternal(Move(aEvent), aFlags, nullptr);
+}
+
+NS_IMETHODIMP
+nsThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
+{
+ NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
+
+ RefPtr<DelayedRunnable> r = new DelayedRunnable(Move(do_AddRef(this)),
+ Move(aEvent),
+ aDelayMs);
+ nsresult rv = r->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return DispatchInternal(r.forget(), 0, nullptr);
+}
+
+NS_IMETHODIMP
+nsThread::IsOnCurrentThread(bool* aResult)
+{
+ *aResult = (PR_GetCurrentThread() == mThread);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsIThread
+
+NS_IMETHODIMP
+nsThread::GetPRThread(PRThread** aResult)
+{
+ *aResult = mThread;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::GetCanInvokeJS(bool* aResult)
+{
+ *aResult = mCanInvokeJS;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::SetCanInvokeJS(bool aCanInvokeJS)
+{
+ mCanInvokeJS = aCanInvokeJS;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::AsyncShutdown()
+{
+ LOG(("THRD(%p) async shutdown\n", this));
+
+ // XXX If we make this warn, then we hit that warning at xpcom shutdown while
+ // shutting down a thread in a thread pool. That happens b/c the thread
+ // in the thread pool is already shutdown by the thread manager.
+ if (!mThread) {
+ return NS_OK;
+ }
+
+ return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
+}
+
+nsThreadShutdownContext*
+nsThread::ShutdownInternal(bool aSync)
+{
+ MOZ_ASSERT(mThread);
+ MOZ_ASSERT(mThread != PR_GetCurrentThread());
+ if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
+ return nullptr;
+ }
+
+ // Prevent multiple calls to this method
+ {
+ MutexAutoLock lock(mLock);
+ if (!mShutdownRequired) {
+ return nullptr;
+ }
+ mShutdownRequired = false;
+ }
+
+ NotNull<nsThread*> currentThread =
+ WrapNotNull(nsThreadManager::get().GetCurrentThread());
+
+ nsAutoPtr<nsThreadShutdownContext>& context =
+ *currentThread->mRequestedShutdownContexts.AppendElement();
+ context = new nsThreadShutdownContext(WrapNotNull(this), currentThread, aSync);
+
+ // Set mShutdownContext and wake up the thread in case it is waiting for
+ // events to process.
+ nsCOMPtr<nsIRunnable> event =
+ new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context.get()));
+ // XXXroc What if posting the event fails due to OOM?
+ PutEvent(event.forget(), nullptr);
+
+ // We could still end up with other events being added after the shutdown
+ // task, but that's okay because we process pending events in ThreadFunc
+ // after setting mShutdownContext just before exiting.
+ return context;
+}
+
+void
+nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
+{
+ MOZ_ASSERT(mThread);
+ MOZ_ASSERT(aContext->mTerminatingThread == this);
+
+ if (aContext->mAwaitingShutdownAck) {
+ // We're in a synchronous shutdown, so tell whatever is up the stack that
+ // we're done and unwind the stack so it can call us again.
+ aContext->mAwaitingShutdownAck = false;
+ return;
+ }
+
+ // Now, it should be safe to join without fear of dead-locking.
+
+ PR_JoinThread(mThread);
+ mThread = nullptr;
+
+ // We hold strong references to our event observers, and once the thread is
+ // shut down the observers can't easily unregister themselves. Do it here
+ // to avoid leaking.
+ ClearObservers();
+
+#ifdef DEBUG
+ {
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
+ }
+#endif
+
+ // Delete aContext.
+ MOZ_ALWAYS_TRUE(
+ aContext->mJoiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
+}
+
+void
+nsThread::WaitForAllAsynchronousShutdowns()
+{
+ while (mRequestedShutdownContexts.Length()) {
+ NS_ProcessNextEvent(this, true);
+ }
+}
+
+NS_IMETHODIMP
+nsThread::Shutdown()
+{
+ LOG(("THRD(%p) sync shutdown\n", this));
+
+ // XXX If we make this warn, then we hit that warning at xpcom shutdown while
+ // shutting down a thread in a thread pool. That happens b/c the thread
+ // in the thread pool is already shutdown by the thread manager.
+ if (!mThread) {
+ return NS_OK;
+ }
+
+ nsThreadShutdownContext* maybeContext = ShutdownInternal(/* aSync = */ true);
+ NS_ENSURE_TRUE(maybeContext, NS_ERROR_UNEXPECTED);
+ NotNull<nsThreadShutdownContext*> context = WrapNotNull(maybeContext);
+
+ // Process events on the current thread until we receive a shutdown ACK.
+ // Allows waiting; ensure no locks are held that would deadlock us!
+ while (context->mAwaitingShutdownAck) {
+ NS_ProcessNextEvent(context->mJoiningThread, true);
+ }
+
+ ShutdownComplete(context);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::HasPendingEvents(bool* aResult)
+{
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+ *aResult = mEvents->HasPendingEvent(lock);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)
+{
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ MutexAutoLock lock(mLock);
+ mIdlePeriod = aIdlePeriod;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent)
+{
+ // Currently the only supported idle dispatch is from the same
+ // thread. To support idle dispatch from another thread we need to
+ // support waking threads that are waiting for an event queue that
+ // isn't mIdleEvents.
+ MOZ_ASSERT(PR_GetCurrentThread() == mThread);
+
+ MutexAutoLock lock(mLock);
+ LeakRefPtr<nsIRunnable> event(Move(aEvent));
+
+ if (NS_WARN_IF(!event)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (mEventsAreDoomed) {
+ NS_WARNING("An idle event was posted to a thread that will never run it (rejected)");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mIdleEvents.PutEvent(event.take(), lock);
+ return NS_OK;
+}
+
+#ifdef MOZ_CANARY
+void canary_alarm_handler(int signum);
+
+class Canary
+{
+ //XXX ToDo: support nested loops
+public:
+ Canary()
+ {
+ if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
+ signal(SIGALRM, canary_alarm_handler);
+ ualarm(15000, 0);
+ }
+ }
+
+ ~Canary()
+ {
+ if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
+ ualarm(0, 0);
+ }
+ }
+
+ static bool EventLatencyIsImportant()
+ {
+ return NS_IsMainThread() && XRE_IsParentProcess();
+ }
+};
+
+void canary_alarm_handler(int signum)
+{
+ void* array[30];
+ const char msg[29] = "event took too long to run:\n";
+ // use write to be safe in the signal handler
+ write(sCanaryOutputFD, msg, sizeof(msg));
+ backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
+}
+
+#endif
+
+#define NOTIFY_EVENT_OBSERVERS(func_, params_) \
+ PR_BEGIN_MACRO \
+ if (!mEventObservers.IsEmpty()) { \
+ nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2>::ForwardIterator \
+ iter_(mEventObservers); \
+ nsCOMPtr<nsIThreadObserver> obs_; \
+ while (iter_.HasMore()) { \
+ obs_ = iter_.GetNext(); \
+ obs_ -> func_ params_ ; \
+ } \
+ } \
+ PR_END_MACRO
+
+void
+nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == mThread);
+ MOZ_ASSERT(aEvent);
+
+ TimeStamp idleDeadline;
+ {
+ MutexAutoUnlock unlock(mLock);
+ mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
+ }
+
+ if (!idleDeadline || idleDeadline < TimeStamp::Now()) {
+ aEvent = nullptr;
+ return;
+ }
+
+ mIdleEvents.GetEvent(false, aEvent, aProofOfLock);
+
+ if (*aEvent) {
+ nsCOMPtr<nsIIncrementalRunnable> incrementalEvent(do_QueryInterface(*aEvent));
+ if (incrementalEvent) {
+ incrementalEvent->SetDeadline(idleDeadline);
+ }
+ }
+}
+
+void
+nsThread::GetEvent(bool aWait, nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == mThread);
+ MOZ_ASSERT(aEvent);
+
+ // We'll try to get an event to execute in three stages.
+ // [1] First we just try to get it from the regular queue without waiting.
+ mEvents->GetEvent(false, aEvent, aProofOfLock);
+
+ // [2] If we didn't get an event from the regular queue, try to
+ // get one from the idle queue
+ if (!*aEvent) {
+ // Since events in mEvents have higher priority than idle
+ // events, we will only consider idle events when there are no
+ // pending events in mEvents. We will for the same reason never
+ // wait for an idle event, since a higher priority event might
+ // appear at any time.
+ GetIdleEvent(aEvent, aProofOfLock);
+ }
+
+ // [3] If we neither got an event from the regular queue nor the
+ // idle queue, then if we should wait for events we block on the
+ // main queue until an event is available.
+ // If we are shutting down, then do not wait for new events.
+ if (!*aEvent && aWait) {
+ mEvents->GetEvent(aWait, aEvent, aProofOfLock);
+ }
+}
+
+NS_IMETHODIMP
+nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
+{
+ LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
+ mNestedEventLoopDepth));
+
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ // The toplevel event loop normally blocks waiting for the next event, but
+ // if we're trying to shut this thread down, we must exit the event loop when
+ // the event queue is empty.
+ // This only applys to the toplevel event loop! Nested event loops (e.g.
+ // during sync dispatch) are waiting for some state change and must be able
+ // to block even if something has requested shutdown of the thread. Otherwise
+ // we'll just busywait as we endlessly look for an event, fail to find one,
+ // and repeat the nested event loop since its state change hasn't happened yet.
+ bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
+
+ if (mIsMainThread == MAIN_THREAD) {
+ DoMainThreadSpecificProcessing(reallyWait);
+ }
+
+ ++mNestedEventLoopDepth;
+
+ // We only want to create an AutoNoJSAPI on threads that actually do DOM stuff
+ // (including workers). Those are exactly the threads that have an
+ // mScriptObserver.
+ Maybe<dom::AutoNoJSAPI> noJSAPI;
+ bool callScriptObserver = !!mScriptObserver;
+ if (callScriptObserver) {
+ noJSAPI.emplace();
+ mScriptObserver->BeforeProcessTask(reallyWait);
+ }
+
+ nsCOMPtr<nsIThreadObserver> obs = mObserver;
+ if (obs) {
+ obs->OnProcessNextEvent(this, reallyWait);
+ }
+
+ NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait));
+
+#ifdef MOZ_CANARY
+ Canary canary;
+#endif
+ nsresult rv = NS_OK;
+
+ {
+ // Scope for |event| to make sure that its destructor fires while
+ // mNestedEventLoopDepth has been incremented, since that destructor can
+ // also do work.
+ nsCOMPtr<nsIRunnable> event;
+ {
+ MutexAutoLock lock(mLock);
+ GetEvent(reallyWait, getter_AddRefs(event), lock);
+ }
+
+ *aResult = (event.get() != nullptr);
+
+ if (event) {
+ LOG(("THRD(%p) running [%p]\n", this, event.get()));
+ if (MAIN_THREAD == mIsMainThread) {
+ HangMonitor::NotifyActivity();
+ }
+ event->Run();
+ } else if (aMayWait) {
+ MOZ_ASSERT(ShuttingDown(),
+ "This should only happen when shutting down");
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult));
+
+ if (obs) {
+ obs->AfterProcessNextEvent(this, *aResult);
+ }
+
+ if (callScriptObserver) {
+ if (mScriptObserver) {
+ mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
+ }
+ noJSAPI.reset();
+ }
+
+ --mNestedEventLoopDepth;
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+// nsISupportsPriority
+
+NS_IMETHODIMP
+nsThread::GetPriority(int32_t* aPriority)
+{
+ *aPriority = mPriority;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::SetPriority(int32_t aPriority)
+{
+ if (NS_WARN_IF(!mThread)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // NSPR defines the following four thread priorities:
+ // PR_PRIORITY_LOW
+ // PR_PRIORITY_NORMAL
+ // PR_PRIORITY_HIGH
+ // PR_PRIORITY_URGENT
+ // We map the priority values defined on nsISupportsPriority to these values.
+
+ mPriority = aPriority;
+
+ PRThreadPriority pri;
+ if (mPriority <= PRIORITY_HIGHEST) {
+ pri = PR_PRIORITY_URGENT;
+ } else if (mPriority < PRIORITY_NORMAL) {
+ pri = PR_PRIORITY_HIGH;
+ } else if (mPriority > PRIORITY_NORMAL) {
+ pri = PR_PRIORITY_LOW;
+ } else {
+ pri = PR_PRIORITY_NORMAL;
+ }
+ // If chaos mode is active, retain the randomly chosen priority
+ if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
+ PR_SetThreadPriority(mThread, pri);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::AdjustPriority(int32_t aDelta)
+{
+ return SetPriority(mPriority + aDelta);
+}
+
+//-----------------------------------------------------------------------------
+// nsIThreadInternal
+
+NS_IMETHODIMP
+nsThread::GetObserver(nsIThreadObserver** aObs)
+{
+ MutexAutoLock lock(mLock);
+ NS_IF_ADDREF(*aObs = mObserver);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::SetObserver(nsIThreadObserver* aObs)
+{
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ MutexAutoLock lock(mLock);
+ mObserver = aObs;
+ return NS_OK;
+}
+
+uint32_t
+nsThread::RecursionDepth() const
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == mThread);
+ return mNestedEventLoopDepth;
+}
+
+NS_IMETHODIMP
+nsThread::AddObserver(nsIThreadObserver* aObserver)
+{
+ if (NS_WARN_IF(!aObserver)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NS_WARNING_ASSERTION(!mEventObservers.Contains(aObserver),
+ "Adding an observer twice!");
+
+ if (!mEventObservers.AppendElement(WrapNotNull(aObserver))) {
+ NS_WARNING("Out of memory!");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::RemoveObserver(nsIThreadObserver* aObserver)
+{
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
+ NS_WARNING("Removing an observer that was never added!");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::PushEventQueue(nsIEventTarget** aResult)
+{
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ NotNull<nsChainedEventQueue*> queue =
+ WrapNotNull(new nsChainedEventQueue(mLock));
+ queue->mEventTarget = new nsNestedEventTarget(WrapNotNull(this), queue);
+
+ {
+ MutexAutoLock lock(mLock);
+ queue->mNext = mEvents;
+ mEvents = queue;
+ }
+
+ NS_ADDREF(*aResult = queue->mEventTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
+{
+ if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ if (NS_WARN_IF(!aInnermostTarget)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // Don't delete or release anything while holding the lock.
+ nsAutoPtr<nsChainedEventQueue> queue;
+ RefPtr<nsNestedEventTarget> target;
+
+ {
+ MutexAutoLock lock(mLock);
+
+ // Make sure we're popping the innermost event target.
+ if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ MOZ_ASSERT(mEvents != &mEventsRoot);
+
+ queue = mEvents;
+ mEvents = WrapNotNull(mEvents->mNext);
+
+ nsCOMPtr<nsIRunnable> event;
+ while (queue->GetEvent(false, getter_AddRefs(event), lock)) {
+ mEvents->PutEvent(event.forget(), lock);
+ }
+
+ // Don't let the event target post any more events.
+ queue->mEventTarget.swap(target);
+ target->mQueue = nullptr;
+ }
+
+ return NS_OK;
+}
+
+void
+nsThread::SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver)
+{
+ if (!aScriptObserver) {
+ mScriptObserver = nullptr;
+ return;
+ }
+
+ MOZ_ASSERT(!mScriptObserver);
+ mScriptObserver = aScriptObserver;
+}
+
+void
+nsThread::DoMainThreadSpecificProcessing(bool aReallyWait)
+{
+ MOZ_ASSERT(mIsMainThread == MAIN_THREAD);
+
+ ipc::CancelCPOWs();
+
+ if (aReallyWait) {
+ HangMonitor::Suspend();
+ }
+
+ // Fire a memory pressure notification, if one is pending.
+ if (!ShuttingDown()) {
+ MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
+ if (mpPending != MemPressure_None) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+
+ // Use no-forward to prevent the notifications from being transferred to
+ // the children of this process.
+ NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
+ NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
+
+ if (os) {
+ os->NotifyObservers(nullptr, "memory-pressure",
+ mpPending == MemPressure_New ? lowMem.get() :
+ lowMemOngoing.get());
+ } else {
+ NS_WARNING("Can't get observer service!");
+ }
+ }
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ if (!ShuttingDown()) {
+ SaveMemoryReportNearOOM(ShouldSaveMemoryReport::kMaybeReport);
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
+
+NS_IMETHODIMP
+nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+nsThread::nsNestedEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
+{
+ LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get().get(),
+ /*XXX aEvent*/ nullptr, aFlags, this));
+
+ return mThread->DispatchInternal(Move(aEvent), aFlags, this);
+}
+
+NS_IMETHODIMP
+nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
+{
+ return mThread->IsOnCurrentThread(aResult);
+}
diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h
new file mode 100644
index 000000000..836123747
--- /dev/null
+++ b/xpcom/threads/nsThread.h
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsThread_h__
+#define nsThread_h__
+
+#include "mozilla/Mutex.h"
+#include "nsIIdlePeriod.h"
+#include "nsIThreadInternal.h"
+#include "nsISupportsPriority.h"
+#include "nsEventQueue.h"
+#include "nsThreadUtils.h"
+#include "nsString.h"
+#include "nsTObserverArray.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/NotNull.h"
+#include "nsAutoPtr.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+class CycleCollectedJSContext;
+}
+
+using mozilla::NotNull;
+
+// A native thread
+class nsThread
+ : public nsIThreadInternal
+ , public nsISupportsPriority
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEVENTTARGET
+ NS_DECL_NSITHREAD
+ NS_DECL_NSITHREADINTERNAL
+ NS_DECL_NSISUPPORTSPRIORITY
+ using nsIEventTarget::Dispatch;
+
+ enum MainThreadFlag
+ {
+ MAIN_THREAD,
+ NOT_MAIN_THREAD
+ };
+
+ nsThread(MainThreadFlag aMainThread, uint32_t aStackSize);
+
+ // Initialize this as a wrapper for a new PRThread.
+ nsresult Init();
+
+ // Initialize this as a wrapper for the current PRThread.
+ nsresult InitCurrentThread();
+
+ // The PRThread corresponding to this thread.
+ PRThread* GetPRThread()
+ {
+ return mThread;
+ }
+
+ // If this flag is true, then the nsThread was created using
+ // nsIThreadManager::NewThread.
+ bool ShutdownRequired()
+ {
+ return mShutdownRequired;
+ }
+
+ // Clear the observer list.
+ void ClearObservers()
+ {
+ mEventObservers.Clear();
+ }
+
+ void
+ SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver);
+
+ uint32_t
+ RecursionDepth() const;
+
+ void ShutdownComplete(NotNull<struct nsThreadShutdownContext*> aContext);
+
+ void WaitForAllAsynchronousShutdowns();
+
+#ifdef MOZ_CRASHREPORTER
+ enum class ShouldSaveMemoryReport
+ {
+ kMaybeReport,
+ kForceReport
+ };
+
+ static bool SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave);
+#endif
+
+private:
+ void DoMainThreadSpecificProcessing(bool aReallyWait);
+
+ void GetIdleEvent(nsIRunnable** aEvent, mozilla::MutexAutoLock& aProofOfLock);
+ void GetEvent(bool aWait, nsIRunnable** aEvent,
+ mozilla::MutexAutoLock& aProofOfLock);
+
+protected:
+ class nsChainedEventQueue;
+
+ class nsNestedEventTarget;
+ friend class nsNestedEventTarget;
+
+ friend class nsThreadShutdownEvent;
+
+ virtual ~nsThread();
+
+ bool ShuttingDown()
+ {
+ return mShutdownContext != nullptr;
+ }
+
+ static void ThreadFunc(void* aArg);
+
+ // Helper
+ already_AddRefed<nsIThreadObserver> GetObserver()
+ {
+ nsIThreadObserver* obs;
+ nsThread::GetObserver(&obs);
+ return already_AddRefed<nsIThreadObserver>(obs);
+ }
+
+ // Wrappers for event queue methods:
+ nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget);
+ nsresult PutEvent(already_AddRefed<nsIRunnable> aEvent,
+ nsNestedEventTarget* aTarget);
+
+ nsresult DispatchInternal(already_AddRefed<nsIRunnable> aEvent,
+ uint32_t aFlags, nsNestedEventTarget* aTarget);
+
+ struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
+
+ // Wrapper for nsEventQueue that supports chaining.
+ class nsChainedEventQueue
+ {
+ public:
+ explicit nsChainedEventQueue(mozilla::Mutex& aLock)
+ : mNext(nullptr)
+ , mEventsAvailable(aLock, "[nsChainedEventQueue.mEventsAvailable]")
+ , mProcessSecondaryQueueRunnable(false)
+ {
+ mNormalQueue =
+ mozilla::MakeUnique<nsEventQueue>(mEventsAvailable,
+ nsEventQueue::eSharedCondVarQueue);
+ // Both queues need to use the same CondVar!
+ mSecondaryQueue =
+ mozilla::MakeUnique<nsEventQueue>(mEventsAvailable,
+ nsEventQueue::eSharedCondVarQueue);
+ }
+
+ bool GetEvent(bool aMayWait, nsIRunnable** aEvent,
+ mozilla::MutexAutoLock& aProofOfLock);
+
+ void PutEvent(nsIRunnable* aEvent, mozilla::MutexAutoLock& aProofOfLock)
+ {
+ RefPtr<nsIRunnable> event(aEvent);
+ PutEvent(event.forget(), aProofOfLock);
+ }
+
+ void PutEvent(already_AddRefed<nsIRunnable> aEvent,
+ mozilla::MutexAutoLock& aProofOfLock)
+ {
+ RefPtr<nsIRunnable> event(aEvent);
+ nsCOMPtr<nsIRunnablePriority> runnablePrio =
+ do_QueryInterface(event);
+ uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL;
+ if (runnablePrio) {
+ runnablePrio->GetPriority(&prio);
+ }
+ MOZ_ASSERT(prio == nsIRunnablePriority::PRIORITY_NORMAL ||
+ prio == nsIRunnablePriority::PRIORITY_HIGH);
+ if (prio == nsIRunnablePriority::PRIORITY_NORMAL) {
+ mNormalQueue->PutEvent(event.forget(), aProofOfLock);
+ } else {
+ mSecondaryQueue->PutEvent(event.forget(), aProofOfLock);
+ }
+ }
+
+ bool HasPendingEvent(mozilla::MutexAutoLock& aProofOfLock)
+ {
+ return mNormalQueue->HasPendingEvent(aProofOfLock) ||
+ mSecondaryQueue->HasPendingEvent(aProofOfLock);
+ }
+
+ nsChainedEventQueue* mNext;
+ RefPtr<nsNestedEventTarget> mEventTarget;
+
+ private:
+ mozilla::CondVar mEventsAvailable;
+ mozilla::UniquePtr<nsEventQueue> mNormalQueue;
+ mozilla::UniquePtr<nsEventQueue> mSecondaryQueue;
+
+ // Try to process one high priority runnable after each normal
+ // priority runnable. This gives the processing model HTML spec has for
+ // 'Update the rendering' in the case only vsync messages are in the
+ // secondary queue and prevents starving the normal queue.
+ bool mProcessSecondaryQueueRunnable;
+ };
+
+ class nsNestedEventTarget final : public nsIEventTarget
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEVENTTARGET
+
+ nsNestedEventTarget(NotNull<nsThread*> aThread,
+ NotNull<nsChainedEventQueue*> aQueue)
+ : mThread(aThread)
+ , mQueue(aQueue)
+
+
+
+ {
+ }
+
+ NotNull<RefPtr<nsThread>> mThread;
+
+ // This is protected by mThread->mLock.
+ nsChainedEventQueue* mQueue;
+
+ private:
+ ~nsNestedEventTarget()
+ {
+ }
+ };
+
+ // This lock protects access to mObserver, mEvents, mIdleEvents,
+ // mIdlePeriod and mEventsAreDoomed. All of those fields are only
+ // modified on the thread itself (never from another thread). This
+ // means that we can avoid holding the lock while using mObserver
+ // and mEvents on the thread itself. When calling PutEvent on
+ // mEvents, we have to hold the lock to synchronize with
+ // PopEventQueue.
+ mozilla::Mutex mLock;
+
+ nsCOMPtr<nsIThreadObserver> mObserver;
+ mozilla::CycleCollectedJSContext* mScriptObserver;
+
+ // Only accessed on the target thread.
+ nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2> mEventObservers;
+
+ NotNull<nsChainedEventQueue*> mEvents; // never null
+ nsChainedEventQueue mEventsRoot;
+
+ // mIdlePeriod keeps track of the current idle period. If at any
+ // time the main event queue is empty, calling
+ // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when
+ // the current idle period will end.
+ nsCOMPtr<nsIIdlePeriod> mIdlePeriod;
+ mozilla::CondVar mIdleEventsAvailable;
+ nsEventQueue mIdleEvents;
+
+ int32_t mPriority;
+ PRThread* mThread;
+ uint32_t mNestedEventLoopDepth;
+ uint32_t mStackSize;
+
+ // The shutdown context for ourselves.
+ struct nsThreadShutdownContext* mShutdownContext;
+ // The shutdown contexts for any other threads we've asked to shut down.
+ nsTArray<nsAutoPtr<struct nsThreadShutdownContext>> mRequestedShutdownContexts;
+
+ bool mShutdownRequired;
+ // Set to true when events posted to this thread will never run.
+ bool mEventsAreDoomed;
+ MainThreadFlag mIsMainThread;
+
+ // Set to true if this thread creates a JSRuntime.
+ bool mCanInvokeJS;
+};
+
+#if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \
+ && defined(_GNU_SOURCE)
+# define MOZ_CANARY
+
+extern int sCanaryOutputFD;
+#endif
+
+#endif // nsThread_h__
diff --git a/xpcom/threads/nsThreadManager.cpp b/xpcom/threads/nsThreadManager.cpp
new file mode 100644
index 000000000..d1eb84b8f
--- /dev/null
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThreadManager.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "nsIClassInfoImpl.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "mozilla/ThreadLocal.h"
+#ifdef MOZ_CANARY
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
+#include "MainThreadIdlePeriod.h"
+
+using namespace mozilla;
+
+static MOZ_THREAD_LOCAL(bool) sTLSIsMainThread;
+
+bool
+NS_IsMainThread()
+{
+ return sTLSIsMainThread.get();
+}
+
+void
+NS_SetMainThread()
+{
+ if (!sTLSIsMainThread.init()) {
+ MOZ_CRASH();
+ }
+ sTLSIsMainThread.set(true);
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray;
+
+//-----------------------------------------------------------------------------
+
+static void
+ReleaseObject(void* aData)
+{
+ static_cast<nsISupports*>(aData)->Release();
+}
+
+// statically allocated instance
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsThreadManager::AddRef()
+{
+ return 2;
+}
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsThreadManager::Release()
+{
+ return 1;
+}
+NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
+ nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
+ NS_THREADMANAGER_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager)
+NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager)
+
+//-----------------------------------------------------------------------------
+
+nsresult
+nsThreadManager::Init()
+{
+ // Child processes need to initialize the thread manager before they
+ // initialize XPCOM in order to set up the crash reporter. This leads to
+ // situations where we get initialized twice.
+ if (mInitialized) {
+ return NS_OK;
+ }
+
+ if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
+ return NS_ERROR_FAILURE;
+ }
+
+
+#ifdef MOZ_CANARY
+ const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ char* env_var_flag = getenv("MOZ_KILL_CANARIES");
+ sCanaryOutputFD =
+ env_var_flag ? (env_var_flag[0] ? open(env_var_flag, flags, mode) :
+ STDERR_FILENO) :
+ 0;
+#endif
+
+ // Setup "main" thread
+ mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
+
+ nsresult rv = mMainThread->InitCurrentThread();
+ if (NS_FAILED(rv)) {
+ mMainThread = nullptr;
+ return rv;
+ }
+
+ {
+ nsCOMPtr<nsIIdlePeriod> idlePeriod = new MainThreadIdlePeriod();
+ mMainThread->RegisterIdlePeriod(idlePeriod.forget());
+ }
+
+ // We need to keep a pointer to the current thread, so we can satisfy
+ // GetIsMainThread calls that occur post-Shutdown.
+ mMainThread->GetPRThread(&mMainPRThread);
+
+ mInitialized = true;
+ return NS_OK;
+}
+
+void
+nsThreadManager::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread");
+
+ // Prevent further access to the thread manager (no more new threads!)
+ //
+ // What happens if shutdown happens before NewThread completes?
+ // We Shutdown() the new thread, and return error if we've started Shutdown
+ // between when NewThread started, and when the thread finished initializing
+ // and registering with ThreadManager.
+ //
+ mInitialized = false;
+
+ // Empty the main thread event queue before we begin shutting down threads.
+ NS_ProcessPendingEvents(mMainThread);
+
+ // We gather the threads from the hashtable into a list, so that we avoid
+ // holding the hashtable lock while calling nsIThread::Shutdown.
+ nsThreadArray threads;
+ {
+ OffTheBooksMutexAutoLock lock(mLock);
+ for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<nsThread>& thread = iter.Data();
+ threads.AppendElement(WrapNotNull(thread));
+ iter.Remove();
+ }
+ }
+
+ // It's tempting to walk the list of threads here and tell them each to stop
+ // accepting new events, but that could lead to badness if one of those
+ // threads is stuck waiting for a response from another thread. To do it
+ // right, we'd need some way to interrupt the threads.
+ //
+ // Instead, we process events on the current thread while waiting for threads
+ // to shutdown. This means that we have to preserve a mostly functioning
+ // world until such time as the threads exit.
+
+ // Shutdown all threads that require it (join with threads that we created).
+ for (uint32_t i = 0; i < threads.Length(); ++i) {
+ NotNull<nsThread*> thread = threads[i];
+ if (thread->ShutdownRequired()) {
+ thread->Shutdown();
+ }
+ }
+
+ // NB: It's possible that there are events in the queue that want to *start*
+ // an asynchronous shutdown. But we have already shutdown the threads above,
+ // so there's no need to worry about them. We only have to wait for all
+ // in-flight asynchronous thread shutdowns to complete.
+ mMainThread->WaitForAllAsynchronousShutdowns();
+
+ // In case there are any more events somehow...
+ NS_ProcessPendingEvents(mMainThread);
+
+ // There are no more background threads at this point.
+
+ // Clear the table of threads.
+ {
+ OffTheBooksMutexAutoLock lock(mLock);
+ mThreadsByPRThread.Clear();
+ }
+
+ // Normally thread shutdown clears the observer for the thread, but since the
+ // main thread is special we do it manually here after we're sure all events
+ // have been processed.
+ mMainThread->SetObserver(nullptr);
+ mMainThread->ClearObservers();
+
+ // Release main thread object.
+ mMainThread = nullptr;
+
+ // Remove the TLS entry for the main thread.
+ PR_SetThreadPrivate(mCurThreadIndex, nullptr);
+}
+
+void
+nsThreadManager::RegisterCurrentThread(nsThread& aThread)
+{
+ MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
+
+ OffTheBooksMutexAutoLock lock(mLock);
+
+ ++mCurrentNumberOfThreads;
+ if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
+ mHighestNumberOfThreads = mCurrentNumberOfThreads;
+ }
+
+ mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread); // XXX check OOM?
+
+ aThread.AddRef(); // for TLS entry
+ PR_SetThreadPrivate(mCurThreadIndex, &aThread);
+}
+
+void
+nsThreadManager::UnregisterCurrentThread(nsThread& aThread)
+{
+ MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
+
+ OffTheBooksMutexAutoLock lock(mLock);
+
+ --mCurrentNumberOfThreads;
+ mThreadsByPRThread.Remove(aThread.GetPRThread());
+
+ PR_SetThreadPrivate(mCurThreadIndex, nullptr);
+ // Ref-count balanced via ReleaseObject
+}
+
+nsThread*
+nsThreadManager::GetCurrentThread()
+{
+ // read thread local storage
+ void* data = PR_GetThreadPrivate(mCurThreadIndex);
+ if (data) {
+ return static_cast<nsThread*>(data);
+ }
+
+ if (!mInitialized) {
+ return nullptr;
+ }
+
+ // OK, that's fine. We'll dynamically create one :-)
+ RefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
+ if (!thread || NS_FAILED(thread->InitCurrentThread())) {
+ return nullptr;
+ }
+
+ return thread.get(); // reference held in TLS
+}
+
+NS_IMETHODIMP
+nsThreadManager::NewThread(uint32_t aCreationFlags,
+ uint32_t aStackSize,
+ nsIThread** aResult)
+{
+ // Note: can be called from arbitrary threads
+
+ // No new threads during Shutdown
+ if (NS_WARN_IF(!mInitialized)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ RefPtr<nsThread> thr = new nsThread(nsThread::NOT_MAIN_THREAD, aStackSize);
+ nsresult rv = thr->Init(); // Note: blocks until the new thread has been set up
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // At this point, we expect that the thread has been registered in mThreadByPRThread;
+ // however, it is possible that it could have also been replaced by now, so
+ // we cannot really assert that it was added. Instead, kill it if we entered
+ // Shutdown() during/before Init()
+
+ if (NS_WARN_IF(!mInitialized)) {
+ if (thr->ShutdownRequired()) {
+ thr->Shutdown(); // ok if it happens multiple times
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ thr.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadManager::GetThreadFromPRThread(PRThread* aThread, nsIThread** aResult)
+{
+ // Keep this functioning during Shutdown
+ if (NS_WARN_IF(!mMainThread)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (NS_WARN_IF(!aThread)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ RefPtr<nsThread> temp;
+ {
+ OffTheBooksMutexAutoLock lock(mLock);
+ mThreadsByPRThread.Get(aThread, getter_AddRefs(temp));
+ }
+
+ NS_IF_ADDREF(*aResult = temp);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadManager::GetMainThread(nsIThread** aResult)
+{
+ // Keep this functioning during Shutdown
+ if (NS_WARN_IF(!mMainThread)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ NS_ADDREF(*aResult = mMainThread);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadManager::GetCurrentThread(nsIThread** aResult)
+{
+ // Keep this functioning during Shutdown
+ if (NS_WARN_IF(!mMainThread)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ *aResult = GetCurrentThread();
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadManager::GetIsMainThread(bool* aResult)
+{
+ // This method may be called post-Shutdown
+
+ *aResult = (PR_GetCurrentThread() == mMainPRThread);
+ return NS_OK;
+}
+
+uint32_t
+nsThreadManager::GetHighestNumberOfThreads()
+{
+ OffTheBooksMutexAutoLock lock(mLock);
+ return mHighestNumberOfThreads;
+}
diff --git a/xpcom/threads/nsThreadManager.h b/xpcom/threads/nsThreadManager.h
new file mode 100644
index 000000000..64ccc9bc9
--- /dev/null
+++ b/xpcom/threads/nsThreadManager.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsThreadManager_h__
+#define nsThreadManager_h__
+
+#include "mozilla/Mutex.h"
+#include "nsIThreadManager.h"
+#include "nsRefPtrHashtable.h"
+#include "nsThread.h"
+
+class nsIRunnable;
+
+class nsThreadManager : public nsIThreadManager
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITHREADMANAGER
+
+ static nsThreadManager& get()
+ {
+ static nsThreadManager sInstance;
+ return sInstance;
+ }
+
+ nsresult Init();
+
+ // Shutdown all threads. This function should only be called on the main
+ // thread of the application process.
+ void Shutdown();
+
+ // Called by nsThread to inform the ThreadManager it exists. This method
+ // must be called when the given thread is the current thread.
+ void RegisterCurrentThread(nsThread& aThread);
+
+ // Called by nsThread to inform the ThreadManager it is going away. This
+ // method must be called when the given thread is the current thread.
+ void UnregisterCurrentThread(nsThread& aThread);
+
+ // Returns the current thread. Returns null if OOM or if ThreadManager isn't
+ // initialized.
+ nsThread* GetCurrentThread();
+
+ // Returns the maximal number of threads that have been in existence
+ // simultaneously during the execution of the thread manager.
+ uint32_t GetHighestNumberOfThreads();
+
+ // This needs to be public in order to support static instantiation of this
+ // class with older compilers (e.g., egcs-2.91.66).
+ ~nsThreadManager()
+ {
+ }
+
+private:
+ nsThreadManager()
+ : mCurThreadIndex(0)
+ , mMainPRThread(nullptr)
+ , mLock("nsThreadManager.mLock")
+ , mInitialized(false)
+ , mCurrentNumberOfThreads(1)
+ , mHighestNumberOfThreads(1)
+ {
+ }
+
+ nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
+ unsigned mCurThreadIndex; // thread-local-storage index
+ RefPtr<nsThread> mMainThread;
+ PRThread* mMainPRThread;
+ mozilla::OffTheBooksMutex mLock; // protects tables
+ mozilla::Atomic<bool> mInitialized;
+
+ // The current number of threads
+ uint32_t mCurrentNumberOfThreads;
+ // The highest number of threads encountered so far during the session
+ uint32_t mHighestNumberOfThreads;
+};
+
+#define NS_THREADMANAGER_CID \
+{ /* 7a4204c6-e45a-4c37-8ebb-6709a22c917c */ \
+ 0x7a4204c6, \
+ 0xe45a, \
+ 0x4c37, \
+ {0x8e, 0xbb, 0x67, 0x09, 0xa2, 0x2c, 0x91, 0x7c} \
+}
+
+#endif // nsThreadManager_h__
diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp
new file mode 100644
index 000000000..241fad39d
--- /dev/null
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIClassInfoImpl.h"
+#include "nsThreadPool.h"
+#include "nsThreadManager.h"
+#include "nsThread.h"
+#include "nsMemory.h"
+#include "nsAutoPtr.h"
+#include "prinrval.h"
+#include "mozilla/Logging.h"
+#include "nsThreadSyncDispatch.h"
+
+using namespace mozilla;
+
+static LazyLogModule sThreadPoolLog("nsThreadPool");
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(args) MOZ_LOG(sThreadPoolLog, mozilla::LogLevel::Debug, args)
+
+// DESIGN:
+// o Allocate anonymous threads.
+// o Use nsThreadPool::Run as the main routine for each thread.
+// o Each thread waits on the event queue's monitor, checking for
+// pending events and rescheduling itself as an idle thread.
+
+#define DEFAULT_THREAD_LIMIT 4
+#define DEFAULT_IDLE_THREAD_LIMIT 1
+#define DEFAULT_IDLE_THREAD_TIMEOUT PR_SecondsToInterval(60)
+
+NS_IMPL_ADDREF(nsThreadPool)
+NS_IMPL_RELEASE(nsThreadPool)
+NS_IMPL_CLASSINFO(nsThreadPool, nullptr, nsIClassInfo::THREADSAFE,
+ NS_THREADPOOL_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsThreadPool, nsIThreadPool, nsIEventTarget,
+ nsIRunnable)
+NS_IMPL_CI_INTERFACE_GETTER(nsThreadPool, nsIThreadPool, nsIEventTarget)
+
+nsThreadPool::nsThreadPool()
+ : mMutex("[nsThreadPool.mMutex]")
+ , mEventsAvailable(mMutex, "[nsThreadPool.mEventsAvailable]")
+ , mEvents(mEventsAvailable, nsEventQueue::eNormalQueue)
+ , mThreadLimit(DEFAULT_THREAD_LIMIT)
+ , mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT)
+ , mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT)
+ , mIdleCount(0)
+ , mStackSize(nsIThreadManager::DEFAULT_STACK_SIZE)
+ , mShutdown(false)
+{
+ LOG(("THRD-P(%p) constructor!!!\n", this));
+}
+
+nsThreadPool::~nsThreadPool()
+{
+ // Threads keep a reference to the nsThreadPool until they return from Run()
+ // after removing themselves from mThreads.
+ MOZ_ASSERT(mThreads.IsEmpty());
+}
+
+nsresult
+nsThreadPool::PutEvent(nsIRunnable* aEvent)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return PutEvent(event.forget(), 0);
+}
+
+nsresult
+nsThreadPool::PutEvent(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
+{
+ // Avoid spawning a new thread while holding the event queue lock...
+
+ bool spawnThread = false;
+ uint32_t stackSize = 0;
+ {
+ MutexAutoLock lock(mMutex);
+
+ if (NS_WARN_IF(mShutdown)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
+ mThreadLimit));
+ MOZ_ASSERT(mIdleCount <= (uint32_t)mThreads.Count(), "oops");
+
+ // Make sure we have a thread to service this event.
+ if (mThreads.Count() < (int32_t)mThreadLimit &&
+ !(aFlags & NS_DISPATCH_AT_END) &&
+ // Spawn a new thread if we don't have enough idle threads to serve
+ // pending events immediately.
+ mEvents.Count(lock) >= mIdleCount) {
+ spawnThread = true;
+ }
+
+ mEvents.PutEvent(Move(aEvent), lock);
+ stackSize = mStackSize;
+ }
+
+ LOG(("THRD-P(%p) put [spawn=%d]\n", this, spawnThread));
+ if (!spawnThread) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIThread> thread;
+ nsThreadManager::get().NewThread(0, stackSize, getter_AddRefs(thread));
+ if (NS_WARN_IF(!thread)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ bool killThread = false;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mThreads.Count() < (int32_t)mThreadLimit) {
+ mThreads.AppendObject(thread);
+ } else {
+ killThread = true; // okay, we don't need this thread anymore
+ }
+ }
+ LOG(("THRD-P(%p) put [%p kill=%d]\n", this, thread.get(), killThread));
+ if (killThread) {
+ // We never dispatched any events to the thread, so we can shut it down
+ // asynchronously without worrying about anything.
+ ShutdownThread(thread);
+ } else {
+ thread->Dispatch(this, NS_DISPATCH_NORMAL);
+ }
+
+ return NS_OK;
+}
+
+void
+nsThreadPool::ShutdownThread(nsIThread* aThread)
+{
+ LOG(("THRD-P(%p) shutdown async [%p]\n", this, aThread));
+
+ // This is either called by a threadpool thread that is out of work, or
+ // a thread that attempted to create a threadpool thread and raced in
+ // such a way that the newly created thread is no longer necessary.
+ // In the first case, we must go to another thread to shut aThread down
+ // (because it is the current thread). In the second case, we cannot
+ // synchronously shut down the current thread (because then Dispatch() would
+ // spin the event loop, and that could blow up the world), and asynchronous
+ // 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.
+ NS_DispatchToMainThread(NewRunnableMethod(aThread,
+ &nsIThread::AsyncShutdown));
+}
+
+NS_IMETHODIMP
+nsThreadPool::Run()
+{
+ mThreadNaming.SetThreadPoolName(mName);
+
+ LOG(("THRD-P(%p) enter %s\n", this, mName.BeginReading()));
+
+ nsCOMPtr<nsIThread> current;
+ nsThreadManager::get().GetCurrentThread(getter_AddRefs(current));
+
+ bool shutdownThreadOnExit = false;
+ bool exitThread = false;
+ bool wasIdle = false;
+ PRIntervalTime idleSince;
+
+ nsCOMPtr<nsIThreadPoolListener> listener;
+ {
+ MutexAutoLock lock(mMutex);
+ listener = mListener;
+ }
+
+ if (listener) {
+ listener->OnThreadCreated();
+ }
+
+ do {
+ nsCOMPtr<nsIRunnable> event;
+ {
+ MutexAutoLock lock(mMutex);
+
+ if (!mEvents.GetPendingEvent(getter_AddRefs(event), lock)) {
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime timeout = PR_MillisecondsToInterval(mIdleThreadTimeout);
+
+ // If we are shutting down, then don't keep any idle threads
+ if (mShutdown) {
+ exitThread = true;
+ } else {
+ if (wasIdle) {
+ // if too many idle threads or idle for too long, then bail.
+ if (mIdleCount > mIdleThreadLimit ||
+ (mIdleThreadTimeout != UINT32_MAX && (now - idleSince) >= timeout)) {
+ exitThread = true;
+ }
+ } else {
+ // if would be too many idle threads...
+ if (mIdleCount == mIdleThreadLimit) {
+ exitThread = true;
+ } else {
+ ++mIdleCount;
+ idleSince = now;
+ wasIdle = true;
+ }
+ }
+ }
+
+ if (exitThread) {
+ if (wasIdle) {
+ --mIdleCount;
+ }
+ shutdownThreadOnExit = mThreads.RemoveObject(current);
+ } else {
+ PRIntervalTime delta = timeout - (now - idleSince);
+ LOG(("THRD-P(%p) %s waiting [%d]\n", this, mName.BeginReading(), delta));
+ mEvents.Wait(delta);
+ LOG(("THRD-P(%p) done waiting\n", this));
+ }
+ } else if (wasIdle) {
+ wasIdle = false;
+ --mIdleCount;
+ }
+ }
+ if (event) {
+ LOG(("THRD-P(%p) %s running [%p]\n", this, mName.BeginReading(), event.get()));
+ event->Run();
+ }
+ } while (!exitThread);
+
+ if (listener) {
+ listener->OnThreadShuttingDown();
+ }
+
+ if (shutdownThreadOnExit) {
+ ShutdownThread(current);
+ }
+
+ LOG(("THRD-P(%p) leave\n", this));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+nsThreadPool::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
+{
+ LOG(("THRD-P(%p) dispatch [%p %x]\n", this, /* XXX aEvent*/ nullptr, aFlags));
+
+ if (NS_WARN_IF(mShutdown)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (aFlags & DISPATCH_SYNC) {
+ nsCOMPtr<nsIThread> thread;
+ nsThreadManager::get().GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(!thread)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ RefPtr<nsThreadSyncDispatch> wrapper =
+ new nsThreadSyncDispatch(thread, Move(aEvent));
+ PutEvent(wrapper);
+
+ while (wrapper->IsPending()) {
+ NS_ProcessNextEvent(thread);
+ }
+ } else {
+ NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
+ aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
+ PutEvent(Move(aEvent), aFlags);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsThreadPool::IsOnCurrentThread(bool* aResult)
+{
+ MutexAutoLock lock(mMutex);
+ if (NS_WARN_IF(mShutdown)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsIThread* thread = NS_GetCurrentThread();
+ for (uint32_t i = 0; i < static_cast<uint32_t>(mThreads.Count()); ++i) {
+ if (mThreads[i] == thread) {
+ *aResult = true;
+ return NS_OK;
+ }
+ }
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::Shutdown()
+{
+ nsCOMArray<nsIThread> threads;
+ nsCOMPtr<nsIThreadPoolListener> listener;
+ {
+ MutexAutoLock lock(mMutex);
+ mShutdown = true;
+ mEvents.NotifyAll();
+
+ threads.AppendObjects(mThreads);
+ mThreads.Clear();
+
+ // Swap in a null listener so that we release the listener at the end of
+ // this method. The listener will be kept alive as long as the other threads
+ // that were created when it was set.
+ mListener.swap(listener);
+ }
+
+ // It's important that we shutdown the threads while outside the event queue
+ // monitor. Otherwise, we could end up dead-locking.
+
+ for (int32_t i = 0; i < threads.Count(); ++i) {
+ threads[i]->Shutdown();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::GetThreadLimit(uint32_t* aValue)
+{
+ *aValue = mThreadLimit;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::SetThreadLimit(uint32_t aValue)
+{
+ MutexAutoLock lock(mMutex);
+ LOG(("THRD-P(%p) thread limit [%u]\n", this, aValue));
+ mThreadLimit = aValue;
+ if (mIdleThreadLimit > mThreadLimit) {
+ mIdleThreadLimit = mThreadLimit;
+ }
+
+ if (static_cast<uint32_t>(mThreads.Count()) > mThreadLimit) {
+ mEvents.NotifyAll(); // wake up threads so they observe this change
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::GetIdleThreadLimit(uint32_t* aValue)
+{
+ *aValue = mIdleThreadLimit;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::SetIdleThreadLimit(uint32_t aValue)
+{
+ MutexAutoLock lock(mMutex);
+ LOG(("THRD-P(%p) idle thread limit [%u]\n", this, aValue));
+ mIdleThreadLimit = aValue;
+ if (mIdleThreadLimit > mThreadLimit) {
+ mIdleThreadLimit = mThreadLimit;
+ }
+
+ // Do we need to kill some idle threads?
+ if (mIdleCount > mIdleThreadLimit) {
+ mEvents.NotifyAll(); // wake up threads so they observe this change
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::GetIdleThreadTimeout(uint32_t* aValue)
+{
+ *aValue = mIdleThreadTimeout;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::SetIdleThreadTimeout(uint32_t aValue)
+{
+ MutexAutoLock lock(mMutex);
+ uint32_t oldTimeout = mIdleThreadTimeout;
+ mIdleThreadTimeout = aValue;
+
+ // Do we need to notify any idle threads that their sleep time has shortened?
+ if (mIdleThreadTimeout < oldTimeout && mIdleCount > 0) {
+ mEvents.NotifyAll(); // wake up threads so they observe this change
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::GetThreadStackSize(uint32_t* aValue)
+{
+ MutexAutoLock lock(mMutex);
+ *aValue = mStackSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::SetThreadStackSize(uint32_t aValue)
+{
+ MutexAutoLock lock(mMutex);
+ mStackSize = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::GetListener(nsIThreadPoolListener** aListener)
+{
+ MutexAutoLock lock(mMutex);
+ NS_IF_ADDREF(*aListener = mListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::SetListener(nsIThreadPoolListener* aListener)
+{
+ nsCOMPtr<nsIThreadPoolListener> swappedListener(aListener);
+ {
+ MutexAutoLock lock(mMutex);
+ mListener.swap(swappedListener);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThreadPool::SetName(const nsACString& aName)
+{
+ {
+ MutexAutoLock lock(mMutex);
+ if (mThreads.Count()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+
+ mName = aName;
+ return NS_OK;
+}
diff --git a/xpcom/threads/nsThreadPool.h b/xpcom/threads/nsThreadPool.h
new file mode 100644
index 000000000..47a4bd1ff
--- /dev/null
+++ b/xpcom/threads/nsThreadPool.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsThreadPool_h__
+#define nsThreadPool_h__
+
+#include "nsIThreadPool.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsEventQueue.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Monitor.h"
+
+class nsThreadPool final
+ : public nsIThreadPool
+ , public nsIRunnable
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEVENTTARGET
+ NS_DECL_NSITHREADPOOL
+ NS_DECL_NSIRUNNABLE
+ using nsIEventTarget::Dispatch;
+
+ nsThreadPool();
+
+private:
+ ~nsThreadPool();
+
+ void ShutdownThread(nsIThread* aThread);
+ nsresult PutEvent(nsIRunnable* aEvent);
+ nsresult PutEvent(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags);
+
+ nsCOMArray<nsIThread> mThreads;
+ mozilla::Mutex mMutex;
+ mozilla::CondVar mEventsAvailable;
+ nsEventQueue mEvents;
+ uint32_t mThreadLimit;
+ uint32_t mIdleThreadLimit;
+ uint32_t mIdleThreadTimeout;
+ uint32_t mIdleCount;
+ uint32_t mStackSize;
+ nsCOMPtr<nsIThreadPoolListener> mListener;
+ bool mShutdown;
+ nsCString mName;
+ nsThreadPoolNaming mThreadNaming;
+};
+
+#define NS_THREADPOOL_CID \
+{ /* 547ec2a8-315e-4ec4-888e-6e4264fe90eb */ \
+ 0x547ec2a8, \
+ 0x315e, \
+ 0x4ec4, \
+ {0x88, 0x8e, 0x6e, 0x42, 0x64, 0xfe, 0x90, 0xeb} \
+}
+
+#endif // nsThreadPool_h__
diff --git a/xpcom/threads/nsThreadSyncDispatch.h b/xpcom/threads/nsThreadSyncDispatch.h
new file mode 100644
index 000000000..ae5e85464
--- /dev/null
+++ b/xpcom/threads/nsThreadSyncDispatch.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsThreadSyncDispatch_h_
+#define nsThreadSyncDispatch_h_
+
+#include "nsThreadUtils.h"
+#include "LeakRefPtr.h"
+#include "mozilla/DebugOnly.h"
+
+class nsThreadSyncDispatch : public mozilla::Runnable
+{
+public:
+ nsThreadSyncDispatch(nsIThread* aOrigin, already_AddRefed<nsIRunnable>&& aTask)
+ : mOrigin(aOrigin)
+ , mSyncTask(mozilla::Move(aTask))
+ {
+ }
+
+ bool IsPending()
+ {
+ return !!mSyncTask;
+ }
+
+private:
+ NS_IMETHOD Run() override
+ {
+ if (nsIRunnable* task = mSyncTask.get()) {
+ mozilla::DebugOnly<nsresult> result = task->Run();
+ MOZ_ASSERT(NS_SUCCEEDED(result),
+ "task in sync dispatch should not fail");
+ // We must release the task here to ensure that when the original
+ // thread is unblocked, this task has been released.
+ mSyncTask.release();
+ // unblock the origin thread
+ mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
+ }
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIThread> mOrigin;
+ // The task is leaked by default when Run() is not called, because
+ // otherwise we may release it in an incorrect thread.
+ mozilla::LeakRefPtr<nsIRunnable> mSyncTask;
+};
+
+#endif // nsThreadSyncDispatch_h_
diff --git a/xpcom/threads/nsTimerImpl.cpp b/xpcom/threads/nsTimerImpl.cpp
new file mode 100644
index 000000000..bc2d338e0
--- /dev/null
+++ b/xpcom/threads/nsTimerImpl.cpp
@@ -0,0 +1,658 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTimerImpl.h"
+#include "TimerThread.h"
+#include "nsAutoPtr.h"
+#include "nsThreadManager.h"
+#include "nsThreadUtils.h"
+#include "pratom.h"
+#include "GeckoProfiler.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Logging.h"
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracerImpl.h"
+using namespace mozilla::tasktracer;
+#endif
+
+#ifdef XP_WIN
+#include <process.h>
+#ifndef getpid
+#define getpid _getpid
+#endif
+#else
+#include <unistd.h>
+#endif
+
+using mozilla::Atomic;
+using mozilla::LogLevel;
+using mozilla::TimeDuration;
+using mozilla::TimeStamp;
+
+static TimerThread* gThread = nullptr;
+
+// This module prints info about the precision of timers.
+static mozilla::LazyLogModule sTimerLog("nsTimerImpl");
+
+mozilla::LogModule*
+GetTimerLog()
+{
+ return sTimerLog;
+}
+
+// This module prints info about which timers are firing, which is useful for
+// wakeups for the purposes of power profiling. Set the following environment
+// variable before starting the browser.
+//
+// MOZ_LOG=TimerFirings:4
+//
+// Then a line will be printed for every timer that fires. The name used for a
+// |Callback::Type::Function| timer depends on the circumstances.
+//
+// - If it was explicitly named (e.g. it was initialized with
+// InitWithNamedFuncCallback()) then that explicit name will be shown.
+//
+// - Otherwise, if we are on a platform that supports function name lookup
+// (Mac or Linux) then the looked-up name will be shown with a
+// "[from dladdr]" annotation. On Mac the looked-up name will be immediately
+// useful. On Linux it'll need post-processing with
+// tools/rb/fix_linux_stack.py.
+//
+// - Otherwise, no name will be printed. If many timers hit this case then
+// you'll need to re-run the workload on a Mac to find out which timers they
+// are, and then give them explicit names.
+//
+// If you redirect this output to a file called "out", you can then
+// post-process it with a command something like the following.
+//
+// cat out | grep timer | sort | uniq -c | sort -r -n
+//
+// This will show how often each unique line appears, with the most common ones
+// first.
+//
+// More detailed docs are here:
+// https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
+//
+static mozilla::LazyLogModule sTimerFiringsLog("TimerFirings");
+
+mozilla::LogModule*
+GetTimerFiringsLog()
+{
+ return sTimerFiringsLog;
+}
+
+#include <math.h>
+
+double nsTimerImpl::sDeltaSumSquared = 0;
+double nsTimerImpl::sDeltaSum = 0;
+double nsTimerImpl::sDeltaNum = 0;
+
+static void
+myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
+ double* meanResult, double* stdDevResult)
+{
+ double mean = 0.0, var = 0.0, stdDev = 0.0;
+ if (n > 0.0 && sumOfValues >= 0) {
+ mean = sumOfValues / n;
+ double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
+ if (temp < 0.0 || n <= 1) {
+ var = 0.0;
+ } else {
+ var = temp / (n * (n - 1));
+ }
+ // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
+ stdDev = var != 0.0 ? sqrt(var) : 0.0;
+ }
+ *meanResult = mean;
+ *stdDevResult = stdDev;
+}
+
+NS_IMPL_QUERY_INTERFACE(nsTimer, nsITimer)
+NS_IMPL_ADDREF(nsTimer)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsTimer::Release(void)
+{
+ nsrefcnt count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "nsTimer");
+
+ if (count == 1) {
+ // Last ref, held by nsTimerImpl. Make sure the cycle is broken.
+ // If there is a nsTimerEvent in a queue for this timer, the nsTimer will
+ // live until that event pops, otherwise the nsTimerImpl will go away and
+ // the nsTimer along with it.
+ mImpl->Cancel();
+ mImpl = nullptr;
+ } else if (count == 0) {
+ delete this;
+ }
+
+ return count;
+}
+
+nsTimerImpl::nsTimerImpl(nsITimer* aTimer) :
+ mGeneration(0),
+ mDelay(0),
+ mITimer(aTimer),
+ mMutex("nsTimerImpl::mMutex")
+{
+ // XXXbsmedberg: shouldn't this be in Init()?
+ mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
+}
+
+//static
+nsresult
+nsTimerImpl::Startup()
+{
+ nsresult rv;
+
+ gThread = new TimerThread();
+
+ NS_ADDREF(gThread);
+ rv = gThread->InitLocks();
+
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(gThread);
+ }
+
+ return rv;
+}
+
+void
+nsTimerImpl::Shutdown()
+{
+ if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
+ double mean = 0, stddev = 0;
+ myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
+
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
+ sDeltaNum, sDeltaSum, sDeltaSumSquared));
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("mean: %fms, stddev: %fms\n", mean, stddev));
+ }
+
+ if (!gThread) {
+ return;
+ }
+
+ gThread->Shutdown();
+ NS_RELEASE(gThread);
+}
+
+
+nsresult
+nsTimerImpl::InitCommon(uint32_t aDelay, uint32_t aType)
+{
+ mMutex.AssertCurrentThreadOwns();
+ nsresult rv;
+
+ if (NS_WARN_IF(!gThread)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (!mEventTarget) {
+ NS_ERROR("mEventTarget is NULL");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ rv = gThread->Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ gThread->RemoveTimer(this);
+ ++mGeneration;
+
+ mType = (uint8_t)aType;
+ mDelay = aDelay;
+ mTimeout = TimeStamp::Now() + TimeDuration::FromMilliseconds(mDelay);
+
+ return gThread->AddTimer(this);
+}
+
+nsresult
+nsTimerImpl::InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
+ void* aClosure,
+ uint32_t aDelay,
+ uint32_t aType,
+ Callback::Name aName)
+{
+ if (NS_WARN_IF(!aFunc)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ Callback cb; // Goes out of scope after the unlock, prevents deadlock
+ cb.mType = Callback::Type::Function;
+ cb.mCallback.c = aFunc;
+ cb.mClosure = aClosure;
+ cb.mName = aName;
+
+ MutexAutoLock lock(mMutex);
+ cb.swap(mCallback);
+
+ return InitCommon(aDelay, aType);
+}
+
+NS_IMETHODIMP
+nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
+ void* aClosure,
+ uint32_t aDelay,
+ uint32_t aType)
+{
+ Callback::Name name(Callback::Nothing);
+ return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
+}
+
+NS_IMETHODIMP
+nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
+ void* aClosure,
+ uint32_t aDelay,
+ uint32_t aType,
+ const char* aNameString)
+{
+ Callback::Name name(aNameString);
+ return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
+}
+
+NS_IMETHODIMP
+nsTimerImpl::InitWithNameableFuncCallback(nsTimerCallbackFunc aFunc,
+ void* aClosure,
+ uint32_t aDelay,
+ uint32_t aType,
+ nsTimerNameCallbackFunc aNameFunc)
+{
+ Callback::Name name(aNameFunc);
+ return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
+}
+
+NS_IMETHODIMP
+nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
+ uint32_t aDelay,
+ uint32_t aType)
+{
+ if (NS_WARN_IF(!aCallback)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ Callback cb; // Goes out of scope after the unlock, prevents deadlock
+ cb.mType = Callback::Type::Interface;
+ cb.mCallback.i = aCallback;
+ NS_ADDREF(cb.mCallback.i);
+
+ MutexAutoLock lock(mMutex);
+ cb.swap(mCallback);
+
+ return InitCommon(aDelay, aType);
+}
+
+NS_IMETHODIMP
+nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelay, uint32_t aType)
+{
+ if (NS_WARN_IF(!aObserver)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ Callback cb; // Goes out of scope after the unlock, prevents deadlock
+ cb.mType = Callback::Type::Observer;
+ cb.mCallback.o = aObserver;
+ NS_ADDREF(cb.mCallback.o);
+
+ MutexAutoLock lock(mMutex);
+ cb.swap(mCallback);
+
+ return InitCommon(aDelay, aType);
+}
+
+NS_IMETHODIMP
+nsTimerImpl::Cancel()
+{
+ Callback cb;
+
+ MutexAutoLock lock(mMutex);
+
+ if (gThread) {
+ gThread->RemoveTimer(this);
+ }
+
+ cb.swap(mCallback);
+ ++mGeneration;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTimerImpl::SetDelay(uint32_t aDelay)
+{
+ MutexAutoLock lock(mMutex);
+ if (GetCallback().mType == Callback::Type::Unknown && !IsRepeating()) {
+ // This may happen if someone tries to re-use a one-shot timer
+ // by re-setting delay instead of reinitializing the timer.
+ NS_ERROR("nsITimer->SetDelay() called when the "
+ "one-shot timer is not set up.");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ bool reAdd = false;
+ if (gThread) {
+ reAdd = NS_SUCCEEDED(gThread->RemoveTimer(this));
+ }
+
+ mDelay = aDelay;
+ mTimeout = TimeStamp::Now() + TimeDuration::FromMilliseconds(mDelay);
+
+ if (reAdd) {
+ gThread->AddTimer(this);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTimerImpl::GetDelay(uint32_t* aDelay)
+{
+ MutexAutoLock lock(mMutex);
+ *aDelay = mDelay;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTimerImpl::SetType(uint32_t aType)
+{
+ MutexAutoLock lock(mMutex);
+ mType = (uint8_t)aType;
+ // XXX if this is called, we should change the actual type.. this could effect
+ // repeating timers. we need to ensure in Fire() that if mType has changed
+ // during the callback that we don't end up with the timer in the queue twice.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTimerImpl::GetType(uint32_t* aType)
+{
+ MutexAutoLock lock(mMutex);
+ *aType = mType;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsTimerImpl::GetClosure(void** aClosure)
+{
+ MutexAutoLock lock(mMutex);
+ *aClosure = GetCallback().mClosure;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsTimerImpl::GetCallback(nsITimerCallback** aCallback)
+{
+ MutexAutoLock lock(mMutex);
+ if (GetCallback().mType == Callback::Type::Interface) {
+ NS_IF_ADDREF(*aCallback = GetCallback().mCallback.i);
+ } else {
+ *aCallback = nullptr;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
+{
+ MutexAutoLock lock(mMutex);
+ NS_IF_ADDREF(*aTarget = mEventTarget);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
+{
+ MutexAutoLock lock(mMutex);
+ if (NS_WARN_IF(mCallback.mType != Callback::Type::Unknown)) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ if (aTarget) {
+ mEventTarget = aTarget;
+ } else {
+ mEventTarget = static_cast<nsIEventTarget*>(NS_GetCurrentThread());
+ }
+ return NS_OK;
+}
+
+
+void
+nsTimerImpl::Fire(int32_t aGeneration)
+{
+ uint8_t oldType;
+ uint32_t oldDelay;
+ TimeStamp oldTimeout;
+
+ {
+ // Don't fire callbacks or fiddle with refcounts when the mutex is locked.
+ // If some other thread Cancels/Inits after this, they're just too late.
+ MutexAutoLock lock(mMutex);
+ if (aGeneration != mGeneration) {
+ return;
+ }
+
+ mCallbackDuringFire.swap(mCallback);
+ oldType = mType;
+ oldDelay = mDelay;
+ oldTimeout = mTimeout;
+ }
+
+ PROFILER_LABEL("Timer", "Fire",
+ js::ProfileEntry::Category::OTHER);
+
+ TimeStamp now = TimeStamp::Now();
+ if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
+ TimeDuration delta = now - oldTimeout;
+ int32_t d = delta.ToMilliseconds(); // delta in ms
+ sDeltaSum += abs(d);
+ sDeltaSumSquared += double(d) * double(d);
+ sDeltaNum++;
+
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("[this=%p] expected delay time %4ums\n", this, oldDelay));
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("[this=%p] actual delay time %4dms\n", this, oldDelay + d));
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("[this=%p] (mType is %d) -------\n", this, oldType));
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("[this=%p] delta %4dms\n", this, d));
+ }
+
+ if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug)) {
+ LogFiring(mCallbackDuringFire, oldType, oldDelay);
+ }
+
+ switch (mCallbackDuringFire.mType) {
+ case Callback::Type::Function:
+ mCallbackDuringFire.mCallback.c(mITimer, mCallbackDuringFire.mClosure);
+ break;
+ case Callback::Type::Interface:
+ mCallbackDuringFire.mCallback.i->Notify(mITimer);
+ break;
+ case Callback::Type::Observer:
+ mCallbackDuringFire.mCallback.o->Observe(mITimer, NS_TIMER_CALLBACK_TOPIC,
+ nullptr);
+ break;
+ default:
+ ;
+ }
+
+ Callback trash; // Swap into here to dispose of callback after the unlock
+ MutexAutoLock lock(mMutex);
+ if (aGeneration == mGeneration && IsRepeating()) {
+ // Repeating timer has not been re-init or canceled; reschedule
+ mCallbackDuringFire.swap(mCallback);
+ TimeDuration delay = TimeDuration::FromMilliseconds(mDelay);
+ if (mType == nsITimer::TYPE_REPEATING_SLACK) {
+ mTimeout = TimeStamp::Now() + delay;
+ } else {
+ mTimeout = mTimeout + delay;
+ }
+ if (gThread) {
+ gThread->AddTimer(this);
+ }
+ }
+
+ mCallbackDuringFire.swap(trash);
+
+ MOZ_LOG(GetTimerLog(), LogLevel::Debug,
+ ("[this=%p] Took %fms to fire timer callback\n",
+ this, (TimeStamp::Now() - now).ToMilliseconds()));
+}
+
+#if defined(HAVE_DLADDR) && defined(HAVE___CXA_DEMANGLE)
+#define USE_DLADDR 1
+#endif
+
+#ifdef USE_DLADDR
+#include <cxxabi.h>
+#include <dlfcn.h>
+#endif
+
+// See the big comment above GetTimerFiringsLog() to understand this code.
+void
+nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay)
+{
+ const char* typeStr;
+ switch (aType) {
+ case nsITimer::TYPE_ONE_SHOT: typeStr = "ONE_SHOT"; break;
+ case nsITimer::TYPE_REPEATING_SLACK: typeStr = "SLACK "; break;
+ case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
+ case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP: typeStr = "PRECISE "; break;
+ default: MOZ_CRASH("bad type");
+ }
+
+ switch (aCallback.mType) {
+ case Callback::Type::Function: {
+ bool needToFreeName = false;
+ const char* annotation = "";
+ const char* name;
+ static const size_t buflen = 1024;
+ char buf[buflen];
+
+ if (aCallback.mName.is<Callback::NameString>()) {
+ name = aCallback.mName.as<Callback::NameString>();
+
+ } else if (aCallback.mName.is<Callback::NameFunc>()) {
+ aCallback.mName.as<Callback::NameFunc>()(
+ mITimer, aCallback.mClosure, buf, buflen);
+ name = buf;
+
+ } else {
+ MOZ_ASSERT(aCallback.mName.is<Callback::NameNothing>());
+#ifdef USE_DLADDR
+ annotation = "[from dladdr] ";
+
+ Dl_info info;
+ void* addr = reinterpret_cast<void*>(aCallback.mCallback.c);
+ if (dladdr(addr, &info) == 0) {
+ name = "???[dladdr: failed]";
+
+ } else if (info.dli_sname) {
+ int status;
+ name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
+ if (status == 0) {
+ // Success. Because we didn't pass in a buffer to __cxa_demangle it
+ // allocates its own one with malloc() which we must free() later.
+ MOZ_ASSERT(name);
+ needToFreeName = true;
+ } else if (status == -1) {
+ name = "???[__cxa_demangle: OOM]";
+ } else if (status == -2) {
+ name = "???[__cxa_demangle: invalid mangled name]";
+ } else if (status == -3) {
+ name = "???[__cxa_demangle: invalid argument]";
+ } else {
+ name = "???[__cxa_demangle: unexpected status value]";
+ }
+
+ } else if (info.dli_fname) {
+ // The "#0: " prefix is necessary for fix_linux_stack.py to interpret
+ // this string as something to convert.
+ snprintf(buf, buflen, "#0: ???[%s +0x%" PRIxPTR "]\n",
+ info.dli_fname, uintptr_t(addr) - uintptr_t(info.dli_fbase));
+ name = buf;
+
+ } else {
+ name = "???[dladdr: no symbol or shared object obtained]";
+ }
+#else
+ name = "???[dladdr is unimplemented or doesn't work well on this OS]";
+#endif
+ }
+
+ MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
+ ("[%d] fn timer (%s %5d ms): %s%s\n",
+ getpid(), typeStr, aDelay, annotation, name));
+
+ if (needToFreeName) {
+ free(const_cast<char*>(name));
+ }
+
+ break;
+ }
+
+ case Callback::Type::Interface: {
+ MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
+ ("[%d] iface timer (%s %5d ms): %p\n",
+ getpid(), typeStr, aDelay, aCallback.mCallback.i));
+ break;
+ }
+
+ case Callback::Type::Observer: {
+ MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
+ ("[%d] obs timer (%s %5d ms): %p\n",
+ getpid(), typeStr, aDelay, aCallback.mCallback.o));
+ break;
+ }
+
+ case Callback::Type::Unknown:
+ default: {
+ MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
+ ("[%d] ??? timer (%s, %5d ms)\n",
+ getpid(), typeStr, aDelay));
+ break;
+ }
+ }
+}
+
+nsTimer::~nsTimer()
+{
+}
+
+size_t
+nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this);
+}
+
+/* static */
+const nsTimerImpl::Callback::NameNothing nsTimerImpl::Callback::Nothing = 0;
+
+#ifdef MOZ_TASK_TRACER
+void
+nsTimerImpl::GetTLSTraceInfo()
+{
+ mTracedTask.GetTLSTraceInfo();
+}
+
+TracedTaskCommon
+nsTimerImpl::GetTracedTask()
+{
+ return mTracedTask;
+}
+
+#endif
+
diff --git a/xpcom/threads/nsTimerImpl.h b/xpcom/threads/nsTimerImpl.h
new file mode 100644
index 000000000..5c731fbb4
--- /dev/null
+++ b/xpcom/threads/nsTimerImpl.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTimerImpl_h___
+#define nsTimerImpl_h___
+
+#include "nsITimer.h"
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+
+#include "nsCOMPtr.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Variant.h"
+
+#ifdef MOZ_TASK_TRACER
+#include "TracedTaskCommon.h"
+#endif
+
+extern mozilla::LogModule* GetTimerLog();
+
+#define NS_TIMER_CID \
+{ /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \
+ 0x5ff24248, \
+ 0x1dd2, \
+ 0x11b2, \
+ {0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8} \
+}
+
+// TimerThread, nsTimerEvent, and nsTimer have references to these. nsTimer has
+// a separate lifecycle so we can Cancel() the underlying timer when the user of
+// the nsTimer has let go of its last reference.
+class nsTimerImpl
+{
+ ~nsTimerImpl() {}
+public:
+ typedef mozilla::TimeStamp TimeStamp;
+
+ explicit nsTimerImpl(nsITimer* aTimer);
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsTimerImpl)
+ NS_DECL_NON_VIRTUAL_NSITIMER
+
+ static nsresult Startup();
+ static void Shutdown();
+
+ void Fire(int32_t aGeneration);
+
+#ifdef MOZ_TASK_TRACER
+ void GetTLSTraceInfo();
+ mozilla::tasktracer::TracedTaskCommon GetTracedTask();
+#endif
+
+ int32_t GetGeneration()
+ {
+ return mGeneration;
+ }
+
+ nsresult InitCommon(uint32_t aDelay, uint32_t aType);
+
+ struct Callback {
+ Callback() :
+ mType(Type::Unknown),
+ mName(Nothing),
+ mClosure(nullptr)
+ {
+ mCallback.c = nullptr;
+ }
+
+ Callback(const Callback& other) = delete;
+ Callback& operator=(const Callback& other) = delete;
+
+ ~Callback()
+ {
+ if (mType == Type::Interface) {
+ NS_RELEASE(mCallback.i);
+ } else if (mType == Type::Observer) {
+ NS_RELEASE(mCallback.o);
+ }
+ }
+
+ void swap(Callback& other)
+ {
+ std::swap(mType, other.mType);
+ std::swap(mCallback, other.mCallback);
+ std::swap(mName, other.mName);
+ std::swap(mClosure, other.mClosure);
+ }
+
+ enum class Type : uint8_t {
+ Unknown = 0,
+ Interface = 1,
+ Function = 2,
+ Observer = 3,
+ };
+ Type mType;
+
+ union CallbackUnion
+ {
+ nsTimerCallbackFunc c;
+ // These refcounted references are managed manually, as they are in a union
+ nsITimerCallback* MOZ_OWNING_REF i;
+ nsIObserver* MOZ_OWNING_REF o;
+ } mCallback;
+
+ // |Name| is a tagged union type representing one of (a) nothing, (b) a
+ // string, or (c) a function. mozilla::Variant doesn't naturally handle the
+ // "nothing" case, so we define a dummy type and value (which is unused and
+ // so the exact value doesn't matter) for it.
+ typedef const int NameNothing;
+ typedef const char* NameString;
+ typedef nsTimerNameCallbackFunc NameFunc;
+ typedef mozilla::Variant<NameNothing, NameString, NameFunc> Name;
+ static const NameNothing Nothing;
+ Name mName;
+
+ void* mClosure;
+ };
+
+ Callback& GetCallback()
+ {
+ mMutex.AssertCurrentThreadOwns();
+ if (mCallback.mType == Callback::Type::Unknown) {
+ return mCallbackDuringFire;
+ }
+
+ return mCallback;
+ }
+
+ bool IsRepeating() const
+ {
+ static_assert(nsITimer::TYPE_ONE_SHOT < nsITimer::TYPE_REPEATING_SLACK,
+ "invalid ordering of timer types!");
+ static_assert(
+ nsITimer::TYPE_REPEATING_SLACK < nsITimer::TYPE_REPEATING_PRECISE,
+ "invalid ordering of timer types!");
+ static_assert(
+ nsITimer::TYPE_REPEATING_PRECISE <
+ nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
+ "invalid ordering of timer types!");
+ return mType >= nsITimer::TYPE_REPEATING_SLACK;
+ }
+
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+ void LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay);
+
+ nsresult InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
+ void* aClosure,
+ uint32_t aDelay,
+ uint32_t aType,
+ Callback::Name aName);
+
+ // These members are set by the initiating thread, when the timer's type is
+ // changed and during the period where it fires on that thread.
+ uint8_t mType;
+
+ // The generation number of this timer, re-generated each time the timer is
+ // initialized so one-shot timers can be canceled and re-initialized by the
+ // arming thread without any bad race conditions.
+ // Updated only after this timer has been removed from the timer thread.
+ int32_t mGeneration;
+
+ uint32_t mDelay;
+ // Updated only after this timer has been removed from the timer thread.
+ TimeStamp mTimeout;
+
+#ifdef MOZ_TASK_TRACER
+ mozilla::tasktracer::TracedTaskCommon mTracedTask;
+#endif
+
+ static double sDeltaSum;
+ static double sDeltaSumSquared;
+ static double sDeltaNum;
+ const RefPtr<nsITimer> mITimer;
+ mozilla::Mutex mMutex;
+ Callback mCallback;
+ Callback mCallbackDuringFire;
+};
+
+class nsTimer final : public nsITimer
+{
+ virtual ~nsTimer();
+public:
+ nsTimer() : mImpl(new nsTimerImpl(this)) {}
+
+ friend class TimerThread;
+ friend class nsTimerEvent;
+ friend struct TimerAdditionComparator;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_SAFE_NSITIMER(mImpl);
+
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
+private:
+ // nsTimerImpl holds a strong ref to us. When our refcount goes to 1, we will
+ // null this to break the cycle.
+ RefPtr<nsTimerImpl> mImpl;
+};
+
+#endif /* nsTimerImpl_h___ */
diff --git a/xpcom/typelib/moz.build b/xpcom/typelib/moz.build
new file mode 100644
index 000000000..3d82a998d
--- /dev/null
+++ b/xpcom/typelib/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += ['xpt']
+
diff --git a/xpcom/typelib/xpt/moz.build b/xpcom/typelib/xpt/moz.build
new file mode 100644
index 000000000..80b9160b0
--- /dev/null
+++ b/xpcom/typelib/xpt/moz.build
@@ -0,0 +1,40 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Library('xpt')
+
+DIRS += ['tools']
+
+UNIFIED_SOURCES += [
+ 'xpt_arena.cpp',
+ 'xpt_struct.cpp',
+ 'xpt_xdr.cpp',
+]
+
+EXPORTS += [
+ 'xpt_arena.h',
+ 'xpt_struct.h',
+ 'xpt_xdr.h',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '!/xpcom/base',
+ '/xpcom/base',
+]
+
+if CONFIG['_MSC_VER']:
+ CFLAGS += ['-Zl']
+
+# Code with FINAL_LIBRARY = 'xul' shouldn't do this, but the code
+# here doesn't use malloc functions anyways, while not setting
+# MOZ_NO_MOZALLOC makes the code include mozalloc.h, which includes
+# inline operator new definitions that MSVC linker doesn't strip
+# when linking the xpt tests.
+DEFINES['MOZ_NO_MOZALLOC'] = True
+
+DIST_INSTALL = True
diff --git a/xpcom/typelib/xpt/tools/moz.build b/xpcom/typelib/xpt/tools/moz.build
new file mode 100644
index 000000000..bd4a2836b
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+PYTHON_UNIT_TESTS += [
+ 'runtests.py',
+]
+
+SDK_FILES.bin += [
+ 'xpt.py',
+]
diff --git a/xpcom/typelib/xpt/tools/runtests.py b/xpcom/typelib/xpt/tools/runtests.py
new file mode 100644
index 000000000..a86e6625d
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/runtests.py
@@ -0,0 +1,770 @@
+#!/usr/bin/env python
+# Copyright 2010,2011 Mozilla Foundation. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE MOZILLA FOUNDATION ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MOZILLA FOUNDATION OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation
+# are those of the authors and should not be interpreted as representing
+# official policies, either expressed or implied, of the Mozilla
+# Foundation.
+
+import difflib
+import os
+from StringIO import StringIO
+import subprocess
+import tempfile
+import mozunit
+import unittest
+import xpt
+
+
+def get_output(bin, file):
+ p = subprocess.Popen([bin, file], stdout=subprocess.PIPE)
+ stdout, _ = p.communicate()
+ return stdout
+
+if "MOZILLA_OBJDIR" in os.environ:
+ class CheckXPTDump(unittest.TestCase):
+ def test_xpt_dump_diffs(self):
+ MOZILLA_OBJDIR = os.environ["MOZILLA_OBJDIR"]
+ xptdump = os.path.abspath(os.path.join(MOZILLA_OBJDIR,
+ "dist", "bin", "xpt_dump"))
+ components = os.path.abspath(os.path.join(MOZILLA_OBJDIR,
+ "dist", "bin", "components"))
+ for f in os.listdir(components):
+ if not f.endswith(".xpt"):
+ continue
+ fullpath = os.path.join(components, f)
+ # read a Typelib and dump it to a string
+ t = xpt.Typelib.read(fullpath)
+ self.assert_(t is not None)
+ outf = StringIO()
+ t.dump(outf)
+ out = outf.getvalue()
+ # now run xpt_dump on it
+ out2 = get_output(xptdump, fullpath)
+ if out != out2:
+ print "diff %s" % f
+ for line in difflib.unified_diff(out2.split("\n"), out.split("\n"), lineterm=""):
+ print line
+ self.assert_(out == out2, "xpt_dump output should be identical for %s" % f)
+
+
+class TestIIDString(unittest.TestCase):
+ def test_iid_str_roundtrip(self):
+ iid_str = "11223344-5566-7788-9900-aabbccddeeff"
+ iid = xpt.Typelib.string_to_iid(iid_str)
+ self.assertEqual(iid_str, xpt.Typelib.iid_to_string(iid))
+
+ def test_iid_roundtrip(self):
+ iid = "\x11\x22\x33\x44\x55\x66\x77\x88\x99\x00\xaa\xbb\xcc\xdd\xee\xff"
+ iid_str = xpt.Typelib.iid_to_string(iid)
+ self.assertEqual(iid, xpt.Typelib.string_to_iid(iid_str))
+
+
+class TypelibCompareMixin:
+ def assertEqualTypelibs(self, t1, t2):
+ self.assert_(t1 is not None, "Should not be None")
+ self.assert_(t2 is not None, "Should not be None")
+ self.assertEqual(t1.version, t2.version, "Versions should be equal")
+ self.assertEqual(len(t1.interfaces), len(t2.interfaces),
+ "Number of interfaces should be equal")
+ for i, j in zip(t1.interfaces, t2.interfaces):
+ self.assertEqualInterfaces(i, j)
+
+ def assertEqualInterfaces(self, i1, i2):
+ self.assert_(i1 is not None, "Should not be None")
+ self.assert_(i2 is not None, "Should not be None")
+ self.assertEqual(i1.name, i2.name, "Names should be equal")
+ self.assertEqual(i1.iid, i2.iid, "IIDs should be equal")
+ self.assertEqual(i1.namespace, i2.namespace,
+ "Namespaces should be equal")
+ self.assertEqual(i1.resolved, i2.resolved,
+ "Resolved status should be equal")
+ if i1.resolved:
+ if i1.parent or i2.parent:
+ # Can't test exact equality, probably different objects
+ self.assertEqualInterfaces(i1.parent, i2.parent)
+ self.assertEqual(len(i1.methods), len(i2.methods))
+ for m, n in zip(i1.methods, i2.methods):
+ self.assertEqualMethods(m, n)
+ self.assertEqual(len(i1.constants), len(i2.constants))
+ for c, d in zip(i1.constants, i2.constants):
+ self.assertEqualConstants(c, d)
+ self.assertEqual(i1.scriptable, i2.scriptable,
+ "Scriptable status should be equal")
+ self.assertEqual(i1.function, i2.function,
+ "Function status should be equal")
+
+ def assertEqualMethods(self, m1, m2):
+ self.assert_(m1 is not None, "Should not be None")
+ self.assert_(m2 is not None, "Should not be None")
+ self.assertEqual(m1.name, m2.name, "Names should be equal")
+ self.assertEqual(m1.getter, m2.getter, "Getter flag should be equal")
+ self.assertEqual(m1.setter, m2.setter, "Setter flag should be equal")
+ self.assertEqual(m1.notxpcom, m2.notxpcom,
+ "notxpcom flag should be equal")
+ self.assertEqual(m1.constructor, m2.constructor,
+ "constructor flag should be equal")
+ self.assertEqual(m1.hidden, m2.hidden, "hidden flag should be equal")
+ self.assertEqual(m1.optargc, m2.optargc, "optargc flag should be equal")
+ self.assertEqual(m1.implicit_jscontext, m2.implicit_jscontext,
+ "implicit_jscontext flag should be equal")
+ for p1, p2 in zip(m1.params, m2.params):
+ self.assertEqualParams(p1, p2)
+ self.assertEqualParams(m1.result, m2.result)
+
+ def assertEqualConstants(self, c1, c2):
+ self.assert_(c1 is not None, "Should not be None")
+ self.assert_(c2 is not None, "Should not be None")
+ self.assertEqual(c1.name, c2.name)
+ self.assertEqual(c1.value, c2.value)
+ self.assertEqualTypes(c1.type, c2.type)
+
+ def assertEqualParams(self, p1, p2):
+ self.assert_(p1 is not None, "Should not be None")
+ self.assert_(p2 is not None, "Should not be None")
+ self.assertEqualTypes(p1.type, p2.type)
+ self.assertEqual(p1.in_, p2.in_)
+ self.assertEqual(p1.out, p2.out)
+ self.assertEqual(p1.retval, p2.retval)
+ self.assertEqual(p1.shared, p2.shared)
+ self.assertEqual(p1.dipper, p2.dipper)
+ self.assertEqual(p1.optional, p2.optional)
+
+ def assertEqualTypes(self, t1, t2):
+ self.assert_(t1 is not None, "Should not be None")
+ self.assert_(t2 is not None, "Should not be None")
+ self.assertEqual(type(t1), type(t2), "type types should be equal")
+ self.assertEqual(t1.pointer, t2.pointer,
+ "pointer flag should be equal for %s and %s" % (t1, t2))
+ self.assertEqual(t1.reference, t2.reference)
+ if isinstance(t1, xpt.SimpleType):
+ self.assertEqual(t1.tag, t2.tag)
+ elif isinstance(t1, xpt.InterfaceType):
+ self.assertEqualInterfaces(t1.iface, t2.iface)
+ elif isinstance(t1, xpt.InterfaceIsType):
+ self.assertEqual(t1.param_index, t2.param_index)
+ elif isinstance(t1, xpt.ArrayType):
+ self.assertEqualTypes(t1.element_type, t2.element_type)
+ self.assertEqual(t1.size_is_arg_num, t2.size_is_arg_num)
+ self.assertEqual(t1.length_is_arg_num, t2.length_is_arg_num)
+ elif isinstance(t1, xpt.StringWithSizeType) or isinstance(t1, xpt.WideStringWithSizeType):
+ self.assertEqual(t1.size_is_arg_num, t2.size_is_arg_num)
+ self.assertEqual(t1.length_is_arg_num, t2.length_is_arg_num)
+
+
+class TestTypelibReadWrite(unittest.TestCase, TypelibCompareMixin):
+ def test_read_file(self):
+ """
+ Test that a Typelib can be read/written from/to a file.
+ """
+ t = xpt.Typelib()
+ # add an unresolved interface
+ t.interfaces.append(xpt.Interface("IFoo"))
+ fd, f = tempfile.mkstemp()
+ os.close(fd)
+ t.write(f)
+ t2 = xpt.Typelib.read(f)
+ os.remove(f)
+ self.assert_(t2 is not None)
+ self.assertEqualTypelibs(t, t2)
+
+
+# TODO: test flags in various combinations
+class TestTypelibRoundtrip(unittest.TestCase, TypelibCompareMixin):
+ def checkRoundtrip(self, t):
+ s = StringIO()
+ t.write(s)
+ s.seek(0)
+ t2 = xpt.Typelib.read(s)
+ self.assert_(t2 is not None)
+ self.assertEqualTypelibs(t, t2)
+
+ def test_simple(self):
+ t = xpt.Typelib()
+ # add an unresolved interface
+ t.interfaces.append(xpt.Interface("IFoo"))
+ self.checkRoundtrip(t)
+
+ t = xpt.Typelib()
+ # add an unresolved interface with an IID
+ t.interfaces.append(xpt.Interface("IBar", "11223344-5566-7788-9900-aabbccddeeff"))
+ self.checkRoundtrip(t)
+
+ def test_parent(self):
+ """
+ Test that an interface's parent property is correctly serialized
+ and deserialized.
+
+ """
+ t = xpt.Typelib()
+ pi = xpt.Interface("IParent")
+ t.interfaces.append(pi)
+ t.interfaces.append(xpt.Interface("IChild", iid="11223344-5566-7788-9900-aabbccddeeff",
+ parent=pi, resolved=True))
+ self.checkRoundtrip(t)
+
+ def test_ifaceFlags(self):
+ """
+ Test that an interface's flags are correctly serialized
+ and deserialized.
+
+ """
+ t = xpt.Typelib()
+ t.interfaces.append(xpt.Interface("IFlags", iid="11223344-5566-7788-9900-aabbccddeeff",
+ resolved=True,
+ scriptable=True,
+ function=True))
+ self.checkRoundtrip(t)
+
+ def test_constants(self):
+ c = xpt.Constant("X", xpt.SimpleType(xpt.Type.Tags.uint32),
+ 0xF000F000)
+ i = xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ constants=[c])
+ t = xpt.Typelib(interfaces=[i])
+ self.checkRoundtrip(t)
+ # tack on some more constants
+ i.constants.append(xpt.Constant("Y",
+ xpt.SimpleType(xpt.Type.Tags.int16),
+ -30000))
+ i.constants.append(xpt.Constant("Z",
+ xpt.SimpleType(xpt.Type.Tags.uint16),
+ 0xB0B0))
+ i.constants.append(xpt.Constant("A",
+ xpt.SimpleType(xpt.Type.Tags.int32),
+ -1000000))
+ self.checkRoundtrip(t)
+
+ def test_methods(self):
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ i = xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ t = xpt.Typelib(interfaces=[i])
+ self.checkRoundtrip(t)
+ # add some more methods
+ i.methods.append(xpt.Method("One", xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ params=[
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int64)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.float, pointer=True))
+ ]))
+ self.checkRoundtrip(t)
+ # test some other types (should really be more thorough)
+ i.methods.append(xpt.Method("Two", xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ params=[
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.UTF8String, pointer=True)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.wchar_t_ptr, pointer=True))
+ ]))
+ self.checkRoundtrip(t)
+ # add a method with an InterfaceType argument
+ bar = xpt.Interface("IBar")
+ t.interfaces.append(bar)
+ i.methods.append(xpt.Method("IFaceMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ params=[
+ xpt.Param(xpt.InterfaceType(bar))
+ ]))
+ self.checkRoundtrip(t)
+
+ # add a method with an InterfaceIsType argument
+ i.methods.append(xpt.Method("IFaceIsMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.void)),
+ params=[
+ xpt.Param(xpt.InterfaceIsType(1)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.nsIID))
+ ]))
+ self.checkRoundtrip(t)
+
+ # add a method with an ArrayType argument
+ i.methods.append(xpt.Method("ArrayMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.void)),
+ params=[
+ xpt.Param(xpt.ArrayType(
+ xpt.SimpleType(xpt.Type.Tags.int32),
+ 1, 2)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ ]))
+ self.checkRoundtrip(t)
+
+ # add a method with a StringWithSize and WideStringWithSize arguments
+ i.methods.append(xpt.Method("StringWithSizeMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.void)),
+ params=[
+ xpt.Param(xpt.StringWithSizeType(
+ 1, 2)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.WideStringWithSizeType(
+ 4, 5)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ ]))
+ self.checkRoundtrip(t)
+
+
+class TestInterfaceCmp(unittest.TestCase):
+ def test_unresolvedName(self):
+ """
+ Test comparison function on xpt.Interface by name.
+
+ """
+ i1 = xpt.Interface("ABC")
+ i2 = xpt.Interface("DEF")
+ self.assert_(i1 < i2)
+ self.assert_(i1 != i2)
+
+ def test_unresolvedEqual(self):
+ """
+ Test comparison function on xpt.Interface with equal names and IIDs.
+
+ """
+ i1 = xpt.Interface("ABC")
+ i2 = xpt.Interface("ABC")
+ self.assert_(i1 == i2)
+
+ def test_unresolvedIID(self):
+ """
+ Test comparison function on xpt.Interface with different IIDs.
+
+ """
+ # IIDs sort before names
+ i1 = xpt.Interface("ABC", iid="22334411-5566-7788-9900-aabbccddeeff")
+ i2 = xpt.Interface("DEF", iid="11223344-5566-7788-9900-aabbccddeeff")
+ self.assert_(i2 < i1)
+ self.assert_(i2 != i1)
+
+ def test_unresolvedResolved(self):
+ """
+ Test comparison function on xpt.Interface with interfaces with
+ identical names and IIDs but different resolved status.
+
+ """
+ i1 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff")
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ i2 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ self.assert_(i2 < i1)
+ self.assert_(i2 != i1)
+
+ def test_resolvedIdentical(self):
+ """
+ Test comparison function on xpt.Interface with interfaces with
+ identical names and IIDs, both of which are resolved.
+
+ """
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ i1 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ i2 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ self.assert_(i2 == i1)
+
+
+class TestXPTLink(unittest.TestCase):
+ def test_mergeDifferent(self):
+ """
+ Test that merging two typelibs with completely different interfaces
+ produces the correctly merged typelib.
+
+ """
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ t2.interfaces.append(xpt.Interface("IBar", scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ # Interfaces should wind up sorted
+ self.assertEqual("IBar", t3.interfaces[0].name)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+
+ # Add some IID values
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff", scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ t2.interfaces.append(xpt.Interface("IBar", iid="44332211-6655-8877-0099-aabbccddeeff", scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ # Interfaces should wind up sorted
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("IBar", t3.interfaces[1].name)
+
+ def test_mergeConflict(self):
+ """
+ Test that merging two typelibs with conflicting interface definitions
+ raises an error.
+
+ """
+ # Same names, different IIDs
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff"))
+ t2 = xpt.Typelib()
+ # add an unresolved interface, same name different IID
+ t2.interfaces.append(xpt.Interface("IFoo", iid="44332211-6655-8877-0099-aabbccddeeff"))
+ self.assertRaises(xpt.DataError, xpt.xpt_link, [t1, t2])
+
+ # Same IIDs, different names
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff"))
+ t2 = xpt.Typelib()
+ # add an unresolved interface, same IID different name
+ t2.interfaces.append(xpt.Interface("IBar", iid="11223344-5566-7788-9900-aabbccddeeff"))
+ self.assertRaises(xpt.DataError, xpt.xpt_link, [t1, t2])
+
+ def test_mergeUnresolvedIID(self):
+ """
+ Test that merging a typelib with an unresolved definition of
+ an interface that's also unresolved in this typelib, but one
+ has a valid IID copies the IID value to the resulting typelib.
+
+ """
+ # Unresolved in both, but t1 has an IID value
+ t1 = xpt.Typelib()
+ # add an unresolved interface with a valid IID
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff", scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface, no IID
+ t2.interfaces.append(xpt.Interface("IFoo"))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+ # Unresolved in both, but t2 has an IID value
+ t1 = xpt.Typelib()
+ # add an unresolved interface, no IID
+ t1.interfaces.append(xpt.Interface("IFoo"))
+ t2 = xpt.Typelib()
+ # add an unresolved interface with a valid IID
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff", scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+
+ def test_mergeResolvedUnresolved(self):
+ """
+ Test that merging two typelibs, one of which contains an unresolved
+ reference to an interface, and the other of which contains a
+ resolved reference to the same interface results in keeping the
+ resolved reference.
+
+ """
+ # t1 has an unresolved interface, t2 has a resolved version
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo"))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assertEqual("Bar", t3.interfaces[0].methods[0].name)
+
+ # t1 has a resolved interface, t2 has an unresolved version
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ t2.interfaces.append(xpt.Interface("IFoo"))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assertEqual("Bar", t3.interfaces[0].methods[0].name)
+
+ def test_mergeReplaceParents(self):
+ """
+ Test that merging an interface results in other interfaces' parent
+ member being updated properly.
+
+ """
+ # t1 has an unresolved interface, t2 has a resolved version,
+ # but t1 also has another interface whose parent is the unresolved
+ # interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ pi = xpt.Interface("IFoo")
+ t1.interfaces.append(pi)
+ # add a child of the unresolved interface
+ t1.interfaces.append(xpt.Interface("IChild", iid="11111111-1111-1111-1111-111111111111",
+ resolved=True, parent=pi, scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IChild", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ # Ensure that IChild's parent has been updated
+ self.assertEqual(t3.interfaces[1], t3.interfaces[0].parent)
+ self.assert_(t3.interfaces[0].parent.resolved)
+
+ # t1 has a resolved interface, t2 has an unresolved version,
+ # but t2 also has another interface whose parent is the unresolved
+ # interface.
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ pi = xpt.Interface("IFoo")
+ t2.interfaces.append(pi)
+ # add a child of the unresolved interface
+ t2.interfaces.append(xpt.Interface("IChild", iid="11111111-1111-1111-1111-111111111111",
+ resolved=True, parent=pi, scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IChild", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ # Ensure that IChild's parent has been updated
+ self.assertEqual(t3.interfaces[1], t3.interfaces[0].parent)
+ self.assert_(t3.interfaces[0].parent.resolved)
+
+ def test_mergeReplaceRetval(self):
+ """
+ Test that merging an interface correctly updates InterfaceType
+ return values on methods of other interfaces.
+
+ """
+ # t1 has an unresolved interface and an interface that uses the
+ # unresolved interface as a return value from a method. t2
+ # has a resolved version of the unresolved interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t1.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a return value in a method.
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("ReturnIface", p)
+ t1.interfaces.append(xpt.Interface("IRetval", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IRetval", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's return value type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].result.type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].result.type.iface)
+
+ # t1 has a resolved interface. t2 has an unresolved version and
+ # an interface that uses the unresolved interface as a return value
+ # from a method.
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t2.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a return value in a method.
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("ReturnIface", p)
+ t2.interfaces.append(xpt.Interface("IRetval", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IRetval", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's return value type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].result.type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].result.type.iface)
+
+ def test_mergeReplaceParams(self):
+ """
+ Test that merging an interface correctly updates InterfaceType
+ params on methods of other interfaces.
+
+ """
+ # t1 has an unresolved interface and an interface that uses the
+ # unresolved interface as a param value in a method. t2
+ # has a resolved version of the unresolved interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t1.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a param value in a method.
+ vp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("IfaceParam", vp, params=[p])
+ t1.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ m = xpt.Method("Bar", vp)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IParam", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's param type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].params[0].type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].params[0].type.iface)
+
+ # t1 has a resolved interface. t2 has an unresolved version
+ # and an interface that uses the unresolved interface as a
+ # param value in a method.
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ m = xpt.Method("Bar", vp)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t2.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a param value in a method.
+ vp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("IfaceParam", vp, params=[p])
+ t2.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IParam", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's param type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].params[0].type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].params[0].type.iface)
+
+ def test_mergeReplaceArrayTypeParams(self):
+ """
+ Test that merging an interface correctly updates ArrayType
+ params whose element_type is an InterfaceType on methods
+ of other interfaces.
+
+ """
+ # t1 has an unresolved interface and an interface that uses the
+ # unresolved interface as a type in an ArrayType in a parameter
+ # of a method. t2 has a resolved version of the unresolved interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t1.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a type in an ArrayType in a param value in a method.
+ vp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ intp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32))
+ p = xpt.Param(xpt.ArrayType(xpt.InterfaceType(i), 1, 2))
+ m = xpt.Method("ArrayIfaceParam", vp, params=[p, intp, intp])
+ t1.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ m = xpt.Method("Bar", vp)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IParam", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's param type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].params[0].type.element_type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].params[0].type.element_type.iface)
+
+if __name__ == '__main__':
+ mozunit.main()
diff --git a/xpcom/typelib/xpt/tools/xpt.py b/xpcom/typelib/xpt/tools/xpt.py
new file mode 100755
index 000000000..67d40f7b2
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/xpt.py
@@ -0,0 +1,1540 @@
+#!/usr/bin/env python
+# Copyright 2010,2011 Mozilla Foundation. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE MOZILLA FOUNDATION ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MOZILLA FOUNDATION OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation
+# are those of the authors and should not be interpreted as representing
+# official policies, either expressed or implied, of the Mozilla
+# Foundation.
+
+"""
+A module for working with XPCOM Type Libraries.
+
+The XPCOM Type Library File Format is described at:
+http://www.mozilla.org/scriptable/typelib_file.html . It is used
+to provide type information for calling methods on XPCOM objects
+from scripting languages such as JavaScript.
+
+This module provides a set of classes representing the parts of
+a typelib in a high-level manner, as well as methods for reading
+and writing them from files.
+
+The usable public interfaces are currently:
+Typelib.read(input_file) - read a typelib from a file on disk or file-like
+ object, return a Typelib object.
+
+xpt_dump(filename) - read a typelib from a file on disk, dump
+ the contents to stdout in a human-readable
+ format.
+
+Typelib() - construct a new Typelib object
+Interface() - construct a new Interface object
+Method() - construct a new object representing a method
+ defined on an Interface
+Constant() - construct a new object representing a constant
+ defined on an Interface
+Param() - construct a new object representing a parameter
+ to a method
+SimpleType() - construct a new object representing a simple
+ data type
+InterfaceType() - construct a new object representing a type that
+ is an IDL-defined interface
+
+"""
+
+from __future__ import with_statement
+import os
+import sys
+import struct
+import operator
+
+# header magic
+XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
+TYPELIB_VERSION = (1, 2)
+
+
+class FileFormatError(Exception):
+ pass
+
+
+class DataError(Exception):
+ pass
+
+
+# Magic for creating enums
+def M_add_class_attribs(attribs):
+ def foo(name, bases, dict_):
+ for v, k in attribs:
+ dict_[k] = v
+ return type(name, bases, dict_)
+ return foo
+
+
+def enum(*names):
+ class Foo(object):
+ __metaclass__ = M_add_class_attribs(enumerate(names))
+
+ def __setattr__(self, name, value): # this makes it read-only
+ raise NotImplementedError
+ return Foo()
+
+
+# Descriptor types as described in the spec
+class Type(object):
+ """
+ Data type of a method parameter or return value. Do not instantiate
+ this class directly. Rather, use one of its subclasses.
+
+ """
+ _prefixdescriptor = struct.Struct(">B")
+ Tags = enum(
+ # The first 18 entries are SimpleTypeDescriptor
+ 'int8',
+ 'int16',
+ 'int32',
+ 'int64',
+ 'uint8',
+ 'uint16',
+ 'uint32',
+ 'uint64',
+ 'float',
+ 'double',
+ 'boolean',
+ 'char',
+ 'wchar_t',
+ 'void',
+ # the following four values are only valid as pointers
+ 'nsIID',
+ 'DOMString',
+ 'char_ptr',
+ 'wchar_t_ptr',
+ # InterfaceTypeDescriptor
+ 'Interface',
+ # InterfaceIsTypeDescriptor
+ 'InterfaceIs',
+ # ArrayTypeDescriptor
+ 'Array',
+ # StringWithSizeTypeDescriptor
+ 'StringWithSize',
+ # WideStringWithSizeTypeDescriptor
+ 'WideStringWithSize',
+ # XXX: These are also SimpleTypes (but not in the spec)
+ # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/tools/xpt_dump.c#l69
+ 'UTF8String',
+ 'CString',
+ 'AString',
+ 'jsval',
+ )
+
+ def __init__(self, pointer=False, reference=False):
+ self.pointer = pointer
+ self.reference = reference
+ if reference and not pointer:
+ raise Exception("If reference is True pointer must be True too")
+
+ def __cmp__(self, other):
+ return (
+ # First make sure we have two Types of the same type (no pun intended!)
+ cmp(type(self), type(other)) or
+ cmp(self.pointer, other.pointer) or
+ cmp(self.reference, other.reference)
+ )
+
+ @staticmethod
+ def decodeflags(byte):
+ """
+ Given |byte|, an unsigned uint8 containing flag bits,
+ decode the flag bits as described in
+ http://www.mozilla.org/scriptable/typelib_file.html#TypeDescriptor
+ and return a dict of flagname: (True|False) suitable
+ for passing to Type.__init__ as **kwargs.
+
+ """
+ return {'pointer': bool(byte & 0x80),
+ 'reference': bool(byte & 0x20),
+ }
+
+ def encodeflags(self):
+ """
+ Encode the flag bits of this Type object. Returns a byte.
+
+ """
+ flags = 0
+ if self.pointer:
+ flags |= 0x80
+ if self.reference:
+ flags |= 0x20
+ return flags
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a TypeDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Type, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this TypeDescriptor.
+
+ """
+ start = data_pool + offset - 1
+ (data,) = Type._prefixdescriptor.unpack_from(map, start)
+ # first three bits are the flags
+ flags = data & 0xE0
+ flags = Type.decodeflags(flags)
+ # last five bits is the tag
+ tag = data & 0x1F
+ offset += Type._prefixdescriptor.size
+ t = None
+ if tag <= Type.Tags.wchar_t_ptr or tag >= Type.Tags.UTF8String:
+ t = SimpleType.get(data, tag, flags)
+ elif tag == Type.Tags.Interface:
+ t, offset = InterfaceType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.InterfaceIs:
+ t, offset = InterfaceIsType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.Array:
+ t, offset = ArrayType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.StringWithSize:
+ t, offset = StringWithSizeType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.WideStringWithSize:
+ t, offset = WideStringWithSizeType.read(typelib, map, data_pool, offset, flags)
+ return t, offset
+
+ def write(self, typelib, file):
+ """
+ Write a TypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position. For types other than
+ SimpleType, this is not sufficient for writing the TypeDescriptor,
+ and the subclass method must be called.
+
+ """
+ file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
+
+
+class SimpleType(Type):
+ """
+ A simple data type. (SimpleTypeDescriptor from the typelib specification.)
+
+ """
+ _cache = {}
+
+ def __init__(self, tag, **kwargs):
+ Type.__init__(self, **kwargs)
+ self.tag = tag
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def get(data, tag, flags):
+ """
+ Get a SimpleType object representing |data| (a TypeDescriptorPrefix).
+ May return an already-created object. If no cached object is found,
+ construct one with |tag| and |flags|.
+
+ """
+ if data not in SimpleType._cache:
+ SimpleType._cache[data] = SimpleType(tag, **flags)
+ return SimpleType._cache[data]
+
+ def __str__(self):
+ s = "unknown"
+ if self.tag == Type.Tags.char_ptr and self.pointer:
+ return "string"
+ if self.tag == Type.Tags.wchar_t_ptr and self.pointer:
+ return "wstring"
+ for t in dir(Type.Tags):
+ if self.tag == getattr(Type.Tags, t):
+ s = t
+ break
+
+ if self.pointer:
+ if self.reference:
+ s += " &"
+ else:
+ s += " *"
+ return s
+
+
+class InterfaceType(Type):
+ """
+ A type representing a pointer to an IDL-defined interface.
+ (InterfaceTypeDescriptor from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">H")
+
+ def __init__(self, iface, pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("InterfaceType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.iface = iface
+ self.tag = Type.Tags.Interface
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ # When comparing interface types, only look at the name.
+ cmp(self.iface.name, other.iface.name) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an InterfaceTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (InterfaceType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this InterfaceTypeDescriptor.
+
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (iface_index,) = InterfaceType._descriptor.unpack_from(map, start)
+ offset += InterfaceType._descriptor.size
+ iface = None
+ # interface indices are 1-based
+ if iface_index > 0 and iface_index <= len(typelib.interfaces):
+ iface = typelib.interfaces[iface_index - 1]
+ return InterfaceType(iface, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write an InterfaceTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ # write out the interface index (1-based)
+ file.write(InterfaceType._descriptor.pack(typelib.interfaces.index(self.iface) + 1))
+
+ def __str__(self):
+ if self.iface:
+ return self.iface.name
+ return "unknown interface"
+
+
+class InterfaceIsType(Type):
+ """
+ A type representing an interface described by one of the other
+ arguments to the method. (InterfaceIsTypeDescriptor from the
+ typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">B")
+ _cache = {}
+
+ def __init__(self, param_index, pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("InterfaceIsType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.param_index = param_index
+ self.tag = Type.Tags.InterfaceIs
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.param_index, other.param_index) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an InterfaceIsTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (InterfaceIsType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this InterfaceIsTypeDescriptor.
+ May return a cached value.
+
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (param_index,) = InterfaceIsType._descriptor.unpack_from(map, start)
+ offset += InterfaceIsType._descriptor.size
+ if param_index not in InterfaceIsType._cache:
+ InterfaceIsType._cache[param_index] = InterfaceIsType(param_index, **flags)
+ return InterfaceIsType._cache[param_index], offset
+
+ def write(self, typelib, file):
+ """
+ Write an InterfaceIsTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(InterfaceIsType._descriptor.pack(self.param_index))
+
+ def __str__(self):
+ return "InterfaceIs *"
+
+
+class ArrayType(Type):
+ """
+ A type representing an Array of elements of another type, whose
+ size and length are passed as separate parameters to a method.
+ (ArrayTypeDescriptor from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">BB")
+
+ def __init__(self, element_type, size_is_arg_num, length_is_arg_num,
+ pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("ArrayType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.element_type = element_type
+ self.size_is_arg_num = size_is_arg_num
+ self.length_is_arg_num = length_is_arg_num
+ self.tag = Type.Tags.Array
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.element_type, other.element_type) or
+ cmp(self.size_is_arg_num, other.size_is_arg_num) or
+ cmp(self.length_is_arg_num, other.length_is_arg_num) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an ArrayTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (ArrayType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this ArrayTypeDescriptor.
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (size_is_arg_num, length_is_arg_num) = ArrayType._descriptor.unpack_from(map, start)
+ offset += ArrayType._descriptor.size
+ t, offset = Type.read(typelib, map, data_pool, offset)
+ return ArrayType(t, size_is_arg_num, length_is_arg_num, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write an ArrayTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(ArrayType._descriptor.pack(self.size_is_arg_num,
+ self.length_is_arg_num))
+ self.element_type.write(typelib, file)
+
+ def __str__(self):
+ return "%s []" % str(self.element_type)
+
+
+class StringWithSizeType(Type):
+ """
+ A type representing a UTF-8 encoded string whose size and length
+ are passed as separate arguments to a method. (StringWithSizeTypeDescriptor
+ from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">BB")
+
+ def __init__(self, size_is_arg_num, length_is_arg_num,
+ pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("StringWithSizeType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.size_is_arg_num = size_is_arg_num
+ self.length_is_arg_num = length_is_arg_num
+ self.tag = Type.Tags.StringWithSize
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.size_is_arg_num, other.size_is_arg_num) or
+ cmp(self.length_is_arg_num, other.length_is_arg_num) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an StringWithSizeTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (StringWithSizeType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this StringWithSizeTypeDescriptor.
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (size_is_arg_num, length_is_arg_num) = StringWithSizeType._descriptor.unpack_from(map, start)
+ offset += StringWithSizeType._descriptor.size
+ return StringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write a StringWithSizeTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
+ self.length_is_arg_num))
+
+ def __str__(self):
+ return "string_s"
+
+
+class WideStringWithSizeType(Type):
+ """
+ A type representing a UTF-16 encoded string whose size and length
+ are passed as separate arguments to a method.
+ (WideStringWithSizeTypeDescriptor from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">BB")
+
+ def __init__(self, size_is_arg_num, length_is_arg_num,
+ pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("WideStringWithSizeType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.size_is_arg_num = size_is_arg_num
+ self.length_is_arg_num = length_is_arg_num
+ self.tag = Type.Tags.WideStringWithSize
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.size_is_arg_num, other.size_is_arg_num) or
+ cmp(self.length_is_arg_num, other.length_is_arg_num) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an WideStringWithSizeTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (WideStringWithSizeType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this WideStringWithSizeTypeDescriptor.
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (size_is_arg_num, length_is_arg_num) = WideStringWithSizeType._descriptor.unpack_from(map, start)
+ offset += WideStringWithSizeType._descriptor.size
+ return WideStringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write a WideStringWithSizeTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(WideStringWithSizeType._descriptor.pack(self.size_is_arg_num,
+ self.length_is_arg_num))
+
+ def __str__(self):
+ return "wstring_s"
+
+
+class Param(object):
+ """
+ A parameter to a method, or the return value of a method.
+ (ParamDescriptor from the typelib specification.)
+
+ """
+ _descriptorstart = struct.Struct(">B")
+
+ def __init__(self, type, in_=True, out=False, retval=False,
+ shared=False, dipper=False, optional=False):
+ """
+ Construct a Param object with the specified |type| and
+ flags. Params default to "in".
+
+ """
+
+ self.type = type
+ self.in_ = in_
+ self.out = out
+ self.retval = retval
+ self.shared = shared
+ self.dipper = dipper
+ self.optional = optional
+
+ def __cmp__(self, other):
+ return (
+ cmp(self.type, other.type) or
+ cmp(self.in_, other.in_) or
+ cmp(self.out, other.out) or
+ cmp(self.retval, other.retval) or
+ cmp(self.shared, other.shared) or
+ cmp(self.dipper, other.dipper) or
+ cmp(self.optional, other.optional)
+ )
+
+ @staticmethod
+ def decodeflags(byte):
+ """
+ Given |byte|, an unsigned uint8 containing flag bits,
+ decode the flag bits as described in
+ http://www.mozilla.org/scriptable/typelib_file.html#ParamDescriptor
+ and return a dict of flagname: (True|False) suitable
+ for passing to Param.__init__ as **kwargs
+ """
+ return {'in_': bool(byte & 0x80),
+ 'out': bool(byte & 0x40),
+ 'retval': bool(byte & 0x20),
+ 'shared': bool(byte & 0x10),
+ 'dipper': bool(byte & 0x08),
+ # XXX: Not in the spec, see:
+ # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l456
+ 'optional': bool(byte & 0x04),
+ }
+
+ def encodeflags(self):
+ """
+ Encode the flags of this Param. Return a byte suitable for
+ writing to a typelib file.
+
+ """
+ flags = 0
+ if self.in_:
+ flags |= 0x80
+ if self.out:
+ flags |= 0x40
+ if self.retval:
+ flags |= 0x20
+ if self.shared:
+ flags |= 0x10
+ if self.dipper:
+ flags |= 0x08
+ if self.optional:
+ flags |= 0x04
+ return flags
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a ParamDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Param, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this ParamDescriptor.
+ """
+ start = data_pool + offset - 1
+ (flags,) = Param._descriptorstart.unpack_from(map, start)
+ # only the first five bits are flags
+ flags &= 0xFC
+ flags = Param.decodeflags(flags)
+ offset += Param._descriptorstart.size
+ t, offset = Type.read(typelib, map, data_pool, offset)
+ p = Param(t, **flags)
+ return p, offset
+
+ def write(self, typelib, file):
+ """
+ Write a ParamDescriptor to |file|, which is assumed to be seeked
+ to the correct position.
+
+ """
+ file.write(Param._descriptorstart.pack(self.encodeflags()))
+ self.type.write(typelib, file)
+
+ def prefix(self):
+ """
+ Return a human-readable string representing the flags set
+ on this Param.
+
+ """
+ s = ""
+ if self.out:
+ if self.in_:
+ s = "inout "
+ else:
+ s = "out "
+ else:
+ s = "in "
+ if self.dipper:
+ s += "dipper "
+ if self.retval:
+ s += "retval "
+ if self.shared:
+ s += "shared "
+ if self.optional:
+ s += "optional "
+ return s
+
+ def __str__(self):
+ return self.prefix() + str(self.type)
+
+
+class Method(object):
+ """
+ A method of an interface, defining its associated parameters
+ and return value.
+ (MethodDescriptor from the typelib specification.)
+
+ """
+ _descriptorstart = struct.Struct(">BIB")
+
+ def __init__(self, name, result,
+ params=[], getter=False, setter=False, notxpcom=False,
+ constructor=False, hidden=False, optargc=False,
+ implicit_jscontext=False):
+ self.name = name
+ self._name_offset = 0
+ self.getter = getter
+ self.setter = setter
+ self.notxpcom = notxpcom
+ self.constructor = constructor
+ self.hidden = hidden
+ self.optargc = optargc
+ self.implicit_jscontext = implicit_jscontext
+ self.params = list(params)
+ if result and not isinstance(result, Param):
+ raise Exception("result must be a Param!")
+ self.result = result
+
+ def __cmp__(self, other):
+ return (
+ cmp(self.name, other.name) or
+ cmp(self.getter, other.getter) or
+ cmp(self.setter, other.setter) or
+ cmp(self.notxpcom, other.notxpcom) or
+ cmp(self.constructor, other.constructor) or
+ cmp(self.hidden, other.hidden) or
+ cmp(self.optargc, other.optargc) or
+ cmp(self.implicit_jscontext, other.implicit_jscontext) or
+ cmp(self.params, other.params) or
+ cmp(self.result, other.result)
+ )
+
+ def read_params(self, typelib, map, data_pool, offset, num_args):
+ """
+ Read |num_args| ParamDescriptors representing this Method's arguments
+ from the mmaped file |map| with data pool at the offset |data_pool|,
+ starting at |offset| into self.params. Returns the offset
+ suitable for reading the data following the ParamDescriptor array.
+
+ """
+ for i in range(num_args):
+ p, offset = Param.read(typelib, map, data_pool, offset)
+ self.params.append(p)
+ return offset
+
+ def read_result(self, typelib, map, data_pool, offset):
+ """
+ Read a ParamDescriptor representing this Method's return type
+ from the mmaped file |map| with data pool at the offset |data_pool|,
+ starting at |offset| into self.result. Returns the offset
+ suitable for reading the data following the ParamDescriptor.
+
+ """
+ self.result, offset = Param.read(typelib, map, data_pool, offset)
+ return offset
+
+ @staticmethod
+ def decodeflags(byte):
+ """
+ Given |byte|, an unsigned uint8 containing flag bits,
+ decode the flag bits as described in
+ http://www.mozilla.org/scriptable/typelib_file.html#MethodDescriptor
+ and return a dict of flagname: (True|False) suitable
+ for passing to Method.__init__ as **kwargs
+
+ """
+ return {'getter': bool(byte & 0x80),
+ 'setter': bool(byte & 0x40),
+ 'notxpcom': bool(byte & 0x20),
+ 'constructor': bool(byte & 0x10),
+ 'hidden': bool(byte & 0x08),
+ # Not in the spec, see
+ # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l489
+ 'optargc': bool(byte & 0x04),
+ 'implicit_jscontext': bool(byte & 0x02),
+ }
+
+ def encodeflags(self):
+ """
+ Encode the flags of this Method object, return a byte suitable
+ for writing to a typelib file.
+
+ """
+ flags = 0
+ if self.getter:
+ flags |= 0x80
+ if self.setter:
+ flags |= 0x40
+ if self.notxpcom:
+ flags |= 0x20
+ if self.constructor:
+ flags |= 0x10
+ if self.hidden:
+ flags |= 0x08
+ if self.optargc:
+ flags |= 0x04
+ if self.implicit_jscontext:
+ flags |= 0x02
+ return flags
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a MethodDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Method, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this MethodDescriptor.
+
+ """
+ start = data_pool + offset - 1
+ flags, name_offset, num_args = Method._descriptorstart.unpack_from(map, start)
+ # only the first seven bits are flags
+ flags &= 0xFE
+ flags = Method.decodeflags(flags)
+ name = Typelib.read_string(map, data_pool, name_offset)
+ m = Method(name, None, **flags)
+ offset += Method._descriptorstart.size
+ offset = m.read_params(typelib, map, data_pool, offset, num_args)
+ offset = m.read_result(typelib, map, data_pool, offset)
+ return m, offset
+
+ def write(self, typelib, file):
+ """
+ Write a MethodDescriptor to |file|, which is assumed to be
+ seeked to the right position.
+
+ """
+ file.write(Method._descriptorstart.pack(self.encodeflags(),
+ self._name_offset,
+ len(self.params)))
+ for p in self.params:
+ p.write(typelib, file)
+ self.result.write(typelib, file)
+
+ def write_name(self, file, data_pool_offset):
+ """
+ Write this method's name to |file|.
+ Assumes that |file| is currently seeked to an unused portion
+ of the data pool.
+
+ """
+ if self.name:
+ self._name_offset = file.tell() - data_pool_offset + 1
+ file.write(self.name + "\x00")
+ else:
+ self._name_offset = 0
+
+
+class Constant(object):
+ """
+ A constant value of a specific type defined on an interface.
+ (ConstantDesciptor from the typelib specification.)
+
+ """
+ _descriptorstart = struct.Struct(">I")
+ # Actual value is restricted to this set of types
+ # XXX: the spec lies, the source allows a bunch more
+ # http://hg.mozilla.org/mozilla-central/annotate/9c85f9aaec8c/xpcom/typelib/xpt/src/xpt_struct.c#l689
+ typemap = {Type.Tags.int16: '>h',
+ Type.Tags.uint16: '>H',
+ Type.Tags.int32: '>i',
+ Type.Tags.uint32: '>I'}
+
+ def __init__(self, name, type, value):
+ self.name = name
+ self._name_offset = 0
+ self.type = type
+ self.value = value
+
+ def __cmp__(self, other):
+ return (
+ cmp(self.name, other.name) or
+ cmp(self.type, other.type) or
+ cmp(self.value, other.value)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a ConstDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Constant, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this ConstDescriptor.
+
+ """
+ start = data_pool + offset - 1
+ (name_offset,) = Constant._descriptorstart.unpack_from(map, start)
+ name = Typelib.read_string(map, data_pool, name_offset)
+ offset += Constant._descriptorstart.size
+ # Read TypeDescriptor
+ t, offset = Type.read(typelib, map, data_pool, offset)
+ c = None
+ if isinstance(t, SimpleType) and t.tag in Constant.typemap:
+ tt = Constant.typemap[t.tag]
+ start = data_pool + offset - 1
+ (val,) = struct.unpack_from(tt, map, start)
+ offset += struct.calcsize(tt)
+ c = Constant(name, t, val)
+ return c, offset
+
+ def write(self, typelib, file):
+ """
+ Write a ConstDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ file.write(Constant._descriptorstart.pack(self._name_offset))
+ self.type.write(typelib, file)
+ tt = Constant.typemap[self.type.tag]
+ file.write(struct.pack(tt, self.value))
+
+ def write_name(self, file, data_pool_offset):
+ """
+ Write this constants's name to |file|.
+ Assumes that |file| is currently seeked to an unused portion
+ of the data pool.
+
+ """
+ if self.name:
+ self._name_offset = file.tell() - data_pool_offset + 1
+ file.write(self.name + "\x00")
+ else:
+ self._name_offset = 0
+
+ def __repr__(self):
+ return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
+
+
+class Interface(object):
+ """
+ An Interface represents an object, with its associated methods
+ and constant values.
+ (InterfaceDescriptor from the typelib specification.)
+
+ """
+ _direntry = struct.Struct(">16sIII")
+ _descriptorstart = struct.Struct(">HH")
+
+ UNRESOLVED_IID = "00000000-0000-0000-0000-000000000000"
+
+ def __init__(self, name, iid=UNRESOLVED_IID, namespace="",
+ resolved=False, parent=None, methods=[], constants=[],
+ scriptable=False, function=False, builtinclass=False,
+ main_process_scriptable_only=False):
+ self.resolved = resolved
+ # TODO: should validate IIDs!
+ self.iid = iid
+ self.name = name
+ self.namespace = namespace
+ # if unresolved, all the members following this are unusable
+ self.parent = parent
+ self.methods = list(methods)
+ self.constants = list(constants)
+ self.scriptable = scriptable
+ self.function = function
+ self.builtinclass = builtinclass
+ self.main_process_scriptable_only = main_process_scriptable_only
+ # For sanity, if someone constructs an Interface and passes
+ # in methods or constants, then it's resolved.
+ if self.methods or self.constants:
+ # make sure it has a valid IID
+ if self.iid == Interface.UNRESOLVED_IID:
+ raise DataError("Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name)
+ self.resolved = True
+ # These are only used for writing out the interface
+ self._descriptor_offset = 0
+ self._name_offset = 0
+ self._namespace_offset = 0
+ self.xpt_filename = None
+
+ def __repr__(self):
+ return "Interface('%s', '%s', '%s', methods=%s)" % (self.name, self.iid, self.namespace, self.methods)
+
+ def __str__(self):
+ return "Interface(name='%s', iid='%s')" % (self.name, self.iid)
+
+ def __hash__(self):
+ return hash((self.name, self.iid))
+
+ def __cmp__(self, other):
+ c = cmp(self.iid, other.iid)
+ if c != 0:
+ return c
+ c = cmp(self.name, other.name)
+ if c != 0:
+ return c
+ c = cmp(self.namespace, other.namespace)
+ if c != 0:
+ return c
+ # names and IIDs are the same, check resolved
+ if self.resolved != other.resolved:
+ if self.resolved:
+ return -1
+ else:
+ return 1
+ else:
+ if not self.resolved:
+ # both unresolved, but names and IIDs are the same, so equal
+ return 0
+ # When comparing parents, only look at the name.
+ if (self.parent is None) != (other.parent is None):
+ if self.parent is None:
+ return -1
+ else:
+ return 1
+ elif self.parent is not None:
+ c = cmp(self.parent.name, other.parent.name)
+ if c != 0:
+ return c
+ return (
+ cmp(self.methods, other.methods) or
+ cmp(self.constants, other.constants) or
+ cmp(self.scriptable, other.scriptable) or
+ cmp(self.function, other.function) or
+ cmp(self.builtinclass, other.builtinclass) or
+ cmp(self.main_process_scriptable_only, other.main_process_scriptable_only)
+ )
+
+ def read_descriptor(self, typelib, map, data_pool):
+ offset = self._descriptor_offset
+ if offset == 0:
+ return
+ start = data_pool + offset - 1
+ parent, num_methods = Interface._descriptorstart.unpack_from(map, start)
+ if parent > 0 and parent <= len(typelib.interfaces):
+ self.parent = typelib.interfaces[parent - 1]
+ # Read methods
+ offset += Interface._descriptorstart.size
+ for i in range(num_methods):
+ m, offset = Method.read(typelib, map, data_pool, offset)
+ self.methods.append(m)
+ # Read constants
+ start = data_pool + offset - 1
+ (num_constants, ) = struct.unpack_from(">H", map, start)
+ offset = offset + struct.calcsize(">H")
+ for i in range(num_constants):
+ c, offset = Constant.read(typelib, map, data_pool, offset)
+ self.constants.append(c)
+ # Read flags
+ start = data_pool + offset - 1
+ (flags, ) = struct.unpack_from(">B", map, start)
+ offset = offset + struct.calcsize(">B")
+ # only the first two bits are flags
+ flags &= 0xf0
+ if flags & 0x80:
+ self.scriptable = True
+ if flags & 0x40:
+ self.function = True
+ if flags & 0x20:
+ self.builtinclass = True
+ if flags & 0x10:
+ self.main_process_scriptable_only = True
+ self.resolved = True
+
+ def write_directory_entry(self, file):
+ """
+ Write an InterfaceDirectoryEntry for this interface
+ to |file|, which is assumed to be seeked to the correct offset.
+
+ """
+ file.write(Interface._direntry.pack(Typelib.string_to_iid(self.iid),
+ self._name_offset,
+ self._namespace_offset,
+ self._descriptor_offset))
+
+ def write(self, typelib, file, data_pool_offset):
+ """
+ Write an InterfaceDescriptor to |file|, which is assumed
+ to be seeked to the proper position. If this interface
+ is not resolved, do not write any data.
+
+ """
+ if not self.resolved:
+ self._descriptor_offset = 0
+ return
+ self._descriptor_offset = file.tell() - data_pool_offset + 1
+ parent_idx = 0
+ if self.parent:
+ parent_idx = typelib.interfaces.index(self.parent) + 1
+ file.write(Interface._descriptorstart.pack(parent_idx, len(self.methods)))
+ for m in self.methods:
+ m.write(typelib, file)
+ file.write(struct.pack(">H", len(self.constants)))
+ for c in self.constants:
+ c.write(typelib, file)
+ flags = 0
+ if self.scriptable:
+ flags |= 0x80
+ if self.function:
+ flags |= 0x40
+ if self.builtinclass:
+ flags |= 0x20
+ if self.main_process_scriptable_only:
+ flags |= 0x10
+ file.write(struct.pack(">B", flags))
+
+ def write_names(self, file, data_pool_offset):
+ """
+ Write this interface's name and namespace to |file|,
+ as well as the names of all of its methods and constants.
+ Assumes that |file| is currently seeked to an unused portion
+ of the data pool.
+
+ """
+ if self.name:
+ self._name_offset = file.tell() - data_pool_offset + 1
+ file.write(self.name + "\x00")
+ else:
+ self._name_offset = 0
+ if self.namespace:
+ self._namespace_offset = file.tell() - data_pool_offset + 1
+ file.write(self.namespace + "\x00")
+ else:
+ self._namespace_offset = 0
+ for m in self.methods:
+ m.write_name(file, data_pool_offset)
+ for c in self.constants:
+ c.write_name(file, data_pool_offset)
+
+
+class Typelib(object):
+ """
+ A typelib represents one entire typelib file and all the interfaces
+ referenced within, whether defined entirely within the typelib or
+ merely referenced by name or IID.
+
+ Typelib objects may be instantiated directly and populated with data,
+ or the static Typelib.read method may be called to read one from a file.
+
+ """
+ _header = struct.Struct(">16sBBHIII")
+
+ def __init__(self, version=TYPELIB_VERSION, interfaces=[], annotations=[]):
+ """
+ Instantiate a new Typelib.
+
+ """
+ self.version = version
+ self.interfaces = list(interfaces)
+ self.annotations = list(annotations)
+ self.filename = None
+
+ @staticmethod
+ def iid_to_string(iid):
+ """
+ Convert a 16-byte IID into a UUID string.
+
+ """
+ def hexify(s):
+ return ''.join(["%02x" % ord(x) for x in s])
+
+ return "%s-%s-%s-%s-%s" % (hexify(iid[:4]), hexify(iid[4:6]),
+ hexify(iid[6:8]), hexify(iid[8:10]),
+ hexify(iid[10:]))
+
+ @staticmethod
+ def string_to_iid(iid_str):
+ """
+ Convert a UUID string into a 16-byte IID.
+
+ """
+ s = iid_str.replace('-', '')
+ return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 2)])
+
+ @staticmethod
+ def read_string(map, data_pool, offset):
+ if offset == 0:
+ return ""
+ sz = map.find('\x00', data_pool + offset - 1)
+ if sz == -1:
+ return ""
+ return map[data_pool + offset - 1:sz]
+
+ @staticmethod
+ def read(input_file):
+ """
+ Read a typelib from |input_file| and return
+ the constructed Typelib object. |input_file| can be a filename
+ or a file-like object.
+
+ """
+ filename = ""
+ data = None
+ expected_size = None
+ if isinstance(input_file, basestring):
+ filename = input_file
+ with open(input_file, "rb") as f:
+ st = os.fstat(f.fileno())
+ data = f.read(st.st_size)
+ expected_size = st.st_size
+ else:
+ data = input_file.read()
+
+ (magic,
+ major_ver,
+ minor_ver,
+ num_interfaces,
+ file_length,
+ interface_directory_offset,
+ data_pool_offset) = Typelib._header.unpack_from(data)
+ if magic != XPT_MAGIC:
+ raise FileFormatError("Bad magic: %s" % magic)
+ xpt = Typelib((major_ver, minor_ver))
+ xpt.filename = filename
+ if expected_size and file_length != expected_size:
+ raise FileFormatError("File is of wrong length, got %d bytes, expected %d" % (expected_size, file_length))
+ # XXX: by spec this is a zero-based file offset. however,
+ # the xpt_xdr code always subtracts 1 from data offsets
+ # (because that's what you do in the data pool) so it
+ # winds up accidentally treating this as 1-based.
+ # Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
+ interface_directory_offset -= 1
+ # make a half-hearted attempt to read Annotations,
+ # since XPIDL doesn't produce any anyway.
+ start = Typelib._header.size
+ (anno, ) = struct.unpack_from(">B", data, start)
+ tag = anno & 0x7F
+ if tag == 0: # EmptyAnnotation
+ xpt.annotations.append(None)
+ # We don't bother handling PrivateAnnotations or anything
+
+ for i in range(num_interfaces):
+ # iid, name, namespace, interface_descriptor
+ start = interface_directory_offset + i * Interface._direntry.size
+ ide = Interface._direntry.unpack_from(data, start)
+ iid = Typelib.iid_to_string(ide[0])
+ name = Typelib.read_string(data, data_pool_offset, ide[1])
+ namespace = Typelib.read_string(data, data_pool_offset, ide[2])
+ iface = Interface(name, iid, namespace)
+ iface._descriptor_offset = ide[3]
+ iface.xpt_filename = xpt.filename
+ xpt.interfaces.append(iface)
+ for iface in xpt.interfaces:
+ iface.read_descriptor(xpt, data, data_pool_offset)
+ return xpt
+
+ def __repr__(self):
+ return "<Typelib with %d interfaces>" % len(self.interfaces)
+
+ def _sanityCheck(self):
+ """
+ Check certain assumptions about data contained in this typelib.
+ Sort the interfaces array by IID, check that all interfaces
+ referenced by methods exist in the array.
+
+ """
+ self.interfaces.sort()
+ for i in self.interfaces:
+ if i.parent and i.parent not in self.interfaces:
+ raise DataError("Interface %s has parent %s not present in typelib!" % (i.name, i.parent.name))
+ for m in i.methods:
+ for n, p in enumerate(m.params):
+ if isinstance(p, InterfaceType) and \
+ p.iface not in self.interfaces:
+ raise DataError("Interface method %s::%s, parameter %d references interface %s not present in typelib!" % (i.name, m.name, n, p.iface.name))
+ if isinstance(m.result, InterfaceType) and m.result.iface not in self.interfaces:
+ raise DataError("Interface method %s::%s, result references interface %s not present in typelib!" % (i.name, m.name, m.result.iface.name))
+
+ def writefd(self, fd):
+ # write out space for a header + one empty annotation,
+ # padded to 4-byte alignment.
+ headersize = (Typelib._header.size + 1)
+ if headersize % 4:
+ headersize += 4 - headersize % 4
+ fd.write("\x00" * headersize)
+ # save this offset, it's the interface directory offset.
+ interface_directory_offset = fd.tell()
+ # write out space for an interface directory
+ fd.write("\x00" * Interface._direntry.size * len(self.interfaces))
+ # save this offset, it's the data pool offset.
+ data_pool_offset = fd.tell()
+ # write out all the interface descriptors to the data pool
+ for i in self.interfaces:
+ i.write_names(fd, data_pool_offset)
+ i.write(self, fd, data_pool_offset)
+ # now, seek back and write the header
+ file_len = fd.tell()
+ fd.seek(0)
+ fd.write(Typelib._header.pack(XPT_MAGIC,
+ TYPELIB_VERSION[0],
+ TYPELIB_VERSION[1],
+ len(self.interfaces),
+ file_len,
+ interface_directory_offset,
+ data_pool_offset))
+ # write an empty annotation
+ fd.write(struct.pack(">B", 0x80))
+ # now write the interface directory
+ # XXX: bug-compatible with existing xpt lib, put it one byte
+ # ahead of where it's supposed to be.
+ fd.seek(interface_directory_offset - 1)
+ for i in self.interfaces:
+ i.write_directory_entry(fd)
+
+ def write(self, output_file):
+ """
+ Write the contents of this typelib to |output_file|,
+ which can be either a filename or a file-like object.
+
+ """
+ self._sanityCheck()
+ if isinstance(output_file, basestring):
+ with open(output_file, "wb") as f:
+ self.writefd(f)
+ else:
+ self.writefd(output_file)
+
+ def dump(self, out):
+ """
+ Print a human-readable listing of the contents of this typelib
+ to |out|, in the format of xpt_dump.
+
+ """
+ out.write("""Header:
+ Major version: %d
+ Minor version: %d
+ Number of interfaces: %d
+ Annotations:\n""" % (self.version[0], self.version[1], len(self.interfaces)))
+ for i, a in enumerate(self.annotations):
+ if a is None:
+ out.write(" Annotation #%d is empty.\n" % i)
+ out.write("\nInterface Directory:\n")
+ for i in self.interfaces:
+ out.write(" - %s::%s (%s):\n" % (i.namespace, i.name, i.iid))
+ if not i.resolved:
+ out.write(" [Unresolved]\n")
+ else:
+ if i.parent:
+ out.write(" Parent: %s::%s\n" % (i.parent.namespace,
+ i.parent.name))
+ out.write(""" Flags:
+ Scriptable: %s
+ BuiltinClass: %s
+ Function: %s\n""" % (i.scriptable and "TRUE" or "FALSE",
+ i.builtinclass and "TRUE" or "FALSE",
+ i.function and "TRUE" or "FALSE"))
+ out.write(" Methods:\n")
+ if len(i.methods) == 0:
+ out.write(" No Methods\n")
+ else:
+ for m in i.methods:
+ out.write(" %s%s%s%s%s%s%s %s %s(%s);\n" % (
+ m.getter and "G" or " ",
+ m.setter and "S" or " ",
+ m.hidden and "H" or " ",
+ m.notxpcom and "N" or " ",
+ m.constructor and "C" or " ",
+ m.optargc and "O" or " ",
+ m.implicit_jscontext and "J" or " ",
+ str(m.result.type),
+ m.name,
+ m.params and ", ".join(str(p) for p in m.params) or ""
+ ))
+ out.write(" Constants:\n")
+ if len(i.constants) == 0:
+ out.write(" No Constants\n")
+ else:
+ for c in i.constants:
+ out.write(" %s %s = %d;\n" % (c.type, c.name, c.value))
+
+
+def xpt_dump(file):
+ """
+ Dump the contents of |file| to stdout in the format of xpt_dump.
+
+ """
+ t = Typelib.read(file)
+ t.dump(sys.stdout)
+
+
+def xpt_link(inputs):
+ """
+ Link all of the xpt files in |inputs| together and return the result
+ as a Typelib object. All entries in inputs may be filenames or
+ file-like objects. Non-scriptable interfaces that are unreferenced
+ from scriptable interfaces will be removed during linking.
+
+ """
+ def read_input(i):
+ if isinstance(i, Typelib):
+ return i
+ return Typelib.read(i)
+
+ if not inputs:
+ print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
+ return None
+ # This is the aggregate list of interfaces.
+ interfaces = []
+ # This will be a dict of replaced interface -> replaced with
+ # containing interfaces that were replaced with interfaces from
+ # another typelib, and the interface that replaced them.
+ merged_interfaces = {}
+ for f in inputs:
+ t = read_input(f)
+ interfaces.extend(t.interfaces)
+ # Sort interfaces by name so we can merge adjacent duplicates
+ interfaces.sort(key=operator.attrgetter('name'))
+
+ Result = enum('Equal', # Interfaces the same, doesn't matter
+ 'NotEqual', # Interfaces differ, keep both
+ 'KeepFirst', # Replace second interface with first
+ 'KeepSecond') # Replace first interface with second
+
+ def compare(i, j):
+ """
+ Compare two interfaces, determine if they're equal or
+ completely different, or should be merged (and indicate which
+ one to keep in that case).
+
+ """
+ if i == j:
+ # Arbitrary, just pick one
+ return Result.Equal
+ if i.name != j.name:
+ if i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
+ # Same IID but different names: raise an exception.
+ raise DataError(
+ "Typelibs contain definitions of interface %s"
+ " with different names (%s (%s) vs %s (%s))!" %
+ (i.iid, i.name, i.xpt_filename, j.name, j.xpt_filename))
+ # Otherwise just different interfaces.
+ return Result.NotEqual
+ # Interfaces have the same name, so either they need to be merged
+ # or there's a data error. Sort out which one to keep
+ if i.resolved != j.resolved:
+ # prefer resolved interfaces over unresolved
+ if j.resolved:
+ assert i.iid == j.iid or i.iid == Interface.UNRESOLVED_IID
+ # keep j
+ return Result.KeepSecond
+ else:
+ assert i.iid == j.iid or j.iid == Interface.UNRESOLVED_IID
+ # replace j with i
+ return Result.KeepFirst
+ elif i.iid != j.iid:
+ # Prefer unresolved interfaces with valid IIDs
+ if j.iid == Interface.UNRESOLVED_IID:
+ # replace j with i
+ assert not j.resolved
+ return Result.KeepFirst
+ elif i.iid == Interface.UNRESOLVED_IID:
+ # keep j
+ assert not i.resolved
+ return Result.KeepSecond
+ else:
+ # Same name but different IIDs: raise an exception.
+ raise DataError(
+ "Typelibs contain definitions of interface %s"
+ " with different IIDs (%s (%s) vs %s (%s))!" %
+ (i.name, i.iid, i.xpt_filename,
+ j.iid, j.xpt_filename))
+ raise DataError("No idea what happened here: %s:%s (%s), %s:%s (%s)" %
+ (i.name, i.iid, i.xpt_filename, j.name, j.iid, j.xpt_filename))
+
+ # Compare interfaces pairwise to find duplicates that should be merged.
+ i = 1
+ while i < len(interfaces):
+ res = compare(interfaces[i-1], interfaces[i])
+ if res == Result.NotEqual:
+ i += 1
+ elif res == Result.Equal:
+ # Need to drop one but it doesn't matter which
+ del interfaces[i]
+ elif res == Result.KeepFirst:
+ merged_interfaces[interfaces[i]] = interfaces[i-1]
+ del interfaces[i]
+ elif res == Result.KeepSecond:
+ merged_interfaces[interfaces[i-1]] = interfaces[i]
+ del interfaces[i-1]
+
+ # Now fixup any merged interfaces
+ def checkType(t):
+ if isinstance(t, InterfaceType) and t.iface in merged_interfaces:
+ t.iface = merged_interfaces[t.iface]
+ elif isinstance(t, ArrayType) and \
+ isinstance(t.element_type, InterfaceType) and \
+ t.element_type.iface in merged_interfaces:
+ t.element_type.iface = merged_interfaces[t.element_type.iface]
+
+ for i in interfaces:
+ # Replace parent references
+ if i.parent in merged_interfaces:
+ i.parent = merged_interfaces[i.parent]
+ for m in i.methods:
+ # Replace InterfaceType params and return values
+ checkType(m.result.type)
+ for p in m.params:
+ checkType(p.type)
+
+ # There's no need to have non-scriptable interfaces in a typelib, and
+ # removing them saves memory when typelibs are loaded. But we can't
+ # just blindly remove all non-scriptable interfaces, since we still
+ # need to know about non-scriptable interfaces referenced from
+ # scriptable interfaces.
+ worklist = set(i for i in interfaces if i.scriptable)
+ required_interfaces = set()
+
+ def maybe_add_to_worklist(iface):
+ if iface in required_interfaces or iface in worklist:
+ return
+ worklist.add(iface)
+
+ while worklist:
+ i = worklist.pop()
+ required_interfaces.add(i)
+ if i.parent:
+ maybe_add_to_worklist(i.parent)
+ for m in i.methods:
+ if isinstance(m.result.type, InterfaceType):
+ maybe_add_to_worklist(m.result.type.iface)
+ for p in m.params:
+ if isinstance(p.type, InterfaceType):
+ maybe_add_to_worklist(p.type.iface)
+ elif isinstance(p.type, ArrayType) and isinstance(p.type.element_type, InterfaceType):
+ maybe_add_to_worklist(p.type.element_type.iface)
+
+ interfaces = list(required_interfaces)
+
+ # Re-sort interfaces (by IID)
+ interfaces.sort()
+ return Typelib(interfaces=interfaces)
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print >>sys.stderr, "xpt <dump|link> <files>"
+ sys.exit(1)
+ if sys.argv[1] == 'dump':
+ xpt_dump(sys.argv[2])
+ elif sys.argv[1] == 'link':
+ xpt_link(sys.argv[3:]).write(sys.argv[2])
diff --git a/xpcom/typelib/xpt/xpt_arena.cpp b/xpcom/typelib/xpt/xpt_arena.cpp
new file mode 100644
index 000000000..21be3c00b
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_arena.cpp
@@ -0,0 +1,196 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Quick arena hack for xpt. */
+
+/* XXX This exists because we don't want to drag in NSPR. It *seemed*
+* to make more sense to write a quick and dirty arena than to clone
+* plarena (like js/src did). This is not optimal, but it works.
+*/
+
+#include "xpt_arena.h"
+#include "mozilla/MemoryReporting.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/****************************************************/
+
+/* Block header for each block in the arena */
+struct BLK_HDR
+{
+ BLK_HDR *next;
+};
+
+#define XPT_MIN_BLOCK_SIZE 32
+
+/* XXX this is lame. Should clone the code to do this bitwise */
+#define ALIGN_RND(s,a) ((a)==1?(s):((((s)+(a)-1)/(a))*(a)))
+
+struct XPTSubArena
+{
+ BLK_HDR *first;
+ uint8_t *next;
+ size_t space;
+ size_t block_size;
+};
+
+struct XPTArena
+{
+ // We have one sub-arena with 8-byte alignment for most allocations, and
+ // one with 1-byte alignment for C string allocations. The latter sub-arena
+ // avoids significant amounts of unnecessary padding between C strings.
+ XPTSubArena subarena8;
+ XPTSubArena subarena1;
+};
+
+XPT_PUBLIC_API(XPTArena *)
+XPT_NewArena(size_t block_size8, size_t block_size1)
+{
+ XPTArena *arena = static_cast<XPTArena*>(calloc(1, sizeof(XPTArena)));
+ if (arena) {
+ if (block_size8 < XPT_MIN_BLOCK_SIZE)
+ block_size8 = XPT_MIN_BLOCK_SIZE;
+ arena->subarena8.block_size = ALIGN_RND(block_size8, 8);
+
+ if (block_size1 < XPT_MIN_BLOCK_SIZE)
+ block_size1 = XPT_MIN_BLOCK_SIZE;
+ arena->subarena1.block_size = block_size1;
+ }
+ return arena;
+}
+
+static void
+DestroySubArena(XPTSubArena *subarena)
+{
+ BLK_HDR* cur = subarena->first;
+ while (cur) {
+ BLK_HDR* next = cur->next;
+ free(cur);
+ cur = next;
+ }
+}
+
+XPT_PUBLIC_API(void)
+XPT_DestroyArena(XPTArena *arena)
+{
+ DestroySubArena(&arena->subarena8);
+ DestroySubArena(&arena->subarena1);
+ free(arena);
+}
+
+/*
+* Our alignment rule is that we always round up the size of each allocation
+* so that the 'arena->next' pointer one will point to properly aligned space.
+*/
+
+XPT_PUBLIC_API(void *)
+XPT_ArenaCalloc(XPTArena *arena, size_t size, size_t alignment)
+{
+ if (!size)
+ return NULL;
+
+ if (!arena) {
+ XPT_ASSERT(0);
+ return NULL;
+ }
+
+ XPTSubArena *subarena;
+ if (alignment == 8) {
+ subarena = &arena->subarena8;
+ } else if (alignment == 1) {
+ subarena = &arena->subarena1;
+ } else {
+ XPT_ASSERT(0);
+ return NULL;
+ }
+
+ size_t bytes = ALIGN_RND(size, alignment);
+
+ if (bytes > subarena->space) {
+ BLK_HDR* new_block;
+ size_t block_header_size = ALIGN_RND(sizeof(BLK_HDR), alignment);
+ size_t new_space = subarena->block_size;
+
+ while (bytes > new_space - block_header_size)
+ new_space += subarena->block_size;
+
+ new_block =
+ static_cast<BLK_HDR*>(calloc(new_space / alignment, alignment));
+ if (!new_block) {
+ subarena->next = NULL;
+ subarena->space = 0;
+ return NULL;
+ }
+
+ /* link block into the list of blocks for use when we destroy */
+ new_block->next = subarena->first;
+ subarena->first = new_block;
+
+ /* set info for current block */
+ subarena->next =
+ reinterpret_cast<uint8_t*>(new_block) + block_header_size;
+ subarena->space = new_space - block_header_size;
+
+#ifdef DEBUG
+ /* mark block for corruption check */
+ memset(subarena->next, 0xcd, subarena->space);
+#endif
+ }
+
+#ifdef DEBUG
+ {
+ /* do corruption check */
+ size_t i;
+ for (i = 0; i < bytes; ++i) {
+ XPT_ASSERT(subarena->next[i] == 0xcd);
+ }
+ /* we guarantee that the block will be filled with zeros */
+ memset(subarena->next, 0, bytes);
+ }
+#endif
+
+ uint8_t* p = subarena->next;
+ subarena->next += bytes;
+ subarena->space -= bytes;
+
+ return p;
+}
+
+/***************************************************************************/
+
+#ifdef DEBUG
+XPT_PUBLIC_API(void)
+XPT_AssertFailed(const char *s, const char *file, uint32_t lineno)
+{
+ fprintf(stderr, "Assertion failed: %s, file %s, line %d\n",
+ s, file, lineno);
+ abort();
+}
+#endif
+
+static size_t
+SizeOfSubArenaExcludingThis(XPTSubArena *subarena, MozMallocSizeOf mallocSizeOf)
+{
+ size_t n = 0;
+
+ BLK_HDR* cur = subarena->first;
+ while (cur) {
+ BLK_HDR* next = cur->next;
+ n += mallocSizeOf(cur);
+ cur = next;
+ }
+
+ return n;
+}
+
+XPT_PUBLIC_API(size_t)
+XPT_SizeOfArenaIncludingThis(XPTArena *arena, MozMallocSizeOf mallocSizeOf)
+{
+ size_t n = mallocSizeOf(arena);
+ n += SizeOfSubArenaExcludingThis(&arena->subarena8, mallocSizeOf);
+ n += SizeOfSubArenaExcludingThis(&arena->subarena1, mallocSizeOf);
+ return n;
+}
diff --git a/xpcom/typelib/xpt/xpt_arena.h b/xpcom/typelib/xpt/xpt_arena.h
new file mode 100644
index 000000000..6ac146ffe
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_arena.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Simple arena allocator for xpt (which avoids using NSPR).
+ */
+
+#ifndef __xpt_arena_h__
+#define __xpt_arena_h__
+
+#include <stdlib.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+#include <stdint.h>
+
+
+/*
+ * The XPT library is statically linked: no functions are exported from
+ * shared libraries.
+ */
+#define XPT_PUBLIC_API(t) t
+#define XPT_PUBLIC_DATA(t) t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple Arena support. Use with caution!
+ */
+
+typedef struct XPTArena XPTArena;
+
+XPT_PUBLIC_API(XPTArena *)
+XPT_NewArena(size_t block_size8, size_t block_size1);
+
+XPT_PUBLIC_API(void)
+XPT_DestroyArena(XPTArena *arena);
+
+XPT_PUBLIC_API(void *)
+XPT_ArenaCalloc(XPTArena *arena, size_t size, size_t alignment);
+
+XPT_PUBLIC_API(size_t)
+XPT_SizeOfArenaIncludingThis(XPTArena *arena, MozMallocSizeOf mallocSizeOf);
+
+/* --------------------------------------------------------- */
+
+#define XPT_CALLOC8(_arena, _bytes) XPT_ArenaCalloc((_arena), (_bytes), 8)
+#define XPT_CALLOC1(_arena, _bytes) XPT_ArenaCalloc((_arena), (_bytes), 1)
+#define XPT_NEWZAP(_arena, _struct) ((_struct *) XPT_CALLOC8((_arena), sizeof(_struct)))
+
+/* --------------------------------------------------------- */
+
+#ifdef DEBUG
+XPT_PUBLIC_API(void)
+XPT_AssertFailed(const char *s, const char *file, uint32_t lineno)
+ MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS;
+#define XPT_ASSERT(_expr) \
+ ((_expr)?((void)0):XPT_AssertFailed(# _expr, __FILE__, __LINE__))
+#else
+#define XPT_ASSERT(_expr) ((void)0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __xpt_arena_h__ */
diff --git a/xpcom/typelib/xpt/xpt_struct.cpp b/xpcom/typelib/xpt/xpt_struct.cpp
new file mode 100644
index 000000000..c2e09abf0
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_struct.cpp
@@ -0,0 +1,432 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implementation of XDR routines for typelib structures. */
+
+#include "xpt_xdr.h"
+#include "xpt_struct.h"
+#include <string.h>
+#include <stdio.h>
+
+using mozilla::WrapNotNull;
+
+/***************************************************************************/
+/* Forward declarations. */
+
+static bool
+DoInterfaceDirectoryEntry(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTInterfaceDirectoryEntry *ide);
+
+static bool
+DoConstDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTConstDescriptor *cd, XPTInterfaceDescriptor *id);
+
+static bool
+DoMethodDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTMethodDescriptor *md, XPTInterfaceDescriptor *id);
+
+static bool
+SkipAnnotation(NotNull<XPTCursor*> cursor, bool *isLast);
+
+static bool
+DoInterfaceDescriptor(XPTArena *arena, NotNull<XPTCursor*> outer,
+ XPTInterfaceDescriptor **idp);
+
+static bool
+DoTypeDescriptorPrefix(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptorPrefix *tdp);
+
+static bool
+DoTypeDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptor *td, XPTInterfaceDescriptor *id);
+
+static bool
+DoParamDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTParamDescriptor *pd, XPTInterfaceDescriptor *id);
+
+/***************************************************************************/
+
+XPT_PUBLIC_API(bool)
+XPT_DoHeader(XPTArena *arena, NotNull<XPTCursor*> cursor, XPTHeader **headerp)
+{
+ unsigned int i;
+ uint32_t file_length = 0;
+ uint32_t ide_offset;
+
+ XPTHeader* header = XPT_NEWZAP(arena, XPTHeader);
+ if (!header)
+ return false;
+ *headerp = header;
+
+ uint8_t magic[16];
+ for (i = 0; i < sizeof(magic); i++) {
+ if (!XPT_Do8(cursor, &magic[i]))
+ return false;
+ }
+
+ if (strncmp((const char*)magic, XPT_MAGIC, 16) != 0) {
+ /* Require that the header contain the proper magic */
+ fprintf(stderr,
+ "libxpt: bad magic header in input file; "
+ "found '%s', expected '%s'\n",
+ magic, XPT_MAGIC_STRING);
+ return false;
+ }
+
+ if (!XPT_Do8(cursor, &header->major_version) ||
+ !XPT_Do8(cursor, &header->minor_version)) {
+ return false;
+ }
+
+ if (header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) {
+ /* This file is newer than we are and set to an incompatible version
+ * number. We must set the header state thusly and return.
+ */
+ header->num_interfaces = 0;
+ return true;
+ }
+
+ if (!XPT_Do16(cursor, &header->num_interfaces) ||
+ !XPT_Do32(cursor, &file_length) ||
+ !XPT_Do32(cursor, &ide_offset)) {
+ return false;
+ }
+
+ /*
+ * Make sure the file length reported in the header is the same size as
+ * as our buffer unless it is zero (not set)
+ */
+ if (file_length != 0 &&
+ cursor->state->pool_allocated < file_length) {
+ fputs("libxpt: File length in header does not match actual length. File may be corrupt\n",
+ stderr);
+ return false;
+ }
+
+ uint32_t data_pool;
+ if (!XPT_Do32(cursor, &data_pool))
+ return false;
+
+ XPT_SetDataOffset(cursor->state, data_pool);
+
+ if (header->num_interfaces) {
+ size_t n = header->num_interfaces * sizeof(XPTInterfaceDirectoryEntry);
+ header->interface_directory =
+ static_cast<XPTInterfaceDirectoryEntry*>(XPT_CALLOC8(arena, n));
+ if (!header->interface_directory)
+ return false;
+ }
+
+ /*
+ * Iterate through the annotations rather than recurring, to avoid blowing
+ * the stack on large xpt files. We don't actually store annotations, we
+ * just skip over them.
+ */
+ bool isLast;
+ do {
+ if (!SkipAnnotation(cursor, &isLast))
+ return false;
+ } while (!isLast);
+
+ /* shouldn't be necessary now, but maybe later */
+ XPT_SeekTo(cursor, ide_offset);
+
+ for (i = 0; i < header->num_interfaces; i++) {
+ if (!DoInterfaceDirectoryEntry(arena, cursor,
+ &header->interface_directory[i]))
+ return false;
+ }
+
+ return true;
+}
+
+/* InterfaceDirectoryEntry records go in the header */
+bool
+DoInterfaceDirectoryEntry(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTInterfaceDirectoryEntry *ide)
+{
+ char* dummy_name_space;
+
+ /* write the IID in our cursor space */
+ if (!XPT_DoIID(cursor, &(ide->iid)) ||
+
+ /* write the name string in the data pool, and the offset in our
+ cursor space */
+ !XPT_DoCString(arena, cursor, &(ide->name)) ||
+
+ /* don't write the name_space string in the data pool, because we don't
+ * need it. Do write the offset in our cursor space */
+ !XPT_DoCString(arena, cursor, &dummy_name_space, /* ignore = */ true) ||
+
+ /* do InterfaceDescriptors */
+ !DoInterfaceDescriptor(arena, cursor, &ide->interface_descriptor)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+InterfaceDescriptorAddTypes(XPTArena *arena, XPTInterfaceDescriptor *id,
+ uint16_t num)
+{
+ XPTTypeDescriptor *old = id->additional_types;
+ XPTTypeDescriptor *new_;
+ size_t old_size = id->num_additional_types * sizeof(XPTTypeDescriptor);
+ size_t new_size = (num * sizeof(XPTTypeDescriptor)) + old_size;
+
+ /* XXX should grow in chunks to minimize alloc overhead */
+ new_ = static_cast<XPTTypeDescriptor*>(XPT_CALLOC8(arena, new_size));
+ if (!new_)
+ return false;
+ if (old) {
+ memcpy(new_, old, old_size);
+ }
+ id->additional_types = new_;
+
+ if (num + uint16_t(id->num_additional_types) > 256)
+ return false;
+
+ id->num_additional_types += num;
+ return true;
+}
+
+bool
+DoInterfaceDescriptor(XPTArena *arena, NotNull<XPTCursor*> outer,
+ XPTInterfaceDescriptor **idp)
+{
+ XPTInterfaceDescriptor *id;
+ XPTCursor curs;
+ NotNull<XPTCursor*> cursor = WrapNotNull(&curs);
+ uint32_t i, id_sz = 0;
+
+ id = XPT_NEWZAP(arena, XPTInterfaceDescriptor);
+ if (!id)
+ return false;
+ *idp = id;
+
+ if (!XPT_MakeCursor(outer->state, XPT_DATA, id_sz, cursor))
+ return false;
+
+ if (!XPT_Do32(outer, &cursor->offset))
+ return false;
+ if (!cursor->offset) {
+ *idp = NULL;
+ return true;
+ }
+ if(!XPT_Do16(cursor, &id->parent_interface) ||
+ !XPT_Do16(cursor, &id->num_methods)) {
+ return false;
+ }
+
+ if (id->num_methods) {
+ size_t n = id->num_methods * sizeof(XPTMethodDescriptor);
+ id->method_descriptors =
+ static_cast<XPTMethodDescriptor*>(XPT_CALLOC8(arena, n));
+ if (!id->method_descriptors)
+ return false;
+ }
+
+ for (i = 0; i < id->num_methods; i++) {
+ if (!DoMethodDescriptor(arena, cursor, &id->method_descriptors[i], id))
+ return false;
+ }
+
+ if (!XPT_Do16(cursor, &id->num_constants)) {
+ return false;
+ }
+
+ if (id->num_constants) {
+ size_t n = id->num_constants * sizeof(XPTConstDescriptor);
+ id->const_descriptors =
+ static_cast<XPTConstDescriptor*>(XPT_CALLOC8(arena, n));
+ if (!id->const_descriptors)
+ return false;
+ }
+
+ for (i = 0; i < id->num_constants; i++) {
+ if (!DoConstDescriptor(arena, cursor, &id->const_descriptors[i], id)) {
+ return false;
+ }
+ }
+
+ if (!XPT_Do8(cursor, &id->flags)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DoConstDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTConstDescriptor *cd, XPTInterfaceDescriptor *id)
+{
+ bool ok = false;
+
+ if (!XPT_DoCString(arena, cursor, &cd->name) ||
+ !DoTypeDescriptor(arena, cursor, &cd->type, id)) {
+
+ return false;
+ }
+
+ switch(XPT_TDP_TAG(cd->type.prefix)) {
+ case TD_INT8:
+ ok = XPT_Do8(cursor, (uint8_t*) &cd->value.i8);
+ break;
+ case TD_INT16:
+ ok = XPT_Do16(cursor, (uint16_t*) &cd->value.i16);
+ break;
+ case TD_INT32:
+ ok = XPT_Do32(cursor, (uint32_t*) &cd->value.i32);
+ break;
+ case TD_INT64:
+ ok = XPT_Do64(cursor, &cd->value.i64);
+ break;
+ case TD_UINT8:
+ ok = XPT_Do8(cursor, &cd->value.ui8);
+ break;
+ case TD_UINT16:
+ ok = XPT_Do16(cursor, &cd->value.ui16);
+ break;
+ case TD_UINT32:
+ ok = XPT_Do32(cursor, &cd->value.ui32);
+ break;
+ case TD_UINT64:
+ ok = XPT_Do64(cursor, (int64_t *)&cd->value.ui64);
+ break;
+ case TD_CHAR:
+ ok = XPT_Do8(cursor, (uint8_t*) &cd->value.ch);
+ break;
+ case TD_WCHAR:
+ ok = XPT_Do16(cursor, &cd->value.wch);
+ break;
+ /* fall-through */
+ default:
+ fprintf(stderr, "illegal type!\n");
+ break;
+ }
+
+ return ok;
+
+}
+
+bool
+DoMethodDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTMethodDescriptor *md, XPTInterfaceDescriptor *id)
+{
+ int i;
+
+ if (!XPT_Do8(cursor, &md->flags) ||
+ !XPT_DoCString(arena, cursor, &md->name) ||
+ !XPT_Do8(cursor, &md->num_args))
+ return false;
+
+ if (md->num_args) {
+ size_t n = md->num_args * sizeof(XPTParamDescriptor);
+ md->params = static_cast<XPTParamDescriptor*>(XPT_CALLOC8(arena, n));
+ if (!md->params)
+ return false;
+ }
+
+ for(i = 0; i < md->num_args; i++) {
+ if (!DoParamDescriptor(arena, cursor, &md->params[i], id))
+ return false;
+ }
+
+ if (!DoParamDescriptor(arena, cursor, &md->result, id))
+ return false;
+
+ return true;
+}
+
+bool
+DoParamDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTParamDescriptor *pd, XPTInterfaceDescriptor *id)
+{
+ if (!XPT_Do8(cursor, &pd->flags) ||
+ !DoTypeDescriptor(arena, cursor, &pd->type, id))
+ return false;
+
+ return true;
+}
+
+bool
+DoTypeDescriptorPrefix(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptorPrefix *tdp)
+{
+ return XPT_Do8(cursor, &tdp->flags);
+}
+
+bool
+DoTypeDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptor *td, XPTInterfaceDescriptor *id)
+{
+ if (!DoTypeDescriptorPrefix(arena, cursor, &td->prefix)) {
+ return false;
+ }
+
+ switch (XPT_TDP_TAG(td->prefix)) {
+ case TD_INTERFACE_TYPE:
+ uint16_t iface;
+ if (!XPT_Do16(cursor, &iface))
+ return false;
+ td->u.iface.iface_hi8 = (iface >> 8) & 0xff;
+ td->u.iface.iface_lo8 = iface & 0xff;
+ break;
+ case TD_INTERFACE_IS_TYPE:
+ if (!XPT_Do8(cursor, &td->u.interface_is.argnum))
+ return false;
+ break;
+ case TD_ARRAY: {
+ // argnum2 appears in the on-disk format but it isn't used.
+ uint8_t argnum2 = 0;
+ if (!XPT_Do8(cursor, &td->u.array.argnum) ||
+ !XPT_Do8(cursor, &argnum2))
+ return false;
+
+ if (!InterfaceDescriptorAddTypes(arena, id, 1))
+ return false;
+ td->u.array.additional_type = id->num_additional_types - 1;
+
+ if (!DoTypeDescriptor(arena, cursor,
+ &id->additional_types[td->u.array.additional_type],
+ id))
+ return false;
+ break;
+ }
+ case TD_PSTRING_SIZE_IS:
+ case TD_PWSTRING_SIZE_IS: {
+ // argnum2 appears in the on-disk format but it isn't used.
+ uint8_t argnum2 = 0;
+ if (!XPT_Do8(cursor, &td->u.pstring_is.argnum) ||
+ !XPT_Do8(cursor, &argnum2))
+ return false;
+ break;
+ }
+ default:
+ /* nothing special */
+ break;
+ }
+ return true;
+}
+
+bool
+SkipAnnotation(NotNull<XPTCursor*> cursor, bool *isLast)
+{
+ uint8_t flags;
+ if (!XPT_Do8(cursor, &flags))
+ return false;
+
+ *isLast = XPT_ANN_IS_LAST(flags);
+
+ if (XPT_ANN_IS_PRIVATE(flags)) {
+ if (!XPT_SkipStringInline(cursor) ||
+ !XPT_SkipStringInline(cursor))
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/xpcom/typelib/xpt/xpt_struct.h b/xpcom/typelib/xpt/xpt_struct.h
new file mode 100644
index 000000000..b4da4a3fd
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_struct.h
@@ -0,0 +1,366 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Structures matching the in-memory representation of typelib structures.
+ * http://www.mozilla.org/scriptable/typelib_file.html
+ */
+
+#ifndef __xpt_struct_h__
+#define __xpt_struct_h__
+
+#include "xpt_arena.h"
+#include <stdint.h>
+
+extern "C" {
+
+/*
+ * Originally, I was going to have structures that exactly matched the on-disk
+ * representation, but that proved difficult: different compilers can pack
+ * their structs differently, and that makes overlaying them atop a
+ * read-from-disk byte buffer troublesome. So now I just have some structures
+ * that are used in memory, and we're going to write a nice XDR library to
+ * write them to disk and stuff. It is pure joy. -- shaver
+ */
+
+/* Structures for the typelib components */
+
+typedef struct XPTHeader XPTHeader;
+typedef struct XPTInterfaceDirectoryEntry XPTInterfaceDirectoryEntry;
+typedef struct XPTInterfaceDescriptor XPTInterfaceDescriptor;
+typedef struct XPTConstDescriptor XPTConstDescriptor;
+typedef struct XPTMethodDescriptor XPTMethodDescriptor;
+typedef struct XPTParamDescriptor XPTParamDescriptor;
+typedef struct XPTTypeDescriptor XPTTypeDescriptor;
+typedef struct XPTTypeDescriptorPrefix XPTTypeDescriptorPrefix;
+
+#ifndef nsID_h__
+/*
+ * We can't include nsID.h, because it's full of C++ goop and we're not doing
+ * C++ here, so we define our own minimal struct. We protect against multiple
+ * definitions of this struct, though, and use the same field naming.
+ */
+struct nsID {
+ uint32_t m0;
+ uint16_t m1;
+ uint16_t m2;
+ uint8_t m3[8];
+};
+
+typedef struct nsID nsID;
+#endif
+
+/*
+ * Every XPCOM typelib file begins with a header.
+ */
+struct XPTHeader {
+ // Some of these fields exists in the on-disk format but don't need to be
+ // stored in memory (other than very briefly, which can be done with local
+ // variables).
+
+ //uint8_t magic[16];
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint16_t num_interfaces;
+ //uint32_t file_length;
+ XPTInterfaceDirectoryEntry *interface_directory;
+ //uint32_t data_pool;
+};
+
+#define XPT_MAGIC "XPCOM\nTypeLib\r\n\032"
+/* For error messages. */
+#define XPT_MAGIC_STRING "XPCOM\\nTypeLib\\r\\n\\032"
+#define XPT_MAJOR_VERSION 0x01
+#define XPT_MINOR_VERSION 0x02
+
+/* Any file with a major version number of XPT_MAJOR_INCOMPATIBLE_VERSION
+ * or higher is to be considered incompatible by this version of xpt and
+ * we will refuse to read it. We will return a header with magic, major and
+ * minor versions set from the file. num_interfaces will be set to zero to
+ * confirm our inability to read the file; i.e. even if some client of this
+ * library gets out of sync with us regarding the agreed upon value for
+ * XPT_MAJOR_INCOMPATIBLE_VERSION, anytime num_interfaces is zero we *know*
+ * that this library refused to read the file due to version incompatibility.
+ */
+#define XPT_MAJOR_INCOMPATIBLE_VERSION 0x02
+
+/*
+ * A contiguous array of fixed-size InterfaceDirectoryEntry records begins at
+ * the byte offset identified by the interface_directory field in the file
+ * header. The array is used to quickly locate an interface description
+ * using its IID. No interface should appear more than once in the array.
+ */
+struct XPTInterfaceDirectoryEntry {
+ nsID iid;
+ char *name;
+
+ // This field exists in the on-disk format. But it isn't used so we don't
+ // allocate space for it in memory.
+ //char *name_space;
+
+ XPTInterfaceDescriptor *interface_descriptor;
+};
+
+/*
+ * An InterfaceDescriptor describes a single XPCOM interface, including all of
+ * its methods.
+ */
+struct XPTInterfaceDescriptor {
+ /* This field ordering minimizes the size of this struct.
+ * The fields are serialized on disk in a different order.
+ * See DoInterfaceDescriptor().
+ */
+ XPTMethodDescriptor *method_descriptors;
+ XPTConstDescriptor *const_descriptors;
+ XPTTypeDescriptor *additional_types;
+ uint16_t parent_interface;
+ uint16_t num_methods;
+ uint16_t num_constants;
+ uint8_t flags;
+
+ /* additional_types are used for arrays where we may need multiple
+ * XPTTypeDescriptors for a single XPTMethodDescriptor. Since we still
+ * want to have a simple array of XPTMethodDescriptor (each with a single
+ * embedded XPTTypeDescriptor), a XPTTypeDescriptor can have a reference
+ * to an 'additional_type'. That reference is an index in this
+ * "additional_types" array. So a given XPTMethodDescriptor might have
+ * a whole chain of these XPTTypeDescriptors to represent, say, a multi
+ * dimensional array.
+ *
+ * Note that in the typelib file these additional types are stored 'inline'
+ * in the MethodDescriptor. But, in the typelib MethodDescriptors can be
+ * of varying sizes, where in XPT's in memory mapping of the data we want
+ * them to be of fixed size. This additional_types scheme is here to allow
+ * for that.
+ */
+ uint8_t num_additional_types;
+};
+
+#define XPT_ID_SCRIPTABLE 0x80
+#define XPT_ID_FUNCTION 0x40
+#define XPT_ID_BUILTINCLASS 0x20
+#define XPT_ID_MAIN_PROCESS_SCRIPTABLE_ONLY 0x10
+#define XPT_ID_FLAGMASK 0xf0
+
+#define XPT_ID_IS_SCRIPTABLE(flags) (!!(flags & XPT_ID_SCRIPTABLE))
+#define XPT_ID_IS_FUNCTION(flags) (!!(flags & XPT_ID_FUNCTION))
+#define XPT_ID_IS_BUILTINCLASS(flags) (!!(flags & XPT_ID_BUILTINCLASS))
+#define XPT_ID_IS_MAIN_PROCESS_SCRIPTABLE_ONLY(flags) (!!(flags & XPT_ID_MAIN_PROCESS_SCRIPTABLE_ONLY))
+
+/*
+ * A TypeDescriptor is a variable-size record used to identify the type of a
+ * method argument or return value.
+ *
+ * There are three types of TypeDescriptors:
+ *
+ * SimpleTypeDescriptor
+ * InterfaceTypeDescriptor
+ * InterfaceIsTypeDescriptor
+ *
+ * The tag field in the prefix indicates which of the variant TypeDescriptor
+ * records is being used, and hence the way any remaining fields should be
+ * parsed. Values from 0 to 17 refer to SimpleTypeDescriptors. The value 18
+ * designates an InterfaceTypeDescriptor, while 19 represents an
+ * InterfaceIsTypeDescriptor.
+ */
+
+/* why bother with a struct? - other code relies on this being a struct */
+struct XPTTypeDescriptorPrefix {
+ uint8_t flags;
+};
+
+/* flag bits */
+
+#define XPT_TDP_FLAGMASK 0xe0
+#define XPT_TDP_TAGMASK (~XPT_TDP_FLAGMASK)
+#define XPT_TDP_TAG(tdp) ((tdp).flags & XPT_TDP_TAGMASK)
+
+/*
+ * The following enum maps mnemonic names to the different numeric values
+ * of XPTTypeDescriptor->tag.
+ */
+enum XPTTypeDescriptorTags {
+ TD_INT8 = 0,
+ TD_INT16 = 1,
+ TD_INT32 = 2,
+ TD_INT64 = 3,
+ TD_UINT8 = 4,
+ TD_UINT16 = 5,
+ TD_UINT32 = 6,
+ TD_UINT64 = 7,
+ TD_FLOAT = 8,
+ TD_DOUBLE = 9,
+ TD_BOOL = 10,
+ TD_CHAR = 11,
+ TD_WCHAR = 12,
+ TD_VOID = 13,
+ TD_PNSIID = 14,
+ TD_DOMSTRING = 15,
+ TD_PSTRING = 16,
+ TD_PWSTRING = 17,
+ TD_INTERFACE_TYPE = 18,
+ TD_INTERFACE_IS_TYPE = 19,
+ TD_ARRAY = 20,
+ TD_PSTRING_SIZE_IS = 21,
+ TD_PWSTRING_SIZE_IS = 22,
+ TD_UTF8STRING = 23,
+ TD_CSTRING = 24,
+ TD_ASTRING = 25,
+ TD_JSVAL = 26
+};
+
+struct XPTTypeDescriptor {
+ XPTTypeDescriptorPrefix prefix;
+
+ // The memory layout here doesn't exactly match (for the appropriate types)
+ // the on-disk format. This is to save memory.
+ union {
+ // Used for TD_INTERFACE_IS_TYPE.
+ struct {
+ uint8_t argnum;
+ } interface_is;
+
+ // Used for TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS.
+ struct {
+ uint8_t argnum;
+ //uint8_t argnum2; // Present on disk, omitted here.
+ } pstring_is;
+
+ // Used for TD_ARRAY.
+ struct {
+ uint8_t argnum;
+ //uint8_t argnum2; // Present on disk, omitted here.
+ uint8_t additional_type; // uint16_t on disk, uint8_t here;
+ // in practice it never exceeds 20.
+ } array;
+
+ // Used for TD_INTERFACE_TYPE.
+ struct {
+ // We store the 16-bit iface value as two 8-bit values in order to
+ // avoid 16-bit alignment requirements for XPTTypeDescriptor, which
+ // reduces its size and also the size of XPTParamDescriptor.
+ uint8_t iface_hi8;
+ uint8_t iface_lo8;
+ } iface;
+ } u;
+};
+
+/*
+ * A ConstDescriptor is a variable-size record that records the name and
+ * value of a scoped interface constant.
+ *
+ * The types of the method parameter are restricted to the following subset
+ * of TypeDescriptors:
+ *
+ * int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ * int64_t, uint64_t, wchar_t, char
+ *
+ * The type (and thus the size) of the value record is determined by the
+ * contents of the associated TypeDescriptor record. For instance, if type
+ * corresponds to int16_t, then value is a two-byte record consisting of a
+ * 16-bit signed integer.
+ */
+union XPTConstValue {
+ int8_t i8;
+ uint8_t ui8;
+ int16_t i16;
+ uint16_t ui16;
+ int32_t i32;
+ uint32_t ui32;
+ int64_t i64;
+ uint64_t ui64;
+ char ch;
+ uint16_t wch;
+}; /* varies according to type */
+
+struct XPTConstDescriptor {
+ char *name;
+ XPTTypeDescriptor type;
+ union XPTConstValue value;
+};
+
+/*
+ * A ParamDescriptor is a variable-size record used to describe either a
+ * single argument to a method or a method's result.
+ */
+struct XPTParamDescriptor {
+ uint8_t flags;
+ XPTTypeDescriptor type;
+};
+
+/* flag bits */
+#define XPT_PD_IN 0x80
+#define XPT_PD_OUT 0x40
+#define XPT_PD_RETVAL 0x20
+#define XPT_PD_SHARED 0x10
+#define XPT_PD_DIPPER 0x08
+#define XPT_PD_OPTIONAL 0x04
+#define XPT_PD_FLAGMASK 0xfc
+
+#define XPT_PD_IS_IN(flags) (flags & XPT_PD_IN)
+#define XPT_PD_IS_OUT(flags) (flags & XPT_PD_OUT)
+#define XPT_PD_IS_RETVAL(flags) (flags & XPT_PD_RETVAL)
+#define XPT_PD_IS_SHARED(flags) (flags & XPT_PD_SHARED)
+#define XPT_PD_IS_DIPPER(flags) (flags & XPT_PD_DIPPER)
+#define XPT_PD_IS_OPTIONAL(flags) (flags & XPT_PD_OPTIONAL)
+
+/*
+ * A MethodDescriptor is a variable-size record used to describe a single
+ * interface method.
+ */
+struct XPTMethodDescriptor {
+ char *name;
+ XPTParamDescriptor *params;
+ XPTParamDescriptor result;
+ uint8_t flags;
+ uint8_t num_args;
+};
+
+/* flag bits */
+#define XPT_MD_GETTER 0x80
+#define XPT_MD_SETTER 0x40
+#define XPT_MD_NOTXPCOM 0x20
+#define XPT_MD_HIDDEN 0x08
+#define XPT_MD_OPT_ARGC 0x04
+#define XPT_MD_CONTEXT 0x02
+#define XPT_MD_FLAGMASK 0xfe
+
+#define XPT_MD_IS_GETTER(flags) (flags & XPT_MD_GETTER)
+#define XPT_MD_IS_SETTER(flags) (flags & XPT_MD_SETTER)
+#define XPT_MD_IS_NOTXPCOM(flags) (flags & XPT_MD_NOTXPCOM)
+#define XPT_MD_IS_HIDDEN(flags) (flags & XPT_MD_HIDDEN)
+#define XPT_MD_WANTS_OPT_ARGC(flags) (flags & XPT_MD_OPT_ARGC)
+#define XPT_MD_WANTS_CONTEXT(flags) (flags & XPT_MD_CONTEXT)
+
+/*
+ * Annotation records are variable-size records used to store secondary
+ * information about the typelib, e.g. such as the name of the tool that
+ * generated the typelib file, the date it was generated, etc. The
+ * information is stored with very loose format requirements so as to
+ * allow virtually any private data to be stored in the typelib.
+ *
+ * There are two types of Annotations:
+ *
+ * EmptyAnnotation
+ * PrivateAnnotation
+ *
+ * The tag field of the prefix discriminates among the variant record
+ * types for Annotation's. If the tag is 0, this record is an
+ * EmptyAnnotation. EmptyAnnotation's are ignored - they're only used to
+ * indicate an array of Annotation's that's completely empty. If the tag
+ * is 1, the record is a PrivateAnnotation.
+ *
+ * We don't actually store annotations; we just skip over them if they are
+ * present.
+ */
+
+#define XPT_ANN_LAST 0x80
+#define XPT_ANN_IS_LAST(flags) (flags & XPT_ANN_LAST)
+#define XPT_ANN_PRIVATE 0x40
+#define XPT_ANN_IS_PRIVATE(flags) (flags & XPT_ANN_PRIVATE)
+
+}
+
+#endif /* __xpt_struct_h__ */
diff --git a/xpcom/typelib/xpt/xpt_xdr.cpp b/xpcom/typelib/xpt/xpt_xdr.cpp
new file mode 100644
index 000000000..04b90422a
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_xdr.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implementation of XDR primitives. */
+
+#include "xpt_xdr.h"
+#include "nscore.h"
+#include <string.h> /* strchr */
+#include "mozilla/EndianUtils.h"
+
+#define CURS_POOL_OFFSET_RAW(cursor) \
+ ((cursor)->pool == XPT_HEADER \
+ ? (cursor)->offset \
+ : (XPT_ASSERT((cursor)->state->data_offset), \
+ (cursor)->offset + (cursor)->state->data_offset))
+
+#define CURS_POOL_OFFSET(cursor) \
+ (CURS_POOL_OFFSET_RAW(cursor) - 1)
+
+/* can be used as lvalue */
+#define CURS_POINT(cursor) \
+ ((cursor)->state->pool_data[CURS_POOL_OFFSET(cursor)])
+
+static bool
+CHECK_COUNT(NotNull<XPTCursor*> cursor, uint32_t space)
+{
+ // Fail if we're in the data area and about to exceed the allocation.
+ // XXX Also fail if we're in the data area and !state->data_offset
+ if (cursor->pool == XPT_DATA &&
+ (CURS_POOL_OFFSET(cursor) + space > (cursor)->state->pool_allocated)) {
+ XPT_ASSERT(0);
+ fprintf(stderr, "FATAL: no room for %d in cursor\n", space);
+ return false;
+ }
+
+ return true;
+}
+
+XPT_PUBLIC_API(void)
+XPT_InitXDRState(XPTState* state, char *data, uint32_t len)
+{
+ state->next_cursor[0] = state->next_cursor[1] = 1;
+ state->pool_data = data;
+ state->pool_allocated = len;
+}
+
+/* All offsets are 1-based */
+XPT_PUBLIC_API(void)
+XPT_SetDataOffset(XPTState *state, uint32_t data_offset)
+{
+ state->data_offset = data_offset;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_MakeCursor(XPTState *state, XPTPool pool, uint32_t len,
+ NotNull<XPTCursor*> cursor)
+{
+ cursor->state = state;
+ cursor->pool = pool;
+ cursor->bits = 0;
+ cursor->offset = state->next_cursor[pool];
+
+ if (!(CHECK_COUNT(cursor, len)))
+ return false;
+
+ /* this check should be in CHECK_CURSOR */
+ if (pool == XPT_DATA && !state->data_offset) {
+ fprintf(stderr, "no data offset for XPT_DATA cursor!\n");
+ return false;
+ }
+
+ state->next_cursor[pool] += len;
+
+ return true;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_SeekTo(NotNull<XPTCursor*> cursor, uint32_t offset)
+{
+ /* XXX do some real checking and update len and stuff */
+ cursor->offset = offset;
+ return true;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_SkipStringInline(NotNull<XPTCursor*> cursor)
+{
+ uint16_t length;
+ if (!XPT_Do16(cursor, &length))
+ return false;
+
+ uint8_t byte;
+ for (uint16_t i = 0; i < length; i++)
+ if (!XPT_Do8(cursor, &byte))
+ return false;
+
+ return true;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_DoCString(XPTArena *arena, NotNull<XPTCursor*> cursor, char **identp,
+ bool ignore)
+{
+ uint32_t offset = 0;
+ if (!XPT_Do32(cursor, &offset))
+ return false;
+
+ if (!offset) {
+ *identp = NULL;
+ return true;
+ }
+
+ XPTCursor my_cursor;
+ my_cursor.pool = XPT_DATA;
+ my_cursor.offset = offset;
+ my_cursor.state = cursor->state;
+ char* start = &CURS_POINT(&my_cursor);
+
+ char* end = strchr(start, 0); /* find the end of the string */
+ if (!end) {
+ fprintf(stderr, "didn't find end of string on decode!\n");
+ return false;
+ }
+ int len = end - start;
+ XPT_ASSERT(len > 0);
+
+ if (!ignore) {
+ char *ident = (char*)XPT_CALLOC1(arena, len + 1u);
+ if (!ident)
+ return false;
+
+ memcpy(ident, start, (size_t)len);
+ ident[len] = 0;
+ *identp = ident;
+ }
+
+ return true;
+}
+
+/*
+ * IIDs are written in struct order, in the usual big-endian way. From the
+ * typelib file spec:
+ *
+ * "For example, this IID:
+ * {00112233-4455-6677-8899-aabbccddeeff}
+ * is converted to the 128-bit value
+ * 0x00112233445566778899aabbccddeeff
+ * Note that the byte storage order corresponds to the layout of the nsIID
+ * C-struct on a big-endian architecture."
+ *
+ * (http://www.mozilla.org/scriptable/typelib_file.html#iid)
+ */
+XPT_PUBLIC_API(bool)
+XPT_DoIID(NotNull<XPTCursor*> cursor, nsID *iidp)
+{
+ int i;
+
+ if (!XPT_Do32(cursor, &iidp->m0) ||
+ !XPT_Do16(cursor, &iidp->m1) ||
+ !XPT_Do16(cursor, &iidp->m2))
+ return false;
+
+ for (i = 0; i < 8; i++)
+ if (!XPT_Do8(cursor, (uint8_t *)&iidp->m3[i]))
+ return false;
+
+ return true;
+}
+
+// MSVC apparently cannot handle functions as template parameters very well,
+// so we need to use a macro approach here.
+
+#define XPT_DOINT(T, func, valuep) \
+ do { \
+ const size_t sz = sizeof(T); \
+ \
+ if (!CHECK_COUNT(cursor, sz)) { \
+ return false; \
+ } \
+ \
+ *valuep = func(&CURS_POINT(cursor)); \
+ cursor->offset += sz; \
+ return true; \
+ } while(0)
+
+XPT_PUBLIC_API(bool)
+XPT_Do64(NotNull<XPTCursor*> cursor, int64_t *u64p)
+{
+ XPT_DOINT(int64_t, mozilla::BigEndian::readInt64, u64p);
+}
+
+/*
+ * When we're handling 32- or 16-bit quantities, we handle a byte at a time to
+ * avoid alignment issues. Someone could come and optimize this to detect
+ * well-aligned cases and do a single store, if they cared. I might care
+ * later.
+ */
+XPT_PUBLIC_API(bool)
+XPT_Do32(NotNull<XPTCursor*> cursor, uint32_t *u32p)
+{
+ XPT_DOINT(uint32_t, mozilla::BigEndian::readUint32, u32p);
+}
+
+XPT_PUBLIC_API(bool)
+XPT_Do16(NotNull<XPTCursor*> cursor, uint16_t *u16p)
+{
+ XPT_DOINT(uint16_t, mozilla::BigEndian::readUint16, u16p);
+}
+
+#undef XPT_DOINT
+
+XPT_PUBLIC_API(bool)
+XPT_Do8(NotNull<XPTCursor*> cursor, uint8_t *u8p)
+{
+ if (!CHECK_COUNT(cursor, 1))
+ return false;
+
+ *u8p = CURS_POINT(cursor);
+
+ cursor->offset++;
+
+ return true;
+}
+
+
diff --git a/xpcom/typelib/xpt/xpt_xdr.h b/xpcom/typelib/xpt/xpt_xdr.h
new file mode 100644
index 000000000..7bf23aa40
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_xdr.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Basic APIs for streaming typelib structures from disk.
+ */
+
+#ifndef __xpt_xdr_h__
+#define __xpt_xdr_h__
+
+#include "xpt_struct.h"
+#include "mozilla/NotNull.h"
+
+using mozilla::NotNull;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct XPTState XPTState;
+typedef struct XPTCursor XPTCursor;
+
+extern XPT_PUBLIC_API(bool)
+XPT_SkipStringInline(NotNull<XPTCursor*> cursor);
+
+extern XPT_PUBLIC_API(bool)
+XPT_DoCString(XPTArena *arena, NotNull<XPTCursor*> cursor, char **strp,
+ bool ignore = false);
+
+extern XPT_PUBLIC_API(bool)
+XPT_DoIID(NotNull<XPTCursor*> cursor, nsID *iidp);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do64(NotNull<XPTCursor*> cursor, int64_t *u64p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do32(NotNull<XPTCursor*> cursor, uint32_t *u32p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do16(NotNull<XPTCursor*> cursor, uint16_t *u16p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do8(NotNull<XPTCursor*> cursor, uint8_t *u8p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_DoHeader(XPTArena *arena, NotNull<XPTCursor*> cursor, XPTHeader **headerp);
+
+typedef enum {
+ XPT_HEADER = 0,
+ XPT_DATA = 1
+} XPTPool;
+
+struct XPTState {
+ uint32_t data_offset;
+ uint32_t next_cursor[2];
+ char *pool_data;
+ uint32_t pool_allocated;
+};
+
+struct XPTCursor {
+ XPTState *state;
+ XPTPool pool;
+ uint32_t offset;
+ uint8_t bits;
+};
+
+extern XPT_PUBLIC_API(void)
+XPT_InitXDRState(XPTState* state, char* data, uint32_t len);
+
+extern XPT_PUBLIC_API(bool)
+XPT_MakeCursor(XPTState *state, XPTPool pool, uint32_t len,
+ NotNull<XPTCursor*> cursor);
+
+extern XPT_PUBLIC_API(bool)
+XPT_SeekTo(NotNull<XPTCursor*> cursor, uint32_t offset);
+
+extern XPT_PUBLIC_API(void)
+XPT_SetDataOffset(XPTState *state, uint32_t data_offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __xpt_xdr_h__ */
diff --git a/xpcom/windbgdlg/Makefile.in b/xpcom/windbgdlg/Makefile.in
new file mode 100644
index 000000000..254509ee7
--- /dev/null
+++ b/xpcom/windbgdlg/Makefile.in
@@ -0,0 +1,6 @@
+#
+# 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/.
+
+MOZ_WINCONSOLE = 0
diff --git a/xpcom/windbgdlg/moz.build b/xpcom/windbgdlg/moz.build
new file mode 100644
index 000000000..f0343d31a
--- /dev/null
+++ b/xpcom/windbgdlg/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SimplePrograms([
+ 'windbgdlg'
+])
diff --git a/xpcom/windbgdlg/windbgdlg.cpp b/xpcom/windbgdlg/windbgdlg.cpp
new file mode 100644
index 000000000..a51fe134f
--- /dev/null
+++ b/xpcom/windbgdlg/windbgdlg.cpp
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Windows only app to show a modal debug dialog - launched by nsDebug.cpp */
+#include <windows.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+#include <strsafe.h>
+#endif
+#ifdef __MINGW32__
+/* MingW currently does not implement a wide version of the
+ startup routines. Workaround is to implement something like
+ it ourselves. See bug 472063 */
+#include <stdio.h>
+#include <shellapi.h>
+int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int);
+
+#undef __argc
+#undef __wargv
+
+static int __argc;
+static wchar_t** __wargv;
+
+int WINAPI
+WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpszCommandLine, int nCmdShow)
+{
+ LPWSTR commandLine = GetCommandLineW();
+
+ /* parse for __argc and __wargv for compatibility, since mingw
+ * doesn't claim to support it :(
+ */
+ __wargv = CommandLineToArgvW(commandLine, &__argc);
+ if (!__wargv)
+ return 127;
+
+ /* need to strip off any leading whitespace plus the first argument
+ * (the executable itself) to match what should be passed to wWinMain
+ */
+ while ((*commandLine <= L' ') && *commandLine) {
+ ++commandLine;
+ }
+ if (*commandLine == L'"') {
+ ++commandLine;
+ while ((*commandLine != L'"') && *commandLine) {
+ ++commandLine;
+ }
+ if (*commandLine) {
+ ++commandLine;
+ }
+ } else {
+ while (*commandLine > L' ') {
+ ++commandLine;
+ }
+ }
+ while ((*commandLine <= L' ') && *commandLine) {
+ ++commandLine;
+ }
+
+ int result = wWinMain(hInstance, hPrevInstance, commandLine, nCmdShow);
+ LocalFree(__wargv);
+ return result;
+}
+#endif /* __MINGW32__ */
+
+
+int WINAPI
+wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPWSTR lpszCmdLine, int nCmdShow)
+{
+ /* support for auto answering based on words in the assertion.
+ * the assertion message is sent as a series of arguements (words) to the commandline.
+ * set a "word" to 0xffffffff to let the word not affect this code.
+ * set a "word" to 0xfffffffe to show the dialog.
+ * set a "word" to 0x5 to ignore (program should continue).
+ * set a "word" to 0x4 to retry (should fall into debugger).
+ * set a "word" to 0x3 to abort (die).
+ */
+ DWORD regType;
+ DWORD regValue = -1;
+ DWORD regLength = sizeof regValue;
+ HKEY hkeyCU, hkeyLM;
+ RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\mozilla.org\\windbgdlg", 0, KEY_READ, &hkeyCU);
+ RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\mozilla.org\\windbgdlg", 0, KEY_READ, &hkeyLM);
+ int argc =0;
+ for (int i = __argc - 1; regValue == (DWORD)-1 && i; --i) {
+ bool ok = false;
+ if (hkeyCU)
+ ok = RegQueryValueExW(hkeyCU, __wargv[i], 0, &regType, (LPBYTE)&regValue, &regLength) == ERROR_SUCCESS;
+ if (!ok && hkeyLM)
+ ok = RegQueryValueExW(hkeyLM, __wargv[i], 0, &regType, (LPBYTE)&regValue, &regLength) == ERROR_SUCCESS;
+ if (!ok)
+ regValue = -1;
+ }
+ if (hkeyCU)
+ RegCloseKey(hkeyCU);
+ if (hkeyLM)
+ RegCloseKey(hkeyLM);
+ if (regValue != (DWORD)-1 && regValue != (DWORD)-2)
+ return regValue;
+ static const int size = 4096;
+ static WCHAR msg[size];
+
+#ifdef _MSC_VER
+ StringCchPrintfW(msg,
+#else
+ snwprintf(msg,
+#endif
+ size,
+ L"%s\n\nClick Abort to exit the Application.\n"
+ L"Click Retry to Debug the Application.\n"
+ L"Click Ignore to continue running the Application.",
+ lpszCmdLine);
+ msg[size - 1] = L'\0';
+ return MessageBoxW(nullptr, msg, L"NSGlue_Assertion",
+ MB_ICONSTOP | MB_SYSTEMMODAL |
+ MB_ABORTRETRYIGNORE | MB_DEFBUTTON3);
+}
diff --git a/xpcom/xpcom-config.h.in b/xpcom/xpcom-config.h.in
new file mode 100644
index 000000000..7b2de5025
--- /dev/null
+++ b/xpcom/xpcom-config.h.in
@@ -0,0 +1,24 @@
+/* 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/. */
+
+/* Global defines needed by xpcom clients */
+
+#ifndef _XPCOM_CONFIG_H_
+#define _XPCOM_CONFIG_H_
+
+/* Define this to throw() if the compiler complains about
+ * constructors returning NULL
+ */
+#undef CPP_THROW_NEW
+
+/* Define if the c++ compiler can resolve ambiguity with |using| */
+#undef HAVE_CPP_AMBIGUITY_RESOLVING_USING
+
+/* Define if a dyanmic_cast to void* gives the most derived object */
+#undef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+
+/* Define to a string describing the XPCOM ABI in use */
+#undef TARGET_XPCOM_ABI
+
+#endif /* _XPCOM_CONFIG_H_ */
diff --git a/xpcom/xpcom-private.h.in b/xpcom/xpcom-private.h.in
new file mode 100644
index 000000000..66c024fd8
--- /dev/null
+++ b/xpcom/xpcom-private.h.in
@@ -0,0 +1,50 @@
+/* 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 following defines are only used by the xpcom implementation */
+
+#ifndef _XPCOM_PRIVATE_H_
+#define _XPCOM_PRIVATE_H_
+
+/* Define if getpagesize() is available */
+#undef HAVE_GETPAGESIZE
+
+/* Define if iconv() is available */
+#undef HAVE_ICONV
+
+/* Define if iconv() supports const input */
+#undef HAVE_ICONV_WITH_CONST_INPUT
+
+/* Define if mbrtowc() is available */
+#undef HAVE_MBRTOWC
+
+/* Define if wcrtomb() is available */
+#undef HAVE_WCRTOMB
+
+/* Define if statvfs64() is available */
+#undef HAVE_STATVFS64
+
+/* Define if statvfs() is available */
+#undef HAVE_STATVFS
+
+/* Define if statfs64() is available */
+#undef HAVE_STATFS64
+
+/* Define if statfs() is available */
+#undef HAVE_STATFS
+
+/* Define if <sys/statvfs.h> is present */
+#undef HAVE_SYS_STATVFS_H
+
+/* Define if <sys/statfs.h> is present */
+#undef HAVE_SYS_STATFS_H
+
+/* Define if <sys/vfs.h> is present */
+#undef HAVE_SYS_VFS_H
+
+/* Define if <sys/mount.h> is present */
+#undef HAVE_SYS_MOUNT_H
+
+#endif /* _XPCOM_PRIVATE_H_ */
+
diff --git a/xpcom/xpidl/Makefile.in b/xpcom/xpidl/Makefile.in
new file mode 100644
index 000000000..b58ad4866
--- /dev/null
+++ b/xpcom/xpidl/Makefile.in
@@ -0,0 +1,10 @@
+# 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/.
+
+export::
+ $(call py_action,process_install_manifest,$(DIST)/idl $(DEPTH)/_build_manifests/install/dist_idl)
+ $(call SUBMAKE,xpidl,$(DEPTH)/config/makefiles/xpidl)
+
+clean clobber realclean clobber_all distclean::
+ $(call SUBMAKE,$@,$(DEPTH)/config/makefiles/xpidl)
diff --git a/xpcom/xpidl/moz.build b/xpcom/xpidl/moz.build
new file mode 100644
index 000000000..568f361a5
--- /dev/null
+++ b/xpcom/xpidl/moz.build
@@ -0,0 +1,5 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.