LCOV - code coverage report
Current view: top level - dom/media - VideoUtils.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 107 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 47 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef VideoUtils_h
       8             : #define VideoUtils_h
       9             : 
      10             : #include "AudioSampleFormat.h"
      11             : #include "MediaInfo.h"
      12             : #include "TimeUnits.h"
      13             : #include "VideoLimits.h"
      14             : #include "mozilla/AbstractThread.h"
      15             : #include "mozilla/Attributes.h"
      16             : #include "mozilla/CheckedInt.h"
      17             : #include "mozilla/MozPromise.h"
      18             : #include "mozilla/ReentrantMonitor.h"
      19             : #include "mozilla/RefPtr.h"
      20             : #include "mozilla/UniquePtr.h"
      21             : #include "nsAutoPtr.h"
      22             : #include "nsCOMPtr.h"
      23             : #include "nsIThread.h"
      24             : #include "nsITimer.h"
      25             : #include "nsRect.h"
      26             : #include "nsSize.h"
      27             : #include "nsThreadUtils.h"
      28             : #include "prtime.h"
      29             : 
      30             : using mozilla::CheckedInt64;
      31             : using mozilla::CheckedUint64;
      32             : using mozilla::CheckedInt32;
      33             : using mozilla::CheckedUint32;
      34             : 
      35             : // This file contains stuff we'd rather put elsewhere, but which is
      36             : // dependent on other changes which we don't want to wait for. We plan to
      37             : // remove this file in the near future.
      38             : 
      39             : 
      40             : // This belongs in xpcom/monitor/Monitor.h, once we've made
      41             : // mozilla::Monitor non-reentrant.
      42             : namespace mozilla {
      43             : 
      44             : class MediaContainerType;
      45             : 
      46             : // EME Key System String.
      47             : extern const nsLiteralCString kEMEKeySystemClearkey;
      48             : extern const nsLiteralCString kEMEKeySystemWidevine;
      49             : 
      50             : /**
      51             :  * ReentrantMonitorConditionallyEnter
      52             :  *
      53             :  * Enters the supplied monitor only if the conditional value |aEnter| is true.
      54             :  * E.g. Used to allow unmonitored read access on the decode thread,
      55             :  * and monitored access on all other threads.
      56             :  */
      57             : class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter
      58             : {
      59             : public:
      60             :   ReentrantMonitorConditionallyEnter(bool aEnter,
      61             :                                      ReentrantMonitor &aReentrantMonitor) :
      62             :     mReentrantMonitor(nullptr)
      63             :   {
      64             :     MOZ_COUNT_CTOR(ReentrantMonitorConditionallyEnter);
      65             :     if (aEnter) {
      66             :       mReentrantMonitor = &aReentrantMonitor;
      67             :       NS_ASSERTION(mReentrantMonitor, "null monitor");
      68             :       mReentrantMonitor->Enter();
      69             :     }
      70             :   }
      71             :   ~ReentrantMonitorConditionallyEnter(void)
      72             :   {
      73             :     if (mReentrantMonitor) {
      74             :       mReentrantMonitor->Exit();
      75             :     }
      76             :     MOZ_COUNT_DTOR(ReentrantMonitorConditionallyEnter);
      77             :   }
      78             : private:
      79             :   // Restrict to constructor and destructor defined above.
      80             :   ReentrantMonitorConditionallyEnter();
      81             :   ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
      82             :   ReentrantMonitorConditionallyEnter& operator =(const ReentrantMonitorConditionallyEnter&);
      83             :   static void* operator new(size_t) CPP_THROW_NEW;
      84             :   static void operator delete(void*);
      85             : 
      86             :   ReentrantMonitor* mReentrantMonitor;
      87             : };
      88             : 
      89             : // Shuts down a thread asynchronously.
      90             : class ShutdownThreadEvent : public Runnable
      91             : {
      92             : public:
      93           0 :   explicit ShutdownThreadEvent(nsIThread* aThread)
      94           0 :     : Runnable("ShutdownThreadEvent")
      95           0 :     , mThread(aThread)
      96             :   {
      97           0 :   }
      98           0 :   ~ShutdownThreadEvent() {}
      99           0 :   NS_IMETHOD Run() override {
     100           0 :     mThread->Shutdown();
     101           0 :     mThread = nullptr;
     102           0 :     return NS_OK;
     103             :   }
     104             : private:
     105             :   nsCOMPtr<nsIThread> mThread;
     106             : };
     107             : 
     108             : class MediaResource;
     109             : 
     110             : // Estimates the buffered ranges of a MediaResource using a simple
     111             : // (byteOffset/length)*duration method. Probably inaccurate, but won't
     112             : // do file I/O, and can be used when we don't have detailed knowledge
     113             : // of the byte->time mapping of a resource. aDurationUsecs is the duration
     114             : // of the media in microseconds. Estimated buffered ranges are stored in
     115             : // aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
     116             : media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
     117             :                                                     int64_t aDurationUsecs);
     118             : 
     119             : // Converts from number of audio frames (aFrames) to microseconds, given
     120             : // the specified audio rate (aRate).
     121             : CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
     122             : // Converts from number of audio frames (aFrames) TimeUnit, given
     123             : // the specified audio rate (aRate).
     124             : media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
     125             : // Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
     126             : // aValue * aMul overflowing.
     127             : CheckedInt64 SaferMultDiv(int64_t aValue, uint32_t aMul, uint32_t aDiv);
     128             : 
     129             : // Converts from microseconds (aUsecs) to number of audio frames, given the
     130             : // specified audio rate (aRate). Stores the result in aOutFrames. Returns
     131             : // true if the operation succeeded, or false if there was an integer
     132             : // overflow while calulating the conversion.
     133             : CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
     134             : 
     135             : // Format TimeUnit as number of frames at given rate.
     136             : CheckedInt64 TimeUnitToFrames(const media::TimeUnit& aTime, uint32_t aRate);
     137             : 
     138             : // Converts milliseconds to seconds.
     139             : #define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))
     140             : 
     141             : // Converts seconds to milliseconds.
     142             : #define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
     143             : 
     144             : // Converts from seconds to microseconds. Returns failure if the resulting
     145             : // integer is too big to fit in an int64_t.
     146             : nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
     147             : 
     148             : // Scales the display rect aDisplay by aspect ratio aAspectRatio.
     149             : // Note that aDisplay must be validated by IsValidVideoRegion()
     150             : // before being used!
     151             : void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio);
     152             : 
     153             : // Downmix Stereo audio samples to Mono.
     154             : // Input are the buffer contains stereo data and the number of frames.
     155             : void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
     156             :                          uint32_t aFrames);
     157             : 
     158             : bool IsVideoContentType(const nsCString& aContentType);
     159             : 
     160             : // Returns true if it's safe to use aPicture as the picture to be
     161             : // extracted inside a frame of size aFrame, and scaled up to and displayed
     162             : // at a size of aDisplay. You should validate the frame, picture, and
     163             : // display regions before using them to display video frames.
     164             : bool IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
     165             :                         const nsIntSize& aDisplay);
     166             : 
     167             : // Template to automatically set a variable to a value on scope exit.
     168             : // Useful for unsetting flags, etc.
     169             : template<typename T>
     170             : class AutoSetOnScopeExit {
     171             : public:
     172             :   AutoSetOnScopeExit(T& aVar, T aValue)
     173             :     : mVar(aVar)
     174             :     , mValue(aValue)
     175             :   {}
     176             :   ~AutoSetOnScopeExit() {
     177             :     mVar = mValue;
     178             :   }
     179             : private:
     180             :   T& mVar;
     181             :   const T mValue;
     182             : };
     183             : 
     184             : class SharedThreadPool;
     185             : 
     186             : // The MediaDataDecoder API blocks, with implementations waiting on platform
     187             : // decoder tasks.  These platform decoder tasks are queued on a separate
     188             : // thread pool to ensure they can run when the MediaDataDecoder clients'
     189             : // thread pool is blocked.  Tasks on the PLATFORM_DECODER thread pool must not
     190             : // wait on tasks in the PLAYBACK thread pool.
     191             : //
     192             : // No new dependencies on this mechanism should be added, as methods are being
     193             : // made async supported by MozPromise, making this unnecessary and
     194             : // permitting unifying the pool.
     195             : enum class MediaThreadType {
     196             :   PLAYBACK, // MediaDecoderStateMachine and MediaDecoderReader
     197             :   PLATFORM_DECODER
     198             : };
     199             : // Returns the thread pool that is shared amongst all decoder state machines
     200             : // for decoding streams.
     201             : already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType);
     202             : 
     203             : enum H264_PROFILE {
     204             :   H264_PROFILE_UNKNOWN                     = 0,
     205             :   H264_PROFILE_BASE                        = 0x42,
     206             :   H264_PROFILE_MAIN                        = 0x4D,
     207             :   H264_PROFILE_EXTENDED                    = 0x58,
     208             :   H264_PROFILE_HIGH                        = 0x64,
     209             : };
     210             : 
     211             : enum H264_LEVEL {
     212             :     H264_LEVEL_1         = 10,
     213             :     H264_LEVEL_1_b       = 11,
     214             :     H264_LEVEL_1_1       = 11,
     215             :     H264_LEVEL_1_2       = 12,
     216             :     H264_LEVEL_1_3       = 13,
     217             :     H264_LEVEL_2         = 20,
     218             :     H264_LEVEL_2_1       = 21,
     219             :     H264_LEVEL_2_2       = 22,
     220             :     H264_LEVEL_3         = 30,
     221             :     H264_LEVEL_3_1       = 31,
     222             :     H264_LEVEL_3_2       = 32,
     223             :     H264_LEVEL_4         = 40,
     224             :     H264_LEVEL_4_1       = 41,
     225             :     H264_LEVEL_4_2       = 42,
     226             :     H264_LEVEL_5         = 50,
     227             :     H264_LEVEL_5_1       = 51,
     228             :     H264_LEVEL_5_2       = 52
     229             : };
     230             : 
     231             : // Extracts the H.264/AVC profile and level from an H.264 codecs string.
     232             : // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
     233             : // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
     234             : // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
     235             : // for more details.
     236             : // Returns false on failure.
     237             : bool
     238             : ExtractH264CodecDetails(const nsAString& aCodecs,
     239             :                         int16_t& aProfile,
     240             :                         int16_t& aLevel);
     241             : 
     242             : // Use a cryptographic quality PRNG to generate raw random bytes
     243             : // and convert that to a base64 string.
     244             : nsresult
     245             : GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
     246             : 
     247             : // This version returns a string suitable for use as a file or URL
     248             : // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
     249             : nsresult
     250             : GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
     251             : 
     252             : already_AddRefed<TaskQueue>
     253             : CreateMediaDecodeTaskQueue(const char* aName);
     254             : 
     255             : // Iteratively invokes aWork until aCondition returns true, or aWork returns false.
     256             : // Use this rather than a while loop to avoid bogarting the task queue.
     257             : template<class Work, class Condition>
     258           0 : RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
     259           0 :   RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
     260             : 
     261           0 :   if (aCondition()) {
     262           0 :     p->Resolve(true, __func__);
     263             :   }
     264             : 
     265             :   struct Helper {
     266           0 :     static void Iteration(const RefPtr<GenericPromise::Private>& aPromise, Work aLocalWork, Condition aLocalCondition) {
     267           0 :       if (!aLocalWork()) {
     268           0 :         aPromise->Reject(NS_ERROR_FAILURE, __func__);
     269           0 :       } else if (aLocalCondition()) {
     270           0 :         aPromise->Resolve(true, __func__);
     271             :       } else {
     272             :         nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
     273             :           "InvokeUntil::Helper::Iteration",
     274           0 :           [aPromise, aLocalWork, aLocalCondition]() {
     275           0 :             Iteration(aPromise, aLocalWork, aLocalCondition);
     276           0 :           });
     277           0 :         AbstractThread::GetCurrent()->Dispatch(r.forget());
     278             :       }
     279           0 :     }
     280             :   };
     281             : 
     282           0 :   Helper::Iteration(p, aWork, aCondition);
     283           0 :   return p.forget();
     284             : }
     285             : 
     286             : // Simple timer to run a runnable after a timeout.
     287           0 : class SimpleTimer : public nsITimerCallback
     288             : {
     289             : public:
     290             :   NS_DECL_ISUPPORTS
     291             : 
     292             :   // Create a new timer to run aTask after aTimeoutMs milliseconds
     293             :   // on thread aTarget. If aTarget is null, task is run on the main thread.
     294             :   static already_AddRefed<SimpleTimer> Create(nsIRunnable* aTask,
     295             :                                               uint32_t aTimeoutMs,
     296             :                                               nsIEventTarget* aTarget = nullptr);
     297             :   void Cancel();
     298             : 
     299             :   NS_IMETHOD Notify(nsITimer *timer) override;
     300             : 
     301             : private:
     302           0 :   virtual ~SimpleTimer() {}
     303             :   nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIEventTarget* aTarget);
     304             : 
     305             :   RefPtr<nsIRunnable> mTask;
     306             :   nsCOMPtr<nsITimer> mTimer;
     307             : };
     308             : 
     309             : void
     310             : LogToBrowserConsole(const nsAString& aMsg);
     311             : 
     312             : bool
     313             : ParseMIMETypeString(const nsAString& aMIMEType,
     314             :                     nsString& aOutContainerType,
     315             :                     nsTArray<nsString>& aOutCodecs);
     316             : 
     317             : bool
     318             : ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
     319             : 
     320             : bool
     321             : IsH264CodecString(const nsAString& aCodec);
     322             : 
     323             : bool
     324             : IsAACCodecString(const nsAString& aCodec);
     325             : 
     326             : bool
     327             : IsVP8CodecString(const nsAString& aCodec);
     328             : 
     329             : bool
     330             : IsVP9CodecString(const nsAString& aCodec);
     331             : 
     332             : // Try and create a TrackInfo with a given codec MIME type.
     333             : UniquePtr<TrackInfo>
     334             : CreateTrackInfoWithMIMEType(const nsACString& aCodecMIMEType);
     335             : 
     336             : // Try and create a TrackInfo with a given codec MIME type, and optional extra
     337             : // parameters from a container type (its MIME type and codecs are ignored).
     338             : UniquePtr<TrackInfo>
     339             : CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
     340             :   const nsACString& aCodecMIMEType,
     341             :   const MediaContainerType& aContainerType);
     342             : 
     343             : namespace detail {
     344             : 
     345             : // aString should start with aMajor + '/'.
     346             : constexpr bool
     347           0 : StartsWithMIMETypeMajor(const char* aString,
     348             :                         const char* aMajor, size_t aMajorRemaining)
     349             : {
     350           0 :   return (aMajorRemaining == 0 && *aString == '/')
     351           0 :          || (*aString == *aMajor
     352           0 :              && StartsWithMIMETypeMajor(aString + 1,
     353           0 :                                         aMajor + 1, aMajorRemaining - 1));
     354             : }
     355             : 
     356             : // aString should only contain [a-z0-9\-\.] and a final '\0'.
     357             : constexpr bool
     358           0 : EndsWithMIMESubtype(const char* aString, size_t aRemaining)
     359             : {
     360             :   return aRemaining == 0
     361           0 :          || (((*aString >= 'a' && *aString <= 'z')
     362           0 :               || (*aString >= '0' && *aString <= '9')
     363           0 :               || *aString == '-'
     364           0 :               || *aString == '.')
     365           0 :              && EndsWithMIMESubtype(aString + 1, aRemaining - 1));
     366             : }
     367             : 
     368             : // Simple MIME-type literal string checker with a given (major) type.
     369             : // Only accepts "{aMajor}/[a-z0-9\-\.]+".
     370             : template <size_t MajorLengthPlus1>
     371             : constexpr bool
     372           0 : IsMIMETypeWithMajor(const char* aString, size_t aLength,
     373             :                     const char (&aMajor)[MajorLengthPlus1])
     374             : {
     375             :   return aLength > MajorLengthPlus1 // Major + '/' + at least 1 char
     376           0 :          && StartsWithMIMETypeMajor(aString, aMajor, MajorLengthPlus1 - 1)
     377           0 :          && EndsWithMIMESubtype(aString + MajorLengthPlus1,
     378           0 :                                 aLength - MajorLengthPlus1);
     379             : }
     380             : 
     381             : } // namespace detail
     382             : 
     383             : // Simple MIME-type string checker.
     384             : // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
     385             : // Add more if necessary.
     386             : constexpr bool
     387           0 : IsMediaMIMEType(const char* aString, size_t aLength)
     388             : {
     389           0 :   return detail::IsMIMETypeWithMajor(aString, aLength, "application")
     390           0 :          || detail::IsMIMETypeWithMajor(aString, aLength, "audio")
     391           0 :          || detail::IsMIMETypeWithMajor(aString, aLength, "video");
     392             : }
     393             : 
     394             : // Simple MIME-type string literal checker.
     395             : // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
     396             : // Add more if necessary.
     397             : template <size_t LengthPlus1>
     398             : constexpr bool
     399             : IsMediaMIMEType(const char (&aString)[LengthPlus1])
     400             : {
     401             :   return IsMediaMIMEType(aString, LengthPlus1 - 1);
     402             : }
     403             : 
     404             : // Simple MIME-type string checker.
     405             : // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
     406             : // Add more if necessary.
     407             : inline bool
     408           0 : IsMediaMIMEType(const nsACString& aString)
     409             : {
     410           0 :   return IsMediaMIMEType(aString.Data(), aString.Length());
     411             : }
     412             : 
     413             : enum class StringListRangeEmptyItems
     414             : {
     415             :   // Skip all empty items (empty string will process nothing)
     416             :   // E.g.: "a,,b" -> ["a", "b"], "" -> nothing
     417             :   Skip,
     418             :   // Process all, except if string is empty
     419             :   // E.g.: "a,,b" -> ["a", "", "b"], "" -> nothing
     420             :   ProcessEmptyItems,
     421             :   // Process all, including 1 empty item in an empty string
     422             :   // E.g.: "a,,b" -> ["a", "", "b"], "" -> [""]
     423             :   ProcessAll
     424             : };
     425             : 
     426             : template <typename String,
     427             :           StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip>
     428             : class StringListRange
     429             : {
     430             :   typedef typename String::char_type CharType;
     431             :   typedef const CharType* Pointer;
     432             : 
     433             : public:
     434             :   // Iterator into range, trims items and optionally skips empty items.
     435             :   class Iterator
     436             :   {
     437             :   public:
     438           0 :     bool operator!=(const Iterator& a) const
     439             :     {
     440           0 :       return mStart != a.mStart || mEnd != a.mEnd;
     441             :     }
     442           0 :     Iterator& operator++()
     443             :     {
     444           0 :       SearchItemAt(mComma + 1);
     445           0 :       return *this;
     446             :     }
     447             :     // DereferencedType should be 'const nsDependent[C]String' pointing into
     448             :     // mList (which is 'const ns[C]String&').
     449             :     typedef decltype(Substring(Pointer(), Pointer())) DereferencedType;
     450           0 :     DereferencedType operator*()
     451             :     {
     452           0 :       return Substring(mStart, mEnd);
     453             :     }
     454             :   private:
     455             :     friend class StringListRange;
     456           0 :     Iterator(const CharType* aRangeStart, uint32_t aLength)
     457           0 :       : mRangeEnd(aRangeStart + aLength)
     458             :     {
     459           0 :       SearchItemAt(aRangeStart);
     460           0 :     }
     461           0 :     void SearchItemAt(Pointer start)
     462             :     {
     463             :       // First, skip leading whitespace.
     464           0 :       for (Pointer p = start; ; ++p) {
     465           0 :         if (p >= mRangeEnd) {
     466           0 :           if (p > mRangeEnd
     467           0 :                   + (empties != StringListRangeEmptyItems::Skip ? 1 : 0)) {
     468           0 :             p = mRangeEnd
     469             :                 + (empties != StringListRangeEmptyItems::Skip ? 1 : 0);
     470             :           }
     471           0 :           mStart = mEnd = mComma = p;
     472           0 :           return;
     473             :         }
     474           0 :         auto c = *p;
     475           0 :         if (c == CharType(',')) {
     476             :           // Comma -> Empty item -> Skip or process?
     477             :           if (empties != StringListRangeEmptyItems::Skip) {
     478           0 :             mStart = mEnd = mComma = p;
     479           0 :             return;
     480             :           }
     481           0 :         } else if (c != CharType(' ')) {
     482           0 :           mStart = p;
     483           0 :           break;
     484             :         }
     485             :       }
     486             :       // Find comma, recording start of trailing space.
     487           0 :       Pointer trailingWhitespace = nullptr;
     488           0 :       for (Pointer p = mStart + 1; ; ++p) {
     489           0 :         if (p >= mRangeEnd) {
     490           0 :           mEnd = trailingWhitespace ? trailingWhitespace : p;
     491           0 :           mComma = p;
     492           0 :           return;
     493             :         }
     494           0 :         auto c = *p;
     495           0 :         if (c == CharType(',')) {
     496           0 :           mEnd = trailingWhitespace ? trailingWhitespace : p;
     497           0 :           mComma = p;
     498           0 :           return;
     499             :         }
     500           0 :         if (c == CharType(' ')) {
     501             :           // Found a whitespace -> Record as trailing if not first one.
     502           0 :           if (!trailingWhitespace) {
     503           0 :             trailingWhitespace = p;
     504             :           }
     505             :         } else {
     506             :           // Found a non-whitespace -> Reset trailing whitespace if needed.
     507           0 :           if (trailingWhitespace) {
     508           0 :             trailingWhitespace = nullptr;
     509             :           }
     510             :         }
     511             :       }
     512             :     }
     513             :     const Pointer mRangeEnd;
     514             :     Pointer mStart;
     515             :     Pointer mEnd;
     516             :     Pointer mComma;
     517             :   };
     518             : 
     519           0 :   explicit StringListRange(const String& aList) : mList(aList) {}
     520           0 :   Iterator begin() const
     521             :   {
     522           0 :     return Iterator(mList.Data()
     523           0 :                     + ((empties == StringListRangeEmptyItems::ProcessEmptyItems
     524           0 :                         && mList.Length() == 0) ? 1 : 0),
     525           0 :                     mList.Length());
     526             :   }
     527           0 :   Iterator end() const
     528             :   {
     529           0 :     return Iterator(mList.Data() + mList.Length()
     530           0 :                     + (empties != StringListRangeEmptyItems::Skip ? 1 : 0),
     531           0 :                     0);
     532             :   }
     533             : private:
     534             :   const String& mList;
     535             : };
     536             : 
     537             : template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
     538             :           typename String>
     539             : StringListRange<String, empties>
     540           0 : MakeStringListRange(const String& aList)
     541             : {
     542           0 :   return StringListRange<String, empties>(aList);
     543             : }
     544             : 
     545             : template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
     546             :           typename ListString, typename ItemString>
     547             : static bool
     548           0 : StringListContains(const ListString& aList, const ItemString& aItem)
     549             : {
     550           0 :   for (const auto& listItem : MakeStringListRange<empties>(aList)) {
     551           0 :     if (listItem.Equals(aItem)) {
     552           0 :       return true;
     553             :     }
     554             :   }
     555           0 :   return false;
     556             : }
     557             : 
     558             : } // end namespace mozilla
     559             : 
     560             : #endif

Generated by: LCOV version 1.13