# Global Metrics

path: .metrics.nargs.sum
old: 1.0
new: 18.0
path: .metrics.nargs.average
old: 0.3333333333333333
new: 1.8
path: .metrics.nom.functions
old: 3.0
new: 10.0
path: .metrics.nom.total
old: 3.0
new: 10.0
path: .metrics.cyclomatic.sum
old: 15.0
new: 41.0
path: .metrics.cyclomatic.average
old: 1.5
new: 3.1538461538461537
path: .metrics.halstead.N2
old: 132.0
new: 488.0
path: .metrics.halstead.N1
old: 165.0
new: 681.0
path: .metrics.halstead.length
old: 297.0
new: 1169.0
path: .metrics.halstead.n1
old: 15.0
new: 27.0
path: .metrics.halstead.level
old: 0.09494949494949496
new: 0.027929568913175475
path: .metrics.halstead.bugs
old: 0.25509545681471807
new: 1.569754314751954
path: .metrics.halstead.estimated_program_length
old: 674.7347109918256
new: 1512.717362472904
path: .metrics.halstead.purity_ratio
old: 2.2718340437435205
new: 1.2940268284627066
path: .metrics.halstead.difficulty
old: 10.53191489361702
new: 35.80434782608695
path: .metrics.halstead.vocabulary
old: 109.0
new: 211.0
path: .metrics.halstead.volume
old: 2010.150744458747
new: 9025.9649515987
path: .metrics.halstead.time
old: 1176.1520313322455
new: 17953.821588506107
path: .metrics.halstead.n2
old: 94.0
new: 184.0
path: .metrics.halstead.effort
old: 21170.73656398042
new: 323168.78859310993
path: .metrics.loc.cloc
old: 60.0
new: 27.0
path: .metrics.loc.lloc
old: 0.0
new: 95.0
path: .metrics.loc.sloc
old: 166.0
new: 323.0
path: .metrics.loc.ploc
old: 75.0
new: 244.0
path: .metrics.loc.blank
old: 31.0
new: 52.0
path: .metrics.cognitive.average
old: 0.0
new: 3.4
path: .metrics.cognitive.sum
old: 0.0
new: 34.0
path: .metrics.mi.mi_sei
old: 31.136531948289665
new: -20.13670153446013
path: .metrics.mi.mi_visual_studio
old: 26.423847866728956
new: 12.053308033728328
path: .metrics.mi.mi_original
old: 45.18477985210651
new: 20.611156737675444
path: .metrics.nexits.sum
old: 0.0
new: 23.0
path: .metrics.nexits.average
old: 0.0
new: 2.3

# Spaces Data

## Minimal test - lines (43, 322)

path: .spaces[0].spaces[0].metrics.loc.ploc
old: 61.0
new: 213.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 144.0
new: 280.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 53.0
new: 19.0
path: .spaces[0].spaces[0].metrics.loc.blank
old: 30.0
new: 48.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 95.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 587.7316317359225
new: 1291.130352448626
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 10.843373493975903
new: 39.056603773584904
path: .spaces[0].spaces[0].metrics.halstead.length
old: 284.0
new: 1138.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 83.0
new: 159.0
path: .spaces[0].spaces[0].metrics.halstead.time
old: 1131.6732504389877
new: 18616.032332258637
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 98.0
new: 186.0
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 1878.5775957287192
new: 8579.562727040939
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 120.0
new: 460.0
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 164.0
new: 678.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.2486228930292329
new: 1.6081201671843095
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 2.0694775765349385
new: 1.1345609423977383
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 15.0
new: 27.0
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 20370.118507901778
new: 335088.5819806555
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.09222222222222222
new: 0.02560386473429952
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 23.0
path: .spaces[0].spaces[0].metrics.nexits.average
old: 0.0
new: 2.3
path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.3333333333333333
new: 1.8
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 1.0
new: 18.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 3.0
new: 10.0
path: .spaces[0].spaces[0].metrics.nom.functions
old: 3.0
new: 10.0
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 28.245625529454458
new: 13.829993946444242
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 48.30001965536712
new: 23.649289648419657
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 35.67872641645842
new: -17.976623491671823
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 13.0
new: 39.0
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.625
new: 3.5454545454545454
path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 34.0
path: .spaces[0].spaces[0].metrics.cognitive.average
old: 0.0
new: 3.4

