Bug 1770237: Part 16 - Remove component loading logic from mozJSComponentLoader. r=mccr8,florian

Differential Revision: https://phabricator.services.mozilla.com/D148196
This commit is contained in:
Kris Maglione 2022-06-22 20:31:37 +00:00
Родитель 37545fb01a
Коммит 2b9e8682a2
12 изменённых файлов: 13 добавлений и 299 удалений

Просмотреть файл

@ -37,15 +37,6 @@ class StartupContentSubframeChild extends JSWindowActorChild {
);
let collectStacks = AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG;
let components = {};
for (let component of Cu.loadedComponents) {
// Keep only the file name for components, as the path is an absolute file
// URL rather than a resource:// URL like for modules.
components[component.replace(/.*\//, "")] = collectStacks
? Cu.getComponentLoadStack(component)
: "";
}
let modules = {};
for (let module of Cu.loadedModules) {
modules[module] = collectStacks ? Cu.getModuleImportStack(module) : "";
@ -60,7 +51,6 @@ class StartupContentSubframeChild extends JSWindowActorChild {
} catch (e) {}
}
this.sendAsyncMessage("LoadedScripts", {
components,
modules,
services,
});

Просмотреть файл

@ -1,8 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* This test records at which phase of startup the JS components and modules
* are first loaded.
/* This test records at which phase of startup the JS modules are first
* loaded.
* If you made changes that cause this test to fail, it's likely because you
* are loading more JS code during startup.
* Most code has no reason to run off of the app-startup notification
@ -56,7 +56,6 @@ const startupPhases = {
// before first paint and delayed it.
"before first paint": {
denylist: {
components: new Set(["nsSearchService.js"]),
modules: new Set([
"chrome://webcompat/content/data/ua_overrides.jsm",
"chrome://webcompat/content/lib/ua_overrider.jsm",
@ -68,6 +67,7 @@ const startupPhases = {
"resource://gre/modules/PageThumbs.jsm",
"resource://gre/modules/PlacesUtils.jsm",
"resource://gre/modules/Preferences.jsm",
"resource://gre/modules/SearchService.jsm",
"resource://gre/modules/Sqlite.jsm",
]),
services: new Set(["@mozilla.org/browser/search-service;1"]),
@ -79,10 +79,6 @@ const startupPhases = {
// interacting with the first browser window.
"before handling user events": {
denylist: {
components: new Set([
"PageIconProtocolHandler.js",
"nsPlacesExpiration.js",
]),
modules: new Set([
"resource://gre/modules/Blocklist.jsm",
// Bug 1391495 - BrowserWindowTracker.jsm is intermittently used.
@ -94,6 +90,7 @@ const startupPhases = {
"resource://gre/modules/FxAccounts.jsm",
"resource://gre/modules/FxAccountsStorage.jsm",
"resource://gre/modules/PlacesBackups.jsm",
"resource://gre/modules/PlacesExpiration.jsm",
"resource://gre/modules/PlacesSyncUtils.jsm",
"resource://gre/modules/PushComponents.jsm",
]),
@ -109,7 +106,6 @@ const startupPhases = {
// be listed here.
"before becoming idle": {
denylist: {
components: new Set(["UnifiedComplete.js"]),
modules: new Set([
"resource://gre/modules/AsyncPrefs.jsm",
"resource://gre/modules/LoginManagerContextMenu.jsm",
@ -150,25 +146,11 @@ add_task(async function() {
.wrappedJSObject;
await startupRecorder.done;
let componentStacks = new Map();
let data = Cu.cloneInto(startupRecorder.data.code, {});
// Keep only the file name for components, as the path is an absolute file
// URL rather than a resource:// URL like for modules.
for (let phase in data) {
data[phase].components = data[phase].components.map(uri => {
let fileName = uri.replace(/.*\//, "");
componentStacks.set(fileName, Cu.getComponentLoadStack(uri));
return fileName;
});
}
function getStack(scriptType, name) {
if (scriptType == "modules") {
return Cu.getModuleImportStack(name);
}
if (scriptType == "components") {
return componentStacks.get(name);
}
return "";
}

Просмотреть файл

@ -1,8 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* This test records which services, JS components, frame scripts, process
* scripts, and JS modules are loaded when creating a new content process.
/* This test records which services, frame scripts, process scripts, and
* JS modules are loaded when creating a new content process.
*
* If you made changes that cause this test to fail, it's likely because you
* are loading more JS code during content process startup. Please try to
@ -132,14 +132,6 @@ add_task(async function() {
"resource://gre/modules/AppConstants.jsm"
);
let collectStacks = AppConstants.NIGHTLY_BUILD || AppConstants.DEBUG;
let components = {};
for (let component of Cu.loadedComponents) {
/* Keep only the file name for components, as the path is an absolute file
URL rather than a resource:// URL like for modules. */
components[component.replace(/.*\//, "")] = collectStacks
? Cu.getComponentLoadStack(component)
: "";
}
let modules = {};
for (let module of Cu.loadedModules) {
modules[module] = collectStacks
@ -157,7 +149,6 @@ add_task(async function() {
} catch (e) {}
}
sendAsyncMessage("Test:LoadedScripts", {
components,
modules,
services,
});

Просмотреть файл

@ -53,8 +53,8 @@ let afterPaintListener = () => {
/**
* The StartupRecorder component observes notifications at various stages of
* startup and records the set of JS components and modules that were already
* loaded at each of these points.
* startup and records the set of JS modules that were already loaded at
* each of these points.
* The records are meant to be used by startup tests in
* browser/base/content/test/performance
* This component only exists in nightly and debug builds, it doesn't ship in
@ -81,7 +81,6 @@ StartupRecorder.prototype = {
record(name) {
ChromeUtils.addProfilerMarker("startupRecorder:" + name);
this.data.code[name] = {
components: Cu.loadedComponents,
modules: Cu.loadedModules,
services: Object.keys(Cc).filter(c => {
try {

Просмотреть файл

@ -9,7 +9,6 @@ XPIDL_SOURCES += [
"nsIXPConnect.idl",
"nsIXPCScriptable.idl",
"xpccomponents.idl",
"xpcIJSGetFactory.idl",
"xpcIJSWeakReference.idl",
]

Просмотреть файл

@ -1,18 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIFactory;
/**
* Every JS module exports a single NSGetFactory symbol which is converted into this
* functional interface type.
*/
[scriptable, function, uuid(3FE0C205-D75B-4CAC-9347-D2B855050143)]
interface xpcIJSGetFactory : nsISupports
{
nsIFactory get(in nsCIDRef aCID);
};

Просмотреть файл

@ -724,8 +724,6 @@ interface nsIXPCComponents_Utils : nsISupports
// Array of the URI of JSM and ESM loaded, converting ESM URI into JSM URI.
readonly attribute Array<ACString> loadedModules;
readonly attribute Array<ACString> loadedComponents;
// Array of the URI of JSM loaded.
readonly attribute Array<ACString> loadedJSModules;
@ -733,11 +731,10 @@ interface nsIXPCComponents_Utils : nsISupports
readonly attribute Array<ACString> loadedESModules;
// These 2 functions will only return useful values if the
// This function will only return useful values if the
// "browser.startup.record" preference was true at the time the JS file
// was loaded.
ACString getModuleImportStack(in AUTF8String aLocation);
ACString getComponentLoadStack(in AUTF8String aLocation);
};
/**

Просмотреть файл

@ -270,8 +270,7 @@ static nsresult MOZ_FORMAT_PRINTF(2, 3)
NS_IMPL_ISUPPORTS(mozJSComponentLoader, nsIMemoryReporter)
mozJSComponentLoader::mozJSComponentLoader()
: mModules(16),
mImports(16),
: mImports(16),
mInProgressImports(16),
mLocations(16),
mInitialized(false),
@ -404,102 +403,6 @@ mozJSComponentLoader::~mozJSComponentLoader() {
StaticRefPtr<mozJSComponentLoader> mozJSComponentLoader::sSelf;
const mozilla::Module* mozJSComponentLoader::LoadModule(FileLocation& aFile) {
if (!NS_IsMainThread()) {
MOZ_ASSERT(false, "Don't use JS components off the main thread");
return nullptr;
}
nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
nsCString spec;
aFile.GetURIString(spec);
ComponentLoaderInfo info(spec);
nsresult rv = info.EnsureURI();
NS_ENSURE_SUCCESS(rv, nullptr);
mInitialized = true;
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("mozJSComponentLoader::LoadModule",
OTHER, spec);
AUTO_PROFILER_MARKER_TEXT("JS XPCOM", JS, MarkerStack::Capture(), spec);
ModuleEntry* mod;
if (mModules.Get(spec, &mod)) {
return mod;
}
dom::AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
auto entry = MakeUnique<ModuleEntry>(RootingContext::get(cx));
RootedValue exn(cx);
rv = ObjectForLocation(info, file, &entry->obj, &entry->thisObjectKey,
&entry->location, /* aPropagateExceptions */ false,
&exn);
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIComponentManager> cm;
rv = NS_GetComponentManager(getter_AddRefs(cm));
if (NS_FAILED(rv)) {
return nullptr;
}
JSAutoRealm ar(cx, entry->obj);
RootedObject entryObj(cx, entry->obj);
RootedObject NSGetFactoryHolder(
cx, ResolveModuleObjectProperty(cx, entryObj, "NSGetFactory"));
RootedValue NSGetFactory_val(cx);
if (!NSGetFactoryHolder ||
!JS_GetProperty(cx, NSGetFactoryHolder, "NSGetFactory",
&NSGetFactory_val) ||
NSGetFactory_val.isUndefined()) {
return nullptr;
}
if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
/*
* spec's encoding is ASCII unless it's zip file, otherwise it's
* random encoding. Latin1 variant is safe for random encoding.
*/
JS_ReportErrorLatin1(
cx, "%s has NSGetFactory property that is not a function", spec.get());
return nullptr;
}
RootedObject jsGetFactoryObj(cx);
if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
!jsGetFactoryObj) {
/* XXX report error properly */
return nullptr;
}
rv = nsXPConnect::XPConnect()->WrapJS(cx, jsGetFactoryObj,
NS_GET_IID(xpcIJSGetFactory),
getter_AddRefs(entry->getfactoryobj));
if (NS_FAILED(rv)) {
/* XXX report error properly */
#ifdef DEBUG
fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
#endif
return nullptr;
}
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
if (Preferences::GetBool("browser.startup.record", false)) {
entry->importStack = xpc_PrintJSStack(cx, false, false, false).get();
}
#endif
// Cache this module for later
mModules.InsertOrUpdate(spec, entry.get());
// The hash owns the ModuleEntry now, forget about it
return entry.release();
}
void mozJSComponentLoader::FindTargetObject(JSContext* aCx,
MutableHandleObject aTargetObject) {
aTargetObject.set(JS::GetJSMEnvironmentOfScriptedCaller(aCx));
@ -559,7 +462,6 @@ static size_t SizeOfTableExcludingThis(
size_t mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
size_t n = aMallocSizeOf(this);
n += SizeOfTableExcludingThis(mModules, aMallocSizeOf);
n += SizeOfTableExcludingThis(mImports, aMallocSizeOf);
n += mLocations.ShallowSizeOfExcludingThis(aMallocSizeOf);
n += SizeOfTableExcludingThis(mInProgressImports, aMallocSizeOf);
@ -622,14 +524,6 @@ mozJSComponentLoader::CollectReports(nsIHandleReportCallback* aHandleReport,
"Loaded JS modules"_ns, aData);
}
for (const auto& entry : mModules.Values()) {
nsAutoCString path("js-component-loader/components/");
path.Append(MangleURL(entry->location, aAnonymize));
aHandleReport->Callback(""_ns, path, KIND_NONHEAP, UNITS_COUNT, 1,
"Loaded JS components"_ns, aData);
}
return NS_OK;
}
@ -1065,11 +959,6 @@ void mozJSComponentLoader::UnloadModules() {
mInProgressImports.Clear();
mImports.Clear();
mLocations.Clear();
for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
iter.Data()->Clear();
iter.Remove();
}
}
/* static */
@ -1257,14 +1146,6 @@ nsresult mozJSComponentLoader::GetLoadedJSAndESModules(
return NS_OK;
}
void mozJSComponentLoader::GetLoadedComponents(
nsTArray<nsCString>& aLoadedComponents) {
aLoadedComponents.SetCapacity(mModules.Count());
for (const auto& data : mModules.Values()) {
aLoadedComponents.AppendElement(data->location);
}
}
nsresult mozJSComponentLoader::GetModuleImportStack(const nsACString& aLocation,
nsACString& retval) {
#ifdef STARTUP_RECORDER_ENABLED
@ -1285,28 +1166,6 @@ nsresult mozJSComponentLoader::GetModuleImportStack(const nsACString& aLocation,
#endif
}
nsresult mozJSComponentLoader::GetComponentLoadStack(
const nsACString& aLocation, nsACString& retval) {
#ifdef STARTUP_RECORDER_ENABLED
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
MOZ_ASSERT(mInitialized);
ComponentLoaderInfo info(aLocation);
nsresult rv = info.EnsureURI();
NS_ENSURE_SUCCESS(rv, rv);
ModuleEntry* mod;
if (!mModules.Get(info.Key(), &mod)) {
return NS_ERROR_FAILURE;
}
retval = mod->importStack;
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult mozJSComponentLoader::ImportInto(const nsACString& aLocation,
HandleObject targetObj, JSContext* cx,
MutableHandleObject vp) {
@ -1731,21 +1590,6 @@ size_t mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(
return n;
}
/* static */
already_AddRefed<nsIFactory> mozJSComponentLoader::ModuleEntry::GetFactory(
const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) {
const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
nsCOMPtr<nsIFactory> f;
nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
if (NS_FAILED(rv)) {
return nullptr;
}
return f.forget();
}
//----------------------------------------------------------------------
JSCLContextHelper::JSCLContextHelper(JSContext* aCx)

Просмотреть файл

@ -11,7 +11,6 @@
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/FileLocation.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Module.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsIMemoryReporter.h"
@ -22,7 +21,6 @@
#include "jsapi.h"
#include "js/experimental/JSStencil.h"
#include "xpcIJSGetFactory.h"
#include "xpcpublic.h"
class nsIFile;
@ -50,13 +48,8 @@ class mozJSComponentLoader final : public nsIMemoryReporter {
// Returns the list of all JSMs and ESMs.
nsresult GetLoadedJSAndESModules(nsTArray<nsCString>& aLoadedModules);
void GetLoadedComponents(nsTArray<nsCString>& aLoadedComponents);
nsresult GetModuleImportStack(const nsACString& aLocation,
nsACString& aRetval);
nsresult GetComponentLoadStack(const nsACString& aLocation,
nsACString& aRetval);
const mozilla::Module* LoadModule(mozilla::FileLocation& aFile);
void FindTargetObject(JSContext* aCx, JS::MutableHandleObject aTargetObject);
@ -150,31 +143,16 @@ class mozJSComponentLoader final : public nsIMemoryReporter {
nsresult ImportInto(const nsACString& aLocation, JS::HandleObject targetObj,
JSContext* callercx, JS::MutableHandleObject vp);
nsCOMPtr<nsIComponentManager> mCompMgr;
class ModuleEntry : public mozilla::Module {
class ModuleEntry {
public:
explicit ModuleEntry(JS::RootingContext* aRootingCx)
: mozilla::Module(),
obj(aRootingCx),
exports(aRootingCx),
thisObjectKey(aRootingCx) {
mVersion = mozilla::Module::kVersion;
mCIDs = nullptr;
mContractIDs = nullptr;
mCategoryEntries = nullptr;
getFactoryProc = GetFactory;
loadProc = nullptr;
unloadProc = nullptr;
: obj(aRootingCx), exports(aRootingCx), thisObjectKey(aRootingCx) {
location = nullptr;
}
~ModuleEntry() { Clear(); }
void Clear() {
getfactoryobj = nullptr;
if (obj) {
if (JS_HasExtensibleLexicalEnvironment(obj)) {
JS::RootedObject lexicalEnv(mozilla::dom::RootingCx(),
@ -200,10 +178,6 @@ class mozJSComponentLoader final : public nsIMemoryReporter {
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
static already_AddRefed<nsIFactory> GetFactory(
const mozilla::Module& module, const mozilla::Module::CIDEntry& entry);
nsCOMPtr<xpcIJSGetFactory> getfactoryobj;
JS::PersistentRootedObject obj;
JS::PersistentRootedObject exports;
JS::PersistentRootedScript thisObjectKey;
@ -217,9 +191,6 @@ class mozJSComponentLoader final : public nsIMemoryReporter {
nsresult ExtractExports(JSContext* aCx, ComponentLoaderInfo& aInfo,
ModuleEntry* aMod, JS::MutableHandleObject aExports);
// Modules are intentionally leaked, but still cleared.
nsTHashMap<nsCStringHashKey, ModuleEntry*> mModules;
nsClassHashtable<nsCStringHashKey, ModuleEntry> mImports;
nsTHashMap<nsCStringHashKey, ModuleEntry*> mInProgressImports;

Просмотреть файл

@ -2541,13 +2541,6 @@ nsXPCComponents_Utils::GetLoadedModules(nsTArray<nsCString>& aLoadedModules) {
return mozJSComponentLoader::Get()->GetLoadedJSAndESModules(aLoadedModules);
}
NS_IMETHODIMP
nsXPCComponents_Utils::GetLoadedComponents(
nsTArray<nsCString>& aLoadedComponents) {
mozJSComponentLoader::Get()->GetLoadedComponents(aLoadedComponents);
return NS_OK;
}
NS_IMETHODIMP
nsXPCComponents_Utils::GetLoadedJSModules(
nsTArray<nsCString>& aLoadedJSModules) {
@ -2567,12 +2560,6 @@ nsXPCComponents_Utils::GetModuleImportStack(const nsACString& aLocation,
return mozJSComponentLoader::Get()->GetModuleImportStack(aLocation, aRetval);
}
NS_IMETHODIMP
nsXPCComponents_Utils::GetComponentLoadStack(const nsACString& aLocation,
nsACString& aRetval) {
return mozJSComponentLoader::Get()->GetComponentLoadStack(aLocation, aRetval);
}
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/

Просмотреть файл

@ -30,7 +30,6 @@ const MAC = AppConstants.platform == "macosx";
const backgroundtaskPhases = {
AfterRunBackgroundTaskNamed: {
allowlist: {
components: [], // At this time, no phase loads a JS component.
modules: [
"resource://gre/modules/AppConstants.jsm",
"resource://gre/modules/AsyncShutdown.jsm",
@ -118,7 +117,6 @@ const backgroundtaskPhases = {
},
AfterFindRunBackgroundTask: {
allowlist: {
components: [],
modules: [
// We have a profile marker for this, even though it failed to load!
"resource:///modules/backgroundtasks/BackgroundTask_wait.jsm",
@ -133,7 +131,6 @@ const backgroundtaskPhases = {
},
AfterAwaitRunBackgroundTask: {
allowlist: {
components: [],
modules: [],
services: [],
},
@ -225,7 +222,6 @@ add_task(async function test_xpcom_graph_wait() {
function newMarkers() {
return {
components: [], // The equivalent of `Cu.loadedComponents`.
modules: [], // The equivalent of `Cu.loadedModules`.
services: [],
};
@ -251,7 +247,6 @@ add_task(async function test_xpcom_graph_wait() {
if (
![
"ChromeUtils.import", // JSMs.
"JS XPCOM", // JavaScript XPCOM components.
"GetService", // XPCOM services.
].includes(markerName)
) {
@ -267,27 +262,6 @@ add_task(async function test_xpcom_graph_wait() {
}
}
if (markerName == "JS XPCOM") {
// The stack will always contain a label like
// `mozJSComponentLoader::LoadModule ...`. Extract the path from that.
let samples = markerData.stack.samples;
let stackId = samples.data[0][samples.schema.stack];
let stackLines = getStackFromProfile(profile, stackId, rootProfile.libs);
let component = stackLines
.filter(s => s.startsWith("mozJSComponentLoader::LoadModule"))[0]
.split(" ", 2)[1];
// Keep only the file name for components, as the path is an absolute file
// URL rather than a resource:// URL like for modules.
component = component.replace(/.*\//, "");
if (!markersForAllPhases.components.includes(component)) {
markersForAllPhases.components.push(component);
markersForCurrentPhase.components.push(component);
}
}
if (markerName == "GetService") {
// We get a CID from the marker itself, but not a human-readable contract
// ID. Now, most of the time, the stack will contain a label like

Просмотреть файл

@ -16,9 +16,7 @@ namespace mozilla {
/**
* A module implements one or more XPCOM components. This structure is used
* for both binary and script modules, but the registration members
* (cids/contractids/categoryentries) are unused for modules which are loaded
* via a module loader.
* for binary modules.
*/
struct Module {
static const unsigned int kVersion = 103;