606 lines
19 KiB
C++
606 lines
19 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "UntrustedModulesDataSerializer.h"
|
|
|
|
#include "core/TelemetryCommon.h"
|
|
#include "js/Array.h" // JS::NewArrayObject
|
|
#include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineProperty, JS_GetProperty
|
|
#include "jsapi.h"
|
|
#include "mozilla/dom/ToJSValue.h"
|
|
#include "nsITelemetry.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "SharedLibraries.h"
|
|
|
|
namespace mozilla {
|
|
namespace Telemetry {
|
|
|
|
static const uint32_t kThirdPartyModulesPingVersion = 1;
|
|
|
|
/**
|
|
* Limits the length of a string by removing the middle of the string, replacing
|
|
* with ellipsis.
|
|
* e.g. LimitStringLength("hello world", 6) would result in "he...d"
|
|
*
|
|
* @param aStr [in,out] The string to transform
|
|
* @param aMaxFieldLength [in] The maximum length of the resulting string.
|
|
*/
|
|
static void LimitStringLength(nsAString& aStr, size_t aMaxFieldLength) {
|
|
if (aStr.Length() <= aMaxFieldLength) {
|
|
return;
|
|
}
|
|
|
|
constexpr auto kEllipsis = u"..."_ns;
|
|
|
|
if (aMaxFieldLength <= (kEllipsis.Length() + 3)) {
|
|
// An ellipsis is useless in this case, as it would obscure the string to
|
|
// the point that we cannot even determine the string's contents. We might
|
|
// as well just truncate.
|
|
aStr.Truncate(aMaxFieldLength);
|
|
return;
|
|
}
|
|
|
|
size_t cutPos = (aMaxFieldLength - kEllipsis.Length()) / 2;
|
|
size_t rightLen = aMaxFieldLength - kEllipsis.Length() - cutPos;
|
|
size_t cutLen = aStr.Length() - (cutPos + rightLen);
|
|
|
|
aStr.Replace(cutPos, cutLen, kEllipsis);
|
|
}
|
|
|
|
/**
|
|
* Adds a string property to a JS object, that's limited in length using
|
|
* LimitStringLength().
|
|
*
|
|
* @param cx [in] The JS context
|
|
* @param aObj [in] The object to add the property to
|
|
* @param aName [in] The name of the property to add
|
|
* @param aVal [in] The JS value of the resulting property.
|
|
* @param aMaxFieldLength [in] The maximum length of the value
|
|
* (see LimitStringLength())
|
|
* @return true upon success
|
|
*/
|
|
static bool AddLengthLimitedStringProp(JSContext* cx,
|
|
JS::Handle<JSObject*> aObj,
|
|
const char* aName, const nsAString& aVal,
|
|
size_t aMaxFieldLength = MAX_PATH) {
|
|
JS::Rooted<JS::Value> jsval(cx);
|
|
nsAutoString shortVal(aVal);
|
|
LimitStringLength(shortVal, aMaxFieldLength);
|
|
jsval.setString(Common::ToJSString(cx, shortVal));
|
|
return JS_DefineProperty(cx, aObj, aName, jsval, JSPROP_ENUMERATE);
|
|
};
|
|
|
|
static JSString* ModuleVersionToJSString(JSContext* aCx,
|
|
const ModuleVersion& aVersion) {
|
|
auto [major, minor, patch, build] = aVersion.AsTuple();
|
|
|
|
constexpr auto dot = u"."_ns;
|
|
|
|
nsAutoString strVer;
|
|
strVer.AppendInt(major);
|
|
strVer.Append(dot);
|
|
strVer.AppendInt(minor);
|
|
strVer.Append(dot);
|
|
strVer.AppendInt(patch);
|
|
strVer.Append(dot);
|
|
strVer.AppendInt(build);
|
|
|
|
return Common::ToJSString(aCx, strVer);
|
|
}
|
|
|
|
/**
|
|
* Convert the given container object to a JavaScript array.
|
|
*
|
|
* @param cx [in] The JS context.
|
|
* @param aRet [out] This gets assigned to the newly created
|
|
* array object.
|
|
* @param aContainer [in] The source container to convert.
|
|
* @param aElementConverter [in] A callable used to convert each element
|
|
* to a JS element. The form of this function is:
|
|
* bool(JSContext *cx,
|
|
* JS::MutableHandleValue aRet,
|
|
* const ElementT& aElement)
|
|
* @return true if aRet was successfully assigned to the new array object.
|
|
*/
|
|
template <typename T, typename Converter, typename... Args>
|
|
static bool ContainerToJSArray(JSContext* cx, JS::MutableHandle<JSObject*> aRet,
|
|
const T& aContainer,
|
|
Converter&& aElementConverter, Args&&... aArgs) {
|
|
JS::Rooted<JSObject*> arr(cx, JS::NewArrayObject(cx, 0));
|
|
if (!arr) {
|
|
return false;
|
|
}
|
|
|
|
size_t i = 0;
|
|
for (auto&& item : aContainer) {
|
|
JS::Rooted<JS::Value> jsel(cx);
|
|
if (!aElementConverter(cx, &jsel, *item, std::forward<Args>(aArgs)...)) {
|
|
return false;
|
|
}
|
|
if (!JS_DefineElement(cx, arr, i, jsel, JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
aRet.set(arr);
|
|
return true;
|
|
}
|
|
|
|
static bool SerializeModule(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aElement,
|
|
const RefPtr<ModuleRecord>& aModule,
|
|
uint32_t aFlags) {
|
|
if (!aModule) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return false;
|
|
}
|
|
|
|
if (aFlags & nsITelemetry::INCLUDE_PRIVATE_FIELDS_IN_LOADEVENTS) {
|
|
JS::Rooted<JS::Value> jsFileObj(aCx);
|
|
if (!dom::ToJSValue(aCx, aModule->mResolvedDosName, &jsFileObj) ||
|
|
!JS_DefineProperty(aCx, obj, "dllFile", jsFileObj, JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!AddLengthLimitedStringProp(aCx, obj, "resolvedDllName",
|
|
aModule->mSanitizedDllName)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (aModule->mVersion.isSome()) {
|
|
JS::Rooted<JS::Value> jsModuleVersion(aCx);
|
|
jsModuleVersion.setString(
|
|
ModuleVersionToJSString(aCx, aModule->mVersion.ref()));
|
|
if (!JS_DefineProperty(aCx, obj, "fileVersion", jsModuleVersion,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if defined(MOZ_GECKO_PROFILER)
|
|
if (aModule->mResolvedDosName) {
|
|
nsAutoString path;
|
|
if (aModule->mResolvedDosName->GetPath(path) == NS_OK) {
|
|
SharedLibraryInfo info = SharedLibraryInfo::GetInfoFromPath(path.Data());
|
|
if (info.GetSize() > 0) {
|
|
nsString breakpadId =
|
|
NS_ConvertUTF8toUTF16(info.GetEntry(0).GetBreakpadId());
|
|
if (!AddLengthLimitedStringProp(aCx, obj, "debugID", breakpadId)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // MOZ_GECKO_PROFILER
|
|
|
|
if (aModule->mVendorInfo.isSome()) {
|
|
const char* propName;
|
|
|
|
const VendorInfo& vendorInfo = aModule->mVendorInfo.ref();
|
|
switch (vendorInfo.mSource) {
|
|
case VendorInfo::Source::Signature:
|
|
propName = "signedBy";
|
|
break;
|
|
case VendorInfo::Source::VersionInfo:
|
|
propName = "companyName";
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unknown VendorInfo Source!");
|
|
return false;
|
|
}
|
|
|
|
MOZ_ASSERT(!vendorInfo.mVendor.IsEmpty());
|
|
if (vendorInfo.mVendor.IsEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
if (!AddLengthLimitedStringProp(aCx, obj, propName, vendorInfo.mVendor)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsTrustFlags(aCx);
|
|
jsTrustFlags.setNumber(static_cast<uint32_t>(aModule->mTrustFlags));
|
|
if (!JS_DefineProperty(aCx, obj, "trustFlags", jsTrustFlags,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
aElement.setObject(*obj);
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
bool UntrustedModulesDataSerializer::SerializeEvent(
|
|
JSContext* aCx, JS::MutableHandle<JS::Value> aElement,
|
|
const ProcessedModuleLoadEventContainer& aEventContainer,
|
|
const IndexMap& aModuleIndices) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
const ProcessedModuleLoadEvent& event = aEventContainer.mEvent;
|
|
if (!event) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsProcessUptimeMS(aCx);
|
|
// Javascript doesn't like 64-bit integers; convert to double.
|
|
jsProcessUptimeMS.setNumber(static_cast<double>(event.mProcessUptimeMS));
|
|
if (!JS_DefineProperty(aCx, obj, "processUptimeMS", jsProcessUptimeMS,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
if (event.mLoadDurationMS) {
|
|
JS::Rooted<JS::Value> jsLoadDurationMS(aCx);
|
|
jsLoadDurationMS.setNumber(event.mLoadDurationMS.value());
|
|
if (!JS_DefineProperty(aCx, obj, "loadDurationMS", jsLoadDurationMS,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsThreadId(aCx);
|
|
jsThreadId.setNumber(static_cast<uint32_t>(event.mThreadId));
|
|
if (!JS_DefineProperty(aCx, obj, "threadID", jsThreadId, JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
nsDependentCString effectiveThreadName;
|
|
if (event.mThreadId == ::GetCurrentThreadId()) {
|
|
effectiveThreadName.Rebind("Main Thread"_ns, 0);
|
|
} else {
|
|
effectiveThreadName.Rebind(event.mThreadName, 0);
|
|
}
|
|
|
|
if (!effectiveThreadName.IsEmpty()) {
|
|
JS::Rooted<JS::Value> jsThreadName(aCx);
|
|
jsThreadName.setString(Common::ToJSString(aCx, effectiveThreadName));
|
|
if (!JS_DefineProperty(aCx, obj, "threadName", jsThreadName,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Don't add this property unless mRequestedDllName differs from
|
|
// the associated module's mSanitizedDllName
|
|
if (!event.mRequestedDllName.IsEmpty() &&
|
|
!event.mRequestedDllName.Equals(event.mModule->mSanitizedDllName,
|
|
nsCaseInsensitiveStringComparator)) {
|
|
if (!AddLengthLimitedStringProp(aCx, obj, "requestedDllName",
|
|
event.mRequestedDllName)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
nsAutoString strBaseAddress;
|
|
strBaseAddress.AppendLiteral(u"0x");
|
|
strBaseAddress.AppendInt(event.mBaseAddress, 16);
|
|
|
|
JS::Rooted<JS::Value> jsBaseAddress(aCx);
|
|
jsBaseAddress.setString(Common::ToJSString(aCx, strBaseAddress));
|
|
if (!JS_DefineProperty(aCx, obj, "baseAddress", jsBaseAddress,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t index;
|
|
if (!aModuleIndices.Get(event.mModule->mResolvedNtName, &index)) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsModuleIndex(aCx);
|
|
jsModuleIndex.setNumber(index);
|
|
if (!JS_DefineProperty(aCx, obj, "moduleIndex", jsModuleIndex,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsIsDependent(aCx);
|
|
jsIsDependent.setBoolean(event.mIsDependent);
|
|
if (!JS_DefineProperty(aCx, obj, "isDependent", jsIsDependent,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsLoadStatus(aCx);
|
|
jsLoadStatus.setNumber(event.mLoadStatus);
|
|
if (!JS_DefineProperty(aCx, obj, "loadStatus", jsLoadStatus,
|
|
JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
aElement.setObject(*obj);
|
|
|
|
return true;
|
|
}
|
|
|
|
static nsDependentCString GetProcessTypeString(GeckoProcessType aType) {
|
|
nsDependentCString strProcType;
|
|
if (aType == GeckoProcessType_Default) {
|
|
strProcType.Rebind("browser"_ns, 0);
|
|
} else {
|
|
strProcType.Rebind(XRE_GeckoProcessTypeToString(aType));
|
|
}
|
|
return strProcType;
|
|
}
|
|
|
|
nsresult UntrustedModulesDataSerializer::GetPerProcObject(
|
|
const UntrustedModulesData& aData, JS::MutableHandle<JSObject*> aObj) {
|
|
JS::Rooted<JS::Value> jsProcType(mCx);
|
|
jsProcType.setString(
|
|
Common::ToJSString(mCx, GetProcessTypeString(aData.mProcessType)));
|
|
if (!JS_DefineProperty(mCx, aObj, "processType", jsProcType,
|
|
JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsElapsed(mCx);
|
|
jsElapsed.setNumber(aData.mElapsed.ToSecondsSigDigits());
|
|
if (!JS_DefineProperty(mCx, aObj, "elapsed", jsElapsed, JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (aData.mXULLoadDurationMS.isSome()) {
|
|
JS::Rooted<JS::Value> jsXulLoadDurationMS(mCx);
|
|
jsXulLoadDurationMS.setNumber(aData.mXULLoadDurationMS.value());
|
|
if (!JS_DefineProperty(mCx, aObj, "xulLoadDurationMS", jsXulLoadDurationMS,
|
|
JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsSanitizationFailures(mCx);
|
|
jsSanitizationFailures.setNumber(aData.mSanitizationFailures);
|
|
if (!JS_DefineProperty(mCx, aObj, "sanitizationFailures",
|
|
jsSanitizationFailures, JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsTrustTestFailures(mCx);
|
|
jsTrustTestFailures.setNumber(aData.mTrustTestFailures);
|
|
if (!JS_DefineProperty(mCx, aObj, "trustTestFailures", jsTrustTestFailures,
|
|
JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
JS::Rooted<JSObject*> eventsArray(mCx);
|
|
if (!ContainerToJSArray(mCx, &eventsArray, aData.mEvents, &SerializeEvent,
|
|
mIndexMap)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!JS_DefineProperty(mCx, aObj, "events", eventsArray, JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!(mFlags & nsITelemetry::EXCLUDE_STACKINFO_FROM_LOADEVENTS)) {
|
|
JS::Rooted<JSObject*> combinedStacksObj(
|
|
mCx, CreateJSStackObject(mCx, aData.mStacks));
|
|
if (!combinedStacksObj) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!JS_DefineProperty(mCx, aObj, "combinedStacks", combinedStacksObj,
|
|
JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult UntrustedModulesDataSerializer::AddLoadEvents(
|
|
const UntrustedModuleLoadingEvents& aEvents,
|
|
JS::MutableHandle<JSObject*> aPerProcObj) {
|
|
JS::Rooted<JS::Value> eventsArrayVal(mCx);
|
|
if (!JS_GetProperty(mCx, aPerProcObj, "events", &eventsArrayVal) ||
|
|
!eventsArrayVal.isObject()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
JS::Rooted<JSObject*> eventsArray(mCx, &eventsArrayVal.toObject());
|
|
bool isArray;
|
|
if (!JS::IsArrayObject(mCx, eventsArray, &isArray) && !isArray) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
uint32_t currentPos;
|
|
if (!GetArrayLength(mCx, eventsArray, ¤tPos)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
for (auto item : aEvents) {
|
|
JS::Rooted<JS::Value> jsel(mCx);
|
|
if (!SerializeEvent(mCx, &jsel, *item, mIndexMap) ||
|
|
!JS_DefineElement(mCx, eventsArray, currentPos++, jsel,
|
|
JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult UntrustedModulesDataSerializer::AddSingleData(
|
|
const UntrustedModulesData& aData) {
|
|
// Serialize each entry in the modules hashtable out to the "modules" array
|
|
// and store the indices in |mIndexMap|
|
|
for (const auto& entry : aData.mModules) {
|
|
if (!mIndexMap.WithEntryHandle(entry.GetKey(), [&](auto&& addPtr) {
|
|
if (!addPtr) {
|
|
addPtr.Insert(mCurModulesArrayIdx);
|
|
|
|
JS::Rooted<JS::Value> jsModule(mCx);
|
|
if (!SerializeModule(mCx, &jsModule, entry.GetData(), mFlags) ||
|
|
!JS_DefineElement(mCx, mModulesArray, mCurModulesArrayIdx,
|
|
jsModule, JSPROP_ENUMERATE)) {
|
|
return false;
|
|
}
|
|
|
|
++mCurModulesArrayIdx;
|
|
}
|
|
return true;
|
|
})) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (mCurModulesArrayIdx >= mMaxModulesArrayLen) {
|
|
return NS_ERROR_CANNOT_CONVERT_DATA;
|
|
}
|
|
|
|
nsAutoCString strPid;
|
|
strPid.Append(GetProcessTypeString(aData.mProcessType));
|
|
strPid.AppendLiteral(".0x");
|
|
strPid.AppendInt(static_cast<uint32_t>(aData.mPid), 16);
|
|
|
|
if (mFlags & nsITelemetry::EXCLUDE_STACKINFO_FROM_LOADEVENTS) {
|
|
JS::Rooted<JS::Value> perProcVal(mCx);
|
|
if (JS_GetProperty(mCx, mPerProcObjContainer, strPid.get(), &perProcVal) &&
|
|
perProcVal.isObject()) {
|
|
// If a corresponding per-proc object already exists in the dictionary,
|
|
// and we skip to serialize CombinedStacks, we can add loading events
|
|
// into the JS object directly.
|
|
JS::Rooted<JSObject*> perProcObj(mCx, &perProcVal.toObject());
|
|
return AddLoadEvents(aData.mEvents, &perProcObj);
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JSObject*> perProcObj(mCx, JS_NewPlainObject(mCx));
|
|
if (!perProcObj) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult rv = GetPerProcObject(aData, &perProcObj);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsPerProcObjValue(mCx);
|
|
jsPerProcObjValue.setObject(*perProcObj);
|
|
if (!JS_DefineProperty(mCx, mPerProcObjContainer, strPid.get(),
|
|
jsPerProcObjValue, JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
UntrustedModulesDataSerializer::UntrustedModulesDataSerializer(
|
|
JSContext* aCx, uint32_t aMaxModulesArrayLen, uint32_t aFlags)
|
|
: mCtorResult(NS_ERROR_FAILURE),
|
|
mCx(aCx),
|
|
mMainObj(mCx, JS_NewPlainObject(mCx)),
|
|
mModulesArray(mCx, JS::NewArrayObject(mCx, 0)),
|
|
mBlockedModulesArray(mCx, JS::NewArrayObject(mCx, 0)),
|
|
mPerProcObjContainer(mCx, JS_NewPlainObject(mCx)),
|
|
mMaxModulesArrayLen(aMaxModulesArrayLen),
|
|
mCurModulesArrayIdx(0),
|
|
mCurBlockedModulesArrayIdx(0),
|
|
mFlags(aFlags) {
|
|
if (!mMainObj || !mModulesArray || !mBlockedModulesArray ||
|
|
!mPerProcObjContainer) {
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsVersion(mCx);
|
|
jsVersion.setNumber(kThirdPartyModulesPingVersion);
|
|
if (!JS_DefineProperty(mCx, mMainObj, "structVersion", jsVersion,
|
|
JSPROP_ENUMERATE)) {
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsModulesArrayValue(mCx);
|
|
jsModulesArrayValue.setObject(*mModulesArray);
|
|
if (!JS_DefineProperty(mCx, mMainObj, "modules", jsModulesArrayValue,
|
|
JSPROP_ENUMERATE)) {
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsBlockedModulesArrayValue(mCx);
|
|
jsBlockedModulesArrayValue.setObject(*mBlockedModulesArray);
|
|
if (!JS_DefineProperty(mCx, mMainObj, "blockedModules",
|
|
jsBlockedModulesArrayValue, JSPROP_ENUMERATE)) {
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> jsPerProcObjContainerValue(mCx);
|
|
jsPerProcObjContainerValue.setObject(*mPerProcObjContainer);
|
|
if (!JS_DefineProperty(mCx, mMainObj, "processes", jsPerProcObjContainerValue,
|
|
JSPROP_ENUMERATE)) {
|
|
return;
|
|
}
|
|
|
|
mCtorResult = NS_OK;
|
|
}
|
|
|
|
UntrustedModulesDataSerializer::operator bool() const {
|
|
return NS_SUCCEEDED(mCtorResult);
|
|
}
|
|
|
|
void UntrustedModulesDataSerializer::GetObject(
|
|
JS::MutableHandle<JS::Value> aRet) {
|
|
aRet.setObject(*mMainObj);
|
|
}
|
|
|
|
nsresult UntrustedModulesDataSerializer::Add(
|
|
const UntrustedModulesBackupData& aData) {
|
|
if (NS_FAILED(mCtorResult)) {
|
|
return mCtorResult;
|
|
}
|
|
|
|
for (const RefPtr<UntrustedModulesDataContainer>& container :
|
|
aData.Values()) {
|
|
if (!container) {
|
|
continue;
|
|
}
|
|
|
|
nsresult rv = AddSingleData(container->mData);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult UntrustedModulesDataSerializer::AddBlockedModules(
|
|
const nsTArray<nsDependentSubstring>& blockedModules) {
|
|
if (NS_FAILED(mCtorResult)) {
|
|
return mCtorResult;
|
|
}
|
|
|
|
if (blockedModules.Length() >= mMaxModulesArrayLen) {
|
|
return NS_ERROR_CANNOT_CONVERT_DATA;
|
|
}
|
|
|
|
for (const auto& blockedModule : blockedModules) {
|
|
JS::Rooted<JS::Value> jsBlockedModule(mCx);
|
|
jsBlockedModule.setString(Common::ToJSString(mCx, blockedModule));
|
|
if (!JS_DefineElement(mCx, mBlockedModulesArray, mCurBlockedModulesArrayIdx,
|
|
jsBlockedModule, JSPROP_ENUMERATE)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
++mCurBlockedModulesArrayIdx;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace Telemetry
|
|
} // namespace mozilla
|