### Code

    namespace dom {

    using namespace fuzzing;
    using namespace ipc;

    /* static */
    void MessageManagerFuzzer::ReadFile(const char* path,
                                        nsTArray& aArray) {
      nsCOMPtr file;
      nsresult rv =
          NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), true, getter_AddRefs(file));
      NS_ENSURE_SUCCESS_VOID(rv);

      bool exists = false;
      rv = file->Exists(&exists);
      if (NS_FAILED(rv) || !exists) {
        return;
      }

      nsCOMPtr fileStream(
          do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
      NS_ENSURE_SUCCESS_VOID(rv);

      rv = fileStream->Init(file, -1, -1, false);
      NS_ENSURE_SUCCESS_VOID(rv);

      nsCOMPtr lineStream(do_QueryInterface(fileStream, &rv));
      NS_ENSURE_SUCCESS_VOID(rv);

      nsAutoCString line;
      bool more = true;
      do {
        rv = lineStream->ReadLine(line, &more);
        NS_ENSURE_SUCCESS_VOID(rv);
        aArray.AppendElement(line);
      } while (more);
    }

    /* static */
    bool MessageManagerFuzzer::IsMessageNameBlacklisted(
        const nsAString& aMessageName) {
      static bool sFileLoaded = false;
      static nsTArray valuesInFile;

      if (!sFileLoaded) {
        ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_BLACKLIST"), valuesInFile);
        sFileLoaded = true;
      }

      if (valuesInFile.Length() == 0) {
        return false;
      }

      return valuesInFile.Contains(NS_ConvertUTF16toUTF8(aMessageName).get());
    }

    /* static */
    nsCString MessageManagerFuzzer::GetFuzzValueFromFile() {
      static bool sFileLoaded = false;
      static nsTArray valuesInFile;

      if (!sFileLoaded) {
        ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_STRINGSFILE"), valuesInFile);
        sFileLoaded = true;
      }

      // If something goes wrong with importing the file we return an empty string.
      if (valuesInFile.Length() == 0) {
        return nsCString();
      }

      unsigned randIdx = RandomIntegerRange(0, valuesInFile.Length());
      return valuesInFile.ElementAt(randIdx);
    }

    /* static */
    void MessageManagerFuzzer::MutateObject(JSContext* aCx, JS::HandleValue aValue,
                                            unsigned short int aRecursionCounter) {
      JS::Rooted object(aCx, &aValue.toObject());
      JS::Rooted ids(aCx, JS::IdVector(aCx));

      if (!JS_Enumerate(aCx, object, &ids)) {
        return;
      }

      for (size_t i = 0, n = ids.length(); i < n; i++) {
        // Retrieve Property name.
        nsAutoJSString propName;
        if (!propName.init(aCx, ids[i])) {
          continue;
        }
        MSGMGR_FUZZER_LOG("%*s- Property: %s", aRecursionCounter * 4, "",
                          NS_ConvertUTF16toUTF8(propName).get());

        // The likelihood when a value gets fuzzed of this object.
        if (!FuzzingTraits::Sometimes(DefaultMutationProbability())) {
          continue;
        }

        // Retrieve Property value.
        JS::RootedValue propertyValue(aCx);
        JS_GetPropertyById(aCx, object, ids[i], &propertyValue);

        JS::RootedValue newPropValue(aCx);
        MutateValue(aCx, propertyValue, &newPropValue, aRecursionCounter);

        JS_SetPropertyById(aCx, object, ids[i], newPropValue);
      }
    }

    /* static */
    bool MessageManagerFuzzer::MutateValue(JSContext* aCx, JS::HandleValue aValue,
                                           JS::MutableHandleValue aOutMutationValue,
                                           unsigned short int aRecursionCounter) {
      if (aValue.isInt32()) {
        if (FuzzingTraits::Sometimes(DefaultMutationProbability() * 2)) {
          aOutMutationValue.set(JS::Int32Value(RandomNumericLimit()));
        } else {
          aOutMutationValue.set(JS::Int32Value(RandomInteger()));
        }
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |int32|: '%d' to '%d'",
                          aRecursionCounter * 4, "", aValue.toInt32(),
                          aOutMutationValue.toInt32());
        return true;
      }

      if (aValue.isDouble()) {
        aOutMutationValue.set(JS::DoubleValue(RandomFloatingPoint()));
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |double|: '%f' to '%f'",
                          aRecursionCounter * 4, "", aValue.toDouble(),
                          aOutMutationValue.toDouble());
        return true;
      }

      if (aValue.isBoolean()) {
        aOutMutationValue.set(JS::BooleanValue(bool(RandomIntegerRange(0, 2))));
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |boolean|: '%d' to '%d'",
                          aRecursionCounter * 4, "", aValue.toBoolean(),
                          aOutMutationValue.toBoolean());
        return true;
      }

      if (aValue.isString()) {
        nsCString x = GetFuzzValueFromFile();
        if (x.IsEmpty()) {
          return false;
        }
        JSString* str = JS_NewStringCopyZ(aCx, x.get());
        aOutMutationValue.set(JS::StringValue(str));
        JS::RootedString rootedValue(aCx, aValue.toString());
        JS::UniqueChars valueChars = JS_EncodeStringToUTF8(aCx, rootedValue);
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |string|: '%s' to '%s'",
                          aRecursionCounter * 4, "", valueChars.get(), x.get());
        return true;
      }

      if (aValue.isObject()) {
        aRecursionCounter++;
        MSGMGR_FUZZER_LOG("%*s", aRecursionCounter * 4,
                          "");
        MutateObject(aCx, aValue, aRecursionCounter);
        aOutMutationValue.set(aValue);
        return true;
      }

      return false;
    }

    /* static */
    bool MessageManagerFuzzer::Mutate(JSContext* aCx, const nsAString& aMessageName,
                                      ipc::StructuredCloneData* aData,
                                      const JS::Value& aTransfer) {
      MSGMGR_FUZZER_LOG("Message: %s in process: %d",
                        NS_ConvertUTF16toUTF8(aMessageName).get(),
                        XRE_GetProcessType());

      unsigned short int aRecursionCounter = 0;
      ErrorResult rv;
      JS::RootedValue t(aCx, aTransfer);

      /* Read original StructuredCloneData. */
      JS::RootedValue scdContent(aCx);
      aData->Read(aCx, &scdContent, rv);
      if (NS_WARN_IF(rv.Failed())) {
        rv.SuppressException();
        JS_ClearPendingException(aCx);
        return false;
      }

      JS::RootedValue scdMutationContent(aCx);
      bool isMutated =
          MutateValue(aCx, scdContent, &scdMutationContent, aRecursionCounter);

      /* Write mutated StructuredCloneData. */
      ipc::StructuredCloneData mutatedStructuredCloneData;
      mutatedStructuredCloneData.Write(aCx, scdMutationContent, t,
                                       JS::CloneDataPolicy(), rv);
      if (NS_WARN_IF(rv.Failed())) {
        rv.SuppressException();
        JS_ClearPendingException(aCx);
        return false;
      }

      // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1346040
      aData->Copy(mutatedStructuredCloneData);

      /* Mutated and successfully written to StructuredCloneData object. */
      if (isMutated) {
        JS::RootedString str(aCx, JS_ValueToSource(aCx, scdMutationContent));
        JS::UniqueChars strChars = JS_EncodeStringToUTF8(aCx, str);
        MSGMGR_FUZZER_LOG("Mutated '%s' Message: %s",
                          NS_ConvertUTF16toUTF8(aMessageName).get(),
                          strChars.get());
      }

      return true;
    }

    /* static */
    unsigned int MessageManagerFuzzer::DefaultMutationProbability() {
      static unsigned long sPropValue =
          MESSAGEMANAGER_FUZZER_DEFAULT_MUTATION_PROBABILITY;
      static bool sInitialized = false;

      if (sInitialized) {
        return sPropValue;
      }
      sInitialized = true;

      // Defines the likelihood of fuzzing a message.
      const char* probability =
          PR_GetEnv("MESSAGEMANAGER_FUZZER_MUTATION_PROBABILITY");
      if (probability) {
        long n = std::strtol(probability, nullptr, 10);
        if (n != 0) {
          sPropValue = n;
          return sPropValue;
        }
      }

      return sPropValue;
    }

    /* static */
    bool MessageManagerFuzzer::IsLoggingEnabled() {
      static bool sInitialized = false;
      static bool sIsLoggingEnabled = false;

      if (!sInitialized) {
        sIsLoggingEnabled = !!PR_GetEnv("MESSAGEMANAGER_FUZZER_ENABLE_LOGGING");
        sInitialized = true;
      }

      return sIsLoggingEnabled;
    }

    /* static */
    bool MessageManagerFuzzer::IsEnabled() {
      return !!PR_GetEnv("MESSAGEMANAGER_FUZZER_ENABLE") && XRE_IsContentProcess();
    }

    /* static */
    void MessageManagerFuzzer::TryMutate(JSContext* aCx,
                                         const nsAString& aMessageName,
                                         ipc::StructuredCloneData* aData,
                                         const JS::Value& aTransfer) {
      if (!IsEnabled()) {
        return;
      }

      if (IsMessageNameBlacklisted(aMessageName)) {
        MSGMGR_FUZZER_LOG("Blacklisted message: %s",
                          NS_ConvertUTF16toUTF8(aMessageName).get());
        return;
      }

      Mutate(aCx, aMessageName, aData, aTransfer);
    }

    }  // namespace dom

