LCOV - code coverage report
Current view: top level - toolkit/components/telemetry - TelemetryScalar.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 169 874 19.3 %
Date: 2017-07-14 16:53:18 Functions: 28 95 29.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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             : #include "nsITelemetry.h"
       8             : #include "nsIVariant.h"
       9             : #include "nsVariant.h"
      10             : #include "nsHashKeys.h"
      11             : #include "nsBaseHashtable.h"
      12             : #include "nsClassHashtable.h"
      13             : #include "nsDataHashtable.h"
      14             : #include "nsIXPConnect.h"
      15             : #include "nsContentUtils.h"
      16             : #include "nsThreadUtils.h"
      17             : #include "mozilla/StaticMutex.h"
      18             : #include "mozilla/Unused.h"
      19             : 
      20             : #include "TelemetryCommon.h"
      21             : #include "TelemetryScalar.h"
      22             : #include "TelemetryScalarData.h"
      23             : #include "ipc/TelemetryComms.h"
      24             : #include "ipc/TelemetryIPCAccumulator.h"
      25             : 
      26             : using mozilla::StaticMutex;
      27             : using mozilla::StaticMutexAutoLock;
      28             : using mozilla::Telemetry::Common::AutoHashtable;
      29             : using mozilla::Telemetry::Common::IsExpiredVersion;
      30             : using mozilla::Telemetry::Common::CanRecordDataset;
      31             : using mozilla::Telemetry::Common::IsInDataset;
      32             : using mozilla::Telemetry::Common::LogToBrowserConsole;
      33             : using mozilla::Telemetry::Common::GetNameForProcessID;
      34             : using mozilla::Telemetry::ScalarActionType;
      35             : using mozilla::Telemetry::ScalarID;
      36             : using mozilla::Telemetry::ScalarVariant;
      37             : using mozilla::Telemetry::ProcessID;
      38             : 
      39             : namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
      40             : 
      41             : ////////////////////////////////////////////////////////////////////////
      42             : ////////////////////////////////////////////////////////////////////////
      43             : //
      44             : // Naming: there are two kinds of functions in this file:
      45             : //
      46             : // * Functions named internal_*: these can only be reached via an
      47             : //   interface function (TelemetryScalar::*). They expect the interface
      48             : //   function to have acquired |gTelemetryScalarsMutex|, so they do not
      49             : //   have to be thread-safe.
      50             : //
      51             : // * Functions named TelemetryScalar::*. This is the external interface.
      52             : //   Entries and exits to these functions are serialised using
      53             : //   |gTelemetryScalarsMutex|.
      54             : //
      55             : // Avoiding races and deadlocks:
      56             : //
      57             : // All functions in the external interface (TelemetryScalar::*) are
      58             : // serialised using the mutex |gTelemetryScalarsMutex|. This means
      59             : // that the external interface is thread-safe, and many of the
      60             : // internal_* functions can ignore thread safety. But it also brings
      61             : // a danger of deadlock if any function in the external interface can
      62             : // get back to that interface. That is, we will deadlock on any call
      63             : // chain like this
      64             : //
      65             : // TelemetryScalar::* -> .. any functions .. -> TelemetryScalar::*
      66             : //
      67             : // To reduce the danger of that happening, observe the following rules:
      68             : //
      69             : // * No function in TelemetryScalar::* may directly call, nor take the
      70             : //   address of, any other function in TelemetryScalar::*.
      71             : //
      72             : // * No internal function internal_* may call, nor take the address
      73             : //   of, any function in TelemetryScalar::*.
      74             : 
      75             : ////////////////////////////////////////////////////////////////////////
      76             : ////////////////////////////////////////////////////////////////////////
      77             : //
      78             : // PRIVATE TYPES
      79             : 
      80             : namespace {
      81             : 
      82             : const uint32_t kMaximumNumberOfKeys = 100;
      83             : const uint32_t kMaximumKeyStringLength = 70;
      84             : const uint32_t kMaximumStringValueLength = 50;
      85             : const uint32_t kScalarCount =
      86             :   static_cast<uint32_t>(mozilla::Telemetry::ScalarID::ScalarCount);
      87             : 
      88             : enum class ScalarResult : uint8_t {
      89             :   // Nothing went wrong.
      90             :   Ok,
      91             :   // General Scalar Errors
      92             :   NotInitialized,
      93             :   CannotUnpackVariant,
      94             :   CannotRecordInProcess,
      95             :   CannotRecordDataset,
      96             :   KeyedTypeMismatch,
      97             :   UnknownScalar,
      98             :   OperationNotSupported,
      99             :   InvalidType,
     100             :   InvalidValue,
     101             :   // Keyed Scalar Errors
     102             :   KeyIsEmpty,
     103             :   KeyTooLong,
     104             :   TooManyKeys,
     105             :   // String Scalar Errors
     106             :   StringTooLong,
     107             :   // Unsigned Scalar Errors
     108             :   UnsignedNegativeValue,
     109             :   UnsignedTruncatedValue
     110             : };
     111             : 
     112             : typedef nsBaseHashtableET<nsDepCharHashKey, mozilla::Telemetry::ScalarID>
     113             :           CharPtrEntryType;
     114             : 
     115             : typedef AutoHashtable<CharPtrEntryType> ScalarMapType;
     116             : 
     117             : bool
     118          16 : IsValidEnumId(mozilla::Telemetry::ScalarID aID)
     119             : {
     120          16 :   return aID < mozilla::Telemetry::ScalarID::ScalarCount;
     121             : }
     122             : 
     123             : /**
     124             :  * Convert a nsIVariant to a mozilla::Variant, which is used for
     125             :  * accumulating child process scalars.
     126             :  */
     127             : ScalarResult
     128           0 : GetVariantFromIVariant(nsIVariant* aInput, uint32_t aScalarKind,
     129             :                        mozilla::Maybe<ScalarVariant>& aOutput)
     130             : {
     131           0 :   switch (aScalarKind) {
     132             :     case nsITelemetry::SCALAR_COUNT:
     133             :       {
     134           0 :         uint32_t val = 0;
     135           0 :         nsresult rv = aInput->GetAsUint32(&val);
     136           0 :         if (NS_FAILED(rv)) {
     137           0 :           return ScalarResult::CannotUnpackVariant;
     138             :         }
     139           0 :         aOutput = mozilla::Some(mozilla::AsVariant(val));
     140           0 :         break;
     141             :       }
     142             :     case nsITelemetry::SCALAR_STRING:
     143             :       {
     144           0 :         nsString val;
     145           0 :         nsresult rv = aInput->GetAsAString(val);
     146           0 :         if (NS_FAILED(rv)) {
     147           0 :           return ScalarResult::CannotUnpackVariant;
     148             :         }
     149           0 :         aOutput = mozilla::Some(mozilla::AsVariant(val));
     150           0 :         break;
     151             :       }
     152             :     case nsITelemetry::SCALAR_BOOLEAN:
     153             :       {
     154           0 :         bool val = false;
     155           0 :         nsresult rv = aInput->GetAsBool(&val);
     156           0 :         if (NS_FAILED(rv)) {
     157           0 :           return ScalarResult::CannotUnpackVariant;
     158             :         }
     159           0 :         aOutput = mozilla::Some(mozilla::AsVariant(val));
     160           0 :         break;
     161             :       }
     162             :     default:
     163           0 :       MOZ_ASSERT(false, "Unknown scalar kind.");
     164             :       return ScalarResult::UnknownScalar;
     165             :   }
     166           0 :   return ScalarResult::Ok;
     167             : }
     168             : 
     169             : // Implements the methods for ScalarInfo.
     170             : const char *
     171         186 : ScalarInfo::name() const
     172             : {
     173         186 :   return &gScalarsStringTable[this->name_offset];
     174             : }
     175             : 
     176             : const char *
     177           7 : ScalarInfo::expiration() const
     178             : {
     179           7 :   return &gScalarsStringTable[this->expiration_offset];
     180             : }
     181             : 
     182             : /**
     183             :  * The base scalar object, that servers as a common ancestor for storage
     184             :  * purposes.
     185             :  */
     186           8 : class ScalarBase
     187             : {
     188             : public:
     189           0 :   virtual ~ScalarBase() = default;
     190             : 
     191             :   // Set, Add and SetMaximum functions as described in the Telemetry IDL.
     192             :   virtual ScalarResult SetValue(nsIVariant* aValue) = 0;
     193           0 :   virtual ScalarResult AddValue(nsIVariant* aValue) { return ScalarResult::OperationNotSupported; }
     194           0 :   virtual ScalarResult SetMaximum(nsIVariant* aValue) { return ScalarResult::OperationNotSupported; }
     195             : 
     196             :   // Convenience methods used by the C++ API.
     197           0 :   virtual void SetValue(uint32_t aValue) { mozilla::Unused << HandleUnsupported(); }
     198           0 :   virtual ScalarResult SetValue(const nsAString& aValue) { return HandleUnsupported(); }
     199           0 :   virtual void SetValue(bool aValue) { mozilla::Unused << HandleUnsupported(); }
     200           0 :   virtual void AddValue(uint32_t aValue) { mozilla::Unused << HandleUnsupported(); }
     201           0 :   virtual void SetMaximum(uint32_t aValue) { mozilla::Unused << HandleUnsupported(); }
     202             : 
     203             :   // GetValue is used to get the value of the scalar when persisting it to JS.
     204             :   virtual nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const = 0;
     205             : 
     206             :   // To measure the memory stats.
     207             :   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
     208             : 
     209             : private:
     210             :   ScalarResult HandleUnsupported() const;
     211             : };
     212             : 
     213             : ScalarResult
     214           0 : ScalarBase::HandleUnsupported() const
     215             : {
     216           0 :   MOZ_ASSERT(false, "This operation is not support for this scalar type.");
     217             :   return ScalarResult::OperationNotSupported;
     218             : }
     219             : 
     220             : /**
     221             :  * The implementation for the unsigned int scalar type.
     222             :  */
     223             : class ScalarUnsigned : public ScalarBase
     224             : {
     225             : public:
     226             :   using ScalarBase::SetValue;
     227             : 
     228           5 :   ScalarUnsigned() : mStorage(0) {};
     229           0 :   ~ScalarUnsigned() override = default;
     230             : 
     231             :   ScalarResult SetValue(nsIVariant* aValue) final;
     232             :   void SetValue(uint32_t aValue) final;
     233             :   ScalarResult AddValue(nsIVariant* aValue) final;
     234             :   void AddValue(uint32_t aValue) final;
     235             :   ScalarResult SetMaximum(nsIVariant* aValue) final;
     236             :   void SetMaximum(uint32_t aValue) final;
     237             :   nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const final;
     238             :   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
     239             : 
     240             : private:
     241             :   uint32_t mStorage;
     242             : 
     243             :   ScalarResult CheckInput(nsIVariant* aValue);
     244             : 
     245             :   // Prevent copying.
     246             :   ScalarUnsigned(const ScalarUnsigned& aOther) = delete;
     247             :   void operator=(const ScalarUnsigned& aOther) = delete;
     248             : };
     249             : 
     250             : ScalarResult
     251           0 : ScalarUnsigned::SetValue(nsIVariant* aValue)
     252             : {
     253           0 :   ScalarResult sr = CheckInput(aValue);
     254           0 :   if (sr == ScalarResult::UnsignedNegativeValue) {
     255           0 :     return sr;
     256             :   }
     257             : 
     258           0 :   if (NS_FAILED(aValue->GetAsUint32(&mStorage))) {
     259           0 :     return ScalarResult::InvalidValue;
     260             :   }
     261           0 :   return sr;
     262             : }
     263             : 
     264             : void
     265           0 : ScalarUnsigned::SetValue(uint32_t aValue)
     266             : {
     267           0 :   mStorage = aValue;
     268           0 : }
     269             : 
     270             : ScalarResult
     271           0 : ScalarUnsigned::AddValue(nsIVariant* aValue)
     272             : {
     273           0 :   ScalarResult sr = CheckInput(aValue);
     274           0 :   if (sr == ScalarResult::UnsignedNegativeValue) {
     275           0 :     return sr;
     276             :   }
     277             : 
     278           0 :   uint32_t newAddend = 0;
     279           0 :   nsresult rv = aValue->GetAsUint32(&newAddend);
     280           0 :   if (NS_FAILED(rv)) {
     281           0 :     return ScalarResult::InvalidValue;
     282             :   }
     283           0 :   mStorage += newAddend;
     284           0 :   return sr;
     285             : }
     286             : 
     287             : void
     288          10 : ScalarUnsigned::AddValue(uint32_t aValue)
     289             : {
     290          10 :   mStorage += aValue;
     291          10 : }
     292             : 
     293             : ScalarResult
     294           0 : ScalarUnsigned::SetMaximum(nsIVariant* aValue)
     295             : {
     296           0 :   ScalarResult sr = CheckInput(aValue);
     297           0 :   if (sr == ScalarResult::UnsignedNegativeValue) {
     298           0 :     return sr;
     299             :   }
     300             : 
     301           0 :   uint32_t newValue = 0;
     302           0 :   nsresult rv = aValue->GetAsUint32(&newValue);
     303           0 :   if (NS_FAILED(rv)) {
     304           0 :     return ScalarResult::InvalidValue;
     305             :   }
     306           0 :   if (newValue > mStorage) {
     307           0 :     mStorage = newValue;
     308             :   }
     309           0 :   return sr;
     310             : }
     311             : 
     312             : void
     313           0 : ScalarUnsigned::SetMaximum(uint32_t aValue)
     314             : {
     315           0 :   if (aValue > mStorage) {
     316           0 :     mStorage = aValue;
     317             :   }
     318           0 : }
     319             : 
     320             : nsresult
     321           0 : ScalarUnsigned::GetValue(nsCOMPtr<nsIVariant>& aResult) const
     322             : {
     323           0 :   nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
     324           0 :   nsresult rv = outVar->SetAsUint32(mStorage);
     325           0 :   if (NS_FAILED(rv)) {
     326           0 :     return rv;
     327             :   }
     328           0 :   aResult = outVar.forget();
     329           0 :   return NS_OK;
     330             : }
     331             : 
     332             : size_t
     333           0 : ScalarUnsigned::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     334             : {
     335           0 :   return aMallocSizeOf(this);
     336             : }
     337             : 
     338             : ScalarResult
     339           0 : ScalarUnsigned::CheckInput(nsIVariant* aValue)
     340             : {
     341             :   // If this is a floating point value/double, we will probably get truncated.
     342             :   uint16_t type;
     343           0 :   aValue->GetDataType(&type);
     344           0 :   if (type == nsIDataType::VTYPE_FLOAT ||
     345           0 :       type == nsIDataType::VTYPE_DOUBLE) {
     346           0 :     return ScalarResult::UnsignedTruncatedValue;
     347             :   }
     348             : 
     349             :   int32_t signedTest;
     350             :   // If we're able to cast the number to an int, check its sign.
     351             :   // Warn the user if he's trying to set the unsigned scalar to a negative
     352             :   // number.
     353           0 :   if (NS_SUCCEEDED(aValue->GetAsInt32(&signedTest)) &&
     354           0 :       signedTest < 0) {
     355           0 :     return ScalarResult::UnsignedNegativeValue;
     356             :   }
     357           0 :   return ScalarResult::Ok;
     358             : }
     359             : 
     360             : /**
     361             :  * The implementation for the string scalar type.
     362             :  */
     363             : class ScalarString : public ScalarBase
     364             : {
     365             : public:
     366             :   using ScalarBase::SetValue;
     367             : 
     368           0 :   ScalarString() : mStorage(EmptyString()) {};
     369           0 :   ~ScalarString() override = default;
     370             : 
     371             :   ScalarResult SetValue(nsIVariant* aValue) final;
     372             :   ScalarResult SetValue(const nsAString& aValue) final;
     373             :   nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const final;
     374             :   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
     375             : 
     376             : private:
     377             :   nsString mStorage;
     378             : 
     379             :   // Prevent copying.
     380             :   ScalarString(const ScalarString& aOther) = delete;
     381             :   void operator=(const ScalarString& aOther) = delete;
     382             : };
     383             : 
     384             : ScalarResult
     385           0 : ScalarString::SetValue(nsIVariant* aValue)
     386             : {
     387             :   // Check that we got the correct data type.
     388             :   uint16_t type;
     389           0 :   aValue->GetDataType(&type);
     390           0 :   if (type != nsIDataType::VTYPE_CHAR &&
     391           0 :       type != nsIDataType::VTYPE_WCHAR &&
     392           0 :       type != nsIDataType::VTYPE_DOMSTRING &&
     393           0 :       type != nsIDataType::VTYPE_CHAR_STR &&
     394           0 :       type != nsIDataType::VTYPE_WCHAR_STR &&
     395           0 :       type != nsIDataType::VTYPE_STRING_SIZE_IS &&
     396           0 :       type != nsIDataType::VTYPE_WSTRING_SIZE_IS &&
     397           0 :       type != nsIDataType::VTYPE_UTF8STRING &&
     398           0 :       type != nsIDataType::VTYPE_CSTRING &&
     399           0 :       type != nsIDataType::VTYPE_ASTRING) {
     400           0 :     return ScalarResult::InvalidType;
     401             :   }
     402             : 
     403           0 :   nsAutoString convertedString;
     404           0 :   nsresult rv = aValue->GetAsAString(convertedString);
     405           0 :   if (NS_FAILED(rv)) {
     406           0 :     return ScalarResult::InvalidValue;
     407             :   }
     408           0 :   return SetValue(convertedString);
     409             : };
     410             : 
     411             : ScalarResult
     412           0 : ScalarString::SetValue(const nsAString& aValue)
     413             : {
     414           0 :   mStorage = Substring(aValue, 0, kMaximumStringValueLength);
     415           0 :   if (aValue.Length() > kMaximumStringValueLength) {
     416           0 :     return ScalarResult::StringTooLong;
     417             :   }
     418           0 :   return ScalarResult::Ok;
     419             : }
     420             : 
     421             : nsresult
     422           0 : ScalarString::GetValue(nsCOMPtr<nsIVariant>& aResult) const
     423             : {
     424           0 :   nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
     425           0 :   nsresult rv = outVar->SetAsAString(mStorage);
     426           0 :   if (NS_FAILED(rv)) {
     427           0 :     return rv;
     428             :   }
     429           0 :   aResult = outVar.forget();
     430           0 :   return NS_OK;
     431             : }
     432             : 
     433             : size_t
     434           0 : ScalarString::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     435             : {
     436           0 :   size_t n = aMallocSizeOf(this);
     437           0 :   n+= mStorage.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
     438           0 :   return n;
     439             : }
     440             : 
     441             : /**
     442             :  * The implementation for the boolean scalar type.
     443             :  */
     444             : class ScalarBoolean : public ScalarBase
     445             : {
     446             : public:
     447             :   using ScalarBase::SetValue;
     448             : 
     449           3 :   ScalarBoolean() : mStorage(false) {};
     450           0 :   ~ScalarBoolean() override = default;
     451             : 
     452             :   ScalarResult SetValue(nsIVariant* aValue) final;
     453             :   void SetValue(bool aValue) final;
     454             :   nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const final;
     455             :   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
     456             : 
     457             : private:
     458             :   bool mStorage;
     459             : 
     460             :   // Prevent copying.
     461             :   ScalarBoolean(const ScalarBoolean& aOther) = delete;
     462             :   void operator=(const ScalarBoolean& aOther) = delete;
     463             : };
     464             : 
     465             : ScalarResult
     466           0 : ScalarBoolean::SetValue(nsIVariant* aValue)
     467             : {
     468             :   // Check that we got the correct data type.
     469             :   uint16_t type;
     470           0 :   aValue->GetDataType(&type);
     471           0 :   if (type != nsIDataType::VTYPE_BOOL &&
     472           0 :       type != nsIDataType::VTYPE_INT8 &&
     473           0 :       type != nsIDataType::VTYPE_INT16 &&
     474           0 :       type != nsIDataType::VTYPE_INT32 &&
     475           0 :       type != nsIDataType::VTYPE_INT64 &&
     476           0 :       type != nsIDataType::VTYPE_UINT8 &&
     477           0 :       type != nsIDataType::VTYPE_UINT16 &&
     478           0 :       type != nsIDataType::VTYPE_UINT32 &&
     479           0 :       type != nsIDataType::VTYPE_UINT64) {
     480           0 :     return ScalarResult::InvalidType;
     481             :   }
     482             : 
     483           0 :   if (NS_FAILED(aValue->GetAsBool(&mStorage))) {
     484           0 :     return ScalarResult::InvalidValue;
     485             :   }
     486           0 :   return ScalarResult::Ok;
     487             : };
     488             : 
     489             : void
     490           3 : ScalarBoolean::SetValue(bool aValue)
     491             : {
     492           3 :   mStorage = aValue;
     493           3 : }
     494             : 
     495             : nsresult
     496           0 : ScalarBoolean::GetValue(nsCOMPtr<nsIVariant>& aResult) const
     497             : {
     498           0 :   nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
     499           0 :   nsresult rv = outVar->SetAsBool(mStorage);
     500           0 :   if (NS_FAILED(rv)) {
     501           0 :     return rv;
     502             :   }
     503           0 :   aResult = outVar.forget();
     504           0 :   return NS_OK;
     505             : }
     506             : 
     507             : size_t
     508           0 : ScalarBoolean::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     509             : {
     510           0 :   return aMallocSizeOf(this);
     511             : }
     512             : 
     513             : /**
     514             :  * Allocate a scalar class given the scalar info.
     515             :  *
     516             :  * @param aInfo The informations for the scalar coming from the definition file.
     517             :  * @return nullptr if the scalar type is unknown, otherwise a valid pointer to the
     518             :  *         scalar type.
     519             :  */
     520             : ScalarBase*
     521           8 : internal_ScalarAllocate(uint32_t aScalarKind)
     522             : {
     523           8 :   ScalarBase* scalar = nullptr;
     524           8 :   switch (aScalarKind) {
     525             :   case nsITelemetry::SCALAR_COUNT:
     526           5 :     scalar = new ScalarUnsigned();
     527           5 :     break;
     528             :   case nsITelemetry::SCALAR_STRING:
     529           0 :     scalar = new ScalarString();
     530           0 :     break;
     531             :   case nsITelemetry::SCALAR_BOOLEAN:
     532           3 :     scalar = new ScalarBoolean();
     533           3 :     break;
     534             :   default:
     535           0 :     MOZ_ASSERT(false, "Invalid scalar type");
     536             :   }
     537           8 :   return scalar;
     538             : }
     539             : 
     540             : /**
     541             :  * The implementation for the keyed scalar type.
     542             :  */
     543             : class KeyedScalar
     544             : {
     545             : public:
     546             :   typedef mozilla::Pair<nsCString, nsCOMPtr<nsIVariant>> KeyValuePair;
     547             : 
     548           1 :   explicit KeyedScalar(uint32_t aScalarKind) : mScalarKind(aScalarKind) {};
     549           0 :   ~KeyedScalar() = default;
     550             : 
     551             :   // Set, Add and SetMaximum functions as described in the Telemetry IDL.
     552             :   // These methods implicitly instantiate a Scalar[*] for each key.
     553             :   ScalarResult SetValue(const nsAString& aKey, nsIVariant* aValue);
     554             :   ScalarResult AddValue(const nsAString& aKey, nsIVariant* aValue);
     555             :   ScalarResult SetMaximum(const nsAString& aKey, nsIVariant* aValue);
     556             : 
     557             :   // Convenience methods used by the C++ API.
     558             :   void SetValue(const nsAString& aKey, uint32_t aValue);
     559             :   void SetValue(const nsAString& aKey, bool aValue);
     560             :   void AddValue(const nsAString& aKey, uint32_t aValue);
     561             :   void SetMaximum(const nsAString& aKey, uint32_t aValue);
     562             : 
     563             :   // GetValue is used to get the key-value pairs stored in the keyed scalar
     564             :   // when persisting it to JS.
     565             :   nsresult GetValue(nsTArray<KeyValuePair>& aValues) const;
     566             : 
     567             :   // To measure the memory stats.
     568             :   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
     569             : 
     570             : private:
     571             :   typedef nsClassHashtable<nsCStringHashKey, ScalarBase> ScalarKeysMapType;
     572             : 
     573             :   ScalarKeysMapType mScalarKeys;
     574             :   const uint32_t mScalarKind;
     575             : 
     576             :   ScalarResult GetScalarForKey(const nsAString& aKey, ScalarBase** aRet);
     577             : };
     578             : 
     579             : ScalarResult
     580           0 : KeyedScalar::SetValue(const nsAString& aKey, nsIVariant* aValue)
     581             : {
     582           0 :   ScalarBase* scalar = nullptr;
     583           0 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     584           0 :   if (sr != ScalarResult::Ok) {
     585           0 :     return sr;
     586             :   }
     587             : 
     588           0 :   return scalar->SetValue(aValue);
     589             : }
     590             : 
     591             : ScalarResult
     592           0 : KeyedScalar::AddValue(const nsAString& aKey, nsIVariant* aValue)
     593             : {
     594           0 :   ScalarBase* scalar = nullptr;
     595           0 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     596           0 :   if (sr != ScalarResult::Ok) {
     597           0 :     return sr;
     598             :   }
     599             : 
     600           0 :   return scalar->AddValue(aValue);
     601             : }
     602             : 
     603             : ScalarResult
     604           0 : KeyedScalar::SetMaximum(const nsAString& aKey, nsIVariant* aValue)
     605             : {
     606           0 :   ScalarBase* scalar = nullptr;
     607           0 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     608           0 :   if (sr != ScalarResult::Ok) {
     609           0 :     return sr;
     610             :   }
     611             : 
     612           0 :   return scalar->SetMaximum(aValue);
     613             : }
     614             : 
     615             : void
     616           0 : KeyedScalar::SetValue(const nsAString& aKey, uint32_t aValue)
     617             : {
     618           0 :   ScalarBase* scalar = nullptr;
     619           0 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     620           0 :   if (sr != ScalarResult::Ok) {
     621           0 :     MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
     622             :     return;
     623             :   }
     624             : 
     625           0 :   return scalar->SetValue(aValue);
     626             : }
     627             : 
     628             : void
     629           2 : KeyedScalar::SetValue(const nsAString& aKey, bool aValue)
     630             : {
     631           2 :   ScalarBase* scalar = nullptr;
     632           2 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     633           2 :   if (sr != ScalarResult::Ok) {
     634           0 :     MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
     635             :     return;
     636             :   }
     637             : 
     638           2 :   return scalar->SetValue(aValue);
     639             : }
     640             : 
     641             : void
     642           0 : KeyedScalar::AddValue(const nsAString& aKey, uint32_t aValue)
     643             : {
     644           0 :   ScalarBase* scalar = nullptr;
     645           0 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     646           0 :   if (sr != ScalarResult::Ok) {
     647           0 :     MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
     648             :     return;
     649             :   }
     650             : 
     651           0 :   return scalar->AddValue(aValue);
     652             : }
     653             : 
     654             : void
     655           0 : KeyedScalar::SetMaximum(const nsAString& aKey, uint32_t aValue)
     656             : {
     657           0 :   ScalarBase* scalar = nullptr;
     658           0 :   ScalarResult sr = GetScalarForKey(aKey, &scalar);
     659           0 :   if (sr != ScalarResult::Ok) {
     660           0 :     MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
     661             :     return;
     662             :   }
     663             : 
     664           0 :   return scalar->SetMaximum(aValue);
     665             : }
     666             : 
     667             : /**
     668             :  * Get a key-value array with the values for the Keyed Scalar.
     669             :  * @param aValue The array that will hold the key-value pairs.
     670             :  * @return {nsresult} NS_OK or an error value as reported by the
     671             :  *         the specific scalar objects implementations (e.g.
     672             :  *         ScalarUnsigned).
     673             :  */
     674             : nsresult
     675           0 : KeyedScalar::GetValue(nsTArray<KeyValuePair>& aValues) const
     676             : {
     677           0 :   for (auto iter = mScalarKeys.ConstIter(); !iter.Done(); iter.Next()) {
     678           0 :     ScalarBase* scalar = static_cast<ScalarBase*>(iter.Data());
     679             : 
     680             :     // Get the scalar value.
     681           0 :     nsCOMPtr<nsIVariant> scalarValue;
     682           0 :     nsresult rv = scalar->GetValue(scalarValue);
     683           0 :     if (NS_FAILED(rv)) {
     684           0 :       return rv;
     685             :     }
     686             : 
     687             :     // Append it to value list.
     688           0 :     aValues.AppendElement(mozilla::MakePair(nsCString(iter.Key()), scalarValue));
     689             :   }
     690             : 
     691           0 :   return NS_OK;
     692             : }
     693             : 
     694             : /**
     695             :  * Get the scalar for the referenced key.
     696             :  * If there's no such key, instantiate a new Scalar object with the
     697             :  * same type of the Keyed scalar and create the key.
     698             :  */
     699             : ScalarResult
     700           2 : KeyedScalar::GetScalarForKey(const nsAString& aKey, ScalarBase** aRet)
     701             : {
     702           2 :   if (aKey.IsEmpty()) {
     703           0 :     return ScalarResult::KeyIsEmpty;
     704             :   }
     705             : 
     706           2 :   if (aKey.Length() >= kMaximumKeyStringLength) {
     707           0 :     return ScalarResult::KeyTooLong;
     708             :   }
     709             : 
     710           2 :   if (mScalarKeys.Count() >= kMaximumNumberOfKeys) {
     711           0 :     return ScalarResult::TooManyKeys;
     712             :   }
     713             : 
     714           4 :   NS_ConvertUTF16toUTF8 utf8Key(aKey);
     715             : 
     716           2 :   ScalarBase* scalar = nullptr;
     717           2 :   if (mScalarKeys.Get(utf8Key, &scalar)) {
     718           0 :     *aRet = scalar;
     719           0 :     return ScalarResult::Ok;
     720             :   }
     721             : 
     722           2 :   scalar = internal_ScalarAllocate(mScalarKind);
     723           2 :   if (!scalar) {
     724           0 :     return ScalarResult::InvalidType;
     725             :   }
     726             : 
     727           2 :   mScalarKeys.Put(utf8Key, scalar);
     728             : 
     729           2 :   *aRet = scalar;
     730           2 :   return ScalarResult::Ok;
     731             : }
     732             : 
     733             : size_t
     734           0 : KeyedScalar::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     735             : {
     736           0 :   size_t n = aMallocSizeOf(this);
     737           0 :   for (auto iter = mScalarKeys.Iter(); !iter.Done(); iter.Next()) {
     738           0 :     ScalarBase* scalar = static_cast<ScalarBase*>(iter.Data());
     739           0 :     n += scalar->SizeOfIncludingThis(aMallocSizeOf);
     740             :   }
     741           0 :   return n;
     742             : }
     743             : 
     744             : typedef nsUint32HashKey ScalarIDHashKey;
     745             : typedef nsUint32HashKey ProcessIDHashKey;
     746             : typedef nsClassHashtable<ScalarIDHashKey, ScalarBase> ScalarStorageMapType;
     747             : typedef nsClassHashtable<ScalarIDHashKey, KeyedScalar> KeyedScalarStorageMapType;
     748             : typedef nsClassHashtable<ProcessIDHashKey, ScalarStorageMapType> ProcessesScalarsMapType;
     749             : typedef nsClassHashtable<ProcessIDHashKey, KeyedScalarStorageMapType> ProcessesKeyedScalarsMapType;
     750             : 
     751             : } // namespace
     752             : 
     753             : ////////////////////////////////////////////////////////////////////////
     754             : ////////////////////////////////////////////////////////////////////////
     755             : //
     756             : // PRIVATE STATE, SHARED BY ALL THREADS
     757             : 
     758             : namespace {
     759             : 
     760             : // Set to true once this global state has been initialized.
     761             : bool gInitDone = false;
     762             : 
     763             : bool gCanRecordBase;
     764             : bool gCanRecordExtended;
     765             : 
     766             : // The Name -> ID cache map.
     767           3 : ScalarMapType gScalarNameIDMap(kScalarCount);
     768             : 
     769             : // The (Process Id -> (Scalar ID -> Scalar Object)) map. This is a nsClassHashtable,
     770             : // it owns the scalar instances and takes care of deallocating them when they are
     771             : // removed from the map.
     772           3 : ProcessesScalarsMapType gScalarStorageMap;
     773             : // As above, for the keyed scalars.
     774           3 : ProcessesKeyedScalarsMapType gKeyedScalarStorageMap;
     775             : 
     776             : } // namespace
     777             : 
     778             : ////////////////////////////////////////////////////////////////////////
     779             : ////////////////////////////////////////////////////////////////////////
     780             : //
     781             : // PRIVATE: Function that may call JS code.
     782             : 
     783             : // NOTE: the functions in this section all run without protection from
     784             : // |gTelemetryScalarsMutex|. If they held the mutex, there would be the
     785             : // possibility of deadlock because the JS_ calls that they make may call
     786             : // back into the TelemetryScalar interface, hence trying to re-acquire the mutex.
     787             : //
     788             : // This means that these functions potentially race against threads, but
     789             : // that seems preferable to risking deadlock.
     790             : 
     791             : namespace {
     792             : 
     793             : /**
     794             :  * Converts the error code to a human readable error message and prints it to the
     795             :  * browser console.
     796             :  *
     797             :  * @param aScalarName The name of the scalar that raised the error.
     798             :  * @param aSr The error code.
     799             :  */
     800             : void
     801           0 : internal_LogScalarError(const nsACString& aScalarName, ScalarResult aSr)
     802             : {
     803           0 :   nsAutoString errorMessage;
     804           0 :   AppendUTF8toUTF16(aScalarName, errorMessage);
     805             : 
     806           0 :   switch (aSr) {
     807             :     case ScalarResult::NotInitialized:
     808           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Telemetry was not yet initialized."));
     809           0 :       break;
     810             :     case ScalarResult::CannotUnpackVariant:
     811           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Cannot convert the provided JS value to nsIVariant."));
     812           0 :       break;
     813             :     case ScalarResult::CannotRecordInProcess:
     814           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Cannot record the scalar in the current process."));
     815           0 :       break;
     816             :     case ScalarResult::KeyedTypeMismatch:
     817           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Attempting to manage a keyed scalar as a scalar (or vice-versa)."));
     818           0 :       break;
     819             :     case ScalarResult::UnknownScalar:
     820           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Unknown scalar."));
     821           0 :       break;
     822             :     case ScalarResult::OperationNotSupported:
     823           0 :       errorMessage.Append(NS_LITERAL_STRING(" - The requested operation is not supported on this scalar."));
     824           0 :       break;
     825             :     case ScalarResult::InvalidType:
     826           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Attempted to set the scalar to an invalid data type."));
     827           0 :       break;
     828             :     case ScalarResult::InvalidValue:
     829           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Attempted to set the scalar to an incompatible value."));
     830           0 :       break;
     831             :     case ScalarResult::StringTooLong:
     832           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Truncating scalar value to 50 characters."));
     833           0 :       break;
     834             :     case ScalarResult::KeyIsEmpty:
     835           0 :       errorMessage.Append(NS_LITERAL_STRING(" - The key must not be empty."));
     836           0 :       break;
     837             :     case ScalarResult::KeyTooLong:
     838           0 :       errorMessage.Append(NS_LITERAL_STRING(" - The key length must be limited to 70 characters."));
     839           0 :       break;
     840             :     case ScalarResult::TooManyKeys:
     841           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Keyed scalars cannot have more than 100 keys."));
     842           0 :       break;
     843             :     case ScalarResult::UnsignedNegativeValue:
     844           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Trying to set an unsigned scalar to a negative number."));
     845           0 :       break;
     846             :     case ScalarResult::UnsignedTruncatedValue:
     847           0 :       errorMessage.Append(NS_LITERAL_STRING(" - Truncating float/double number."));
     848           0 :       break;
     849             :     default:
     850             :       // Nothing.
     851           0 :       return;
     852             :   }
     853             : 
     854           0 :   LogToBrowserConsole(nsIScriptError::warningFlag, errorMessage);
     855             : }
     856             : 
     857             : } // namespace
     858             : 
     859             : ////////////////////////////////////////////////////////////////////////
     860             : ////////////////////////////////////////////////////////////////////////
     861             : //
     862             : // PRIVATE: thread-unsafe helpers for the external interface
     863             : 
     864             : namespace {
     865             : 
     866             : bool
     867           5 : internal_CanRecordBase()
     868             : {
     869           5 :   return gCanRecordBase;
     870             : }
     871             : 
     872             : bool
     873           3 : internal_CanRecordExtended()
     874             : {
     875           3 :   return gCanRecordExtended;
     876             : }
     877             : 
     878             : const ScalarInfo&
     879           9 : internal_InfoForScalarID(mozilla::Telemetry::ScalarID aId)
     880             : {
     881           9 :   return gScalars[static_cast<uint32_t>(aId)];
     882             : }
     883             : 
     884             : /**
     885             :  * Check if the given scalar is a keyed scalar.
     886             :  *
     887             :  * @param aId The scalar enum.
     888             :  * @return true if aId refers to a keyed scalar, false otherwise.
     889             :  */
     890             : bool
     891           3 : internal_IsKeyedScalar(mozilla::Telemetry::ScalarID aId)
     892             : {
     893           3 :   return internal_InfoForScalarID(aId).keyed;
     894             : }
     895             : 
     896             : /**
     897             :  * Check if we're allowed to record the given scalar in the current
     898             :  * process.
     899             :  *
     900             :  * @param aId The id of the scalar to check.
     901             :  * @return true if the scalar is allowed to be recorded in the current process, false
     902             :  *         otherwise.
     903             :  */
     904             : bool
     905           3 : internal_CanRecordProcess(mozilla::Telemetry::ScalarID aId)
     906             : {
     907           3 :   const ScalarInfo &info = internal_InfoForScalarID(aId);
     908           3 :   return CanRecordInProcess(info.record_in_processes, XRE_GetProcessType());
     909             : }
     910             : 
     911             : bool
     912           3 : internal_CanRecordForScalarID(mozilla::Telemetry::ScalarID aId)
     913             : {
     914             :   // Get the scalar info from the id.
     915           3 :   const ScalarInfo &info = internal_InfoForScalarID(aId);
     916             : 
     917             :   // Can we record at all?
     918           3 :   bool canRecordBase = internal_CanRecordBase();
     919           3 :   if (!canRecordBase) {
     920           0 :     return false;
     921             :   }
     922             : 
     923           3 :   bool canRecordDataset = CanRecordDataset(info.dataset,
     924             :                                            canRecordBase,
     925           6 :                                            internal_CanRecordExtended());
     926           3 :   if (!canRecordDataset) {
     927           0 :     return false;
     928             :   }
     929             : 
     930           3 :   return true;
     931             : }
     932             : 
     933             : /**
     934             :  * Check if we are allowed to record the provided scalar.
     935             :  *
     936             :  * @param aId The scalar id.
     937             :  * @param aKeyed Are we attempting to write a keyed scalar?
     938             :  * @return ScalarResult::Ok if we can record, an error code otherwise.
     939             :  */
     940             : ScalarResult
     941           3 : internal_CanRecordScalar(mozilla::Telemetry::ScalarID aId, bool aKeyed)
     942             : {
     943             :   // Make sure that we have a keyed scalar if we are trying to change one.
     944           3 :   if (internal_IsKeyedScalar(aId) != aKeyed) {
     945           0 :     return ScalarResult::KeyedTypeMismatch;
     946             :   }
     947             : 
     948             :   // Are we allowed to record this scalar based on the current Telemetry
     949             :   // settings?
     950           3 :   if (!internal_CanRecordForScalarID(aId)) {
     951           0 :     return ScalarResult::CannotRecordDataset;
     952             :   }
     953             : 
     954             :   // Can we record in this process?
     955           3 :   if (!internal_CanRecordProcess(aId)) {
     956           0 :     return ScalarResult::CannotRecordInProcess;
     957             :   }
     958             : 
     959           3 :   return ScalarResult::Ok;
     960             : }
     961             : 
     962             : /**
     963             :  * Get the scalar enum id from the scalar name.
     964             :  *
     965             :  * @param aName The scalar name.
     966             :  * @param aId The output variable to contain the enum.
     967             :  * @return
     968             :  *   NS_ERROR_FAILURE if this was called before init is completed.
     969             :  *   NS_ERROR_INVALID_ARG if the name can't be found in the scalar definitions.
     970             :  *   NS_OK if the scalar was found and aId contains a valid enum id.
     971             :  */
     972             : nsresult
     973           0 : internal_GetEnumByScalarName(const nsACString& aName, mozilla::Telemetry::ScalarID* aId)
     974             : {
     975           0 :   if (!gInitDone) {
     976           0 :     return NS_ERROR_FAILURE;
     977             :   }
     978             : 
     979           0 :   CharPtrEntryType *entry = gScalarNameIDMap.GetEntry(PromiseFlatCString(aName).get());
     980           0 :   if (!entry) {
     981           0 :     return NS_ERROR_INVALID_ARG;
     982             :   }
     983           0 :   *aId = entry->mData;
     984           0 :   return NS_OK;
     985             : }
     986             : 
     987             : /**
     988             :  * Get a scalar object by its enum id. This implicitly allocates the scalar
     989             :  * object in the storage if it wasn't previously allocated.
     990             :  *
     991             :  * @param aId The scalar id.
     992             :  * @param aProcessStorage This drives the selection of the map to use to store
     993             :  *        the scalar data coming from child processes. This is only meaningful when
     994             :  *        this function is called in parent process. If that's the case, if
     995             :  *        this is not |GeckoProcessType_Default|, the process id is used to
     996             :  *        allocate and store the scalars.
     997             :  * @param aRes The output variable that stores scalar object.
     998             :  * @return
     999             :  *   NS_ERROR_INVALID_ARG if the scalar id is unknown.
    1000             :  *   NS_ERROR_NOT_AVAILABLE if the scalar is expired.
    1001             :  *   NS_OK if the scalar was found. If that's the case, aResult contains a
    1002             :  *   valid pointer to a scalar type.
    1003             :  */
    1004             : nsresult
    1005          11 : internal_GetScalarByEnum(mozilla::Telemetry::ScalarID aId,
    1006             :                          ProcessID aProcessStorage,
    1007             :                          ScalarBase** aRet)
    1008             : {
    1009          11 :   if (!IsValidEnumId(aId)) {
    1010           0 :     MOZ_ASSERT(false, "Requested a scalar with an invalid id.");
    1011             :     return NS_ERROR_INVALID_ARG;
    1012             :   }
    1013             : 
    1014          11 :   const uint32_t id = static_cast<uint32_t>(aId);
    1015          11 :   const ScalarInfo &info = gScalars[id];
    1016             : 
    1017          11 :   ScalarBase* scalar = nullptr;
    1018          11 :   ScalarStorageMapType* scalarStorage = nullptr;
    1019             :   // Initialize the scalar storage to the parent storage. This will get
    1020             :   // set to the child storage if needed.
    1021          11 :   uint32_t storageId = static_cast<uint32_t>(aProcessStorage);
    1022             : 
    1023             :   // Get the process-specific storage or create one if it's not
    1024             :   // available.
    1025          11 :   if (!gScalarStorageMap.Get(storageId, &scalarStorage)) {
    1026           2 :     scalarStorage = new ScalarStorageMapType();
    1027           2 :     gScalarStorageMap.Put(storageId, scalarStorage);
    1028             :   }
    1029             : 
    1030             :   // Check if the scalar is already allocated in the parent or in the child storage.
    1031          11 :   if (scalarStorage->Get(id, &scalar)) {
    1032           5 :     *aRet = scalar;
    1033           5 :     return NS_OK;
    1034             :   }
    1035             : 
    1036           6 :   if (IsExpiredVersion(info.expiration())) {
    1037           0 :     return NS_ERROR_NOT_AVAILABLE;
    1038             :   }
    1039             : 
    1040           6 :   scalar = internal_ScalarAllocate(info.kind);
    1041           6 :   if (!scalar) {
    1042           0 :     return NS_ERROR_INVALID_ARG;
    1043             :   }
    1044             : 
    1045           6 :   scalarStorage->Put(id, scalar);
    1046           6 :   *aRet = scalar;
    1047           6 :   return NS_OK;
    1048             : }
    1049             : 
    1050             : /**
    1051             :  * Update the scalar with the provided value. This is used by the JS API.
    1052             :  *
    1053             :  * @param aName The scalar name.
    1054             :  * @param aType The action type for updating the scalar.
    1055             :  * @param aValue The value to use for updating the scalar.
    1056             :  * @return a ScalarResult error value.
    1057             :  */
    1058             : ScalarResult
    1059           0 : internal_UpdateScalar(const nsACString& aName, ScalarActionType aType,
    1060             :                       nsIVariant* aValue)
    1061             : {
    1062             :   mozilla::Telemetry::ScalarID id;
    1063           0 :   nsresult rv = internal_GetEnumByScalarName(aName, &id);
    1064           0 :   if (NS_FAILED(rv)) {
    1065           0 :     return (rv == NS_ERROR_FAILURE) ?
    1066           0 :            ScalarResult::NotInitialized : ScalarResult::UnknownScalar;
    1067             :   }
    1068             : 
    1069           0 :   ScalarResult sr = internal_CanRecordScalar(id, false);
    1070           0 :   if (sr != ScalarResult::Ok) {
    1071           0 :     if (sr == ScalarResult::CannotRecordDataset) {
    1072           0 :       return ScalarResult::Ok;
    1073             :     }
    1074           0 :     return sr;
    1075             :   }
    1076             : 
    1077             :   // Accumulate in the child process if needed.
    1078           0 :   if (!XRE_IsParentProcess()) {
    1079           0 :     const ScalarInfo &info = gScalars[static_cast<uint32_t>(id)];
    1080             :     // Convert the nsIVariant to a Variant.
    1081           0 :     mozilla::Maybe<ScalarVariant> variantValue;
    1082           0 :     sr = GetVariantFromIVariant(aValue, info.kind, variantValue);
    1083           0 :     if (sr != ScalarResult::Ok) {
    1084           0 :       MOZ_ASSERT(false, "Unable to convert nsIVariant to mozilla::Variant.");
    1085             :       return sr;
    1086             :     }
    1087           0 :     TelemetryIPCAccumulator::RecordChildScalarAction(id, aType, variantValue.ref());
    1088           0 :     return ScalarResult::Ok;
    1089             :   }
    1090             : 
    1091             :   // Finally get the scalar.
    1092           0 :   ScalarBase* scalar = nullptr;
    1093           0 :   rv = internal_GetScalarByEnum(id, ProcessID::Parent, &scalar);
    1094           0 :   if (NS_FAILED(rv)) {
    1095             :     // Don't throw on expired scalars.
    1096           0 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
    1097           0 :       return ScalarResult::Ok;
    1098             :     }
    1099           0 :     return ScalarResult::UnknownScalar;
    1100             :   }
    1101             : 
    1102           0 :   if (aType == ScalarActionType::eAdd) {
    1103           0 :     return scalar->AddValue(aValue);
    1104             :   }
    1105           0 :   if (aType == ScalarActionType::eSet) {
    1106           0 :     return scalar->SetValue(aValue);
    1107             :   }
    1108             : 
    1109           0 :   return scalar->SetMaximum(aValue);
    1110             : }
    1111             : 
    1112             : } // namespace
    1113             : 
    1114             : 
    1115             : 
    1116             : ////////////////////////////////////////////////////////////////////////
    1117             : ////////////////////////////////////////////////////////////////////////
    1118             : //
    1119             : // PRIVATE: thread-unsafe helpers for the keyed scalars
    1120             : 
    1121             : namespace {
    1122             : 
    1123             : /**
    1124             :  * Get a keyed scalar object by its enum id. This implicitly allocates the keyed
    1125             :  * scalar object in the storage if it wasn't previously allocated.
    1126             :  *
    1127             :  * @param aId The scalar id.
    1128             :  * @param aProcessStorage This drives the selection of the map to use to store
    1129             :  *        the scalar data coming from child processes. This is only meaningful when
    1130             :  *        this function is called in parent process. If that's the case, if
    1131             :  *        this is not |GeckoProcessType_Default|, the process id is used to
    1132             :  *        allocate and store the scalars.
    1133             :  * @param aRet The output variable that stores scalar object.
    1134             :  * @return
    1135             :  *   NS_ERROR_INVALID_ARG if the scalar id is unknown or a this is a keyed string
    1136             :  *                        scalar.
    1137             :  *   NS_ERROR_NOT_AVAILABLE if the scalar is expired.
    1138             :  *   NS_OK if the scalar was found. If that's the case, aResult contains a
    1139             :  *   valid pointer to a scalar type.
    1140             :  */
    1141             : nsresult
    1142           2 : internal_GetKeyedScalarByEnum(mozilla::Telemetry::ScalarID aId,
    1143             :                               ProcessID aProcessStorage,
    1144             :                               KeyedScalar** aRet)
    1145             : {
    1146           2 :   if (!IsValidEnumId(aId)) {
    1147           0 :     MOZ_ASSERT(false, "Requested a keyed scalar with an invalid id.");
    1148             :     return NS_ERROR_INVALID_ARG;
    1149             :   }
    1150             : 
    1151           2 :   const uint32_t id = static_cast<uint32_t>(aId);
    1152           2 :   const ScalarInfo &info = gScalars[id];
    1153             : 
    1154           2 :   KeyedScalar* scalar = nullptr;
    1155           2 :   KeyedScalarStorageMapType* scalarStorage = nullptr;
    1156             :   // Initialize the scalar storage to the parent storage. This will get
    1157             :   // set to the child storage if needed.
    1158           2 :   uint32_t storageId = static_cast<uint32_t>(aProcessStorage);
    1159             : 
    1160             :   // Get the process-specific storage or create one if it's not
    1161             :   // available.
    1162           2 :   if (!gKeyedScalarStorageMap.Get(storageId, &scalarStorage)) {
    1163           1 :     scalarStorage = new KeyedScalarStorageMapType();
    1164           1 :     gKeyedScalarStorageMap.Put(storageId, scalarStorage);
    1165             :   }
    1166             : 
    1167           2 :   if (scalarStorage->Get(id, &scalar)) {
    1168           1 :     *aRet = scalar;
    1169           1 :     return NS_OK;
    1170             :   }
    1171             : 
    1172           1 :   if (IsExpiredVersion(info.expiration())) {
    1173           0 :     return NS_ERROR_NOT_AVAILABLE;
    1174             :   }
    1175             : 
    1176             :   // We don't currently support keyed string scalars. Disable them.
    1177           1 :   if (info.kind == nsITelemetry::SCALAR_STRING) {
    1178           0 :     MOZ_ASSERT(false, "Keyed string scalars are not currently supported.");
    1179             :     return NS_ERROR_INVALID_ARG;
    1180             :   }
    1181             : 
    1182           1 :   scalar = new KeyedScalar(info.kind);
    1183           1 :   if (!scalar) {
    1184           0 :     return NS_ERROR_INVALID_ARG;
    1185             :   }
    1186             : 
    1187           1 :   scalarStorage->Put(id, scalar);
    1188           1 :   *aRet = scalar;
    1189           1 :   return NS_OK;
    1190             : }
    1191             : 
    1192             : /**
    1193             :  * Update the keyed scalar with the provided value. This is used by the JS API.
    1194             :  *
    1195             :  * @param aName The scalar name.
    1196             :  * @param aKey The key name.
    1197             :  * @param aType The action type for updating the scalar.
    1198             :  * @param aValue The value to use for updating the scalar.
    1199             :  * @return a ScalarResult error value.
    1200             :  */
    1201             : ScalarResult
    1202           0 : internal_UpdateKeyedScalar(const nsACString& aName, const nsAString& aKey,
    1203             :                            ScalarActionType aType, nsIVariant* aValue)
    1204             : {
    1205             :   mozilla::Telemetry::ScalarID id;
    1206           0 :   nsresult rv = internal_GetEnumByScalarName(aName, &id);
    1207           0 :   if (NS_FAILED(rv)) {
    1208           0 :     return (rv == NS_ERROR_FAILURE) ?
    1209           0 :            ScalarResult::NotInitialized : ScalarResult::UnknownScalar;
    1210             :   }
    1211             : 
    1212           0 :   ScalarResult sr = internal_CanRecordScalar(id, true);
    1213           0 :   if (sr != ScalarResult::Ok) {
    1214           0 :     if (sr == ScalarResult::CannotRecordDataset) {
    1215           0 :       return ScalarResult::Ok;
    1216             :     }
    1217           0 :     return sr;
    1218             :   }
    1219             : 
    1220             :   // Accumulate in the child process if needed.
    1221           0 :   if (!XRE_IsParentProcess()) {
    1222           0 :     const ScalarInfo &info = gScalars[static_cast<uint32_t>(id)];
    1223             :     // Convert the nsIVariant to a Variant.
    1224           0 :     mozilla::Maybe<ScalarVariant> variantValue;
    1225           0 :     sr = GetVariantFromIVariant(aValue, info.kind, variantValue);
    1226           0 :     if (sr != ScalarResult::Ok) {
    1227           0 :       MOZ_ASSERT(false, "Unable to convert nsIVariant to mozilla::Variant.");
    1228             :       return sr;
    1229             :     }
    1230           0 :     TelemetryIPCAccumulator::RecordChildKeyedScalarAction(id, aKey, aType, variantValue.ref());
    1231           0 :     return ScalarResult::Ok;
    1232             :   }
    1233             : 
    1234             :   // Finally get the scalar.
    1235           0 :   KeyedScalar* scalar = nullptr;
    1236           0 :   rv = internal_GetKeyedScalarByEnum(id, ProcessID::Parent, &scalar);
    1237           0 :   if (NS_FAILED(rv)) {
    1238             :     // Don't throw on expired scalars.
    1239           0 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
    1240           0 :       return ScalarResult::Ok;
    1241             :     }
    1242           0 :     return ScalarResult::UnknownScalar;
    1243             :   }
    1244             : 
    1245           0 :   if (aType == ScalarActionType::eAdd) {
    1246           0 :     return scalar->AddValue(aKey, aValue);
    1247             :   }
    1248           0 :   if (aType == ScalarActionType::eSet) {
    1249           0 :     return scalar->SetValue(aKey, aValue);
    1250             :   }
    1251             : 
    1252           0 :   return scalar->SetMaximum(aKey, aValue);
    1253             : }
    1254             : 
    1255             : } // namespace
    1256             : 
    1257             : ////////////////////////////////////////////////////////////////////////
    1258             : ////////////////////////////////////////////////////////////////////////
    1259             : //
    1260             : // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryScalars::
    1261             : 
    1262             : // This is a StaticMutex rather than a plain Mutex (1) so that
    1263             : // it gets initialised in a thread-safe manner the first time
    1264             : // it is used, and (2) because it is never de-initialised, and
    1265             : // a normal Mutex would show up as a leak in BloatView.  StaticMutex
    1266             : // also has the "OffTheBooks" property, so it won't show as a leak
    1267             : // in BloatView.
    1268             : // Another reason to use a StaticMutex instead of a plain Mutex is
    1269             : // that, due to the nature of Telemetry, we cannot rely on having a
    1270             : // mutex initialized in InitializeGlobalState. Unfortunately, we
    1271             : // cannot make sure that no other function is called before this point.
    1272           3 : static StaticMutex gTelemetryScalarsMutex;
    1273             : 
    1274             : void
    1275           3 : TelemetryScalar::InitializeGlobalState(bool aCanRecordBase, bool aCanRecordExtended)
    1276             : {
    1277           6 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1278           3 :   MOZ_ASSERT(!gInitDone, "TelemetryScalar::InitializeGlobalState "
    1279             :              "may only be called once");
    1280             : 
    1281           3 :   gCanRecordBase = aCanRecordBase;
    1282           3 :   gCanRecordExtended = aCanRecordExtended;
    1283             : 
    1284             :   // Populate the static scalar name->id cache. Note that the scalar names are
    1285             :   // statically allocated and come from the automatically generated TelemetryScalarData.h.
    1286           3 :   uint32_t scalarCount = static_cast<uint32_t>(mozilla::Telemetry::ScalarID::ScalarCount);
    1287         189 :   for (uint32_t i = 0; i < scalarCount; i++) {
    1288         186 :     CharPtrEntryType *entry = gScalarNameIDMap.PutEntry(gScalars[i].name());
    1289         186 :     entry->mData = static_cast<mozilla::Telemetry::ScalarID>(i);
    1290             :   }
    1291             : 
    1292             : #ifdef DEBUG
    1293           3 :   gScalarNameIDMap.MarkImmutable();
    1294             : #endif
    1295           3 :   gInitDone = true;
    1296           3 : }
    1297             : 
    1298             : void
    1299           0 : TelemetryScalar::DeInitializeGlobalState()
    1300             : {
    1301           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1302           0 :   gCanRecordBase = false;
    1303           0 :   gCanRecordExtended = false;
    1304           0 :   gScalarNameIDMap.Clear();
    1305           0 :   gScalarStorageMap.Clear();
    1306           0 :   gKeyedScalarStorageMap.Clear();
    1307           0 :   gInitDone = false;
    1308           0 : }
    1309             : 
    1310             : void
    1311           0 : TelemetryScalar::SetCanRecordBase(bool b)
    1312             : {
    1313           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1314           0 :   gCanRecordBase = b;
    1315           0 : }
    1316             : 
    1317             : void
    1318           3 : TelemetryScalar::SetCanRecordExtended(bool b) {
    1319           6 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1320           3 :   gCanRecordExtended = b;
    1321           3 : }
    1322             : 
    1323             : /**
    1324             :  * Adds the value to the given scalar.
    1325             :  *
    1326             :  * @param aName The scalar name.
    1327             :  * @param aVal The numeric value to add to the scalar.
    1328             :  * @param aCx The JS context.
    1329             :  * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
    1330             :  *         a warning level message is printed in the browser console.
    1331             :  */
    1332             : nsresult
    1333           0 : TelemetryScalar::Add(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
    1334             : {
    1335             :   // Unpack the aVal to nsIVariant. This uses the JS context.
    1336           0 :   nsCOMPtr<nsIVariant> unpackedVal;
    1337             :   nsresult rv =
    1338           0 :     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal,  getter_AddRefs(unpackedVal));
    1339           0 :   if (NS_FAILED(rv)) {
    1340           0 :     internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
    1341           0 :     return NS_OK;
    1342             :   }
    1343             : 
    1344             :   ScalarResult sr;
    1345             :   {
    1346           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1347           0 :     sr = internal_UpdateScalar(aName, ScalarActionType::eAdd, unpackedVal);
    1348             :   }
    1349             : 
    1350             :   // Warn the user about the error if we need to.
    1351           0 :   if (sr != ScalarResult::Ok) {
    1352           0 :     internal_LogScalarError(aName, sr);
    1353             :   }
    1354             : 
    1355           0 :   return NS_OK;
    1356             : }
    1357             : 
    1358             : /**
    1359             :  * Adds the value to the given scalar.
    1360             :  *
    1361             :  * @param aName The scalar name.
    1362             :  * @param aKey The key name.
    1363             :  * @param aVal The numeric value to add to the scalar.
    1364             :  * @param aCx The JS context.
    1365             :  * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
    1366             :  *         a warning level message is printed in the browser console.
    1367             :  */
    1368             : nsresult
    1369           0 : TelemetryScalar::Add(const nsACString& aName, const nsAString& aKey, JS::HandleValue aVal,
    1370             :                      JSContext* aCx)
    1371             : {
    1372             :   // Unpack the aVal to nsIVariant. This uses the JS context.
    1373           0 :   nsCOMPtr<nsIVariant> unpackedVal;
    1374             :   nsresult rv =
    1375           0 :     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal,  getter_AddRefs(unpackedVal));
    1376           0 :   if (NS_FAILED(rv)) {
    1377           0 :     internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
    1378           0 :     return NS_OK;
    1379             :   }
    1380             : 
    1381             :   ScalarResult sr;
    1382             :   {
    1383           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1384           0 :     sr = internal_UpdateKeyedScalar(aName, aKey, ScalarActionType::eAdd, unpackedVal);
    1385             :   }
    1386             : 
    1387             :   // Warn the user about the error if we need to.
    1388           0 :   if (sr != ScalarResult::Ok) {
    1389           0 :     internal_LogScalarError(aName, sr);
    1390             :   }
    1391             : 
    1392           0 :   return NS_OK;
    1393             : }
    1394             : 
    1395             : /**
    1396             :  * Adds the value to the given scalar.
    1397             :  *
    1398             :  * @param aId The scalar enum id.
    1399             :  * @param aVal The numeric value to add to the scalar.
    1400             :  */
    1401             : void
    1402           0 : TelemetryScalar::Add(mozilla::Telemetry::ScalarID aId, uint32_t aValue)
    1403             : {
    1404           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1405           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1406             :     return;
    1407             :   }
    1408             : 
    1409           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1410             : 
    1411           0 :   if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
    1412             :     // We can't record this scalar. Bail out.
    1413           0 :     return;
    1414             :   }
    1415             : 
    1416             :   // Accumulate in the child process if needed.
    1417           0 :   if (!XRE_IsParentProcess()) {
    1418             :     TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eAdd,
    1419           0 :                                                      ScalarVariant(aValue));
    1420           0 :     return;
    1421             :   }
    1422             : 
    1423           0 :   ScalarBase* scalar = nullptr;
    1424           0 :   nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
    1425           0 :   if (NS_FAILED(rv)) {
    1426           0 :     return;
    1427             :   }
    1428             : 
    1429           0 :   scalar->AddValue(aValue);
    1430             : }
    1431             : 
    1432             : /**
    1433             :  * Adds the value to the given keyed scalar.
    1434             :  *
    1435             :  * @param aId The scalar enum id.
    1436             :  * @param aKey The key name.
    1437             :  * @param aVal The numeric value to add to the scalar.
    1438             :  */
    1439             : void
    1440           0 : TelemetryScalar::Add(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
    1441             :                      uint32_t aValue)
    1442             : {
    1443           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1444           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1445             :     return;
    1446             :   }
    1447             : 
    1448           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1449             : 
    1450           0 :   if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
    1451             :     // We can't record this scalar. Bail out.
    1452           0 :     return;
    1453             :   }
    1454             : 
    1455             :   // Accumulate in the child process if needed.
    1456           0 :   if (!XRE_IsParentProcess()) {
    1457             :     TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
    1458           0 :       aId, aKey, ScalarActionType::eAdd, ScalarVariant(aValue));
    1459           0 :     return;
    1460             :   }
    1461             : 
    1462           0 :   KeyedScalar* scalar = nullptr;
    1463           0 :   nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
    1464           0 :   if (NS_FAILED(rv)) {
    1465           0 :     return;
    1466             :   }
    1467             : 
    1468           0 :   scalar->AddValue(aKey, aValue);
    1469             : }
    1470             : 
    1471             : /**
    1472             :  * Sets the scalar to the given value.
    1473             :  *
    1474             :  * @param aName The scalar name.
    1475             :  * @param aVal The value to set the scalar to.
    1476             :  * @param aCx The JS context.
    1477             :  * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
    1478             :  *         a warning level message is printed in the browser console.
    1479             :  */
    1480             : nsresult
    1481           0 : TelemetryScalar::Set(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
    1482             : {
    1483             :   // Unpack the aVal to nsIVariant. This uses the JS context.
    1484           0 :   nsCOMPtr<nsIVariant> unpackedVal;
    1485             :   nsresult rv =
    1486           0 :     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal,  getter_AddRefs(unpackedVal));
    1487           0 :   if (NS_FAILED(rv)) {
    1488           0 :     internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
    1489           0 :     return NS_OK;
    1490             :   }
    1491             : 
    1492             :   ScalarResult sr;
    1493             :   {
    1494           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1495           0 :     sr = internal_UpdateScalar(aName, ScalarActionType::eSet, unpackedVal);
    1496             :   }
    1497             : 
    1498             :   // Warn the user about the error if we need to.
    1499           0 :   if (sr != ScalarResult::Ok) {
    1500           0 :     internal_LogScalarError(aName, sr);
    1501             :   }
    1502             : 
    1503           0 :   return NS_OK;
    1504             : }
    1505             : 
    1506             : /**
    1507             :  * Sets the keyed scalar to the given value.
    1508             :  *
    1509             :  * @param aName The scalar name.
    1510             :  * @param aKey The key name.
    1511             :  * @param aVal The value to set the scalar to.
    1512             :  * @param aCx The JS context.
    1513             :  * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
    1514             :  *         a warning level message is printed in the browser console.
    1515             :  */
    1516             : nsresult
    1517           0 : TelemetryScalar::Set(const nsACString& aName, const nsAString& aKey, JS::HandleValue aVal,
    1518             :                      JSContext* aCx)
    1519             : {
    1520             :   // Unpack the aVal to nsIVariant. This uses the JS context.
    1521           0 :   nsCOMPtr<nsIVariant> unpackedVal;
    1522             :   nsresult rv =
    1523           0 :     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal,  getter_AddRefs(unpackedVal));
    1524           0 :   if (NS_FAILED(rv)) {
    1525           0 :     internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
    1526           0 :     return NS_OK;
    1527             :   }
    1528             : 
    1529             :   ScalarResult sr;
    1530             :   {
    1531           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1532           0 :     sr = internal_UpdateKeyedScalar(aName, aKey, ScalarActionType::eSet, unpackedVal);
    1533             :   }
    1534             : 
    1535             :   // Warn the user about the error if we need to.
    1536           0 :   if (sr != ScalarResult::Ok) {
    1537           0 :     internal_LogScalarError(aName, sr);
    1538             :   }
    1539             : 
    1540           0 :   return NS_OK;
    1541             : }
    1542             : 
    1543             : /**
    1544             :  * Sets the scalar to the given numeric value.
    1545             :  *
    1546             :  * @param aId The scalar enum id.
    1547             :  * @param aValue The numeric, unsigned value to set the scalar to.
    1548             :  */
    1549             : void
    1550           0 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, uint32_t aValue)
    1551             : {
    1552           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1553           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1554             :     return;
    1555             :   }
    1556             : 
    1557           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1558             : 
    1559           0 :   if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
    1560             :     // We can't record this scalar. Bail out.
    1561           0 :     return;
    1562             :   }
    1563             : 
    1564             :   // Accumulate in the child process if needed.
    1565           0 :   if (!XRE_IsParentProcess()) {
    1566             :     TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSet,
    1567           0 :                                                      ScalarVariant(aValue));
    1568           0 :     return;
    1569             :   }
    1570             : 
    1571           0 :   ScalarBase* scalar = nullptr;
    1572           0 :   nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
    1573           0 :   if (NS_FAILED(rv)) {
    1574           0 :     return;
    1575             :   }
    1576             : 
    1577           0 :   scalar->SetValue(aValue);
    1578             : }
    1579             : 
    1580             : /**
    1581             :  * Sets the scalar to the given string value.
    1582             :  *
    1583             :  * @param aId The scalar enum id.
    1584             :  * @param aValue The string value to set the scalar to.
    1585             :  */
    1586             : void
    1587           0 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, const nsAString& aValue)
    1588             : {
    1589           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1590           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1591             :     return;
    1592             :   }
    1593             : 
    1594           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1595             : 
    1596           0 :   if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
    1597             :     // We can't record this scalar. Bail out.
    1598           0 :     return;
    1599             :   }
    1600             : 
    1601             :   // Accumulate in the child process if needed.
    1602           0 :   if (!XRE_IsParentProcess()) {
    1603             :     TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSet,
    1604           0 :                                                      ScalarVariant(nsString(aValue)));
    1605           0 :     return;
    1606             :   }
    1607             : 
    1608           0 :   ScalarBase* scalar = nullptr;
    1609           0 :   nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
    1610           0 :   if (NS_FAILED(rv)) {
    1611           0 :     return;
    1612             :   }
    1613             : 
    1614           0 :   scalar->SetValue(aValue);
    1615             : }
    1616             : 
    1617             : /**
    1618             :  * Sets the scalar to the given boolean value.
    1619             :  *
    1620             :  * @param aId The scalar enum id.
    1621             :  * @param aValue The boolean value to set the scalar to.
    1622             :  */
    1623             : void
    1624           1 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, bool aValue)
    1625             : {
    1626           1 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1627           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1628             :     return;
    1629             :   }
    1630             : 
    1631           2 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1632             : 
    1633           1 :   if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
    1634             :     // We can't record this scalar. Bail out.
    1635           0 :     return;
    1636             :   }
    1637             : 
    1638             :   // Accumulate in the child process if needed.
    1639           1 :   if (!XRE_IsParentProcess()) {
    1640             :     TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSet,
    1641           0 :                                                      ScalarVariant(aValue));
    1642           0 :     return;
    1643             :   }
    1644             : 
    1645           1 :   ScalarBase* scalar = nullptr;
    1646           1 :   nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
    1647           1 :   if (NS_FAILED(rv)) {
    1648           0 :     return;
    1649             :   }
    1650             : 
    1651           1 :   scalar->SetValue(aValue);
    1652             : }
    1653             : 
    1654             : /**
    1655             :  * Sets the keyed scalar to the given numeric value.
    1656             :  *
    1657             :  * @param aId The scalar enum id.
    1658             :  * @param aKey The scalar key.
    1659             :  * @param aValue The numeric, unsigned value to set the scalar to.
    1660             :  */
    1661             : void
    1662           0 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
    1663             :                      uint32_t aValue)
    1664             : {
    1665           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1666           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1667             :     return;
    1668             :   }
    1669             : 
    1670           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1671             : 
    1672           0 :   if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
    1673             :     // We can't record this scalar. Bail out.
    1674           0 :     return;
    1675             :   }
    1676             : 
    1677             :   // Accumulate in the child process if needed.
    1678           0 :   if (!XRE_IsParentProcess()) {
    1679             :     TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
    1680           0 :       aId, aKey, ScalarActionType::eSet, ScalarVariant(aValue));
    1681           0 :     return;
    1682             :   }
    1683             : 
    1684           0 :   KeyedScalar* scalar = nullptr;
    1685           0 :   nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
    1686           0 :   if (NS_FAILED(rv)) {
    1687           0 :     return;
    1688             :   }
    1689             : 
    1690           0 :   scalar->SetValue(aKey, aValue);
    1691             : }
    1692             : 
    1693             : /**
    1694             :  * Sets the scalar to the given boolean value.
    1695             :  *
    1696             :  * @param aId The scalar enum id.
    1697             :  * @param aKey The scalar key.
    1698             :  * @param aValue The boolean value to set the scalar to.
    1699             :  */
    1700             : void
    1701           2 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
    1702             :                      bool aValue)
    1703             : {
    1704           2 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1705           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1706             :     return;
    1707             :   }
    1708             : 
    1709           4 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1710             : 
    1711           2 :   if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
    1712             :     // We can't record this scalar. Bail out.
    1713           0 :     return;
    1714             :   }
    1715             : 
    1716             :   // Accumulate in the child process if needed.
    1717           2 :   if (!XRE_IsParentProcess()) {
    1718             :     TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
    1719           0 :       aId, aKey, ScalarActionType::eSet, ScalarVariant(aValue));
    1720           0 :     return;
    1721             :   }
    1722             : 
    1723           2 :   KeyedScalar* scalar = nullptr;
    1724           2 :   nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
    1725           2 :   if (NS_FAILED(rv)) {
    1726           0 :     return;
    1727             :   }
    1728             : 
    1729           2 :   scalar->SetValue(aKey, aValue);
    1730             : }
    1731             : 
    1732             : /**
    1733             :  * Sets the scalar to the maximum of the current and the passed value.
    1734             :  *
    1735             :  * @param aName The scalar name.
    1736             :  * @param aVal The numeric value to set the scalar to.
    1737             :  * @param aCx The JS context.
    1738             :  * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
    1739             :  *         a warning level message is printed in the browser console.
    1740             :  */
    1741             : nsresult
    1742           0 : TelemetryScalar::SetMaximum(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
    1743             : {
    1744             :   // Unpack the aVal to nsIVariant. This uses the JS context.
    1745           0 :   nsCOMPtr<nsIVariant> unpackedVal;
    1746             :   nsresult rv =
    1747           0 :     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal,  getter_AddRefs(unpackedVal));
    1748           0 :   if (NS_FAILED(rv)) {
    1749           0 :     internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
    1750           0 :     return NS_OK;
    1751             :   }
    1752             : 
    1753             :   ScalarResult sr;
    1754             :   {
    1755           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1756           0 :     sr = internal_UpdateScalar(aName, ScalarActionType::eSetMaximum, unpackedVal);
    1757             :   }
    1758             : 
    1759             :   // Warn the user about the error if we need to.
    1760           0 :   if (sr != ScalarResult::Ok) {
    1761           0 :     internal_LogScalarError(aName, sr);
    1762             :   }
    1763             : 
    1764           0 :   return NS_OK;
    1765             : }
    1766             : 
    1767             : /**
    1768             :  * Sets the scalar to the maximum of the current and the passed value.
    1769             :  *
    1770             :  * @param aName The scalar name.
    1771             :  * @param aKey The key name.
    1772             :  * @param aVal The numeric value to set the scalar to.
    1773             :  * @param aCx The JS context.
    1774             :  * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
    1775             :  *         a warning level message is printed in the browser console.
    1776             :  */
    1777             : nsresult
    1778           0 : TelemetryScalar::SetMaximum(const nsACString& aName, const nsAString& aKey, JS::HandleValue aVal,
    1779             :                             JSContext* aCx)
    1780             : {
    1781             :   // Unpack the aVal to nsIVariant. This uses the JS context.
    1782           0 :   nsCOMPtr<nsIVariant> unpackedVal;
    1783             :   nsresult rv =
    1784           0 :     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal,  getter_AddRefs(unpackedVal));
    1785           0 :   if (NS_FAILED(rv)) {
    1786           0 :     internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
    1787           0 :     return NS_OK;
    1788             :   }
    1789             : 
    1790             :   ScalarResult sr;
    1791             :   {
    1792           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1793           0 :     sr = internal_UpdateKeyedScalar(aName, aKey, ScalarActionType::eSetMaximum, unpackedVal);
    1794             :   }
    1795             : 
    1796             :   // Warn the user about the error if we need to.
    1797           0 :   if (sr != ScalarResult::Ok) {
    1798           0 :     internal_LogScalarError(aName, sr);
    1799             :   }
    1800             : 
    1801           0 :   return NS_OK;
    1802             : }
    1803             : 
    1804             : /**
    1805             :  * Sets the scalar to the maximum of the current and the passed value.
    1806             :  *
    1807             :  * @param aId The scalar enum id.
    1808             :  * @param aValue The numeric value to set the scalar to.
    1809             :  */
    1810             : void
    1811           0 : TelemetryScalar::SetMaximum(mozilla::Telemetry::ScalarID aId, uint32_t aValue)
    1812             : {
    1813           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1814           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1815             :     return;
    1816             :   }
    1817             : 
    1818           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1819             : 
    1820           0 :   if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
    1821             :     // We can't record this scalar. Bail out.
    1822           0 :     return;
    1823             :   }
    1824             : 
    1825             :   // Accumulate in the child process if needed.
    1826           0 :   if (!XRE_IsParentProcess()) {
    1827             :     TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSetMaximum,
    1828           0 :                                                      ScalarVariant(aValue));
    1829           0 :     return;
    1830             :   }
    1831             : 
    1832           0 :   ScalarBase* scalar = nullptr;
    1833           0 :   nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
    1834           0 :   if (NS_FAILED(rv)) {
    1835           0 :     return;
    1836             :   }
    1837             : 
    1838           0 :   scalar->SetMaximum(aValue);
    1839             : }
    1840             : 
    1841             : /**
    1842             :  * Sets the keyed scalar to the maximum of the current and the passed value.
    1843             :  *
    1844             :  * @param aId The scalar enum id.
    1845             :  * @param aKey The key name.
    1846             :  * @param aValue The numeric value to set the scalar to.
    1847             :  */
    1848             : void
    1849           0 : TelemetryScalar::SetMaximum(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
    1850             :                             uint32_t aValue)
    1851             : {
    1852           0 :   if (NS_WARN_IF(!IsValidEnumId(aId))) {
    1853           0 :     MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    1854             :     return;
    1855             :   }
    1856             : 
    1857           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1858             : 
    1859           0 :   if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
    1860             :     // We can't record this scalar. Bail out.
    1861           0 :     return;
    1862             :   }
    1863             : 
    1864             :   // Accumulate in the child process if needed.
    1865           0 :   if (!XRE_IsParentProcess()) {
    1866             :     TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
    1867           0 :       aId, aKey, ScalarActionType::eSetMaximum, ScalarVariant(aValue));
    1868           0 :     return;
    1869             :   }
    1870             : 
    1871           0 :   KeyedScalar* scalar = nullptr;
    1872           0 :   nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
    1873           0 :   if (NS_FAILED(rv)) {
    1874           0 :     return;
    1875             :   }
    1876             : 
    1877           0 :   scalar->SetMaximum(aKey, aValue);
    1878             : }
    1879             : 
    1880             : /**
    1881             :  * Serializes the scalars from the given dataset to a json-style object and resets them.
    1882             :  * The returned structure looks like:
    1883             :  *    {"process": {"group1.probe":1,"group1.other_probe":false,...}, ... }.
    1884             :  *
    1885             :  * @param aDataset DATASET_RELEASE_CHANNEL_OPTOUT or DATASET_RELEASE_CHANNEL_OPTIN.
    1886             :  * @param aClear Whether to clear out the scalars after snapshotting.
    1887             :  */
    1888             : nsresult
    1889           0 : TelemetryScalar::CreateSnapshots(unsigned int aDataset, bool aClearScalars, JSContext* aCx,
    1890             :                                  uint8_t optional_argc, JS::MutableHandle<JS::Value> aResult)
    1891             : {
    1892           0 :   MOZ_ASSERT(XRE_IsParentProcess(),
    1893             :              "Snapshotting scalars should only happen in the parent processes.");
    1894             :   // If no arguments were passed in, apply the default value.
    1895           0 :   if (!optional_argc) {
    1896           0 :     aClearScalars = false;
    1897             :   }
    1898             : 
    1899           0 :   JS::Rooted<JSObject*> root_obj(aCx, JS_NewPlainObject(aCx));
    1900           0 :   if (!root_obj) {
    1901           0 :     return NS_ERROR_FAILURE;
    1902             :   }
    1903           0 :   aResult.setObject(*root_obj);
    1904             : 
    1905             :   // Return `{}` in child processes.
    1906           0 :   if (!XRE_IsParentProcess()) {
    1907           0 :     return NS_OK;
    1908             :   }
    1909             : 
    1910             :   // Only lock the mutex while accessing our data, without locking any JS related code.
    1911             :   typedef mozilla::Pair<const char*, nsCOMPtr<nsIVariant>> DataPair;
    1912             :   typedef nsTArray<DataPair> ScalarArray;
    1913           0 :   nsDataHashtable<ProcessIDHashKey, ScalarArray> scalarsToReflect;
    1914             :   {
    1915           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    1916             :     // Iterate the scalars in gScalarStorageMap. The storage may contain empty or yet to be
    1917             :     // initialized scalars from all the supported processes.
    1918           0 :     for (auto iter = gScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
    1919           0 :       ScalarStorageMapType* scalarStorage = static_cast<ScalarStorageMapType*>(iter.Data());
    1920           0 :       ScalarArray& processScalars = scalarsToReflect.GetOrInsert(iter.Key());
    1921             : 
    1922             :       // Iterate each available child storage.
    1923           0 :       for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
    1924           0 :         ScalarBase* scalar = static_cast<ScalarBase*>(childIter.Data());
    1925             : 
    1926             :         // Get the informations for this scalar.
    1927           0 :         const ScalarInfo& info = gScalars[childIter.Key()];
    1928             : 
    1929             :         // Serialize the scalar if it's in the desired dataset.
    1930           0 :         if (IsInDataset(info.dataset, aDataset)) {
    1931             :           // Get the scalar value.
    1932           0 :           nsCOMPtr<nsIVariant> scalarValue;
    1933           0 :           nsresult rv = scalar->GetValue(scalarValue);
    1934           0 :           if (NS_FAILED(rv)) {
    1935           0 :             return rv;
    1936             :           }
    1937             :           // Append it to our list.
    1938           0 :           processScalars.AppendElement(mozilla::MakePair(info.name(), scalarValue));
    1939             :         }
    1940             :       }
    1941             :     }
    1942             : 
    1943           0 :     if (aClearScalars) {
    1944             :       // The map already takes care of freeing the allocated memory.
    1945           0 :       gScalarStorageMap.Clear();
    1946             :     }
    1947             :   }
    1948             : 
    1949             :   // Reflect it to JS.
    1950           0 :   for (auto iter = scalarsToReflect.Iter(); !iter.Done(); iter.Next()) {
    1951           0 :     ScalarArray& processScalars = iter.Data();
    1952           0 :     const char* processName = GetNameForProcessID(ProcessID(iter.Key()));
    1953             : 
    1954             :     // Create the object that will hold the scalars for this process and add it
    1955             :     // to the returned root object.
    1956           0 :     JS::RootedObject processObj(aCx, JS_NewPlainObject(aCx));
    1957           0 :     if (!processObj ||
    1958           0 :         !JS_DefineProperty(aCx, root_obj, processName, processObj, JSPROP_ENUMERATE)) {
    1959           0 :       return NS_ERROR_FAILURE;
    1960             :     }
    1961             : 
    1962           0 :     for (nsTArray<DataPair>::size_type i = 0; i < processScalars.Length(); i++) {
    1963           0 :       const DataPair& scalar = processScalars[i];
    1964             : 
    1965             :       // Convert it to a JS Val.
    1966           0 :       JS::Rooted<JS::Value> scalarJsValue(aCx);
    1967             :       nsresult rv =
    1968           0 :         nsContentUtils::XPConnect()->VariantToJS(aCx, processObj, scalar.second(), &scalarJsValue);
    1969           0 :       if (NS_FAILED(rv)) {
    1970           0 :         return rv;
    1971             :       }
    1972             : 
    1973             :       // Add it to the scalar object.
    1974           0 :       if (!JS_DefineProperty(aCx, processObj, scalar.first(), scalarJsValue, JSPROP_ENUMERATE)) {
    1975           0 :         return NS_ERROR_FAILURE;
    1976             :       }
    1977             :     }
    1978             :   }
    1979             : 
    1980           0 :   return NS_OK;
    1981             : }
    1982             : 
    1983             : /**
    1984             :  * Serializes the scalars from the given dataset to a json-style object and resets them.
    1985             :  * The returned structure looks like:
    1986             :  *   { "process": { "group1.probe": { "key_1": 2, "key_2": 1, ... }, ... }, ... }
    1987             :  *
    1988             :  * @param aDataset DATASET_RELEASE_CHANNEL_OPTOUT or DATASET_RELEASE_CHANNEL_OPTIN.
    1989             :  * @param aClear Whether to clear out the keyed scalars after snapshotting.
    1990             :  */
    1991             : nsresult
    1992           0 : TelemetryScalar::CreateKeyedSnapshots(unsigned int aDataset, bool aClearScalars, JSContext* aCx,
    1993             :                                       uint8_t optional_argc, JS::MutableHandle<JS::Value> aResult)
    1994             : {
    1995           0 :   MOZ_ASSERT(XRE_IsParentProcess(),
    1996             :              "Snapshotting scalars should only happen in the parent processes.");
    1997             :   // If no arguments were passed in, apply the default value.
    1998           0 :   if (!optional_argc) {
    1999           0 :     aClearScalars = false;
    2000             :   }
    2001             : 
    2002           0 :   JS::Rooted<JSObject*> root_obj(aCx, JS_NewPlainObject(aCx));
    2003           0 :   if (!root_obj) {
    2004           0 :     return NS_ERROR_FAILURE;
    2005             :   }
    2006           0 :   aResult.setObject(*root_obj);
    2007             : 
    2008             :   // Return `{}` in child processes.
    2009           0 :   if (!XRE_IsParentProcess()) {
    2010           0 :     return NS_OK;
    2011             :   }
    2012             : 
    2013             :   // Only lock the mutex while accessing our data, without locking any JS related code.
    2014             :   typedef mozilla::Pair<const char*, nsTArray<KeyedScalar::KeyValuePair>> DataPair;
    2015             :   typedef nsTArray<DataPair> ScalarArray;
    2016           0 :   nsDataHashtable<ProcessIDHashKey, ScalarArray> scalarsToReflect;
    2017             :   {
    2018           0 :     StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2019             :     // Iterate the scalars in gKeyedScalarStorageMap. The storage may contain empty or yet
    2020             :     // to be initialized scalars from all the supported processes.
    2021           0 :     for (auto iter = gKeyedScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
    2022             :       KeyedScalarStorageMapType* scalarStorage =
    2023           0 :         static_cast<KeyedScalarStorageMapType*>(iter.Data());
    2024           0 :       ScalarArray& processScalars = scalarsToReflect.GetOrInsert(iter.Key());
    2025             : 
    2026           0 :       for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
    2027           0 :         KeyedScalar* scalar = static_cast<KeyedScalar*>(childIter.Data());
    2028             : 
    2029             :         // Get the informations for this scalar.
    2030           0 :         const ScalarInfo& info = gScalars[childIter.Key()];
    2031             : 
    2032             :         // Serialize the scalar if it's in the desired dataset.
    2033           0 :         if (IsInDataset(info.dataset, aDataset)) {
    2034             :           // Get the keys for this scalar.
    2035           0 :           nsTArray<KeyedScalar::KeyValuePair> scalarKeyedData;
    2036           0 :           nsresult rv = scalar->GetValue(scalarKeyedData);
    2037           0 :           if (NS_FAILED(rv)) {
    2038           0 :             return rv;
    2039             :           }
    2040             :           // Append it to our list.
    2041           0 :           processScalars.AppendElement(mozilla::MakePair(info.name(), scalarKeyedData));
    2042             :         }
    2043             :       }
    2044             :     }
    2045             : 
    2046           0 :     if (aClearScalars) {
    2047             :       // The map already takes care of freeing the allocated memory.
    2048           0 :       gKeyedScalarStorageMap.Clear();
    2049             :     }
    2050             :   }
    2051             : 
    2052             :   // Reflect it to JS.
    2053           0 :   for (auto iter = scalarsToReflect.Iter(); !iter.Done(); iter.Next()) {
    2054           0 :     ScalarArray& processScalars = iter.Data();
    2055           0 :     const char* processName = GetNameForProcessID(ProcessID(iter.Key()));
    2056             : 
    2057             :     // Create the object that will hold the scalars for this process and add it
    2058             :     // to the returned root object.
    2059           0 :     JS::RootedObject processObj(aCx, JS_NewPlainObject(aCx));
    2060           0 :     if (!processObj ||
    2061           0 :         !JS_DefineProperty(aCx, root_obj, processName, processObj, JSPROP_ENUMERATE)) {
    2062           0 :       return NS_ERROR_FAILURE;
    2063             :     }
    2064             : 
    2065           0 :     for (nsTArray<DataPair>::size_type i = 0; i < processScalars.Length(); i++) {
    2066           0 :       const DataPair& keyedScalarData = processScalars[i];
    2067             : 
    2068             :       // Go through each keyed scalar and create a keyed scalar object.
    2069             :       // This object will hold the values for all the keyed scalar keys.
    2070           0 :       JS::RootedObject keyedScalarObj(aCx, JS_NewPlainObject(aCx));
    2071             : 
    2072             :       // Define a property for each scalar key, then add it to the keyed scalar
    2073             :       // object.
    2074           0 :       const nsTArray<KeyedScalar::KeyValuePair>& keyProps = keyedScalarData.second();
    2075           0 :       for (uint32_t i = 0; i < keyProps.Length(); i++) {
    2076           0 :         const KeyedScalar::KeyValuePair& keyData = keyProps[i];
    2077             : 
    2078             :         // Convert the value for the key to a JSValue.
    2079           0 :         JS::Rooted<JS::Value> keyJsValue(aCx);
    2080             :         nsresult rv =
    2081           0 :           nsContentUtils::XPConnect()->VariantToJS(aCx, keyedScalarObj, keyData.second(), &keyJsValue);
    2082           0 :         if (NS_FAILED(rv)) {
    2083           0 :           return rv;
    2084             :         }
    2085             : 
    2086             :         // Add the key to the scalar representation.
    2087           0 :         const NS_ConvertUTF8toUTF16 key(keyData.first());
    2088           0 :         if (!JS_DefineUCProperty(aCx, keyedScalarObj, key.Data(), key.Length(), keyJsValue, JSPROP_ENUMERATE)) {
    2089           0 :           return NS_ERROR_FAILURE;
    2090             :         }
    2091             :       }
    2092             : 
    2093             :       // Add the scalar to the root object.
    2094           0 :       if (!JS_DefineProperty(aCx, processObj, keyedScalarData.first(), keyedScalarObj, JSPROP_ENUMERATE)) {
    2095           0 :         return NS_ERROR_FAILURE;
    2096             :       }
    2097             :     }
    2098             :   }
    2099             : 
    2100           0 :   return NS_OK;
    2101             : }
    2102             : 
    2103             : /**
    2104             :  * Resets all the stored scalars. This is intended to be only used in tests.
    2105             :  */
    2106             : void
    2107           0 : TelemetryScalar::ClearScalars()
    2108             : {
    2109           0 :   MOZ_ASSERT(XRE_IsParentProcess(), "Scalars should only be cleared in the parent process.");
    2110           0 :   if (!XRE_IsParentProcess()) {
    2111           0 :     return;
    2112             :   }
    2113             : 
    2114           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2115           0 :   gScalarStorageMap.Clear();
    2116           0 :   gKeyedScalarStorageMap.Clear();
    2117             : }
    2118             : 
    2119             : size_t
    2120           0 : TelemetryScalar::GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
    2121             : {
    2122           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2123           0 :   return gScalarNameIDMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
    2124             : }
    2125             : 
    2126             : size_t
    2127           0 : TelemetryScalar::GetScalarSizesOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
    2128             : {
    2129           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2130           0 :   size_t n = 0;
    2131             :   // Account for scalar data coming from parent and child processes.
    2132           0 :   for (auto iter = gScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
    2133           0 :     ScalarStorageMapType* scalarStorage = static_cast<ScalarStorageMapType*>(iter.Data());
    2134           0 :     for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
    2135           0 :       ScalarBase* scalar = static_cast<ScalarBase*>(childIter.Data());
    2136           0 :       n += scalar->SizeOfIncludingThis(aMallocSizeOf);
    2137             :     }
    2138             :   }
    2139             :   // Also account for keyed scalar data coming from parent and child processes.
    2140           0 :   for (auto iter = gKeyedScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
    2141             :     KeyedScalarStorageMapType* scalarStorage =
    2142           0 :       static_cast<KeyedScalarStorageMapType*>(iter.Data());
    2143           0 :     for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
    2144           0 :       KeyedScalar* scalar = static_cast<KeyedScalar*>(childIter.Data());
    2145           0 :       n += scalar->SizeOfIncludingThis(aMallocSizeOf);
    2146             :     }
    2147             :   }
    2148           0 :   return n;
    2149             : }
    2150             : 
    2151             : void
    2152           0 : TelemetryScalar::UpdateChildData(ProcessID aProcessType,
    2153             :                                  const nsTArray<mozilla::Telemetry::ScalarAction>& aScalarActions)
    2154             : {
    2155           0 :   MOZ_ASSERT(XRE_IsParentProcess(),
    2156             :              "The stored child processes scalar data must be updated from the parent process.");
    2157           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2158           0 :   if (!internal_CanRecordBase()) {
    2159           0 :     return;
    2160             :   }
    2161             : 
    2162           0 :   for (auto& upd : aScalarActions) {
    2163           0 :     if (NS_WARN_IF(!IsValidEnumId(upd.mId))) {
    2164           0 :       MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    2165           0 :       continue;
    2166             :     }
    2167             : 
    2168           0 :     if (internal_IsKeyedScalar(upd.mId)) {
    2169           0 :       continue;
    2170             :     }
    2171             : 
    2172             :     // Are we allowed to record this scalar? We don't need to check for
    2173             :     // allowed processes here, that's taken care of when recording
    2174             :     // in child processes.
    2175           0 :     if (!internal_CanRecordForScalarID(upd.mId)) {
    2176           0 :       continue;
    2177             :     }
    2178             : 
    2179             :     // Refresh the data in the parent process with the data coming from the child
    2180             :     // processes.
    2181           0 :     ScalarBase* scalar = nullptr;
    2182           0 :     nsresult rv = internal_GetScalarByEnum(upd.mId, aProcessType, &scalar);
    2183           0 :     if (NS_FAILED(rv)) {
    2184           0 :       NS_WARNING("NS_FAILED internal_GetScalarByEnum for CHILD");
    2185           0 :       continue;
    2186             :     }
    2187             : 
    2188           0 :     if (upd.mData.isNothing()) {
    2189           0 :       MOZ_ASSERT(false, "There is no data in the ScalarActionType.");
    2190             :       continue;
    2191             :     }
    2192             : 
    2193             :     // Get the type of this scalar from the scalar ID. We already checked
    2194             :     // for its validity a few lines above.
    2195           0 :     const uint32_t scalarType = gScalars[static_cast<uint32_t>(upd.mId)].kind;
    2196             : 
    2197             :     // Extract the data from the mozilla::Variant.
    2198           0 :     switch (upd.mActionType)
    2199             :     {
    2200             :       case ScalarActionType::eSet:
    2201             :         {
    2202           0 :           switch (scalarType)
    2203             :           {
    2204             :             case nsITelemetry::SCALAR_COUNT:
    2205           0 :               scalar->SetValue(upd.mData->as<uint32_t>());
    2206           0 :               break;
    2207             :             case nsITelemetry::SCALAR_BOOLEAN:
    2208           0 :               scalar->SetValue(upd.mData->as<bool>());
    2209           0 :               break;
    2210             :             case nsITelemetry::SCALAR_STRING:
    2211           0 :               scalar->SetValue(upd.mData->as<nsString>());
    2212           0 :               break;
    2213             :           }
    2214           0 :           break;
    2215             :         }
    2216             :       case ScalarActionType::eAdd:
    2217             :         {
    2218           0 :           if (scalarType != nsITelemetry::SCALAR_COUNT) {
    2219           0 :             NS_WARNING("Attempting to add on a non count scalar.");
    2220           0 :             continue;
    2221             :           }
    2222             :           // We only support adding uint32_t.
    2223           0 :           scalar->AddValue(upd.mData->as<uint32_t>());
    2224           0 :           break;
    2225             :         }
    2226             :       case ScalarActionType::eSetMaximum:
    2227             :         {
    2228           0 :           if (scalarType != nsITelemetry::SCALAR_COUNT) {
    2229           0 :             NS_WARNING("Attempting to add on a non count scalar.");
    2230           0 :             continue;
    2231             :           }
    2232             :           // We only support SetMaximum on uint32_t.
    2233           0 :           scalar->SetMaximum(upd.mData->as<uint32_t>());
    2234           0 :           break;
    2235             :         }
    2236             :       default:
    2237           0 :         NS_WARNING("Unsupported action coming from scalar child updates.");
    2238             :     }
    2239             :   }
    2240             : }
    2241             : 
    2242             : void
    2243           0 : TelemetryScalar::UpdateChildKeyedData(ProcessID aProcessType,
    2244             :                                       const nsTArray<mozilla::Telemetry::KeyedScalarAction>& aScalarActions)
    2245             : {
    2246           0 :   MOZ_ASSERT(XRE_IsParentProcess(),
    2247             :              "The stored child processes keyed scalar data must be updated from the parent process.");
    2248           0 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2249           0 :   if (!internal_CanRecordBase()) {
    2250           0 :     return;
    2251             :   }
    2252             : 
    2253           0 :   for (auto& upd : aScalarActions) {
    2254           0 :     if (NS_WARN_IF(!IsValidEnumId(upd.mId))) {
    2255           0 :       MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
    2256           0 :       continue;
    2257             :     }
    2258             : 
    2259           0 :     if (!internal_IsKeyedScalar(upd.mId)) {
    2260           0 :       continue;
    2261             :     }
    2262             : 
    2263             :     // Are we allowed to record this scalar? We don't need to check for
    2264             :     // allowed processes here, that's taken care of when recording
    2265             :     // in child processes.
    2266           0 :     if (!internal_CanRecordForScalarID(upd.mId)) {
    2267           0 :       continue;
    2268             :     }
    2269             : 
    2270             :     // Refresh the data in the parent process with the data coming from the child
    2271             :     // processes.
    2272           0 :     KeyedScalar* scalar = nullptr;
    2273           0 :     nsresult rv = internal_GetKeyedScalarByEnum(upd.mId, aProcessType, &scalar);
    2274           0 :     if (NS_FAILED(rv)) {
    2275           0 :       NS_WARNING("NS_FAILED internal_GetScalarByEnum for CHILD");
    2276           0 :       continue;
    2277             :     }
    2278             : 
    2279           0 :     if (upd.mData.isNothing()) {
    2280           0 :       MOZ_ASSERT(false, "There is no data in the KeyedScalarAction.");
    2281             :       continue;
    2282             :     }
    2283             : 
    2284             :     // Get the type of this scalar from the scalar ID. We already checked
    2285             :     // for its validity a few lines above.
    2286           0 :     const uint32_t scalarType = gScalars[static_cast<uint32_t>(upd.mId)].kind;
    2287             : 
    2288             :     // Extract the data from the mozilla::Variant.
    2289           0 :     switch (upd.mActionType)
    2290             :     {
    2291             :       case ScalarActionType::eSet:
    2292             :         {
    2293           0 :           switch (scalarType)
    2294             :           {
    2295             :             case nsITelemetry::SCALAR_COUNT:
    2296           0 :               scalar->SetValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
    2297           0 :               break;
    2298             :             case nsITelemetry::SCALAR_BOOLEAN:
    2299           0 :               scalar->SetValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<bool>());
    2300           0 :               break;
    2301             :             default:
    2302           0 :               NS_WARNING("Unsupported type coming from scalar child updates.");
    2303             :           }
    2304           0 :           break;
    2305             :         }
    2306             :       case ScalarActionType::eAdd:
    2307             :         {
    2308           0 :           if (scalarType != nsITelemetry::SCALAR_COUNT) {
    2309           0 :             NS_WARNING("Attempting to add on a non count scalar.");
    2310           0 :             continue;
    2311             :           }
    2312             :           // We only support adding on uint32_t.
    2313           0 :           scalar->AddValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
    2314           0 :           break;
    2315             :         }
    2316             :       case ScalarActionType::eSetMaximum:
    2317             :         {
    2318           0 :           if (scalarType != nsITelemetry::SCALAR_COUNT) {
    2319           0 :             NS_WARNING("Attempting to add on a non count scalar.");
    2320           0 :             continue;
    2321             :           }
    2322             :           // We only support SetMaximum on uint32_t.
    2323           0 :           scalar->SetMaximum(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
    2324           0 :           break;
    2325             :         }
    2326             :       default:
    2327           0 :         NS_WARNING("Unsupported action coming from keyed scalar child updates.");
    2328             :     }
    2329             :   }
    2330             : }
    2331             : 
    2332             : void
    2333           2 : TelemetryScalar::RecordDiscardedData(ProcessID aProcessType,
    2334             :                                      const mozilla::Telemetry::DiscardedData& aDiscardedData)
    2335             : {
    2336           2 :   MOZ_ASSERT(XRE_IsParentProcess(),
    2337             :              "Discarded Data must be updated from the parent process.");
    2338           4 :   StaticMutexAutoLock locker(gTelemetryScalarsMutex);
    2339           2 :   if (!internal_CanRecordBase()) {
    2340           0 :     return;
    2341             :   }
    2342             : 
    2343           2 :   ScalarBase* scalar = nullptr;
    2344           4 :   mozilla::DebugOnly<nsresult> rv;
    2345             : 
    2346           4 :   rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_ACCUMULATIONS,
    2347           2 :                                 aProcessType, &scalar);
    2348           2 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2349           2 :   scalar->AddValue(aDiscardedData.mDiscardedHistogramAccumulations);
    2350             : 
    2351           4 :   rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_KEYED_ACCUMULATIONS,
    2352           2 :                                 aProcessType, &scalar);
    2353           2 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2354           2 :   scalar->AddValue(aDiscardedData.mDiscardedKeyedHistogramAccumulations);
    2355             : 
    2356           4 :   rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_SCALAR_ACTIONS,
    2357           2 :                                 aProcessType, &scalar);
    2358           2 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2359           2 :   scalar->AddValue(aDiscardedData.mDiscardedScalarActions);
    2360             : 
    2361           4 :   rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_KEYED_SCALAR_ACTIONS,
    2362           2 :                                 aProcessType, &scalar);
    2363           2 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2364           2 :   scalar->AddValue(aDiscardedData.mDiscardedKeyedScalarActions);
    2365             : 
    2366           4 :   rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_CHILD_EVENTS,
    2367           2 :                                 aProcessType, &scalar);
    2368           2 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2369           2 :   scalar->AddValue(aDiscardedData.mDiscardedChildEvents);
    2370           9 : }

Generated by: LCOV version 1.13