зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1398198 - browser_startup.js should show the stack when a JS file was loaded earlier than expected, r=felipe,mccr8.
This commit is contained in:
Родитель
775235cd02
Коммит
c3c302fe33
|
@ -1,4 +1,6 @@
|
|||
[DEFAULT]
|
||||
prefs =
|
||||
browser.startup.record=true
|
||||
support-files =
|
||||
head.js
|
||||
[browser_appmenu_reflows.js]
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/* Set this to true only for debugging purpose; it makes the output noisy. */
|
||||
const kDumpAllStacks = false;
|
||||
|
||||
const startupPhases = {
|
||||
// For app-startup, we have a whitelist of acceptable JS files.
|
||||
// Anything loaded during app-startup must have a compelling reason
|
||||
|
@ -152,13 +155,25 @@ add_task(async function() {
|
|||
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
|
||||
await startupRecorder.done;
|
||||
|
||||
let loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
|
||||
let componentStacks = new Map();
|
||||
let data = 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(f => f.replace(/.*\//, ""))
|
||||
.filter(c => c != "startupRecorder.js");
|
||||
data[phase].components.map(uri => {
|
||||
let fileName = uri.replace(/.*\//, "");
|
||||
componentStacks.set(fileName, loader.getComponentLoadStack(uri));
|
||||
return fileName;
|
||||
}).filter(c => c != "startupRecorder.js");
|
||||
}
|
||||
|
||||
function printStack(scriptType, name) {
|
||||
if (scriptType == "modules")
|
||||
info(loader.getModuleImportStack(name));
|
||||
else if (scriptType == "components")
|
||||
info(componentStacks.get(name));
|
||||
}
|
||||
|
||||
// This block only adds debug output to help find the next bugs to file,
|
||||
|
@ -171,8 +186,11 @@ add_task(async function() {
|
|||
// phases are ordered, so if a script wasn't loaded yet at the immediate
|
||||
// previous phase, it wasn't loaded during any of the previous phases
|
||||
// either, and is new in the current phase.
|
||||
if (!previous || !data[previous][scriptType].includes(f))
|
||||
if (!previous || !data[previous][scriptType].includes(f)) {
|
||||
info(`${scriptType} loaded ${phase}: ${f}`);
|
||||
if (kDumpAllStacks)
|
||||
printStack(scriptType, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
previous = phase;
|
||||
|
@ -193,6 +211,7 @@ add_task(async function() {
|
|||
`should have no unexpected ${scriptType} loaded ${phase}`);
|
||||
for (let script of loadedList[scriptType]) {
|
||||
ok(false, `unexpected ${scriptType}: ${script}`);
|
||||
printStack(scriptType, script);
|
||||
}
|
||||
is(whitelist[scriptType].size, 0,
|
||||
`all ${scriptType} whitelist entries should have been used`);
|
||||
|
@ -205,7 +224,10 @@ add_task(async function() {
|
|||
if (blacklist) {
|
||||
for (let scriptType in blacklist) {
|
||||
for (let file of blacklist[scriptType]) {
|
||||
ok(!loadedList[scriptType].includes(file), `${file} is not allowed ${phase}`);
|
||||
let loaded = loadedList[scriptType].includes(file);
|
||||
ok(!loaded, `${file} is not allowed ${phase}`);
|
||||
if (loaded)
|
||||
printStack(scriptType, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ startupRecorder.prototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
record(name) {
|
||||
if (!Services.prefs.getBoolPref("browser.startup.record", false))
|
||||
return;
|
||||
|
||||
this.data.code[name] = {
|
||||
components: this.loader.loadedComponents(),
|
||||
modules: this.loader.loadedModules(),
|
||||
|
@ -81,6 +84,12 @@ startupRecorder.prototype = {
|
|||
Services.obs.removeObserver(this, topic);
|
||||
|
||||
if (topic == "sessionstore-windows-restored") {
|
||||
if (!Services.prefs.getBoolPref("browser.startup.record", false)) {
|
||||
this._resolve();
|
||||
this._resolve = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// We use idleDispatchToMainThread here to record the set of
|
||||
// loaded scripts after we are fully done with startup and ready
|
||||
// to react to user events.
|
||||
|
|
|
@ -8,11 +8,17 @@
|
|||
[scriptable, builtinclass, uuid(4f94b21f-2920-4bd9-8251-5fb60fb054b2)]
|
||||
interface xpcIJSModuleLoader : nsISupports
|
||||
{
|
||||
// These 2 functions are for startup testing purposes. They are not expected
|
||||
// These functions are for startup testing purposes. They are not expected
|
||||
// to be used for production code.
|
||||
void loadedModules([optional] out unsigned long length,
|
||||
[retval, array, size_is(length)] out string aModules);
|
||||
|
||||
void loadedComponents([optional] out unsigned long length,
|
||||
[retval, array, size_is(length)] out string aComponents);
|
||||
|
||||
// These 2 functions 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);
|
||||
};
|
||||
|
|
|
@ -433,6 +433,12 @@ mozJSComponentLoader::LoadModule(FileLocation& aFile)
|
|||
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.Put(spec, entry);
|
||||
|
||||
|
@ -1000,6 +1006,52 @@ NS_IMETHODIMP mozJSComponentLoader::LoadedComponents(uint32_t* length,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
mozJSComponentLoader::GetModuleImportStack(const nsACString& aLocation,
|
||||
nsACString& retval)
|
||||
{
|
||||
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
|
||||
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
MOZ_ASSERT(mInitialized);
|
||||
|
||||
ComponentLoaderInfo info(aLocation);
|
||||
nsresult rv = info.EnsureKey();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ModuleEntry* mod;
|
||||
if (!mImports.Get(info.Key(), &mod))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
retval = mod->importStack;
|
||||
return NS_OK;
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
mozJSComponentLoader::GetComponentLoadStack(const nsACString& aLocation,
|
||||
nsACString& retval)
|
||||
{
|
||||
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
|
||||
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
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId id)
|
||||
{
|
||||
|
@ -1100,6 +1152,13 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
|
|||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
|
||||
if (Preferences::GetBool("browser.startup.record", false)) {
|
||||
newEntry->importStack =
|
||||
xpc_PrintJSStack(callercx, false, false, false).get();
|
||||
}
|
||||
#endif
|
||||
|
||||
mod = newEntry;
|
||||
}
|
||||
|
||||
|
|
|
@ -163,6 +163,9 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
|
|||
obj = nullptr;
|
||||
thisObjectKey = nullptr;
|
||||
location = nullptr;
|
||||
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
|
||||
importStack.Truncate();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
@ -175,6 +178,9 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
|
|||
JS::PersistentRootedScript thisObjectKey;
|
||||
char* location;
|
||||
nsCString resolvedURL;
|
||||
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
|
||||
nsCString importStack;
|
||||
#endif
|
||||
};
|
||||
|
||||
static size_t DataEntrySizeOfExcludingThis(const nsACString& aKey, ModuleEntry* const& aData,
|
||||
|
|
Загрузка…
Ссылка в новой задаче