## Minimal test - lines (119, 151)

path: .spaces[0].spaces[0].spaces[3].metrics.mi.mi_original
old: 85.33504329003327
new: 77.27385721915498
path: .spaces[0].spaces[0].spaces[3].metrics.mi.mi_visual_studio
old: 49.90353408773875
new: 45.18939018664034
path: .spaces[0].spaces[0].spaces[3].metrics.mi.mi_sei
old: 68.74918323500764
new: 58.805771452490326
path: .spaces[0].spaces[0].spaces[3].metrics.nom.functions
old: 2.0
new: 1.0
path: .spaces[0].spaces[0].spaces[3].metrics.nom.total
old: 2.0
new: 1.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.ploc
old: 15.0
new: 24.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.blank
old: 9.0
new: 6.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.lloc
old: 0.0
new: 11.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.sloc
old: 26.0
new: 33.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.cloc
old: 2.0
new: 3.0
path: .spaces[0].spaces[0].spaces[3].metrics.cyclomatic.average
old: 1.6666666666666667
new: 5.0
path: .spaces[0].spaces[0].spaces[3].metrics.nexits.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[3].metrics.nexits.average
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[3].metrics.cognitive.sum
old: 0.0
new: 6.0
path: .spaces[0].spaces[0].spaces[3].metrics.cognitive.average
old: 0.0
new: 6.0
path: .spaces[0].spaces[0].spaces[3].metrics.nargs.sum
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[3].metrics.nargs.average
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.estimated_program_length
old: 174.6997636784959
new: 253.68435935793968
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.bugs
old: 0.08985337630790075
new: 0.242361658278911
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.time
old: 245.87307798379615
new: 1089.1940243460658
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.effort
old: 4425.715403708331
new: 19605.492438229183
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.n1
old: 15.0
new: 19.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.N1
old: 51.0
new: 105.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.level
old: 0.101010101010101
new: 0.05112781954887218
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.n2
old: 25.0
new: 34.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.purity_ratio
old: 2.079759091410666
new: 1.449624910616798
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.volume
old: 447.04195997053847
new: 1002.3860795485598
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.vocabulary
old: 40.0
new: 53.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.difficulty
old: 9.9
new: 19.558823529411764
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.length
old: 84.0
new: 175.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.N2
old: 33.0
new: 70.0

