Bug 1363059 - Add a test for images loaded at startup vs. images shown at startup. r=florian,jwatt
This patch enables startupRecorder.js to collect data on loaded and shown raster and SVG images on startup via events from native code. It also adds a test that uses this data to find images that are unnecessarily loaded. I've not fixed any of the affected images yet, there's a fairly comprehensive whitelist that I want to gradually decrease by opening bugs in the respective components. MozReview-Commit-ID: 9KqQvKLtZhu
This commit is contained in:
@@ -3,6 +3,8 @@ support-files =
|
|||||||
head.js
|
head.js
|
||||||
[browser_appmenu_reflows.js]
|
[browser_appmenu_reflows.js]
|
||||||
[browser_startup.js]
|
[browser_startup.js]
|
||||||
|
[browser_startup_images.js]
|
||||||
|
skip-if = !debug
|
||||||
[browser_tabclose_grow_reflows.js]
|
[browser_tabclose_grow_reflows.js]
|
||||||
[browser_tabclose_reflows.js]
|
[browser_tabclose_reflows.js]
|
||||||
[browser_tabopen_reflows.js]
|
[browser_tabopen_reflows.js]
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ function test() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject.data;
|
let data = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject.data.code;
|
||||||
// Keep only the file name for components, as the path is an absolute file
|
// Keep only the file name for components, as the path is an absolute file
|
||||||
// URL rather than a resource:// URL like for modules.
|
// URL rather than a resource:// URL like for modules.
|
||||||
for (let phase in data) {
|
for (let phase in data) {
|
||||||
|
|||||||
201
browser/base/content/test/performance/browser_startup_images.js
Normal file
201
browser/base/content/test/performance/browser_startup_images.js
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* A whitelist of images that are loaded at startup but not shown.
|
||||||
|
* List items support the following attributes:
|
||||||
|
* - file: The location of the loaded image file.
|
||||||
|
* - hidpi: An alternative hidpi file location for retina screens, if one exists.
|
||||||
|
* May be the magic string <not loaded> in strange cases where
|
||||||
|
* only the low-resolution image is loaded but not shown.
|
||||||
|
* - platforms: An array of the platforms where the issue is occurring.
|
||||||
|
* Possible values are linux, win, macosx.
|
||||||
|
* - intermittentNotLoaded: an array of platforms where this image is
|
||||||
|
* intermittently not loaded, e.g. because it is
|
||||||
|
* loaded during the time we stop recording.
|
||||||
|
* - intermittentShown: An array of platforms where this image is
|
||||||
|
* intermittently shown, contrary to what our
|
||||||
|
* whitelist says.
|
||||||
|
*
|
||||||
|
* Please don't add items to this list. Please remove items from this list.
|
||||||
|
*/
|
||||||
|
const whitelist = [
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/fxa/sync-illustration.svg",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-overflow-indicator.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/stop.svg",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/sidebars.svg",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
intermittentNotLoaded: ["macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://pocket-shared/skin/pocket.svg",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
intermittentNotLoaded: ["macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/places/toolbarDropMarker.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tracking-protection-16.svg#enabled",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/toolbarbutton-dropdown-arrow.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://global/skin/icons/autoscroll.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-background-end.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/tab-background-end@2x.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-background-middle.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/tab-background-middle@2x.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-background-start.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/tab-background-start@2x.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tabDragIndicator.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/tabDragIndicator@2x.png",
|
||||||
|
platforms: ["linux", "win", "macosx"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "resource://gre-resources/loading-image.png",
|
||||||
|
platforms: ["win", "macosx"],
|
||||||
|
intermittentNotLoaded: ["win", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "resource://gre-resources/broken-image.png",
|
||||||
|
platforms: ["win", "macosx"],
|
||||||
|
intermittentNotLoaded: ["win", "macosx"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/places/unfiledBookmarks.png",
|
||||||
|
hidpi: "<not loaded>",
|
||||||
|
platforms: ["win", "macosx"],
|
||||||
|
intermittentNotLoaded: ["win"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/urlbar-history-dropmarker.png",
|
||||||
|
hidpi: "<not loaded>",
|
||||||
|
platforms: ["win", "macosx"],
|
||||||
|
intermittentShown: ["win"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://global/skin/icons/chevron.png",
|
||||||
|
hidpi: "chrome://global/skin/icons/chevron@2x.png",
|
||||||
|
platforms: ["macosx"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon@2x.png",
|
||||||
|
platforms: ["macosx"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://global/skin/toolbar/chevron.gif",
|
||||||
|
platforms: ["win", "linux"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/reload-stop-go.png",
|
||||||
|
platforms: ["win", "linux"],
|
||||||
|
intermittentShown: ["win"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/alltabs.png",
|
||||||
|
platforms: ["linux"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-arrow-left.svg",
|
||||||
|
platforms: ["win"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://global/skin/icons/resizer.png",
|
||||||
|
platforms: ["win"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://global/skin/icons/resizer.png",
|
||||||
|
platforms: ["win"],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-arrow-left.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/tab-arrow-left@2x.png",
|
||||||
|
platforms: ["linux", "macosx"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "chrome://browser/skin/tabbrowser/tab-arrow-right.png",
|
||||||
|
hidpi: "chrome://browser/skin/tabbrowser/tab-arrow-right@2x.png",
|
||||||
|
platforms: ["macosx"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
let data = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject.data.images;
|
||||||
|
let platformWhitelist = whitelist.filter(el => el.platforms.includes(AppConstants.platform));
|
||||||
|
|
||||||
|
let loadedImages = data["image-loading"];
|
||||||
|
let shownImages = data["image-drawing"];
|
||||||
|
|
||||||
|
for (let loaded of loadedImages.values()) {
|
||||||
|
let whitelistItem = platformWhitelist.find(el => {
|
||||||
|
if (window.devicePixelRatio >= 2 && el.hidpi && el.hidpi == loaded) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return el.file == loaded;
|
||||||
|
});
|
||||||
|
if (whitelistItem) {
|
||||||
|
if (!whitelistItem.intermittentShown ||
|
||||||
|
!whitelistItem.intermittentShown.includes(AppConstants.platform)) {
|
||||||
|
todo(shownImages.has(loaded), `Loaded image ${loaded} is not shown but whitelisted.`);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ok(shownImages.has(loaded), `Loaded image ${loaded} was shown.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unneeded whitelist entries.
|
||||||
|
for (let item of platformWhitelist) {
|
||||||
|
if (!item.intermittentNotLoaded ||
|
||||||
|
!item.intermittentNotLoaded.includes(AppConstants.platform)) {
|
||||||
|
if (window.devicePixelRatio >= 2 && item.hidpi) {
|
||||||
|
if (item.hidpi != "<not loaded>") {
|
||||||
|
ok(loadedImages.has(item.hidpi), `Whitelisted image ${item.hidpi} was loaded.`);
|
||||||
|
ok(!shownImages.has(item.hidpi), `Whitelisted image ${item.hidpi} was not shown.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ok(loadedImages.has(item.file), `Whitelisted image ${item.file} was loaded.`);
|
||||||
|
ok(!shownImages.has(item.file), `Whitelisted image ${item.file} was not shown.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,13 @@ if (AppConstants.platform == "linux")
|
|||||||
function startupRecorder() {
|
function startupRecorder() {
|
||||||
this.wrappedJSObject = this;
|
this.wrappedJSObject = this;
|
||||||
this.loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
|
this.loader = Cc["@mozilla.org/moz/jsloader;1"].getService(Ci.xpcIJSModuleLoader);
|
||||||
this.data = {};
|
this.data = {
|
||||||
|
images: {
|
||||||
|
"image-drawing": new Set(),
|
||||||
|
"image-loading": new Set(),
|
||||||
|
},
|
||||||
|
code: {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
startupRecorder.prototype = {
|
startupRecorder.prototype = {
|
||||||
classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
|
classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
|
||||||
@@ -34,7 +40,7 @@ startupRecorder.prototype = {
|
|||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||||
|
|
||||||
record(name) {
|
record(name) {
|
||||||
this.data[name] = {
|
this.data.code[name] = {
|
||||||
components: this.loader.loadedComponents(),
|
components: this.loader.loadedComponents(),
|
||||||
modules: this.loader.loadedModules(),
|
modules: this.loader.loadedModules(),
|
||||||
services: Object.keys(Cc).filter(c => {
|
services: Object.keys(Cc).filter(c => {
|
||||||
@@ -57,6 +63,8 @@ startupRecorder.prototype = {
|
|||||||
let topics = [
|
let topics = [
|
||||||
"profile-do-change", // This catches stuff loaded during app-startup
|
"profile-do-change", // This catches stuff loaded during app-startup
|
||||||
"toplevel-window-ready", // Catches stuff from final-ui-startup
|
"toplevel-window-ready", // Catches stuff from final-ui-startup
|
||||||
|
"image-loading",
|
||||||
|
"image-drawing",
|
||||||
firstPaintNotification,
|
firstPaintNotification,
|
||||||
"sessionstore-windows-restored",
|
"sessionstore-windows-restored",
|
||||||
];
|
];
|
||||||
@@ -65,9 +73,16 @@ startupRecorder.prototype = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (topic == "image-drawing" || topic == "image-loading") {
|
||||||
|
this.data.images[topic].add(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Services.obs.removeObserver(this, topic);
|
Services.obs.removeObserver(this, topic);
|
||||||
|
|
||||||
if (topic == "sessionstore-windows-restored") {
|
if (topic == "sessionstore-windows-restored") {
|
||||||
|
Services.obs.removeObserver(this, "image-drawing");
|
||||||
|
Services.obs.removeObserver(this, "image-loading");
|
||||||
// We use idleDispatchToMainThread here to record the set of
|
// We use idleDispatchToMainThread here to record the set of
|
||||||
// loaded scripts after we are fully done with startup and ready
|
// loaded scripts after we are fully done with startup and ready
|
||||||
// to react to user events.
|
// to react to user events.
|
||||||
|
|||||||
@@ -92,6 +92,18 @@ ImageFactory::CreateImage(nsIRequest* aRequest,
|
|||||||
// Compute the image's initialization flags.
|
// Compute the image's initialization flags.
|
||||||
uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
|
uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Record the image load for startup performance testing.
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||||
|
if (NS_WARN_IF(obs)) {
|
||||||
|
nsAutoCString spec;
|
||||||
|
aURI->GetSpec(spec);
|
||||||
|
obs->NotifyObservers(nullptr, "image-loading", NS_ConvertUTF8toUTF16(spec).get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Select the type of image to create based on MIME type.
|
// Select the type of image to create based on MIME type.
|
||||||
if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
|
if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
|
||||||
return CreateVectorImage(aRequest, aProgressTracker, aMimeType,
|
return CreateVectorImage(aRequest, aProgressTracker, aMimeType,
|
||||||
|
|||||||
@@ -1399,6 +1399,19 @@ RasterImage::DrawInternal(DrawableSurface&& aSurface,
|
|||||||
ImageRegion region(aRegion);
|
ImageRegion region(aRegion);
|
||||||
bool frameIsFinished = aSurface->IsFinished();
|
bool frameIsFinished = aSurface->IsFinished();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Record the image drawing for startup performance testing.
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||||
|
if (NS_WARN_IF(obs)) {
|
||||||
|
nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
|
||||||
|
nsAutoCString spec;
|
||||||
|
imageURI->GetSpec(spec);
|
||||||
|
obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// By now we may have a frame with the requested size. If not, we need to
|
// By now we may have a frame with the requested size. If not, we need to
|
||||||
// adjust the drawing parameters accordingly.
|
// adjust the drawing parameters accordingly.
|
||||||
IntSize finalSize = aSurface->GetImageSize();
|
IntSize finalSize = aSurface->GetImageSize();
|
||||||
|
|||||||
@@ -1025,6 +1025,19 @@ VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
|
|||||||
aParams.samplingFilter,
|
aParams.samplingFilter,
|
||||||
aParams.flags, aParams.opacity);
|
aParams.flags, aParams.opacity);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Record the image drawing for startup performance testing.
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||||
|
if (NS_WARN_IF(obs)) {
|
||||||
|
nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
|
||||||
|
nsAutoCString spec;
|
||||||
|
imageURI->GetSpec(spec);
|
||||||
|
obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
|
MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
|
||||||
mRenderingObserver->ResumeHonoringInvalidations();
|
mRenderingObserver->ResumeHonoringInvalidations();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user