### Code

    void MessageManagerFuzzer::MutateObject(JSContext* aCx, JS::HandleValue aValue,
                                            unsigned short int aRecursionCounter) {
      JS::Rooted object(aCx, &aValue.toObject());
      JS::Rooted ids(aCx, JS::IdVector(aCx));

      if (!JS_Enumerate(aCx, object, &ids)) {
        return;
      }

      for (size_t i = 0, n = ids.length(); i < n; i++) {
        // Retrieve Property name.
        nsAutoJSString propName;
        if (!propName.init(aCx, ids[i])) {
          continue;
        }
        MSGMGR_FUZZER_LOG("%*s- Property: %s", aRecursionCounter * 4, "",
                          NS_ConvertUTF16toUTF8(propName).get());

        // The likelihood when a value gets fuzzed of this object.
        if (!FuzzingTraits::Sometimes(DefaultMutationProbability())) {
          continue;
        }

        // Retrieve Property value.
        JS::RootedValue propertyValue(aCx);
        JS_GetPropertyById(aCx, object, ids[i], &propertyValue);

        JS::RootedValue newPropValue(aCx);
        MutateValue(aCx, propertyValue, &newPropValue, aRecursionCounter);

        JS_SetPropertyById(aCx, object, ids[i], newPropValue);
      }
    }

## Minimal test - lines (42, 323)

path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 3.4
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 34.0
path: .spaces[0].metrics.loc.cloc
old: 54.0
new: 20.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 95.0
path: .spaces[0].metrics.loc.sloc
old: 146.0
new: 282.0
path: .spaces[0].metrics.loc.ploc
old: 63.0
new: 215.0
path: .spaces[0].metrics.loc.blank
old: 29.0
new: 47.0
path: .spaces[0].metrics.halstead.difficulty
old: 10.803571428571429
new: 38.896875
path: .spaces[0].metrics.halstead.N1
old: 165.0
new: 679.0
path: .spaces[0].metrics.halstead.effort
old: 20483.528142363837
new: 334647.6959066638
path: .spaces[0].metrics.halstead.N2
old: 121.0
new: 461.0
path: .spaces[0].metrics.halstead.n1
old: 15.0
new: 27.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 2.0823707078585443
new: 1.1402547874915718
path: .spaces[0].metrics.halstead.vocabulary
old: 99.0
new: 187.0
path: .spaces[0].metrics.halstead.level
old: 0.09256198347107436
new: 0.02570900618622961
path: .spaces[0].metrics.halstead.n2
old: 84.0
new: 160.0
path: .spaces[0].metrics.halstead.time
old: 1137.97378568688
new: 18591.53866148132
path: .spaces[0].metrics.halstead.volume
old: 1895.9959933427683
new: 8603.459684271906
path: .spaces[0].metrics.halstead.bugs
old: 0.24954483603492397
new: 1.6067092898002944
path: .spaces[0].metrics.halstead.length
old: 286.0
new: 1140.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 595.5580224475436
new: 1299.890457740392
path: .spaces[0].metrics.cyclomatic.average
old: 1.5555555555555556
new: 3.3333333333333335
path: .spaces[0].metrics.cyclomatic.sum
old: 14.0
new: 40.0
path: .spaces[0].metrics.nargs.sum
old: 1.0
new: 18.0
path: .spaces[0].metrics.nargs.average
old: 0.3333333333333333
new: 1.8
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 2.3
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 23.0
path: .spaces[0].metrics.nom.total
old: 3.0
new: 10.0
path: .spaces[0].metrics.nom.functions
old: 3.0
new: 10.0
path: .spaces[0].metrics.mi.mi_original
old: 47.7985749664211
new: 23.289523061690005
path: .spaces[0].metrics.mi.mi_sei
old: 35.12499097891461
new: -17.980196489746838
path: .spaces[0].metrics.mi.mi_visual_studio
old: 27.95238302129889
new: 13.619604129643278

### Code

    namespace mozilla {
    namespace dom {

    using namespace fuzzing;
    using namespace ipc;

    /* static */
    void MessageManagerFuzzer::ReadFile(const char* path,
                                        nsTArray& aArray) {
      nsCOMPtr file;
      nsresult rv =
          NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), true, getter_AddRefs(file));
      NS_ENSURE_SUCCESS_VOID(rv);

      bool exists = false;
      rv = file->Exists(&exists);
      if (NS_FAILED(rv) || !exists) {
        return;
      }

      nsCOMPtr fileStream(
          do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
      NS_ENSURE_SUCCESS_VOID(rv);

      rv = fileStream->Init(file, -1, -1, false);
      NS_ENSURE_SUCCESS_VOID(rv);

      nsCOMPtr lineStream(do_QueryInterface(fileStream, &rv));
      NS_ENSURE_SUCCESS_VOID(rv);

      nsAutoCString line;
      bool more = true;
      do {
        rv = lineStream->ReadLine(line, &more);
        NS_ENSURE_SUCCESS_VOID(rv);
        aArray.AppendElement(line);
      } while (more);
    }

    /* static */
    bool MessageManagerFuzzer::IsMessageNameBlacklisted(
        const nsAString& aMessageName) {
      static bool sFileLoaded = false;
      static nsTArray valuesInFile;

      if (!sFileLoaded) {
        ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_BLACKLIST"), valuesInFile);
        sFileLoaded = true;
      }

      if (valuesInFile.Length() == 0) {
        return false;
      }

      return valuesInFile.Contains(NS_ConvertUTF16toUTF8(aMessageName).get());
    }

    /* static */
    nsCString MessageManagerFuzzer::GetFuzzValueFromFile() {
      static bool sFileLoaded = false;
      static nsTArray valuesInFile;

      if (!sFileLoaded) {
        ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_STRINGSFILE"), valuesInFile);
        sFileLoaded = true;
      }

      // If something goes wrong with importing the file we return an empty string.
      if (valuesInFile.Length() == 0) {
        return nsCString();
      }

      unsigned randIdx = RandomIntegerRange(0, valuesInFile.Length());
      return valuesInFile.ElementAt(randIdx);
    }

    /* static */
    void MessageManagerFuzzer::MutateObject(JSContext* aCx, JS::HandleValue aValue,
                                            unsigned short int aRecursionCounter) {
      JS::Rooted object(aCx, &aValue.toObject());
      JS::Rooted ids(aCx, JS::IdVector(aCx));

      if (!JS_Enumerate(aCx, object, &ids)) {
        return;
      }

      for (size_t i = 0, n = ids.length(); i < n; i++) {
        // Retrieve Property name.
        nsAutoJSString propName;
        if (!propName.init(aCx, ids[i])) {
          continue;
        }
        MSGMGR_FUZZER_LOG("%*s- Property: %s", aRecursionCounter * 4, "",
                          NS_ConvertUTF16toUTF8(propName).get());

        // The likelihood when a value gets fuzzed of this object.
        if (!FuzzingTraits::Sometimes(DefaultMutationProbability())) {
          continue;
        }

        // Retrieve Property value.
        JS::RootedValue propertyValue(aCx);
        JS_GetPropertyById(aCx, object, ids[i], &propertyValue);

        JS::RootedValue newPropValue(aCx);
        MutateValue(aCx, propertyValue, &newPropValue, aRecursionCounter);

        JS_SetPropertyById(aCx, object, ids[i], newPropValue);
      }
    }

    /* static */
    bool MessageManagerFuzzer::MutateValue(JSContext* aCx, JS::HandleValue aValue,
                                           JS::MutableHandleValue aOutMutationValue,
                                           unsigned short int aRecursionCounter) {
      if (aValue.isInt32()) {
        if (FuzzingTraits::Sometimes(DefaultMutationProbability() * 2)) {
          aOutMutationValue.set(JS::Int32Value(RandomNumericLimit()));
        } else {
          aOutMutationValue.set(JS::Int32Value(RandomInteger()));
        }
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |int32|: '%d' to '%d'",
                          aRecursionCounter * 4, "", aValue.toInt32(),
                          aOutMutationValue.toInt32());
        return true;
      }

      if (aValue.isDouble()) {
        aOutMutationValue.set(JS::DoubleValue(RandomFloatingPoint()));
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |double|: '%f' to '%f'",
                          aRecursionCounter * 4, "", aValue.toDouble(),
                          aOutMutationValue.toDouble());
        return true;
      }

      if (aValue.isBoolean()) {
        aOutMutationValue.set(JS::BooleanValue(bool(RandomIntegerRange(0, 2))));
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |boolean|: '%d' to '%d'",
                          aRecursionCounter * 4, "", aValue.toBoolean(),
                          aOutMutationValue.toBoolean());
        return true;
      }

      if (aValue.isString()) {
        nsCString x = GetFuzzValueFromFile();
        if (x.IsEmpty()) {
          return false;
        }
        JSString* str = JS_NewStringCopyZ(aCx, x.get());
        aOutMutationValue.set(JS::StringValue(str));
        JS::RootedString rootedValue(aCx, aValue.toString());
        JS::UniqueChars valueChars = JS_EncodeStringToUTF8(aCx, rootedValue);
        MSGMGR_FUZZER_LOG("%*s! Mutated value of type |string|: '%s' to '%s'",
                          aRecursionCounter * 4, "", valueChars.get(), x.get());
        return true;
      }

      if (aValue.isObject()) {
        aRecursionCounter++;
        MSGMGR_FUZZER_LOG("%*s", aRecursionCounter * 4,
                          "");
        MutateObject(aCx, aValue, aRecursionCounter);
        aOutMutationValue.set(aValue);
        return true;
      }

      return false;
    }

    /* static */
    bool MessageManagerFuzzer::Mutate(JSContext* aCx, const nsAString& aMessageName,
                                      ipc::StructuredCloneData* aData,
                                      const JS::Value& aTransfer) {
      MSGMGR_FUZZER_LOG("Message: %s in process: %d",
                        NS_ConvertUTF16toUTF8(aMessageName).get(),
                        XRE_GetProcessType());

      unsigned short int aRecursionCounter = 0;
      ErrorResult rv;
      JS::RootedValue t(aCx, aTransfer);

      /* Read original StructuredCloneData. */
      JS::RootedValue scdContent(aCx);
      aData->Read(aCx, &scdContent, rv);
      if (NS_WARN_IF(rv.Failed())) {
        rv.SuppressException();
        JS_ClearPendingException(aCx);
        return false;
      }

      JS::RootedValue scdMutationContent(aCx);
      bool isMutated =
          MutateValue(aCx, scdContent, &scdMutationContent, aRecursionCounter);

      /* Write mutated StructuredCloneData. */
      ipc::StructuredCloneData mutatedStructuredCloneData;
      mutatedStructuredCloneData.Write(aCx, scdMutationContent, t,
                                       JS::CloneDataPolicy(), rv);
      if (NS_WARN_IF(rv.Failed())) {
        rv.SuppressException();
        JS_ClearPendingException(aCx);
        return false;
      }

      // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1346040
      aData->Copy(mutatedStructuredCloneData);

      /* Mutated and successfully written to StructuredCloneData object. */
      if (isMutated) {
        JS::RootedString str(aCx, JS_ValueToSource(aCx, scdMutationContent));
        JS::UniqueChars strChars = JS_EncodeStringToUTF8(aCx, str);
        MSGMGR_FUZZER_LOG("Mutated '%s' Message: %s",
                          NS_ConvertUTF16toUTF8(aMessageName).get(),
                          strChars.get());
      }

      return true;
    }

    /* static */
    unsigned int MessageManagerFuzzer::DefaultMutationProbability() {
      static unsigned long sPropValue =
          MESSAGEMANAGER_FUZZER_DEFAULT_MUTATION_PROBABILITY;
      static bool sInitialized = false;

      if (sInitialized) {
        return sPropValue;
      }
      sInitialized = true;

      // Defines the likelihood of fuzzing a message.
      const char* probability =
          PR_GetEnv("MESSAGEMANAGER_FUZZER_MUTATION_PROBABILITY");
      if (probability) {
        long n = std::strtol(probability, nullptr, 10);
        if (n != 0) {
          sPropValue = n;
          return sPropValue;
        }
      }

      return sPropValue;
    }

    /* static */
    bool MessageManagerFuzzer::IsLoggingEnabled() {
      static bool sInitialized = false;
      static bool sIsLoggingEnabled = false;

      if (!sInitialized) {
        sIsLoggingEnabled = !!PR_GetEnv("MESSAGEMANAGER_FUZZER_ENABLE_LOGGING");
        sInitialized = true;
      }

      return sIsLoggingEnabled;
    }

    /* static */
    bool MessageManagerFuzzer::IsEnabled() {
      return !!PR_GetEnv("MESSAGEMANAGER_FUZZER_ENABLE") && XRE_IsContentProcess();
    }

    /* static */
    void MessageManagerFuzzer::TryMutate(JSContext* aCx,
                                         const nsAString& aMessageName,
                                         ipc::StructuredCloneData* aData,
                                         const JS::Value& aTransfer) {
      if (!IsEnabled()) {
        return;
      }

      if (IsMessageNameBlacklisted(aMessageName)) {
        MSGMGR_FUZZER_LOG("Blacklisted message: %s",
                          NS_ConvertUTF16toUTF8(aMessageName).get());
        return;
      }

      Mutate(aCx, aMessageName, aData, aTransfer);
    }

    }  // namespace dom
    }  // namespace mozilla

## Minimal test - lines (82, 97)

path: .spaces[0].spaces[0].spaces[1].metrics.nexits.average
old: null
new: 2.0
path: .spaces[0].spaces[0].spaces[1].metrics.nexits.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[1].metrics.nargs.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[1].metrics.nargs.average
old: null
new: 1.0
path: .spaces[0].spaces[0].spaces[1].metrics.cyclomatic.sum
old: 1.0
new: 3.0
path: .spaces[0].spaces[0].spaces[1].metrics.cyclomatic.average
old: 1.0
new: 3.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.sloc
old: 1.0
new: 16.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.blank
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.ploc
old: 1.0
new: 13.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.lloc
old: 0.0
new: 6.0
path: .spaces[0].spaces[0].spaces[1].metrics.nom.functions
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[1].metrics.nom.total
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[1].metrics.cognitive.average
old: null
new: 2.0
path: .spaces[0].spaces[0].spaces[1].metrics.cognitive.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.effort
old: 0.0
new: 3176.470588235294
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.N1
old: 0.0
new: 36.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.n1
old: 0.0
new: 15.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.n2
old: 1.0
new: 17.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.purity_ratio
old: null
new: 2.1348371205897263
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.volume
old: 0.0
new: 300.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.bugs
old: 0.0
new: 0.07202920768089435
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.vocabulary
old: 1.0
new: 32.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.N2
old: 1.0
new: 24.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.estimated_program_length
old: null
new: 128.09022723538357
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.difficulty
old: 0.0
new: 10.588235294117649
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.length
old: 1.0
new: 60.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.time
old: 0.0
new: 176.47058823529412
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.level
old: null
new: 0.09444444444444444
path: .spaces[0].spaces[0].spaces[1].metrics.mi.mi_original
old: null
new: 95.73439383150333
path: .spaces[0].spaces[0].spaces[1].metrics.mi.mi_sei
old: null
new: 62.72014280942143
path: .spaces[0].spaces[0].spaces[1].metrics.mi.mi_visual_studio
old: null
new: 55.98502563245808

### Code

    bool MessageManagerFuzzer::IsMessageNameBlacklisted(
        const nsAString& aMessageName) {
      static bool sFileLoaded = false;
      static nsTArray valuesInFile;

      if (!sFileLoaded) {
        ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_BLACKLIST"), valuesInFile);
        sFileLoaded = true;
      }

      if (valuesInFile.Length() == 0) {
        return false;
      }

      return valuesInFile.Contains(NS_ConvertUTF16toUTF8(aMessageName).get());
    }

## Minimal test - lines (100, 116)

path: .spaces[0].spaces[0].spaces[2].metrics.mi.mi_sei
old: 50.27352412643289
new: 79.15219482753368
path: .spaces[0].spaces[0].spaces[2].metrics.mi.mi_visual_studio
old: 33.22799464828005
new: 55.208479527239
path: .spaces[0].spaces[0].spaces[2].metrics.mi.mi_original
old: 56.81987084855889
new: 94.4064999915787
path: .spaces[0].spaces[0].spaces[2].metrics.cyclomatic.sum
old: 5.0
new: 3.0
path: .spaces[0].spaces[0].spaces[2].metrics.cyclomatic.average
old: 2.5
new: 3.0
path: .spaces[0].spaces[0].spaces[2].metrics.nexits.average
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[2].metrics.nexits.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[2].metrics.cognitive.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[2].metrics.cognitive.average
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.volume
old: 1203.223015813421
new: 320.62674567841975
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.level
old: 0.10714285714285714
new: 0.08241758241758242
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.n2
old: 63.0
new: 15.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.purity_ratio
old: 2.2389146046317774
new: 1.69555042186264
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.bugs
old: 0.16716063676739715
new: 0.08245151929185504
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.length
old: 192.0
new: 66.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.vocabulary
old: 77.0
new: 29.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.effort
old: 11230.081480925262
new: 3890.2711808981594
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.time
old: 623.893415606959
new: 216.1261767165644
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.difficulty
old: 9.333333333333334
new: 12.133333333333333
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.N1
old: 108.0
new: 40.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.estimated_program_length
old: 429.8716040893012
new: 111.90632784293425
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.N2
old: 84.0
new: 26.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.cloc
old: 51.0
new: 1.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.blank
old: 17.0
new: 3.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.lloc
old: 0.0
new: 6.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.sloc
old: 110.0
new: 17.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.ploc
old: 42.0
new: 13.0
path: .spaces[0].spaces[0].spaces[2].metrics.nargs.sum
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].spaces[2].metrics.nargs.average
old: 1.0
new: 0.0

### Code

    nsCString MessageManagerFuzzer::GetFuzzValueFromFile() {
      static bool sFileLoaded = false;
      static nsTArray valuesInFile;

      if (!sFileLoaded) {
        ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_STRINGSFILE"), valuesInFile);
        sFileLoaded = true;
      }

      // If something goes wrong with importing the file we return an empty string.
      if (valuesInFile.Length() == 0) {
        return nsCString();
      }

      unsigned randIdx = RandomIntegerRange(0, valuesInFile.Length());
      return valuesInFile.ElementAt(randIdx);
    }

## Minimal test - lines (49, 79)

path: .spaces[0].spaces[0].spaces[0].metrics.loc.blank
old: 0.0
new: 5.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.ploc
old: 1.0
new: 26.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.sloc
old: 1.0
new: 31.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 11.0
path: .spaces[0].spaces[0].spaces[0].metrics.nom.total
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nom.functions
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nexits.average
old: null
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: null
new: 247.17562869425709
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n2
old: 1.0
new: 33.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N2
old: 1.0
new: 67.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.length
old: 1.0
new: 152.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.time
old: 0.0
new: 928.461518267196
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.level
old: null
new: 0.051846032992930086
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.effort
old: 0.0
new: 16712.307328809526
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.volume
old: 0.0
new: 866.466837157446
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.difficulty
old: 0.0
new: 19.28787878787879
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n1
old: 0.0
new: 19.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N1
old: 0.0
new: 85.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.bugs
old: 0.0
new: 0.21788952606716772
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.6261554519359018
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 52.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.average
old: null
new: 2.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[0].metrics.cognitive.average
old: null
new: 3.0
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: null
new: 46.359300198167944
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_original
old: null
new: 79.27440333886719
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_sei
old: null
new: 39.07521601202025

### Code

    void MessageManagerFuzzer::ReadFile(const char* path,
                                        nsTArray& aArray) {
      nsCOMPtr file;
      nsresult rv =
          NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), true, getter_AddRefs(file));
      NS_ENSURE_SUCCESS_VOID(rv);

      bool exists = false;
      rv = file->Exists(&exists);
      if (NS_FAILED(rv) || !exists) {
        return;
      }

      nsCOMPtr fileStream(
          do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
      NS_ENSURE_SUCCESS_VOID(rv);

      rv = fileStream->Init(file, -1, -1, false);
      NS_ENSURE_SUCCESS_VOID(rv);

      nsCOMPtr lineStream(do_QueryInterface(fileStream, &rv));
      NS_ENSURE_SUCCESS_VOID(rv);

      nsAutoCString line;
      bool more = true;
      do {
        rv = lineStream->ReadLine(line, &more);
        NS_ENSURE_SUCCESS_VOID(rv);
        aArray.AppendElement(line);
      } while (more);
    }