Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-03-02 16:09:27 +01:00
Родитель ae97eae57b de8d151c26
Коммит 32191b51ac
150 изменённых файлов: 1899 добавлений и 1484 удалений

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

@ -15,6 +15,7 @@ const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC =
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
@ -322,27 +323,24 @@ DistributionCustomizer.prototype = {
if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"]))
return this._checkCustomizationComplete();
let defaults = this._prefSvc.getDefaultBranch(null);
let defaults = new Preferences({defaultBranch: true});
// Global really contains info we set as prefs. They're only
// separate because they are "special" (read: required)
defaults.setCharPref("distribution.id", this._ini.getString("Global", "id"));
defaults.setCharPref("distribution.version",
this._ini.getString("Global", "version"));
defaults.set("distribution.id", this._ini.getString("Global", "id"));
defaults.set("distribution.version", this._ini.getString("Global", "version"));
let partnerAbout = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
let partnerAbout;
try {
if (globalPrefs["about." + this._locale]) {
partnerAbout.data = this._ini.getString("Global", "about." + this._locale);
partnerAbout = this._ini.getString("Global", "about." + this._locale);
} else if (globalPrefs["about." + this._language]) {
partnerAbout.data = this._ini.getString("Global", "about." + this._language);
partnerAbout = this._ini.getString("Global", "about." + this._language);
} else {
partnerAbout.data = this._ini.getString("Global", "about");
partnerAbout = this._ini.getString("Global", "about");
}
defaults.setComplexValue("distribution.about",
Ci.nsISupportsString, partnerAbout);
defaults.set("distribution.about", partnerAbout);
} catch (e) {
/* ignore bad prefs due to bug 895473 and move on */
Cu.reportError(e);
@ -352,28 +350,11 @@ DistributionCustomizer.prototype = {
for (let key of enumerate(this._ini.getKeys("Preferences"))) {
try {
let value = parseValue(this._ini.getString("Preferences", key));
switch (typeof value) {
case "boolean":
defaults.setBoolPref(key, value);
break;
case "number":
defaults.setIntPref(key, value);
break;
case "string":
defaults.setCharPref(key, value);
break;
case "undefined":
defaults.setCharPref(key, value);
break;
}
Preferences.set(key, value);
} catch (e) { /* ignore bad prefs and move on */ }
}
}
// We eval() the localizable prefs as well (even though they'll
// always get set as a string) to keep the INI format consistent:
// string prefs always need to be in quotes
let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"].
createInstance(Ci.nsIPrefLocalizedString);
@ -385,7 +366,7 @@ DistributionCustomizer.prototype = {
let value = parseValue(this._ini.getString("LocalizablePreferences-" + this._locale, key));
if (value !== undefined) {
localizedStr.data = "data:text/plain," + key + "=" + value;
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
}
usedLocalizablePreferences.push(key);
} catch (e) { /* ignore bad prefs and move on */ }
@ -401,7 +382,7 @@ DistributionCustomizer.prototype = {
let value = parseValue(this._ini.getString("LocalizablePreferences-" + this._language, key));
if (value !== undefined) {
localizedStr.data = "data:text/plain," + key + "=" + value;
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
}
usedLocalizablePreferences.push(key);
} catch (e) { /* ignore bad prefs and move on */ }
@ -419,7 +400,7 @@ DistributionCustomizer.prototype = {
value = value.replace(/%LOCALE%/g, this._locale);
value = value.replace(/%LANGUAGE%/g, this._language);
localizedStr.data = "data:text/plain," + key + "=" + value;
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
}
} catch (e) { /* ignore bad prefs and move on */ }
}

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

@ -33,20 +33,13 @@ function BrowserAction(options, extension) {
this.tabManager = TabManager.for(extension);
let title = extension.localize(options.default_title || "");
let popup = extension.localize(options.default_popup || "");
if (popup) {
popup = extension.baseURI.resolve(popup);
}
this.defaults = {
enabled: true,
title: title || extension.name,
title: options.default_title || extension.name,
badgeText: "",
badgeBackgroundColor: null,
icon: IconDetails.normalize({path: options.default_icon}, extension,
null, true),
popup: popup,
icon: IconDetails.normalize({path: options.default_icon}, extension),
popup: options.default_popup || "",
};
this.tabContext = new TabContext(tab => Object.create(this.defaults),

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

@ -19,18 +19,11 @@ function PageAction(options, extension) {
this.tabManager = TabManager.for(extension);
let title = extension.localize(options.default_title || "");
let popup = extension.localize(options.default_popup || "");
if (popup) {
popup = extension.baseURI.resolve(popup);
}
this.defaults = {
show: false,
title: title || extension.name,
icon: IconDetails.normalize({path: options.default_icon}, extension,
null, true),
popup: popup && extension.baseURI.resolve(popup),
title: options.default_title || extension.name,
icon: IconDetails.normalize({path: options.default_icon}, extension),
popup: options.default_popup || "",
};
this.tabContext = new TabContext(tab => Object.create(this.defaults),

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

@ -656,6 +656,19 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
message, recipient);
},
detectLanguage: function(tabId) {
let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
if (!tab) {
return Promise.reject({message: `Invalid tab ID: ${tabId}`});
}
let browser = tab.linkedBrowser;
let recipient = {innerWindowID: browser.innerWindowID};
return context.sendMessage(browser.messageManager, "Extension:DetectLanguage",
{}, recipient);
},
_execute: function(tabId, details, kind, method) {
let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
let mm = tab.linkedBrowser.messageManager;

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

@ -36,7 +36,7 @@ global.IconDetails = {
//
// If no context is specified, instead of throwing an error, this
// function simply logs a warning message.
normalize(details, extension, context = null, localize = false) {
normalize(details, extension, context = null) {
let result = {};
try {
@ -73,12 +73,7 @@ global.IconDetails = {
throw new Error(`Invalid icon size ${size}, must be an integer`);
}
let url = path[size];
if (localize) {
url = extension.localize(url);
}
url = baseURI.resolve(path[size]);
let url = baseURI.resolve(path[size]);
// The Chrome documentation specifies these parameters as
// relative paths. We currently accept absolute URLs as well,

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

@ -12,9 +12,21 @@
"browser_action": {
"type": "object",
"properties": {
"default_title": { "type": "string", "optional": true },
"default_icon": { "$ref": "IconPath", "optional": true },
"default_popup": { "type": "string", "format": "relativeUrl", "optional": true }
"default_title": {
"type": "string",
"optional": true,
"preprocess": "localize"
},
"default_icon": {
"$ref": "IconPath",
"optional": true
},
"default_popup": {
"type": "string",
"format": "relativeUrl",
"optional": true,
"preprocess": "localize"
}
},
"optional": true
}

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

@ -12,9 +12,21 @@
"page_action": {
"type": "object",
"properties": {
"default_title": { "type": "string", "optional": true },
"default_icon": { "$ref": "IconPath", "optional": true },
"default_popup": { "type": "string", "format": "relativeUrl", "optional": true }
"default_title": {
"type": "string",
"optional": true,
"preprocess": "localize"
},
"default_icon": {
"$ref": "IconPath",
"optional": true
},
"default_popup": {
"type": "string",
"format": "relativeUrl",
"optional": true,
"preprocess": "localize"
}
},
"optional": true
}

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

@ -15,6 +15,7 @@
// Test harness globals
"ExtensionTestUtils": false,
"TestUtils": false,
"clickBrowserAction": true,
"clickPageAction": true,

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

@ -9,6 +9,8 @@ support-files =
file_popup_api_injection_b.html
file_iframe_document.html
file_iframe_document.sjs
file_language_fr_en.html
file_language_ja.html
[browser_ext_simple.js]
[browser_ext_currentWindow.js]
@ -28,6 +30,7 @@ support-files =
[browser_ext_runtime_setUninstallURL.js]
[browser_ext_tabs_audio.js]
[browser_ext_tabs_captureVisibleTab.js]
[browser_ext_tabs_detectLanguage.js]
[browser_ext_tabs_events.js]
[browser_ext_tabs_executeScript.js]
[browser_ext_tabs_executeScript_good.js]
@ -50,4 +53,5 @@ support-files =
[browser_ext_windows_update.js]
[browser_ext_contentscript_connect.js]
[browser_ext_tab_runtimeConnect.js]
[browser_ext_topwindowid.js]
[browser_ext_webNavigation_getFrames.js]

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

@ -82,6 +82,8 @@ function* runTests(options) {
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
files: options.files || {},
background: `(${background})(${options.getTests})`,
});
@ -140,12 +142,29 @@ add_task(function* testTabSwitchContext() {
manifest: {
"browser_action": {
"default_icon": "default.png",
"default_popup": "default.html",
"default_title": "Default Title",
"default_popup": "__MSG_popup__",
"default_title": "Default __MSG_title__",
},
"default_locale": "en",
"permissions": ["tabs"],
},
"files": {
"_locales/en/messages.json": {
"popup": {
"message": "default.html",
"description": "Popup",
},
"title": {
"message": "Title",
"description": "Title",
},
},
},
getTests(tabs, expectDefaults) {
let details = [
{"icon": browser.runtime.getURL("default.png"),

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

@ -82,6 +82,8 @@ function* runTests(options) {
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
files: options.files || {},
background: `(${background})(${options.getTests})`,
});
@ -154,13 +156,29 @@ add_task(function* testTabSwitchContext() {
"page_action": {
"default_icon": "default.png",
"default_popup": "default.html",
"default_title": "Default Title \u263a",
"default_popup": "__MSG_popup__",
"default_title": "Default __MSG_title__ \u263a",
},
"default_locale": "en",
"permissions": ["tabs"],
},
"files": {
"_locales/en/messages.json": {
"popup": {
"message": "default.html",
"description": "Popup",
},
"title": {
"message": "Title",
"description": "Title",
},
},
},
getTests(tabs) {
let details = [
{"icon": browser.runtime.getURL("default.png"),

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

@ -0,0 +1,57 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* testDetectLanguage() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
},
background() {
const BASE_PATH = "browser/browser/components/extensions/test/browser";
function loadTab(url) {
let tabId;
let awaitUpdated = new Promise(resolve => {
browser.tabs.onUpdated.addListener(function onUpdated(changedTabId, changed, tab) {
if (changedTabId === tabId && changed.url) {
browser.tabs.onUpdated.removeListener(onUpdated);
resolve(tab);
}
});
});
return browser.tabs.create({url}).then(tab => {
tabId = tab.id;
return awaitUpdated;
});
}
loadTab(`http://example.co.jp/${BASE_PATH}/file_language_ja.html`).then(tab => {
return browser.tabs.detectLanguage(tab.id).then(lang => {
browser.test.assertEq("ja", lang, "Japanese document should be detected as Japanese");
return browser.tabs.remove(tab.id);
});
}).then(() => {
return loadTab(`http://example.co.jp/${BASE_PATH}/file_language_fr_en.html`);
}).then(tab => {
return browser.tabs.detectLanguage(tab.id).then(lang => {
browser.test.assertEq("fr", lang, "French/English document should be detected as primarily French");
return browser.tabs.remove(tab.id);
});
}).then(() => {
browser.test.notifyPass("detectLanguage");
}).catch(e => {
browser.test.fail(`Error: ${e} :: ${e.stack}`);
browser.test.notifyFail("detectLanguage");
});
},
});
yield extension.startup();
yield extension.awaitFinish("detectLanguage");
yield extension.unload();
});

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

@ -2,6 +2,95 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* tabsSendMessageReply() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://example.com/"],
"js": ["content-script.js"],
"run_at": "document_start",
}],
},
background: function() {
let promiseResponse = new Promise(resolve => {
browser.runtime.onMessage.addListener((msg, sender, respond) => {
if (msg == "content-script-ready") {
let tabId = sender.tab.id;
browser.tabs.sendMessage(tabId, "respond-never", response => {
browser.test.fail("Got unexpected response callback");
browser.test.notifyFail("sendMessage");
});
Promise.all([
promiseResponse,
browser.tabs.sendMessage(tabId, "respond-now"),
new Promise(resolve => browser.tabs.sendMessage(tabId, "respond-soon", resolve)),
browser.tabs.sendMessage(tabId, "respond-promise"),
browser.tabs.sendMessage(tabId, "respond-never"),
browser.tabs.sendMessage(tabId, "respond-error").catch(error => Promise.resolve({error})),
browser.tabs.sendMessage(tabId, "throw-error").catch(error => Promise.resolve({error})),
]).then(([response, respondNow, respondSoon, respondPromise, respondNever, respondError, throwError]) => {
browser.test.assertEq("expected-response", response, "Content script got the expected response");
browser.test.assertEq("respond-now", respondNow, "Got the expected immediate response");
browser.test.assertEq("respond-soon", respondSoon, "Got the expected delayed response");
browser.test.assertEq("respond-promise", respondPromise, "Got the expected promise response");
browser.test.assertEq(undefined, respondNever, "Got the expected no-response resolution");
browser.test.assertEq("respond-error", respondError.error.message, "Got the expected error response");
browser.test.assertEq("throw-error", throwError.error.message, "Got the expected thrown error response");
return browser.tabs.remove(tabId);
}).then(() => {
browser.test.notifyPass("sendMessage");
});
return Promise.resolve("expected-response");
} else if (msg[0] == "got-response") {
resolve(msg[1]);
}
});
});
browser.tabs.create({url: "http://example.com/"});
},
files: {
"content-script.js": function() {
browser.runtime.onMessage.addListener((msg, sender, respond) => {
if (msg == "respond-now") {
respond(msg);
} else if (msg == "respond-soon") {
setTimeout(() => { respond(msg); }, 0);
return true;
} else if (msg == "respond-promise") {
return Promise.resolve(msg);
} else if (msg == "respond-never") {
return;
} else if (msg == "respond-error") {
return Promise.reject(new Error(msg));
} else if (msg == "throw-error") {
throw new Error(msg);
}
});
browser.runtime.sendMessage("content-script-ready").then(response => {
browser.runtime.sendMessage(["got-response", response]);
});
},
},
});
yield extension.startup();
yield extension.awaitFinish("sendMessage");
yield extension.unload();
});
add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {

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

@ -0,0 +1,23 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* test_topwindowid_cleanup() {
let {Frames} = Cu.import("resource://gre/modules/ExtensionManagement.jsm", {});
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
let {outerWindowID, messageManager} = tab.linkedBrowser;
ok(Frames.topWindowIds.has(outerWindowID), "Outer window ID is registered");
let awaitDisconnect = TestUtils.topicObserved("message-manager-disconnect",
subject => subject === messageManager);
yield BrowserTestUtils.removeTab(tab);
yield awaitDisconnect;
ok(!Frames.topWindowIds.has(outerWindowID), "Outer window ID is no longer registered");
});

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

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
France is the largest country in Western Europe and the third-largest in Europe as a whole.
A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter
Cet article concerne le pays européen aujourdhui appelé République française. Pour dautres usages du nom France,
Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus.
Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumps over the lazy dog.
</body>
</html>

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

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
このペ ジでは アカウントに指定された予算の履歴を一覧にしています それぞれの項目には 予算額と特定期間のステ タスが表示されます 現在または今後の予算を設定するには
</body>
</html>

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

@ -5,6 +5,7 @@
id=disttest
version=1.0
about=Test distribution file
about.en-US=Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè
[Preferences]
distribution.test.string="Test String"

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

@ -62,6 +62,10 @@ add_task(function* () {
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
Assert.equal(Services.prefs.getCharPref("distribution.id"), "disttest");
Assert.equal(Services.prefs.getCharPref("distribution.version"), "1.0");
Assert.equal(Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString).data, "Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè");
Assert.equal(Services.prefs.getCharPref("distribution.test.string"), "Test String");
Assert.equal(Services.prefs.getCharPref("distribution.test.string.noquotes"), "Test String");
Assert.equal(Services.prefs.getIntPref("distribution.test.int"), 777);

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

@ -24,6 +24,7 @@ var helpers = {};
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var { TargetFactory } = require("devtools/client/framework/target");
var Services = require("Services");
var assert = { ok: ok, is: is, log: info };
var util = require('gcli/util/util');

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

@ -100,13 +100,24 @@ function _removeOrDisableBreakpoint(location, isDisabled) {
}
const bpClient = getBreakpointClient(bp.actor);
return dispatch({
const action = {
type: constants.REMOVE_BREAKPOINT,
breakpoint: bp,
disabled: isDisabled,
[PROMISE]: bpClient.remove()
});
disabled: isDisabled
};
// If the breakpoint is already disabled, we don't need to remove
// it from the server. We just need to dispatch an action
// simulating a successful server request to remove it, and it
// will be removed completely from the state.
if(!bp.disabled) {
return dispatch(Object.assign({}, action, {
[PROMISE]: bpClient.remove()
}));
}
else {
return dispatch(Object.assign({}, action, { status: "done" }));
}
}
}

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

@ -5,6 +5,7 @@
const constants = require('../constants');
const promise = require('promise');
const Services = require('Services');
const { dumpn } = require("devtools/shared/DevToolsUtils");
const { PROMISE, HISTOGRAM_ID } = require('devtools/client/shared/redux/middleware/promise');
const { getSource, getSourceText } = require('../queries');

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

@ -1,6 +1,7 @@
/* 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/. */
/* import-globals-from ../../debugger-controller.js */
"use strict";
const utils = require('../utils');

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

@ -4,6 +4,7 @@
* 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/. */
/* globals document, window */
/* import-globals-from ./debugger-controller.js */
"use strict";
// Maps known URLs to friendly source group names and put them at the

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

@ -9,6 +9,7 @@
const { Ci, Cu } = require("chrome");
const promise = require("promise");
const EventEmitter = require("devtools/shared/event-emitter");
const Services = require("Services");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);

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

@ -6,8 +6,8 @@
"use strict";
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ./shared-head.js */
// Currently this file expects "promise" to be imported into scope.
/* globals promise */
// Common utility functions for working with Redux stores. The file is meant
// to be safe to load in both mochitest and xpcshell environments.

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

@ -10,6 +10,7 @@ const {Cu} = require("chrome");
const {setTimeout, clearTimeout} =
Cu.import("resource://gre/modules/Timer.jsm", {});
const {gDevTools} = require("devtools/client/framework/devtools");
const Services = require("Services");
const DEFAULT_PREVIEW_TEXT = "Abc";
const PREVIEW_UPDATE_DELAY = 150;

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

@ -28,6 +28,7 @@ const {editableField, InplaceEditor} =
require("devtools/client/shared/inplace-editor");
const {HTMLEditor} = require("devtools/client/inspector/markup/html-editor");
const promise = require("promise");
const Services = require("Services");
const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
const EventEmitter = require("devtools/shared/event-emitter");
const Heritage = require("sdk/core/heritage");

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

@ -26,6 +26,7 @@ const {
SELECTOR_PSEUDO_CLASS
} = require("devtools/client/shared/css-parsing-utils");
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
XPCOMUtils.defineLazyGetter(this, "_strings", function() {

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

@ -4,6 +4,8 @@
"use strict";
/* globals Services, sendAsyncMessage, addMessageListener */
// XXX Some helper API could go to testing/mochitest/tests/SimpleTest/AsyncContentUtils.js
// (or at least to share test API in devtools)

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

@ -7,6 +7,7 @@
"use strict";
const {Cu, Cc, Ci} = require("chrome");
const Services = require("Services");
const {getMostRecentBrowserWindow} = require("sdk/window/utils");
const OPEN_FLAGS = {

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

@ -48,14 +48,6 @@ aggregate.mb=%S MB
# it has a creation time to display.
snapshot-title.loading=Processing…
# LOCALIZATION NOTE (checkbox.invertTree): The label describing the boolean
# checkbox whether or not to invert the tree.
checkbox.invertTree=Invert tree
# LOCALIZATION NOTE (checkbox.invertTree): The tooltip for the label describing
# the boolean checkbox whether or not to invert the tree.
checkbox.invertTree.tooltip=When uninverted, the tree is shown top-down, giving an overview of memory consumption. When inverted, the tree is shown bottom-up, highlighting the heaviest memory consumers.
# LOCALIZATION NOTE (checkbox.recordAllocationStacks): The label describing the
# boolean checkbox whether or not to record allocation stacks.
checkbox.recordAllocationStacks=Record allocation stacks
@ -65,29 +57,33 @@ checkbox.recordAllocationStacks=Record allocation stacks
# stacks.
checkbox.recordAllocationStacks.tooltip=Toggle the recording of allocation stacks. Subsequent heap snapshots will be able to label and group objects created when allocation stack recording is active by their allocation stack. Recording allocation stacks has a performance overhead.
# LOCALIZATION NOTE (toolbar.breakdownBy): The label describing the select menu
# options of the breakdown options.
toolbar.breakdownBy=Group by:
# LOCALIZATION NOTE (toolbar.displayBy): The label describing the select menu
# options of the display options.
toolbar.displayBy=Group by:
# LOCALIZATION NOTE (toolbar.breakdownBy): The tooltip for the label describing
# the select menu options of the breakdown options.
toolbar.breakdownBy.tooltip=Change how objects are grouped
# LOCALIZATION NOTE (toolbar.displayBy.tooltip): The tooltip for the label
# describing the select menu options of the display options.
toolbar.displayBy.tooltip=Change how objects are grouped
# LOCALIZATION NOTE (breakdowns.coarseType.tooltip): The tooltip for the "coarse
# type" breakdown option.
breakdowns.coarseType.tooltip=Group items into broad categories
# LOCALIZATION NOTE (censusDisplays.coarseType.tooltip): The tooltip for the
# "coarse type" display option.
censusDisplays.coarseType.tooltip=Group items by their type
# LOCALIZATION NOTE (breakdowns.allocationStack.tooltip): The tooltip for the
# "allocation stack" breakdown option.
breakdowns.allocationStack.tooltip=Group items by the JavaScript stack recorded when the object was allocated
# LOCALIZATION NOTE (censusDisplays.allocationStack.tooltip): The tooltip for
# the "allocation stack" display option.
censusDisplays.allocationStack.tooltip=Group items by the JavaScript stack recorded when the object was allocated
# LOCALIZATION NOTE (breakdowns.objectClass.tooltip): The tooltip for the
# "object class" breakdown option.
breakdowns.objectClass.tooltip=Group items by their JavaScript Object [[class]] name
# LOCALIZATION NOTE (censusDisplays.invertedAllocationStack.tooltip): The
# tooltip for the "inverted allocation stack" display option.
censusDisplays.invertedAllocationStack.tooltip=Group items by the inverted JavaScript call stack recorded when the object was created
# LOCALIZATION NOTE (breakdowns.internalType.tooltip): The tooltip for the
# "internal type" breakdown option.
breakdowns.internalType.tooltip=Group items by their internal C++ type
# LOCALIZATION NOTE (censusDisplays.objectClass.tooltip): The tooltip for the
# "object class" display option.
censusDisplays.objectClass.tooltip=Group items by their JavaScript Object [[class]] name
# LOCALIZATION NOTE (censusDisplays.internalType.tooltip): The tooltip for the
# "internal type" display option.
censusDisplays.internalType.tooltip=Group items by their internal C++ type
# LOCALIZATION NOTE (toolbar.labelBy): The label describing the select menu
# options of the label options.
@ -97,17 +93,17 @@ toolbar.labelBy=Label by:
# select menu options of the label options.
toolbar.labelBy.tooltip=Change how objects are labeled
# LOCALIZATION NOTE (dominatorTreeBreakdowns.coarseType.tooltip): The tooltip for the "coarse
# type" dominator tree breakdown option.
dominatorTreeBreakdowns.coarseType.tooltip=Label objects by the broad categories they fit in
# LOCALIZATION NOTE (dominatorTreeDisplays.coarseType.tooltip): The tooltip for
# the "coarse type" dominator tree display option.
dominatorTreeDisplays.coarseType.tooltip=Label objects by the broad categories they fit in
# LOCALIZATION NOTE (dominatorTreeBreakdowns.allocationStack.tooltip): The
# tooltip for the "allocation stack" dominator tree breakdown option.
dominatorTreeBreakdowns.allocationStack.tooltip=Label objects by the JavaScript stack recorded when it was allocated
# LOCALIZATION NOTE (dominatorTreeDisplays.allocationStack.tooltip): The
# tooltip for the "allocation stack" dominator tree display option.
dominatorTreeDisplays.allocationStack.tooltip=Label objects by the JavaScript stack recorded when it was allocated
# LOCALIZATION NOTE (dominatorTreeBreakdowns.internalType.tooltip): The
# tooltip for the "internal type" dominator tree breakdown option.
dominatorTreeBreakdowns.internalType.tooltip=Label objects by their internal C++ type name
# LOCALIZATION NOTE (dominatorTreeDisplays.internalType.tooltip): The
# tooltip for the "internal type" dominator tree display option.
dominatorTreeDisplays.internalType.tooltip=Label objects by their internal C++ type name
# LOCALIZATION NOTE (toolbar.view): The label for the view selector in the
# toolbar.
@ -301,8 +297,8 @@ snapshot.state.saving-census=Saving census…
snapshot.state.error=Error
# LOCALIZATION NOTE (heapview.noAllocationStacks): The message displayed to
# users when selecting a breakdown by "allocation stack" but no allocation
# stacks were recorded in the heap snapshot.
# users when selecting a display by "allocation stack" but no allocation stacks
# were recorded in the heap snapshot.
heapview.noAllocationStacks=No allocation stacks found. Record allocation stacks before taking a heap snapshot.
# LOCALIZATION NOTE (heapview.field.retainedSize): The name of the column in the

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

@ -18,6 +18,9 @@ responsiveUI.rotate2=Rotate
# LOCALIZATION NOTE (responsiveUI.screenshot): tooltip of the screenshot button.
responsiveUI.screenshot=Screenshot
# LOCALIZATION NOTE (responsiveUI.userAgentPlaceholder): placeholder for the user agent input.
responsiveUI.userAgentPlaceholder=Custom User Agent
# LOCALIZATION NOTE (responsiveUI.screenshotGeneratedFilename): The auto generated filename.
# The first argument (%1$S) is the date string in yyyy-mm-dd format and the second
# argument (%2$S) is the time string in HH.MM.SS format.

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

@ -98,11 +98,6 @@ stacktrace.anonymousFunction=<anonymous>
# %S is the "Async Cause" of the frame.
stacktrace.asyncStack=(Async: %S)
# LOCALIZATION NOTE (unknownLocation): this string is used to
# display messages with sources that have an unknown location, eg. from
# console.trace() calls.
unknownLocation=<unknown>
# LOCALIZATION NOTE (timerStarted): this string is used to display the result
# of the console.time() call. Parameters: %S is the name of the timer.
timerStarted=%S: timer started

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

@ -1,35 +0,0 @@
/* 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/. */
"use strict";
const { assert } = require("devtools/shared/DevToolsUtils");
const { breakdownEquals, createSnapshot } = require("../utils");
const { actions, snapshotState: states } = require("../constants");
const { refresh } = require("./refresh");
const setBreakdownAndRefresh = exports.setBreakdownAndRefresh = function (heapWorker, breakdown) {
return function *(dispatch, getState) {
// Clears out all stored census data and sets the breakdown.
dispatch(setBreakdown(breakdown));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all census data in the snapshots and sets
* a new breakdown.
*
* @param {Breakdown} breakdown
*/
const setBreakdown = exports.setBreakdown = function (breakdown) {
assert(typeof breakdown === "object"
&& breakdown
&& breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(breakdown)}`);
return {
type: actions.SET_BREAKDOWN,
breakdown,
};
};

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

@ -0,0 +1,34 @@
/* 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/. */
"use strict";
const { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");
exports.setCensusDisplayAndRefresh = function(heapWorker, display) {
return function*(dispatch, getState) {
dispatch(setCensusDisplay(display));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all cached census data in the snapshots and sets new display data
* for censuses.
*
* @param {censusDisplayModel} display
*/
const setCensusDisplay = exports.setCensusDisplay = function(display) {
assert(typeof display === "object"
&& display
&& display.breakdown
&& display.breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(display)}`);
return {
type: actions.SET_CENSUS_DISPLAY,
display,
};
};

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

@ -7,7 +7,6 @@ const { assert, reportException } = require("devtools/shared/DevToolsUtils");
const { actions, diffingState, viewState } = require("../constants");
const telemetry = require("../telemetry");
const {
breakdownEquals,
getSnapshot,
censusIsUpToDate,
snapshotIsDiffable
@ -51,11 +50,10 @@ const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, sec
`Second snapshot must be in a diffable state, found ${second.state}`);
let report, parentMap;
let inverted = getState().inverted;
let breakdown = getState().breakdown;
let display = getState().censusDisplay;
let filter = getState().filter;
if (censusIsUpToDate(inverted, filter, breakdown, getState().diffing.census)) {
if (censusIsUpToDate(filter, display, getState().diffing.census)) {
return;
}
@ -70,36 +68,36 @@ const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, sec
return;
}
inverted = getState().inverted;
breakdown = getState().breakdown;
display = getState().censusDisplay;
filter = getState().filter;
dispatch({
type: actions.TAKE_CENSUS_DIFF_START,
first,
second,
inverted,
filter,
breakdown,
display,
});
let opts = inverted ? { asInvertedTreeNode: true } : { asTreeNode: true };
let opts = display.inverted
? { asInvertedTreeNode: true }
: { asTreeNode: true };
opts.filter = filter || null;
try {
({ delta: report, parentMap } = yield heapWorker.takeCensusDiff(first.path,
second.path,
{ breakdown },
opts));
({ delta: report, parentMap } = yield heapWorker.takeCensusDiff(
first.path,
second.path,
{ breakdown: display.breakdown },
opts));
} catch (error) {
reportException("actions/diffing/takeCensusDiff", error);
dispatch({ type: actions.DIFFING_ERROR, error });
return;
}
}
while (inverted !== getState().inverted
|| filter !== getState().filter
|| !breakdownEquals(breakdown, getState().breakdown));
while (filter !== getState().filter
|| display !== getState().censusDisplay);
dispatch({
type: actions.TAKE_CENSUS_DIFF_END,
@ -107,19 +105,18 @@ const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, sec
second,
report,
parentMap,
inverted,
filter,
breakdown,
display,
});
telemetry.countDiff({ inverted, filter, breakdown });
telemetry.countDiff({ filter, display });
};
};
/**
* Ensure that the current diffing data is up to date with the currently
* selected breakdown, filter, inversion, etc. If the state is not up-to-date,
* then a recompute is triggered.
* selected display, filter, etc. If the state is not up-to-date, then a
* recompute is triggered.
*
* @param {HeapAnalysesClient} heapWorker
*/

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

@ -0,0 +1,35 @@
/* 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/. */
"use strict";
const { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");
exports.setDominatorTreeDisplayAndRefresh = function(heapWorker, display) {
return function*(dispatch, getState) {
// Clears out all stored census data and sets the display.
dispatch(setDominatorTreeDisplay(display));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all census data in the snapshots and sets
* a new display.
*
* @param {dominatorTreeDisplayModel} display
*/
const setDominatorTreeDisplay = exports.setDominatorTreeDisplay = function (display) {
assert(typeof display === "object"
&& display
&& display.breakdown
&& display.breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(display)}`);
return {
type: actions.SET_DOMINATOR_TREE_DISPLAY,
display,
};
};

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

@ -1,37 +0,0 @@
/* 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/. */
"use strict";
const { assert } = require("devtools/shared/DevToolsUtils");
const { breakdownEquals, createSnapshot } = require("../utils");
const { actions, snapshotState: states } = require("../constants");
const { refresh } = require("./refresh");
const setDominatorTreeBreakdownAndRefresh =
exports.setDominatorTreeBreakdownAndRefresh =
function (heapWorker, breakdown) {
return function *(dispatch, getState) {
// Clears out all stored census data and sets the breakdown.
dispatch(setDominatorTreeBreakdown(breakdown));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all census data in the snapshots and sets
* a new breakdown.
*
* @param {Breakdown} breakdown
*/
const setDominatorTreeBreakdown = exports.setDominatorTreeBreakdown = function (breakdown) {
assert(typeof breakdown === "object"
&& breakdown
&& breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(breakdown)}`);
return {
type: actions.SET_DOMINATOR_TREE_BREAKDOWN,
breakdown,
};
};

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

@ -1,18 +0,0 @@
/* 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/. */
"use strict";
const { actions } = require("../constants");
const { refresh } = require("./refresh");
const toggleInverted = exports.toggleInverted = function () {
return { type: actions.TOGGLE_INVERTED };
};
exports.toggleInvertedAndRefresh = function (heapWorker) {
return function* (dispatch, getState) {
dispatch(toggleInverted());
yield dispatch(refresh(heapWorker));
};
};

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

@ -5,11 +5,10 @@
DevToolsModules(
'allocations.js',
'breakdown.js',
'census-display.js',
'diffing.js',
'dominatorTreeBreakdown.js',
'dominator-tree-display.js',
'filter.js',
'inverted.js',
'io.js',
'refresh.js',
'sizes.js',

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

@ -7,7 +7,6 @@ const { assert, reportException } = require("devtools/shared/DevToolsUtils");
const {
censusIsUpToDate,
getSnapshot,
breakdownEquals,
createSnapshot,
dominatorTreeIsComputed,
} = require("../utils");
@ -44,7 +43,7 @@ const takeSnapshotAndCensus = exports.takeSnapshotAndCensus = function (front, h
/**
* Selects a snapshot and if the snapshot's census is using a different
* breakdown, take a new census.
* display, take a new census.
*
* @param {HeapAnalysesClient} heapWorker
* @param {snapshotId} id
@ -137,65 +136,63 @@ const takeCensus = exports.takeCensus = function (heapWorker, id) {
`Can only take census of snapshots in READ or SAVED_CENSUS state, found ${snapshot.state}`);
let report, parentMap;
let inverted = getState().inverted;
let breakdown = getState().breakdown;
let display = getState().censusDisplay;
let filter = getState().filter;
// If breakdown, filter and inversion haven't changed, don't do anything.
if (censusIsUpToDate(inverted, filter, breakdown, snapshot.census)) {
// If display, filter and inversion haven't changed, don't do anything.
if (censusIsUpToDate(filter, display, snapshot.census)) {
return;
}
// Keep taking a census if the breakdown changes during. Recheck
// that the breakdown used for the census is the same as
// the state's breakdown.
// Keep taking a census if the display changes while our request is in
// flight. Recheck that the display used for the census is the same as the
// state's display.
do {
inverted = getState().inverted;
breakdown = getState().breakdown;
display = getState().censusDisplay;
filter = getState().filter;
dispatch({
type: actions.TAKE_CENSUS_START,
id,
inverted,
filter,
breakdown
display
});
let opts = inverted ? { asInvertedTreeNode: true } : { asTreeNode: true };
let opts = display.inverted
? { asInvertedTreeNode: true }
: { asTreeNode: true };
opts.filter = filter || null;
try {
({ report, parentMap } = yield heapWorker.takeCensus(snapshot.path,
{ breakdown },
opts));
({ report, parentMap } = yield heapWorker.takeCensus(
snapshot.path,
{ breakdown: display.breakdown },
opts));
} catch (error) {
reportException("takeCensus", error);
dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
return;
}
}
while (inverted !== getState().inverted ||
filter !== getState().filter ||
!breakdownEquals(breakdown, getState().breakdown));
while (filter !== getState().filter ||
display !== getState().censusDisplay);
dispatch({
type: actions.TAKE_CENSUS_END,
id,
breakdown,
inverted,
display,
filter,
report,
parentMap
});
telemetry.countCensus({ inverted, filter, breakdown });
telemetry.countCensus({ filter, display });
};
};
/**
* Refresh the selected snapshot's census data, if need be (for example,
* breakdown configuration changed).
* display configuration changed).
*
* @param {HeapAnalysesClient} heapWorker
*/
@ -261,18 +258,19 @@ const fetchDominatorTree = exports.fetchDominatorTree = function (heapWorker, id
assert(dominatorTreeIsComputed(snapshot),
"Should have dominator tree model and it should be computed");
let breakdown;
let display;
let root;
do {
breakdown = getState().dominatorTreeBreakdown;
assert(breakdown, "Should have a breakdown to describe nodes with.");
display = getState().dominatorTreeDisplay;
assert(display && display.breakdown,
`Should have a breakdown to describe nodes with, got: ${uneval(display)}`);
dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, breakdown });
dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, display });
try {
root = yield heapWorker.getDominatorTree({
dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
breakdown,
breakdown: display.breakdown,
});
} catch (error) {
reportException("actions/snapshot/fetchDominatorTree", error);
@ -280,10 +278,10 @@ const fetchDominatorTree = exports.fetchDominatorTree = function (heapWorker, id
return null;
}
}
while (!breakdownEquals(breakdown, getState().dominatorTreeBreakdown));
while (display !== getState().dominatorTreeDisplay);
dispatch({ type: actions.FETCH_DOMINATOR_TREE_END, id, root });
telemetry.countDominatorTree({ breakdown });
telemetry.countDominatorTree({ display });
return root;
};
};
@ -305,18 +303,18 @@ const fetchImmediatelyDominated = exports.fetchImmediatelyDominated = function (
"Cannot fetch immediately dominated nodes in a dominator tree unless " +
" the dominator tree has already been computed");
let breakdown;
let display;
let response;
do {
breakdown = getState().dominatorTreeBreakdown;
assert(breakdown, "Should have a breakdown to describe nodes with.");
display = getState().dominatorTreeDisplay;
assert(display, "Should have a display to describe nodes with.");
dispatch({ type: actions.FETCH_IMMEDIATELY_DOMINATED_START, id });
try {
response = yield heapWorker.getImmediatelyDominated({
dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
breakdown,
breakdown: display.breakdown,
nodeId: lazyChildren.parentNodeId(),
startIndex: lazyChildren.siblingIndex(),
});
@ -326,7 +324,7 @@ const fetchImmediatelyDominated = exports.fetchImmediatelyDominated = function (
return null;
}
}
while (!breakdownEquals(breakdown, getState().dominatorTreeBreakdown));
while (display !== getState().dominatorTreeDisplay);
dispatch({
type: actions.FETCH_IMMEDIATELY_DOMINATED_END,

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

@ -6,10 +6,14 @@ const { assert } = require("devtools/shared/DevToolsUtils");
const { appinfo } = require("Services");
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { breakdowns, diffingState, viewState } = require("./constants");
const { censusDisplays, dominatorTreeDisplays, diffingState, viewState } = require("./constants");
const { toggleRecordingAllocationStacks } = require("./actions/allocations");
const { setBreakdownAndRefresh } = require("./actions/breakdown");
const { setDominatorTreeBreakdownAndRefresh } = require("./actions/dominatorTreeBreakdown");
const { setCensusDisplayAndRefresh } = require("./actions/census-display");
const { setDominatorTreeDisplayAndRefresh } = require("./actions/dominator-tree-display");
const {
getCustomCensusDisplays,
getCustomDominatorTreeDisplays,
} = require("devtools/client/memory/utils");
const {
selectSnapshotForDiffingAndRefresh,
toggleDiffing,
@ -17,7 +21,6 @@ const {
collapseDiffingCensusNode,
focusDiffingCensusNode,
} = require("./actions/diffing");
const { toggleInvertedAndRefresh } = require("./actions/inverted");
const { setFilterStringAndRefresh } = require("./actions/filter");
const { pickFileAndExportSnapshot, pickFileAndImportSnapshotAndCensus } = require("./actions/io");
const {
@ -34,12 +37,6 @@ const {
} = require("./actions/snapshot");
const { changeViewAndRefresh } = require("./actions/view");
const { resizeShortestPaths } = require("./actions/sizes");
const {
breakdownNameToSpec,
getBreakdownDisplayData,
dominatorTreeBreakdownNameToSpec,
getDominatorTreeBreakdownDisplayData,
} = require("./utils");
const Toolbar = createFactory(require("./components/toolbar"));
const List = createFactory(require("./components/list"));
const SnapshotListItem = createFactory(require("./components/snapshot-list-item"));
@ -87,12 +84,14 @@ const MemoryApp = createClass({
let isOSX = appinfo.OS == "Darwin";
let isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey);
// On ACCEL+UP, select previous snapshot.
if (isAccelKey && e.key === "ArrowUp") {
let previousIndex = Math.max(0, selectedIndex - 1);
let previousSnapshotId = snapshots[previousIndex].id;
dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId));
}
// On ACCEL+DOWN, select next snapshot.
if (isAccelKey && e.key === "ArrowDown") {
let nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1);
@ -101,15 +100,40 @@ const MemoryApp = createClass({
}
},
_getCensusDisplays() {
const customDisplays = getCustomCensusDisplays();
const custom = Object.keys(customDisplays).reduce((arr, key) => {
arr.push(customDisplays[key]);
return arr;
}, []);
return [
censusDisplays.coarseType,
censusDisplays.allocationStack,
censusDisplays.invertedAllocationStack,
].concat(custom);
},
_getDominatorTreeDisplays() {
const customDisplays = getCustomDominatorTreeDisplays();
const custom = Object.keys(customDisplays).reduce((arr, key) => {
arr.push(customDisplays[key]);
return arr;
}, []);
return [
dominatorTreeDisplays.coarseType,
dominatorTreeDisplays.allocationStack,
].concat(custom);
},
render() {
let {
dispatch,
snapshots,
front,
heapWorker,
breakdown,
allocations,
inverted,
toolbox,
filter,
diffing,
@ -131,30 +155,24 @@ const MemoryApp = createClass({
Toolbar({
snapshots,
breakdowns: getBreakdownDisplayData(),
censusDisplays: this._getCensusDisplays(),
onCensusDisplayChange: newDisplay =>
dispatch(setCensusDisplayAndRefresh(heapWorker, newDisplay)),
onImportClick: () => dispatch(pickFileAndImportSnapshotAndCensus(heapWorker)),
onClearSnapshotsClick: () => dispatch(clearSnapshots(heapWorker)),
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
onBreakdownChange: breakdown =>
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),
onToggleRecordAllocationStacks: () =>
dispatch(toggleRecordingAllocationStacks(front)),
allocations,
inverted,
onToggleInverted: () =>
dispatch(toggleInvertedAndRefresh(heapWorker)),
filterString: filter,
setFilterString: filterString =>
dispatch(setFilterStringAndRefresh(filterString, heapWorker)),
diffing,
onToggleDiffing: () => dispatch(toggleDiffing()),
view,
dominatorTreeBreakdowns: getDominatorTreeBreakdownDisplayData(),
onDominatorTreeBreakdownChange: breakdown => {
const spec = dominatorTreeBreakdownNameToSpec(breakdown);
assert(spec, "Should have a breakdown spec");
dispatch(setDominatorTreeBreakdownAndRefresh(heapWorker, spec));
},
dominatorTreeDisplays: this._getDominatorTreeDisplays(),
onDominatorTreeDisplayChange: newDisplay =>
dispatch(setDominatorTreeDisplayAndRefresh(heapWorker, newDisplay)),
onViewChange: v => dispatch(changeViewAndRefresh(v, heapWorker)),
}),

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

@ -66,7 +66,7 @@ const Census = module.exports = createClass({
getPercentBytes,
getPercentCount,
showSign: !!diffing,
inverted: census.inverted,
inverted: census.display.inverted,
}),
getRoots: () => report.children || [],
getKey: node => node.id,

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

@ -258,7 +258,7 @@ const Heap = module.exports = createClass({
_renderCensus(state, census, diffing, onViewSourceInDebugger) {
const contents = [];
if (census.breakdown.by === "allocationStack"
if (census.display.breakdown.by === "allocationStack"
&& census.report
&& census.report.children
&& census.report.children.length === 1

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

@ -12,11 +12,6 @@ const { isSavedFrame } = require("devtools/shared/DevToolsUtils");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const { L10N } = require("../utils");
const { ViewHelpers } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
const COMPONENTS_STRINGS_URI = "chrome://devtools/locale/components.properties";
const componentsL10N = new ViewHelpers.L10N(COMPONENTS_STRINGS_URI);
const UNKNOWN_SOURCE_STRING = componentsL10N.getStr("frame.unknownSource");
const GRAPH_DEFAULTS = {
translate: [20, 20],
scale: 1
@ -33,7 +28,7 @@ function stringifyLabel(label, id) {
const piece = label[i];
if (isSavedFrame(piece)) {
const { short } = getSourceNames(piece.source, UNKNOWN_SOURCE_STRING);
const { short } = getSourceNames(piece.source);
sanitized[i] = `${piece.functionDisplayName} @ ${short}:${piece.line}:${piece.column}`;
} else if (piece === NO_STACK) {
sanitized[i] = L10N.getStr("tree-item.nostack");

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

@ -1,6 +1,7 @@
/* 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/. */
"use strict";
const { assert } = require("devtools/shared/DevToolsUtils");
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
@ -8,32 +9,30 @@ const { L10N } = require("../utils");
const models = require("../models");
const { viewState } = require("../constants");
const Toolbar = module.exports = createClass({
module.exports = createClass({
displayName: "Toolbar",
propTypes: {
breakdowns: PropTypes.arrayOf(PropTypes.shape({
censusDisplays: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
})).isRequired,
onTakeSnapshotClick: PropTypes.func.isRequired,
onImportClick: PropTypes.func.isRequired,
onClearSnapshotsClick: PropTypes.func.isRequired,
onBreakdownChange: PropTypes.func.isRequired,
onCensusDisplayChange: PropTypes.func.isRequired,
onToggleRecordAllocationStacks: PropTypes.func.isRequired,
allocations: models.allocations,
onToggleInverted: PropTypes.func.isRequired,
inverted: PropTypes.bool.isRequired,
filterString: PropTypes.string,
setFilterString: PropTypes.func.isRequired,
diffing: models.diffingModel,
onToggleDiffing: PropTypes.func.isRequired,
view: PropTypes.string.isRequired,
onViewChange: PropTypes.func.isRequired,
dominatorTreeBreakdowns: PropTypes.arrayOf(PropTypes.shape({
dominatorTreeDisplays: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
})).isRequired,
onDominatorTreeBreakdownChange: PropTypes.func.isRequired,
onDominatorTreeDisplayChange: PropTypes.func.isRequired,
snapshots: PropTypes.arrayOf(models.snapshot).isRequired,
},
@ -42,14 +41,12 @@ const Toolbar = module.exports = createClass({
onTakeSnapshotClick,
onImportClick,
onClearSnapshotsClick,
onBreakdownChange,
breakdowns,
dominatorTreeBreakdowns,
onDominatorTreeBreakdownChange,
onCensusDisplayChange,
censusDisplays,
dominatorTreeDisplays,
onDominatorTreeDisplayChange,
onToggleRecordAllocationStacks,
allocations,
onToggleInverted,
inverted,
filterString,
setFilterString,
snapshots,
@ -68,20 +65,24 @@ const Toolbar = module.exports = createClass({
dom.label(
{
className: "breakdown-by",
title: L10N.getStr("toolbar.breakdownBy.tooltip"),
className: "display-by",
title: L10N.getStr("toolbar.displayBy.tooltip"),
},
L10N.getStr("toolbar.breakdownBy"),
L10N.getStr("toolbar.displayBy"),
dom.select(
{
id: "select-breakdown",
className: "select-breakdown",
onChange: e => onBreakdownChange(e.target.value),
id: "select-display",
className: "select-display",
onChange: e => {
const newDisplay =
censusDisplays.find(b => b.displayName === e.target.value);
onCensusDisplayChange(newDisplay);
},
},
breakdowns.map(({ name, tooltip, displayName }) => dom.option(
censusDisplays.map(({ tooltip, displayName }) => dom.option(
{
key: name,
value: name,
key: `display-${displayName}`,
value: displayName,
title: tooltip,
},
displayName
@ -89,19 +90,6 @@ const Toolbar = module.exports = createClass({
)
),
dom.label(
{
title: L10N.getStr("checkbox.invertTree.tooltip")
},
dom.input({
id: "invert-tree-checkbox",
type: "checkbox",
checked: inverted,
onChange: onToggleInverted,
}),
L10N.getStr("checkbox.invertTree")
),
dom.div({ id: "toolbar-spacer", className: "spacer" }),
dom.input({
@ -111,7 +99,7 @@ const Toolbar = module.exports = createClass({
placeholder: L10N.getStr("filter.placeholder"),
title: L10N.getStr("filter.tooltip"),
onChange: event => setFilterString(event.target.value),
value: !!filterString ? filterString : undefined,
value: filterString || undefined,
})
);
} else {
@ -130,13 +118,17 @@ const Toolbar = module.exports = createClass({
L10N.getStr("toolbar.labelBy"),
dom.select(
{
id: "select-dominator-tree-breakdown",
onChange: e => onDominatorTreeBreakdownChange(e.target.value),
id: "select-dominator-tree-display",
onChange: e => {
const newDisplay =
dominatorTreeDisplays.find(b => b.displayName === e.target.value);
onDominatorTreeDisplayChange(newDisplay);
},
},
dominatorTreeBreakdowns.map(({ name, tooltip, displayName }) => dom.option(
dominatorTreeDisplays.map(({ tooltip, displayName }) => dom.option(
{
key: name,
value: name,
key: `dominator-tree-display-${displayName}`,
value: displayName,
title: tooltip,
},
displayName

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

@ -65,11 +65,11 @@ actions.TAKE_CENSUS_DIFF_START = "take-census-diff-start";
actions.TAKE_CENSUS_DIFF_END = "take-census-diff-end";
actions.DIFFING_ERROR = "diffing-error";
// Fired to set a new breakdown.
actions.SET_BREAKDOWN = "set-breakdown";
// Fired to set a new census display.
actions.SET_CENSUS_DISPLAY = "set-census-display";
// Fired to change the breakdown that controls the dominator tree labels.
actions.SET_DOMINATOR_TREE_BREAKDOWN = "set-dominator-tree-breakdown";
// Fired to change the display that controls the dominator tree labels.
actions.SET_DOMINATOR_TREE_DISPLAY = "set-dominator-tree-display";
// Fired when changing between census or dominators view.
actions.CHANGE_VIEW = "change-view";
@ -103,23 +103,24 @@ actions.COLLAPSE_DOMINATOR_TREE_NODE = "collapse-dominator-tree-node";
actions.RESIZE_SHORTEST_PATHS = "resize-shortest-paths";
/*** Breakdowns ***************************************************************/
/*** Census Displays ***************************************************************/
const COUNT = { by: "count", count: true, bytes: true };
const INTERNAL_TYPE = { by: "internalType", then: COUNT };
const ALLOCATION_STACK = { by: "allocationStack", then: COUNT, noStack: COUNT };
const OBJECT_CLASS = { by: "objectClass", then: COUNT, other: COUNT };
const COUNT = Object.freeze({ by: "count", count: true, bytes: true });
const INTERNAL_TYPE = Object.freeze({ by: "internalType", then: COUNT });
const ALLOCATION_STACK = Object.freeze({ by: "allocationStack", then: COUNT, noStack: COUNT });
const OBJECT_CLASS = Object.freeze({ by: "objectClass", then: COUNT, other: COUNT });
const breakdowns = exports.breakdowns = {
coarseType: {
exports.censusDisplays = Object.freeze({
coarseType: Object.freeze({
displayName: "Type",
get tooltip() {
// Importing down here is necessary because of the circular dependency
// this introduces with `./utils.js`.
const { L10N } = require("./utils");
return L10N.getStr("breakdowns.coarseType.tooltip");
return L10N.getStr("censusDisplays.coarseType.tooltip");
},
breakdown: {
inverted: true,
breakdown: Object.freeze({
by: "coarseType",
objects: OBJECT_CLASS,
strings: COUNT,
@ -129,57 +130,68 @@ const breakdowns = exports.breakdowns = {
noFilename: INTERNAL_TYPE
},
other: INTERNAL_TYPE,
}
},
})
}),
allocationStack: {
allocationStack: Object.freeze({
displayName: "Call Stack",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("breakdowns.allocationStack.tooltip");
return L10N.getStr("censusDisplays.allocationStack.tooltip");
},
inverted: false,
breakdown: ALLOCATION_STACK,
},
};
}),
const DOMINATOR_TREE_LABEL_COARSE_TYPE = {
invertedAllocationStack: Object.freeze({
displayName: "Inverted Call Stack",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("censusDisplays.invertedAllocationStack.tooltip");
},
inverted: true,
breakdown: ALLOCATION_STACK,
}),
});
const DOMINATOR_TREE_LABEL_COARSE_TYPE = Object.freeze({
by: "coarseType",
objects: OBJECT_CLASS,
scripts: {
scripts: Object.freeze({
by: "internalType",
then: {
then: Object.freeze({
by: "filename",
then: COUNT,
noFilename: COUNT,
},
},
}),
}),
strings: INTERNAL_TYPE,
other: INTERNAL_TYPE,
};
});
const dominatorTreeBreakdowns = exports.dominatorTreeBreakdowns = {
coarseType: {
exports.dominatorTreeDisplays = Object.freeze({
coarseType: Object.freeze({
displayName: "Type",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("dominatorTreeBreakdowns.coarseType.tooltip");
return L10N.getStr("dominatorTreeDisplays.coarseType.tooltip");
},
breakdown: DOMINATOR_TREE_LABEL_COARSE_TYPE
},
}),
allocationStack: {
allocationStack: Object.freeze({
displayName: "Call Stack",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("dominatorTreeBreakdowns.allocationStack.tooltip");
return L10N.getStr("dominatorTreeDisplays.allocationStack.tooltip");
},
breakdown: {
breakdown: Object.freeze({
by: "allocationStack",
then: DOMINATOR_TREE_LABEL_COARSE_TYPE,
noStack: DOMINATOR_TREE_LABEL_COARSE_TYPE,
},
},
};
}),
}),
});
/*** View States **************************************************************/

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

@ -45,12 +45,32 @@ function catchAndIgnore(fn) {
}
/**
* The breakdown object DSL describing how we want
* the census data to be.
* The data describing the census report's shape, and its associated metadata.
*
* @see `js/src/doc/Debugger/Debugger.Memory.md`
*/
let breakdownModel = exports.breakdown = PropTypes.shape({
by: PropTypes.string.isRequired,
const censusDisplayModel = exports.censusDisplay = PropTypes.shape({
displayName: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
inverted: PropTypes.bool.isRequired,
breakdown: PropTypes.shape({
by: PropTypes.string.isRequired,
})
});
/**
* How we want to label nodes in the dominator tree, and associated
* metadata. The notable difference from `censusDisplayModel` is the lack of
* an `inverted` property.
*
* @see `js/src/doc/Debugger/Debugger.Memory.md`
*/
const dominatorTreeDisplayModel = exports.dominatorTreeDisplay = PropTypes.shape({
displayName: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
breakdown: PropTypes.shape({
by: PropTypes.string.isRequired,
})
});
let censusModel = exports.censusModel = PropTypes.shape({
@ -58,10 +78,8 @@ let censusModel = exports.censusModel = PropTypes.shape({
report: PropTypes.object,
// The parent map for the report.
parentMap: PropTypes.object,
// The breakdown used to generate the current census
breakdown: breakdownModel,
// Whether the currently cached report tree is inverted or not.
inverted: PropTypes.bool,
// The display data used to generate the current census.
display: censusDisplayModel,
// If present, the currently cached report's filter string used for pruning
// the tree items.
filter: PropTypes.string,
@ -99,9 +117,9 @@ let dominatorTreeModel = exports.dominatorTreeModel = PropTypes.shape({
PropTypes.object,
]),
// The breakdown used to generate descriptive labels of nodes in this
// dominator tree.
breakdown: breakdownModel,
// The display used to generate descriptive labels of nodes in this dominator
// tree.
display: dominatorTreeDisplayModel,
// The number of active requests to incrementally fetch subtrees. This should
// only be non-zero when the state is INCREMENTAL_FETCHING.
@ -197,7 +215,8 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
if (shouldHavePath.includes(current) && !snapshot.path) {
throw new Error(`Snapshots in state ${current} must have a snapshot path.`);
}
if (shouldHaveCensus.includes(current) && (!snapshot.census || !snapshot.census.breakdown)) {
if (shouldHaveCensus.includes(current) &&
(!snapshot.census || !snapshot.census.display || !snapshot.census.display.breakdown)) {
throw new Error(`Snapshots in state ${current} must have a census and breakdown.`);
}
if (shouldHaveCreationTime.includes(current) && !snapshot.creationTime) {
@ -269,22 +288,16 @@ let appModel = exports.app = {
// {HeapAnalysesClient} Used to interface with snapshots
heapWorker: PropTypes.instanceOf(HeapAnalysesClient),
// The breakdown object DSL describing how we want
// the census data to be.
// @see `js/src/doc/Debugger/Debugger.Memory.md`
breakdown: breakdownModel.isRequired,
// The display data describing how we want the census data to be.
censusDisplay: censusDisplayModel.isRequired,
// The breakdown object DSL describing how we want
// the dominator tree labels to be computed.
// @see `js/src/doc/Debugger/Debugger.Memory.md`
dominatorTreeBreakdown: breakdownModel.isRequired,
// The display data describing how we want the dominator tree labels to be
// computed.
dominatorTreeDisplay: dominatorTreeDisplayModel.isRequired,
// List of reference to all snapshots taken
snapshots: PropTypes.arrayOf(snapshotModel).isRequired,
// True iff we want the tree displayed inverted.
inverted: PropTypes.bool.isRequired,
// If present, a filter string for pruning the tree items.
filter: PropTypes.string,

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

@ -4,12 +4,11 @@
"use strict";
exports.allocations = require("./reducers/allocations");
exports.breakdown = require("./reducers/breakdown");
exports.censusDisplay = require("./reducers/census-display");
exports.diffing = require("./reducers/diffing");
exports.dominatorTreeBreakdown = require("./reducers/dominatorTreeBreakdown");
exports.dominatorTreeDisplay = require("./reducers/dominator-tree-display");
exports.errors = require("./reducers/errors");
exports.filter = require("./reducers/filter");
exports.inverted = require("./reducers/inverted");
exports.sizes = require("./reducers/sizes");
exports.snapshots = require("./reducers/snapshots");
exports.view = require("./reducers/view");

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

@ -1,17 +1,18 @@
/* 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/. */
"use strict";
const { actions, breakdowns } = require("../constants");
const DEFAULT_BREAKDOWN = breakdowns.coarseType.breakdown;
const { actions, censusDisplays } = require("../constants");
const DEFAULT_CENSUS_DISPLAY = censusDisplays.coarseType;
let handlers = Object.create(null);
handlers[actions.SET_BREAKDOWN] = function (_, action) {
return Object.assign({}, action.breakdown);
handlers[actions.SET_CENSUS_DISPLAY] = function(_, { display }) {
return display;
};
module.exports = function (state=DEFAULT_BREAKDOWN, action) {
module.exports = function(state = DEFAULT_CENSUS_DISPLAY, action) {
let handle = handlers[action.type];
if (handle) {
return handle(state, action);

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

@ -65,7 +65,7 @@ handlers[actions.TAKE_CENSUS_DIFF_START] = function (diffing, action) {
report: null,
inverted: action.inverted,
filter: action.filter,
breakdown: action.breakdown,
display: action.display,
}
});
};
@ -85,7 +85,7 @@ handlers[actions.TAKE_CENSUS_DIFF_END] = function (diffing, action) {
expanded: new Set(),
inverted: action.inverted,
filter: action.filter,
breakdown: action.breakdown,
display: action.display,
}
});
};

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

@ -4,16 +4,16 @@
"use strict";
const { actions, dominatorTreeBreakdowns } = require("../constants");
const DEFAULT_BREAKDOWN = dominatorTreeBreakdowns.coarseType.breakdown;
const { actions, dominatorTreeDisplays } = require("../constants");
const DEFAULT_DOMINATOR_TREE_DISPLAY = dominatorTreeDisplays.coarseType;
const handlers = Object.create(null);
handlers[actions.SET_DOMINATOR_TREE_BREAKDOWN] = function (_, { breakdown }) {
return breakdown;
handlers[actions.SET_DOMINATOR_TREE_DISPLAY] = function (_, { display }) {
return display;
};
module.exports = function (state = DEFAULT_BREAKDOWN, action) {
module.exports = function (state = DEFAULT_DOMINATOR_TREE_DISPLAY, action) {
const handler = handlers[action.type];
return handler ? handler(state, action) : state;
};

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

@ -1,14 +0,0 @@
/* 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/. */
"use strict";
const { actions } = require("../constants");
module.exports = function (inverted = false, action) {
if (action.type === actions.TOGGLE_INVERTED) {
return !inverted;
} else {
return inverted;
}
};

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

@ -5,12 +5,11 @@
DevToolsModules(
'allocations.js',
'breakdown.js',
'census-display.js',
'diffing.js',
'dominatorTreeBreakdown.js',
'dominator-tree-display.js',
'errors.js',
'filter.js',
'inverted.js',
'sizes.js',
'snapshots.js',
'view.js',

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

@ -52,11 +52,10 @@ handlers[actions.READ_SNAPSHOT_END] = function (snapshots, { id, creationTime })
});
};
handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, breakdown, inverted, filter }) {
handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, display, filter }) {
const census = {
report: null,
breakdown,
inverted,
display,
filter,
};
@ -70,15 +69,13 @@ handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, breakdown, inve
handlers[actions.TAKE_CENSUS_END] = function (snapshots, { id,
report,
parentMap,
breakdown,
inverted,
display,
filter }) {
const census = {
report,
parentMap,
expanded: new Set(),
breakdown,
inverted,
display,
filter,
};
@ -194,7 +191,7 @@ handlers[actions.COMPUTE_DOMINATOR_TREE_END] = function (snapshots, { id, domina
});
};
handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, breakdown }) {
handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, display }) {
return snapshots.map(snapshot => {
if (snapshot.id !== id) {
return snapshot;
@ -208,7 +205,7 @@ handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, breakd
const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
state: dominatorTreeState.FETCHING,
root: undefined,
breakdown,
display,
});
return immutableUpdate(snapshot, { dominatorTree });
});

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

@ -10,7 +10,7 @@
const { telemetry } = require("Services");
const { makeInfallible, immutableUpdate } = require("devtools/shared/DevToolsUtils");
const { dominatorTreeBreakdowns, breakdowns } = require("./constants");
const { dominatorTreeDisplays, censusDisplays } = require("./constants");
exports.countTakeSnapshot = makeInfallible(function () {
const histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_TAKE_SNAPSHOT_COUNT");
@ -29,26 +29,22 @@ exports.countExportSnapshot = makeInfallible(function () {
const COARSE_TYPE = "Coarse Type";
const ALLOCATION_STACK = "Allocation Stack";
const OBJECT_CLASS = "Object Class";
const INTERNAL_TYPE = "Internal Type";
const INVERTED_ALLOCATION_STACK = "Inverted Allocation Stack";
const CUSTOM = "Custom";
/**
* @param {Boolean} inverted
* True if the census was inverted, false otherwise.
*
* @param {String|null} filter
* The filter string used, if any.
*
* @param {Boolean} diffing
* True if the census was a diffing census, false otherwise.
*
* @param {Object} breakdown
* The breakdown used with the census.
* @param {censusDisplayModel} display
* The display used with the census.
*/
exports.countCensus = makeInfallible(function ({ inverted, filter, diffing, breakdown }) {
exports.countCensus = makeInfallible(function ({ filter, diffing, display }) {
let histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_INVERTED_CENSUS");
histogram.add(!!inverted);
histogram.add(!!display.inverted);
histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_FILTER_CENSUS");
histogram.add(!!filter);
@ -57,10 +53,12 @@ exports.countCensus = makeInfallible(function ({ inverted, filter, diffing, brea
histogram.add(!!diffing);
histogram = telemetry.getKeyedHistogramById("DEVTOOLS_MEMORY_BREAKDOWN_CENSUS_COUNT");
if (breakdown === breakdowns.coarseType.breakdown) {
if (display === censusDisplays.coarseType) {
histogram.add(COARSE_TYPE);
} else if (breakdown === breakdowns.allocationStack.breakdown) {
} else if (display === censusDisplays.allocationStack) {
histogram.add(ALLOCATION_STACK);
} else if (display === censusDisplays.invertedAllocationStack) {
histogram.add(INVERTED_ALLOCATION_STACK);
} else {
histogram.add(CUSTOM);
}
@ -75,17 +73,17 @@ exports.countDiff = makeInfallible(function (opts) {
}, "devtools/client/memory/telemetry#countDiff");
/**
* @param {Object} breakdown
* The breakdown used to label nodes in the dominator tree.
* @param {Object} display
* The display used to label nodes in the dominator tree.
*/
exports.countDominatorTree = makeInfallible(function ({ breakdown }) {
exports.countDominatorTree = makeInfallible(function ({ display }) {
let histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_DOMINATOR_TREE_COUNT");
histogram.add(1);
histogram = telemetry.getKeyedHistogramById("DEVTOOLS_MEMORY_BREAKDOWN_DOMINATOR_TREE_COUNT");
if (breakdown === dominatorTreeBreakdowns.coarseType.breakdown) {
if (display === dominatorTreeDisplays.coarseType) {
histogram.add(COARSE_TYPE);
} else if (breakdown === dominatorTreeBreakdowns.allocationStack.breakdown) {
} else if (display === dominatorTreeDisplays.allocationStack) {
histogram.add(ALLOCATION_STACK);
} else {
histogram.add(CUSTOM);

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

@ -6,9 +6,9 @@ support-files =
doc_big_tree.html
doc_steady_allocation.html
[browser_memory_allocationStackBreakdown_01.js]
[browser_memory_allocationStackDisplay_01.js]
skip-if = debug # bug 1219554
[browser_memory_breakdowns_01.js]
[browser_memory_displays_01.js]
[browser_memory_clear_snapshots.js]
[browser_memory_diff_01.js]
[browser_memory_dominator_trees_01.js]

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

@ -1,16 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
// Sanity test that we can show allocation stack displays in the tree.
"use strict";
const { waitForTime } = require("devtools/shared/DevToolsUtils");
const { breakdowns } = require("devtools/client/memory/constants");
const { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
const censusDisplayActions = require("devtools/client/memory/actions/census-display");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -20,13 +18,8 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
yield dispatch(toggleInvertedAndRefresh(heapWorker));
ok(getState().inverted, true);
ok(doc.getElementById("invert-tree-checkbox").checked,
"invert-tree-checkbox should be checked");
dispatch(breakdownActions.setBreakdown(breakdowns.allocationStack.breakdown));
is(getState().breakdown.by, "allocationStack");
dispatch(censusDisplayActions.setCensusDisplay(censusDisplays.invertedAllocationStack));
is(getState().censusDisplay.breakdown.by, "allocationStack");
yield dispatch(toggleRecordingAllocationStacks(front));
ok(getState().allocations.recording);

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

@ -1,15 +1,19 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that the heap tree renders rows based on the breakdown
* Tests that the heap tree renders rows based on the display
*/
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { gStore, document } = panel.panelWin;
const $$ = document.querySelectorAll.bind(document);
function $$(selector) {
return [...document.querySelectorAll(selector)];
}
yield takeSnapshot(panel.panelWin);
@ -17,14 +21,15 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
state.snapshots[0].state === states.SAVED_CENSUS);
info("Check coarse type heap view");
["objects", "other", "scripts", "strings"].forEach(findNameCell);
["Function", "js::Shape", "Object", "strings"].forEach(findNameCell);
yield setBreakdown(panel.panelWin, "allocationStack");
yield setCensusDisplay(panel.panelWin, censusDisplays.allocationStack);
info("Check allocation stack heap view");
[L10N.getStr("tree-item.nostack")].forEach(findNameCell);
function findNameCell (name) {
let el = Array.prototype.find.call($$(".tree .heap-tree-item-name span"), el => el.textContent === name);
function findNameCell(name) {
const el = $$(".tree .heap-tree-item-name span")
.find(e => e.textContent === name);
ok(el, `Found heap tree item cell for ${name}.`);
}
});

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

@ -8,14 +8,11 @@
const {
dominatorTreeState,
snapshotState,
viewState,
} = require("devtools/client/memory/constants");
const {
expandDominatorTreeNode,
takeSnapshotAndCensus,
} = require("devtools/client/memory/actions/snapshot");
const { toggleInverted } = require("devtools/client/memory/actions/inverted");
const { changeView } = require("devtools/client/memory/actions/view");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_big_tree.html";
@ -24,8 +21,6 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
// Taking snapshots and computing dominator trees is slow :-/
requestLongerTimeout(4);
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const store = panel.panelWin.gStore;
const { getState, dispatch } = store;
const doc = panel.panelWin.document;

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

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
// Sanity test that we can show allocation stack displays in the tree.
"use strict";
@ -21,11 +21,6 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = store;
const doc = panel.panelWin.document;
ok(!getState().inverted, "not inverted by default");
const invertCheckbox = doc.getElementById("invert-tree-checkbox");
EventUtils.synthesizeMouseAtCenter(invertCheckbox, {}, panel.panelWin);
yield waitUntilState(store, state => state.inverted === true);
const takeSnapshotButton = doc.getElementById("take-snapshot");
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);

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

@ -39,7 +39,7 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = store;
const doc = panel.panelWin.document;
is(getState().breakdown.by, "coarseType");
is(getState().censusDisplay.breakdown.by, "coarseType");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
let census = getState().snapshots[0].census;
let root1 = census.report.children[0];

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

@ -1,13 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
// Sanity test that we can show allocation stack displays in the tree.
"use strict";
const { breakdowns } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const censusDisplayActions = require("devtools/client/memory/actions/census-display");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -21,10 +20,11 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
"Should not be recording allocagtions");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
yield dispatch(breakdownActions.setBreakdownAndRefresh(heapWorker,
breakdowns.allocationStack.breakdown));
yield dispatch(censusDisplayActions.setCensusDisplayAndRefresh(
heapWorker,
censusDisplays.allocationStack));
is(getState().breakdown.by, "allocationStack",
is(getState().censusDisplay.breakdown.by, "allocationStack",
"Should be using allocation stack breakdown");
ok(!getState().allocations.recording,

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

@ -16,7 +16,6 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
is(getState().breakdown.by, "coarseType");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
is(getState().allocations.recording, false);
@ -25,23 +24,10 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
is(getState().allocations.recording, true);
const nameElems = [...doc.querySelectorAll(".heap-tree-item-field.heap-tree-item-name")];
is(nameElems.length, 4, "Should get 4 items, one for each coarse type");
for (let el of nameElems) {
dumpn(`Found ${el.textContent.trim()}`);
}
ok(nameElems.some(e => e.textContent.indexOf("objects") >= 0),
"One for coarse type 'objects'");
ok(nameElems.some(e => e.textContent.indexOf("scripts") >= 0),
"One for coarse type 'scripts'");
ok(nameElems.some(e => e.textContent.indexOf("strings") >= 0),
"One for coarse type 'strings'");
ok(nameElems.some(e => e.textContent.indexOf("other") >= 0),
"One for coarse type 'other'");
for (let e of nameElems) {
is(e.style.marginLeft, "0px",
is(el.style.marginLeft, "0px",
"None of the elements should be an indented/expanded child");
}
});

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

@ -5,9 +5,7 @@
"use strict";
const { breakdowns } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -28,7 +26,7 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const doc = panel.panelWin.document;
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
is(getState().breakdown.by, "coarseType",
is(getState().censusDisplay.breakdown.by, "coarseType",
"Should be using coarse type breakdown");
const bytesCells = [...doc.querySelectorAll(".heap-tree-item-bytes")];

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

@ -13,8 +13,8 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
this);
var { snapshotState: states } = require("devtools/client/memory/constants");
var { breakdownEquals, breakdownNameToSpec, L10N } = require("devtools/client/memory/utils");
var { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
var { L10N } = require("devtools/client/memory/utils");
Services.prefs.setBoolPref("devtools.memory.enabled", true);
@ -117,22 +117,22 @@ function clearSnapshots (window) {
}
/**
* Sets breakdown and waits for currently selected breakdown to use it
* and be completed the census.
* Sets the current requested display and waits for the selected snapshot to use
* it and complete the new census that entails.
*/
function setBreakdown (window, type) {
info(`Setting breakdown to ${type}...`);
function setCensusDisplay(window, display) {
info(`Setting census display to ${display}...`);
let { gStore, gHeapAnalysesClient } = window;
// XXX: Should handle this via clicking the DOM, but React doesn't
// fire the onChange event, so just change it in the store.
// window.document.querySelector(`.select-breakdown`).value = type;
gStore.dispatch(require("devtools/client/memory/actions/breakdown")
.setBreakdownAndRefresh(gHeapAnalysesClient, breakdownNameToSpec(type)));
// window.document.querySelector(`.select-display`).value = type;
gStore.dispatch(require("devtools/client/memory/actions/census-display")
.setCensusDisplayAndRefresh(gHeapAnalysesClient, display));
return waitUntilState(window.gStore, () => {
let selected = window.gStore.getState().snapshots.find(s => s.selected);
return selected.state === states.SAVED_CENSUS &&
breakdownEquals(breakdownNameToSpec(type), selected.census.breakdown);
selected.census.display === display;
});
}

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

@ -14,4 +14,3 @@ support-files =
[test_ShortestPaths_01.html]
[test_ShortestPaths_02.html]
[test_Toolbar_01.html]
[test_Toolbar_02.html]

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

@ -17,17 +17,15 @@ var { immutableUpdate } = DevToolsUtils;
var constants = require("devtools/client/memory/constants");
var {
breakdowns,
censusDisplays,
diffingState,
dominatorTreeBreakdowns,
dominatorTreeDisplays,
dominatorTreeState,
snapshotState,
viewState
} = constants;
const {
getBreakdownDisplayData,
getDominatorTreeBreakdownDisplayData,
L10N,
} = require("devtools/client/memory/utils");
@ -122,7 +120,7 @@ var TEST_DOMINATOR_TREE = Object.freeze({
expanded: new Set(),
focused: null,
error: null,
breakdown: dominatorTreeBreakdowns.coarseType.breakdown,
display: dominatorTreeDisplays.coarseType,
activeFetchRequestCount: null,
state: dominatorTreeState.LOADED,
});
@ -173,12 +171,17 @@ var TEST_HEAP_PROPS = Object.freeze({
strings: Object.freeze({ count: 2, bytes: 200 }),
other: Object.freeze({ count: 1, bytes: 100 }),
}),
breakdown: Object.freeze({
by: "coarseType",
objects: Object.freeze({ by: "count", count: true, bytes: true }),
scripts: Object.freeze({ by: "count", count: true, bytes: true }),
strings: Object.freeze({ by: "count", count: true, bytes: true }),
other: Object.freeze({ by: "count", count: true, bytes: true }),
display: Object.freeze({
displayName: "Test Display",
tooltip: "Test display tooltup",
inverted: false,
breakdown: Object.freeze({
by: "coarseType",
objects: Object.freeze({ by: "count", count: true, bytes: true }),
scripts: Object.freeze({ by: "count", count: true, bytes: true }),
strings: Object.freeze({ by: "count", count: true, bytes: true }),
other: Object.freeze({ by: "count", count: true, bytes: true }),
}),
}),
inverted: false,
filter: null,
@ -196,10 +199,14 @@ var TEST_HEAP_PROPS = Object.freeze({
});
var TEST_TOOLBAR_PROPS = Object.freeze({
breakdowns: getBreakdownDisplayData(),
censusDisplays: [
censusDisplays.coarseType,
censusDisplays.allocationStack,
censusDisplays.invertedAllocationStack,
],
onTakeSnapshotClick: noop,
onImportClick: noop,
onBreakdownChange: noop,
onCensusDisplayChange: noop,
onToggleRecordAllocationStacks: noop,
allocations: models.allocations,
onToggleInverted: noop,
@ -210,8 +217,11 @@ var TEST_TOOLBAR_PROPS = Object.freeze({
onToggleDiffing: noop,
view: viewState.CENSUS,
onViewChange: noop,
dominatorTreeBreakdowns: getDominatorTreeBreakdownDisplayData(),
onDominatorTreeBreakdownChange: noop,
dominatorTreeDisplays: [
dominatorTreeDisplays.coarseType,
dominatorTreeDisplays.allocationStack,
],
onDominatorTreeDisplayChange: noop,
snapshots: [],
});

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

@ -41,7 +41,7 @@ Test that we show the "hey you're not recording allocation stacks" message at th
}
]
},
breakdown: breakdowns.allocationStack.breakdown,
display: censusDisplays.allocationStack,
}),
}),
})), container);
@ -82,7 +82,7 @@ Test that we show the "hey you're not recording allocation stacks" message at th
}
]
},
breakdown: breakdowns.allocationStack.breakdown,
display: censusDisplays.allocationStack,
}),
}),
})), container);
@ -102,7 +102,7 @@ Test that we show the "hey you're not recording allocation stacks" message at th
parent: undefined,
children: undefined
},
breakdown: breakdowns.allocationStack.breakdown,
display: censusDisplays.allocationStack,
}),
}),
})), container);

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

@ -1,47 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test that the Toolbar component shows the tree inversion checkbox only at the appropriate times.
-->
<head>
<meta charset="utf-8">
<title>Tree component test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<div id="container"></div>
<pre id="test">
<script src="head.js" type="application/javascript;version=1.8"></script>
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
const container = document.getElementById("container");
// Census and dominator tree views.
for (let view of [viewState.CENSUS, viewState.DIFFING]) {
yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
view,
})), container);
ok(container.querySelector("#invert-tree-checkbox"),
`The invert checkbox is shown in view = ${view}`);
}
yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
view: viewState.DOMINATOR_TREE,
})), container);
ok(!container.querySelector("#invert-tree-checkbox"),
"The invert checkbox is NOT shown in the DOMINATOR_TREE view");
} catch(e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
SimpleTest.finish();
}
});
</script>
</pre>
</body>
</html>

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

@ -73,21 +73,6 @@ function waitUntilSnapshotState (store, expected) {
return waitUntilState(store, predicate);
}
function isBreakdownType (report, type) {
// Little sanity check, all reports should have at least a children array.
if (!report || !Array.isArray(report.children)) {
return false;
}
switch (type) {
case "coarseType":
return report.children.find(c => c.name === "objects");
case "allocationStack":
return report.children.find(c => c.name === "noStack");
default:
throw new Error(`isBreakdownType does not yet support ${type}`);
}
}
function *createTempFile () {
let file = FileUtils.getFile("TmpD", ["tmp.fxsnapshot"]);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);

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

@ -1,9 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that changing filter state properly refreshes the selected census.
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { snapshotState: states } = require("devtools/client/memory/constants");
let { setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
@ -11,7 +12,7 @@ function run_test() {
run_next_test();
}
add_task(function *() {
add_task(function*() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();

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

@ -1,5 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `importSnapshotAndCensus()` for the whole flow of
@ -7,7 +8,6 @@
*/
let { actions, snapshotState: states } = require("devtools/client/memory/constants");
let { breakdownEquals } = require("devtools/client/memory/utils");
let { exportSnapshot, importSnapshotAndCensus } = require("devtools/client/memory/actions/io");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
@ -61,8 +61,8 @@ add_task(function *() {
let snapshot1 = getState().snapshots[0];
let snapshot2 = getState().snapshots[1];
ok(breakdownEquals(snapshot1.breakdown, snapshot2.breakdown),
"imported snapshot has correct breakdown");
equal(snapshot1.display, snapshot2.display,
"imported snapshot has correct display");
// Clone the census data so we can destructively remove the ID/parents to compare
// equal census data

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

@ -1,98 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the task creator `setBreakdownAndRefreshAndRefresh()` for breakdown changing.
* We test this rather than `setBreakdownAndRefresh` directly, as we use the refresh action
* in the app itself composed from `setBreakdownAndRefresh`
*/
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { breakdownEquals } = require("devtools/client/memory/utils");
let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default breakdown with no snapshots
equal(getState().breakdown.by, "coarseType", "default coarseType breakdown selected at start.");
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown));
equal(getState().breakdown.by, "allocationStack", "breakdown changed with no snapshots");
// Test invalid breakdowns
ok(getState().errors.length === 0, "No error actions in the queue.");
dispatch(setBreakdownAndRefresh(heapWorker, {}));
yield waitUntilState(store, () => getState().errors.length === 1);
ok(true, "Emits an error action when passing in an invalid breakdown object");
equal(getState().breakdown.by, "allocationStack",
"current breakdown unchanged when passing invalid breakdown");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(isBreakdownType(getState().snapshots[0].census.report, "allocationStack"),
"New snapshots use the current, non-default breakdown");
// Updates when changing breakdown during `SAVING`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING]);
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.coarseType.breakdown));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(isBreakdownType(getState().snapshots[1].census.report, "coarseType"),
"Breakdown can be changed while saving snapshots, uses updated breakdown in census");
// Updates when changing breakdown during `SAVING_CENSUS`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]);
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(breakdownEquals(getState().snapshots[2].census.breakdown, breakdowns.allocationStack.breakdown),
"Breakdown can be changed while saving census, stores updated breakdown in snapshot");
ok(isBreakdownType(getState().snapshots[2].census.report, "allocationStack"),
"Breakdown can be changed while saving census, uses updated breakdown in census");
// Updates census on currently selected snapshot when changing breakdown
ok(getState().snapshots[2].selected, "Third snapshot currently selected");
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.coarseType.breakdown));
yield waitUntilState(store, () => isBreakdownType(getState().snapshots[2].census.report, "coarseType"));
ok(isBreakdownType(getState().snapshots[2].census.report, "coarseType"),
"Snapshot census updated when changing breakdowns after already generating one census");
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown));
yield waitUntilState(store, () => isBreakdownType(getState().snapshots[2].census.report, "allocationStack"));
// Does not update unselected censuses
ok(!getState().snapshots[1].selected, "Second snapshot unselected currently");
ok(breakdownEquals(getState().snapshots[1].census.breakdown, breakdowns.coarseType.breakdown),
"Second snapshot using `coarseType` breakdown still and not yet updated to correct breakdown");
ok(isBreakdownType(getState().snapshots[1].census.report, "coarseType"),
"Second snapshot using `coarseType` still for census and not yet updated to correct breakdown");
// Updates to current breakdown when switching to stale snapshot
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING_CENSUS, states.SAVED_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(getState().snapshots[1].selected, "Second snapshot selected currently");
ok(breakdownEquals(getState().snapshots[1].census.breakdown, breakdowns.allocationStack.breakdown),
"Second snapshot using `allocationStack` breakdown and updated to correct breakdown");
ok(isBreakdownType(getState().snapshots[1].census.report, "allocationStack"),
"Second snapshot using `allocationStack` for census and updated to correct breakdown");
heapWorker.destroy();
yield front.detach();
});

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

@ -1,45 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the action creator `setBreakdown()` for breakdown changing.
* Does not test refreshing the census information, check `setBreakdownAndRefresh` action
* for that.
*/
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { setBreakdown } = require("devtools/client/memory/actions/breakdown");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default breakdown with no snapshots
equal(getState().breakdown.by, "coarseType", "default coarseType breakdown selected at start.");
dispatch(setBreakdown(breakdowns.allocationStack.breakdown));
equal(getState().breakdown.by, "allocationStack", "breakdown changed with no snapshots");
// Test invalid breakdowns
try {
dispatch(setBreakdown({}));
ok(false, "Throws when passing in an invalid breakdown object");
} catch (e) {
ok(true, "Throws when passing in an invalid breakdown object");
}
equal(getState().breakdown.by, "allocationStack",
"current breakdown unchanged when passing invalid breakdown");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(isBreakdownType(getState().snapshots[0].census.report, "allocationStack"),
"New snapshots use the current, non-default breakdown");
});

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

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for display
* changing. We test this rather than `setCensusDisplayAndRefresh` directly, as
* we use the refresh action in the app itself composed from
* `setCensusDisplayAndRefresh`.
*/
let { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default display with no snapshots
equal(getState().censusDisplay.breakdown.by, "coarseType",
"default coarseType display selected at start.");
dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.allocationStack));
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"display changed with no snapshots");
// Test invalid displays
ok(getState().errors.length === 0, "No error actions in the queue.");
dispatch(setCensusDisplayAndRefresh(heapWorker, {}));
yield waitUntilState(store, () => getState().errors.length === 1);
ok(true, "Emits an error action when passing in an invalid display object");
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"current display unchanged when passing invalid display");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
// Updates when changing display during `SAVING`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING]);
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
// Updates when changing display during `SAVING_CENSUS`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]);
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
"Display can be changed while saving census, stores updated display in snapshot");
// Updates census on currently selected snapshot when changing display
ok(getState().snapshots[2].selected, "Third snapshot currently selected");
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
yield waitUntilState(store, state => state.snapshots[2].state === states.SAVED_CENSUS);
equal(getState().snapshots[2].census.display, censusDisplays.coarseType,
"Snapshot census updated when changing displays after already generating one census");
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
yield waitUntilState(store, state => state.snapshots[2].state === states.SAVED_CENSUS);
equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
"Snapshot census updated when changing displays after already generating one census");
// Does not update unselected censuses
ok(!getState().snapshots[1].selected, "Second snapshot unselected currently");
equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
"Second snapshot using `coarseType` display still and not yet updated to correct display");
// Updates to current display when switching to stale snapshot
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING_CENSUS, states.SAVED_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(getState().snapshots[1].selected, "Second snapshot selected currently");
equal(getState().snapshots[1].census.display, censusDisplays.allocationStack,
"Second snapshot using `allocationStack` display and updated to correct display");
heapWorker.destroy();
yield front.detach();
});

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

@ -1,16 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `setBreakdownAndRefreshAndRefresh()` for custom
* breakdowns.
* Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for custom
* displays.
*/
let { snapshotState: states } = require("devtools/client/memory/constants");
let { breakdownEquals } = require("devtools/client/memory/utils");
let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown");
let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
let custom = { by: "internalType", then: { by: "count", bytes: true, count: false }};
let CUSTOM = {
displayName: "Custom",
tooltip: "Custom tooltip",
inverted: false,
breakdown: {
by: "internalType",
then: { by: "count", bytes: true, count: false }
}
};
function run_test() {
run_next_test();
@ -23,20 +32,20 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
dispatch(setBreakdownAndRefresh(heapWorker, custom));
ok(breakdownEquals(getState().breakdown, custom),
"Custom breakdown stored in breakdown state.");
dispatch(setCensusDisplayAndRefresh(heapWorker, CUSTOM));
equal(getState().censusDisplay, CUSTOM,
"CUSTOM display stored in display state.");
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(breakdownEquals(getState().snapshots[0].census.breakdown, custom),
"New snapshot stored custom breakdown when done taking census");
equal(getState().snapshots[0].census.display, CUSTOM,
"New snapshot stored CUSTOM display when done taking census");
ok(getState().snapshots[0].census.report.children.length, "Census has some children");
// Ensure we don't have `count` in any results
ok(getState().snapshots[0].census.report.children.every(c => !c.count),
"Census used custom breakdown without counts");
"Census used CUSTOM display without counts");
// Ensure we do have `bytes` in the results
ok(getState().snapshots[0].census.report.children.every(c => typeof c.bytes === "number"),
"Census used custom breakdown with bytes");
"Census used CUSTOM display with bytes");
});

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

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the action creator `setCensusDisplay()` for display changing. Does not
* test refreshing the census information, check `setCensusDisplayAndRefresh`
* action for that.
*/
let { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
let { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function*() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default display with no snapshots
equal(getState().censusDisplay.breakdown.by, "coarseType",
"default coarseType display selected at start.");
dispatch(setCensusDisplay(censusDisplays.allocationStack));
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"display changed with no snapshots");
// Test invalid displays
try {
dispatch(setCensusDisplay({}));
ok(false, "Throws when passing in an invalid display object");
} catch (e) {
ok(true, "Throws when passing in an invalid display object");
}
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"current display unchanged when passing invalid display");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
"New snapshots use the current, non-default display");
});

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

@ -5,9 +5,7 @@
* Tests the async reducer responding to the action `takeCensus(heapWorker, snapshot)`
*/
var { snapshotState: states, breakdowns } = require("devtools/client/memory/constants");
var { breakdownEquals } = require("devtools/client/memory/utils");
var { ERROR_TYPE } = require("devtools/client/shared/redux/middleware/task");
var { snapshotState: states, censusDisplays } = require("devtools/client/memory/constants");
var actions = require("devtools/client/memory/actions/snapshot");
function run_test() {
@ -45,8 +43,6 @@ add_task(function *() {
snapshot = store.getState().snapshots[0];
ok(snapshot.census, "Snapshot has census after saved census");
ok(snapshot.census.report.children.length, "Census is in tree node form");
ok(isBreakdownType(snapshot.census.report, "coarseType"),
"Census is in tree node form with the default breakdown");
ok(breakdownEquals(snapshot.census.breakdown, breakdowns.coarseType.breakdown),
"Snapshot stored correct breakdown used for the census");
equal(snapshot.census.display, censusDisplays.coarseType,
"Snapshot stored correct display used for the census");
});

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

@ -1,11 +1,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that changing inverted state properly refreshes the selected census.
// Test that changing displays with different inverted state properly
// refreshes the selected census.
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
const {
censusDisplays,
snapshotState: states,
} = require("devtools/client/memory/constants");
const {
setCensusDisplayAndRefresh
} = require("devtools/client/memory/actions/census-display");
const {
takeSnapshotAndCensus,
selectSnapshotAndRefresh,
} = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
@ -18,7 +27,9 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
equal(getState().inverted, false, "not inverted by default");
// Select a non-inverted display.
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false, "not inverted by default");
dispatch(takeSnapshotAndCensus(front, heapWorker));
dispatch(takeSnapshotAndCensus(front, heapWorker));
@ -29,21 +40,23 @@ add_task(function *() {
states.SAVED_CENSUS]);
ok(true, "saved 3 snapshots and took a census of each of them");
dispatch(toggleInvertedAndRefresh(heapWorker));
// Select an inverted display.
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
states.SAVED_CENSUS,
states.SAVING_CENSUS]);
ok(true, "toggling inverted should recompute the selected snapshot's census");
equal(getState().inverted, true, "now inverted");
equal(getState().censusDisplay.inverted, true, "now inverted");
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
states.SAVED_CENSUS,
states.SAVED_CENSUS]);
equal(getState().snapshots[0].census.inverted, false);
equal(getState().snapshots[1].census.inverted, false);
equal(getState().snapshots[2].census.inverted, true);
equal(getState().snapshots[0].census.display.inverted, false);
equal(getState().snapshots[1].census.display.inverted, false);
equal(getState().snapshots[2].census.display.inverted, true);
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
@ -55,9 +68,9 @@ add_task(function *() {
states.SAVED_CENSUS,
states.SAVED_CENSUS]);
equal(getState().snapshots[0].census.inverted, false);
equal(getState().snapshots[1].census.inverted, true);
equal(getState().snapshots[2].census.inverted, true);
equal(getState().snapshots[0].census.display.inverted, false);
equal(getState().snapshots[1].census.display.inverted, true);
equal(getState().snapshots[2].census.display.inverted, true);
heapWorker.destroy();
yield front.detach();

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

@ -1,12 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that changing inverted state in the middle of taking a snapshot results
// in an inverted census.
let { snapshotState: states } = require("devtools/client/memory/constants");
let { toggleInverted, toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
function run_test() {
run_next_test();
@ -19,26 +20,29 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false);
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVING]);
dispatch(toggleInverted());
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().inverted,
ok(getState().censusDisplay.inverted,
"should want inverted trees");
ok(getState().snapshots[0].census.inverted,
ok(getState().snapshots[0].census.display.inverted,
"snapshot-we-were-in-the-middle-of-saving's census should be inverted");
dispatch(toggleInvertedAndRefresh(heapWorker));
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]);
ok(true, "toggling inverted retriggers census");
ok(!getState().inverted, "no long inverted");
ok(!getState().censusDisplay.inverted, "no longer inverted");
dispatch(toggleInverted());
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().inverted, "inverted again");
ok(getState().snapshots[0].census.inverted,
ok(getState().censusDisplay.inverted, "inverted again");
ok(getState().snapshots[0].census.display.inverted,
"census-we-were-in-the-middle-of-recomputing should be inverted again");
heapWorker.destroy();

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

@ -1,29 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test toggling the top level inversion state of the tree.
let { toggleInverted } = require("devtools/client/memory/actions/inverted");
const { censusDisplays } = require("devtools/client/memory/constants");
const { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
add_task(function*() {
let store = Store();
const { getState, dispatch } = store;
equal(getState().inverted, false, "not inverted by default");
dispatch(setCensusDisplay(censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false,
"not inverted initially");
dispatch(toggleInverted());
equal(getState().inverted, true, "now inverted after toggling");
dispatch(setCensusDisplay(censusDisplays.invertedAllocationStack));
equal(getState().censusDisplay.inverted, true, "now inverted after toggling");
dispatch(toggleInverted());
equal(getState().inverted, false, "not inverted again after toggling again");
heapWorker.destroy();
yield front.detach();
dispatch(setCensusDisplay(censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false,
"not inverted again after toggling again");
});

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

@ -1,10 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the action creator `setBreakdown()` for breakdown changing.
* Does not test refreshing the census information, check `setBreakdownAndRefresh` action
* for that.
* Test toggling the recording of allocation stacks.
*/
let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
@ -15,7 +14,6 @@ function run_test() {
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
@ -39,4 +37,6 @@ add_task(function *() {
equal(getState().allocations.recording, false, "now we are not recording");
ok(front.recordingAllocations, "front is not recording anymore");
yield front.detach();
});

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

@ -15,7 +15,6 @@ const {
takeSnapshot,
readSnapshot
} = require("devtools/client/memory/actions/snapshot");
const { breakdownEquals } = require("devtools/client/memory/utils");
function run_test() {
run_next_test();
@ -63,11 +62,12 @@ add_task(function *() {
ok(true, "And then the diff should complete.");
ok(getState().diffing.census, "And we should have a census.");
ok(getState().diffing.census.report, "And that census should have a report.");
ok(breakdownEquals(getState().diffing.census.breakdown, getState().breakdown),
"And that census should have the correct breakdown");
equal(getState().diffing.census.display, getState().censusDisplay,
"And that census should have the correct display");
equal(getState().diffing.census.filter, getState().filter,
"And that census should have the correct filter");
equal(getState().diffing.census.inverted, getState().inverted,
equal(getState().diffing.census.display.inverted,
getState().censusDisplay.inverted,
"And that census should have the correct inversion");
heapWorker.destroy();

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

@ -6,11 +6,11 @@
const {
diffingState,
snapshotState,
breakdowns,
censusDisplays,
} = require("devtools/client/memory/constants");
const {
setBreakdownAndRefresh,
} = require("devtools/client/memory/actions/breakdown");
setCensusDisplayAndRefresh,
} = require("devtools/client/memory/actions/census-display");
const {
toggleDiffing,
selectSnapshotForDiffingAndRefresh,
@ -18,14 +18,10 @@ const {
const {
setFilterStringAndRefresh,
} = require("devtools/client/memory/actions/filter");
const {
toggleInvertedAndRefresh,
} = require("devtools/client/memory/actions/inverted");
const {
takeSnapshot,
readSnapshot,
} = require("devtools/client/memory/actions/snapshot");
const { breakdownEquals } = require("devtools/client/memory/utils");
function run_test() {
run_next_test();
@ -38,6 +34,11 @@ add_task(function *() {
let store = Store();
const { getState, dispatch } = store;
yield dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false,
"not inverted at start");
equal(getState().diffing, null, "not diffing by default");
const s1 = yield dispatch(takeSnapshot(front, heapWorker));
@ -61,17 +62,19 @@ add_task(function *() {
const shouldTriggerRecompute = [
{
name: "toggling inversion",
func: () => dispatch(toggleInvertedAndRefresh(heapWorker))
func: () => dispatch(setCensusDisplayAndRefresh(
heapWorker,
censusDisplays.invertedAllocationStack))
},
{
name: "filtering",
func: () => dispatch(setFilterStringAndRefresh("scr", heapWorker))
},
{
name: "changing breakdowns",
name: "changing displays",
func: () =>
dispatch(setBreakdownAndRefresh(heapWorker,
breakdowns.allocationStack.breakdown))
dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.coarseType))
}
];
@ -91,12 +94,13 @@ add_task(function *() {
ok(getState().diffing.census, "And we should have a census.");
ok(getState().diffing.census.report,
"And that census should have a report.");
ok(breakdownEquals(getState().diffing.census.breakdown,
getState().breakdown),
"And that census should have the correct breakdown");
equal(getState().diffing.census.display,
getState().censusDisplay,
"And that census should have the correct display");
equal(getState().diffing.census.filter, getState().filter,
"And that census should have the correct filter");
equal(getState().diffing.census.inverted, getState().inverted,
equal(getState().diffing.census.display.inverted,
getState().censusDisplay.inverted,
"And that census should have the correct inversion");
}

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

@ -1,18 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we can change the breakdown with which we describe a dominator tree
// Test that we can change the display with which we describe a dominator tree
// and that the dominator tree is re-fetched.
const {
snapshotState: states,
dominatorTreeState,
viewState,
dominatorTreeBreakdowns,
dominatorTreeDisplays,
} = require("devtools/client/memory/constants");
const {
setDominatorTreeBreakdownAndRefresh
} = require("devtools/client/memory/actions/dominatorTreeBreakdown");
setDominatorTreeDisplayAndRefresh
} = require("devtools/client/memory/actions/dominator-tree-display");
const {
changeView,
} = require("devtools/client/memory/actions/view");
@ -46,34 +46,34 @@ add_task(function *() {
state.snapshots[0].dominatorTree &&
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
ok(getState().dominatorTreeBreakdown,
"We have a default breakdown for describing nodes in a dominator tree");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.coarseType.breakdown,
ok(getState().dominatorTreeDisplay,
"We have a default display for describing nodes in a dominator tree");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.coarseType,
"and the default is coarse type");
equal(getState().dominatorTreeBreakdown,
getState().snapshots[0].dominatorTree.breakdown,
"and the newly computed dominator tree has that breakdown");
equal(getState().dominatorTreeDisplay,
getState().snapshots[0].dominatorTree.display,
"and the newly computed dominator tree has that display");
// Switch to the allocationStack breakdown.
dispatch(setDominatorTreeBreakdownAndRefresh(
// Switch to the allocationStack display.
dispatch(setDominatorTreeDisplayAndRefresh(
heapWorker,
dominatorTreeBreakdowns.allocationStack.breakdown));
dominatorTreeDisplays.allocationStack));
yield waitUntilState(store, state =>
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
ok(true,
"switching breakdown types caused the dominator tree to be fetched " +
"switching display types caused the dominator tree to be fetched " +
"again.");
yield waitUntilState(store, state =>
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
equal(getState().snapshots[0].dominatorTree.breakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"The new dominator tree's breakdown is allocationStack");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"as is our requested dominator tree breakdown");
equal(getState().snapshots[0].dominatorTree.display,
dominatorTreeDisplays.allocationStack,
"The new dominator tree's display is allocationStack");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.allocationStack,
"as is our requested dominator tree display");
heapWorker.destroy();
yield front.detach();

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

@ -1,18 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we can change the breakdown with which we describe a dominator tree
// Test that we can change the display with which we describe a dominator tree
// while the dominator tree is in the middle of being fetched.
const {
snapshotState: states,
dominatorTreeState,
viewState,
dominatorTreeBreakdowns,
dominatorTreeDisplays,
} = require("devtools/client/memory/constants");
const {
setDominatorTreeBreakdownAndRefresh
} = require("devtools/client/memory/actions/dominatorTreeBreakdown");
setDominatorTreeDisplayAndRefresh
} = require("devtools/client/memory/actions/dominator-tree-display");
const {
changeView,
} = require("devtools/client/memory/actions/view");
@ -46,31 +46,31 @@ add_task(function *() {
state.snapshots[0].dominatorTree &&
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
ok(getState().dominatorTreeBreakdown,
"We have a default breakdown for describing nodes in a dominator tree");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.coarseType.breakdown,
ok(getState().dominatorTreeDisplay,
"We have a default display for describing nodes in a dominator tree");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.coarseType,
"and the default is coarse type");
equal(getState().dominatorTreeBreakdown,
getState().snapshots[0].dominatorTree.breakdown,
"and the newly computed dominator tree has that breakdown");
equal(getState().dominatorTreeDisplay,
getState().snapshots[0].dominatorTree.display,
"and the newly computed dominator tree has that display");
// Switch to the allocationStack breakdown while we are still fetching the
// Switch to the allocationStack display while we are still fetching the
// dominator tree.
dispatch(setDominatorTreeBreakdownAndRefresh(
dispatch(setDominatorTreeDisplayAndRefresh(
heapWorker,
dominatorTreeBreakdowns.allocationStack.breakdown));
dominatorTreeDisplays.allocationStack));
// Wait for the dominator tree to finish being fetched.
yield waitUntilState(store, state =>
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
equal(getState().snapshots[0].dominatorTree.breakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"The new dominator tree's breakdown is allocationStack");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"as is our requested dominator tree breakdown");
equal(getState().snapshots[0].dominatorTree.display,
dominatorTreeDisplays.allocationStack,
"The new dominator tree's display is allocationStack");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.allocationStack,
"as is our requested dominator tree display");
heapWorker.destroy();
yield front.detach();

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

@ -1,15 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that we use the correct snapshot aggregate value
* in `utils.getSnapshotTotals(snapshot)`
*/
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { getSnapshotTotals, breakdownEquals } = require("devtools/client/memory/utils");
let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
const { getSnapshotTotals } = require("devtools/client/memory/utils");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
function run_test() {
run_next_test();
@ -22,12 +23,13 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
yield dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.allocationStack));
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(!getState().snapshots[0].census.inverted, "Snapshot is not inverted");
ok(isBreakdownType(getState().snapshots[0].census.report, "coarseType"),
"Snapshot using `coarseType` breakdown");
ok(!getState().snapshots[0].census.display.inverted, "Snapshot is not inverted");
let census = getState().snapshots[0].census;
let result = aggregate(census.report);
@ -41,14 +43,17 @@ add_task(function *() {
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes");
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count");
dispatch(toggleInvertedAndRefresh(heapWorker));
dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().snapshots[0].census.inverted, "Snapshot is inverted");
ok(getState().snapshots[0].census.display.inverted, "Snapshot is inverted");
result = getSnapshotTotals(getState().snapshots[0].census);
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes when inverted");
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count when inverted");
equal(totalBytes, result.bytes,
"getSnapshotTotals reuslted in correct bytes when inverted");
equal(totalCount, result.count,
"getSnapshotTotals reuslted in correct count when inverted");
});
function aggregate (report) {

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

@ -1,5 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `takeSnapshotAndCensus()` for the whole flow of
@ -8,7 +9,7 @@
*/
let utils = require("devtools/client/memory/utils");
let { snapshotState: states, breakdowns } = require("devtools/client/memory/constants");
let { snapshotState: states, censusDisplays } = require("devtools/client/memory/constants");
let { Preferences } = require("resource://gre/modules/Preferences.jsm");
function run_test() {
@ -16,39 +17,16 @@ function run_test() {
}
add_task(function *() {
ok(utils.breakdownEquals(breakdowns.allocationStack.breakdown, {
by: "allocationStack",
then: { by: "count", count: true, bytes: true },
noStack: { by: "count", count: true, bytes: true },
}), "utils.breakdownEquals() passes with preset"),
ok(!utils.breakdownEquals(breakdowns.allocationStack.breakdown, {
by: "allocationStack",
then: { by: "count", count: false, bytes: true },
noStack: { by: "count", count: true, bytes: true },
}), "utils.breakdownEquals() fails when deep properties do not match");
ok(!utils.breakdownEquals(breakdowns.allocationStack.breakdown, {
by: "allocationStack",
then: { by: "count", bytes: true },
noStack: { by: "count", count: true, bytes: true },
}), "utils.breakdownEquals() fails when deep properties are missing.");
let s1 = utils.createSnapshot({});
let s2 = utils.createSnapshot({});
equal(s1.state, states.SAVING, "utils.createSnapshot() creates snapshot in saving state");
ok(s1.id !== s2.id, "utils.createSnapshot() creates snapshot with unique ids");
ok(utils.breakdownEquals(utils.breakdownNameToSpec("coarseType"), breakdowns.coarseType.breakdown),
"utils.breakdownNameToSpec() works for presets");
ok(utils.breakdownEquals(utils.breakdownNameToSpec("coarseType"), breakdowns.coarseType.breakdown),
"utils.breakdownNameToSpec() works for presets");
let custom = { by: "internalType", then: { by: "count", bytes: true }};
Preferences.set("devtools.memory.custom-breakdowns", JSON.stringify({ "My Breakdown": custom }));
Preferences.set("devtools.memory.custom-census-displays", JSON.stringify({ "My Display": custom }));
ok(utils.breakdownEquals(utils.getCustomBreakdowns()["My Breakdown"], custom),
"utils.getCustomBreakdowns() returns custom breakdowns");
equal(utils.getCustomCensusDisplays()["My Display"].by, custom.by,
"utils.getCustomCensusDisplays() returns custom displays");
ok(true, "test formatNumber util functions");
equal(utils.formatNumber(12), "12", "formatNumber returns 12 for 12");

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

@ -21,9 +21,9 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-filter-03.js]
[test_action-import-snapshot-and-census.js]
[test_action-select-snapshot.js]
[test_action-set-breakdown.js]
[test_action-set-breakdown-and-refresh-01.js]
[test_action-set-breakdown-and-refresh-02.js]
[test_action-set-display.js]
[test_action-set-display-and-refresh-01.js]
[test_action-set-display-and-refresh-02.js]
[test_action-take-census.js]
[test_action-take-snapshot.js]
[test_action-take-snapshot-and-census.js]

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

@ -11,20 +11,20 @@ const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI);
const { OS } = require("resource://gre/modules/osfile.jsm");
const { assert } = require("devtools/shared/DevToolsUtils");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
const CUSTOM_DOMINATOR_TREE_BREAKDOWN_PREF = "devtools.memory.custom-dominator-tree-breakdowns";
const CUSTOM_CENSUS_DISPLAY_PREF = "devtools.memory.custom-census-displays";
const CUSTOM_DOMINATOR_TREE_DISPLAY_PREF = "devtools.memory.custom-dominator-tree-displays";
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {
snapshotState: states,
diffingState,
breakdowns,
dominatorTreeBreakdowns,
censusDisplays,
dominatorTreeDisplays,
dominatorTreeState
} = require("./constants");
/**
* Takes a snapshot object and returns the
* localized form of its timestamp to be used as a title.
* Takes a snapshot object and returns the localized form of its timestamp to be
* used as a title.
*
* @param {Snapshot} snapshot
* @return {String}
@ -48,160 +48,35 @@ exports.getSnapshotTitle = function (snapshot) {
});
};
/**
* Returns an array of objects with the unique key `name`
* and `displayName` for each breakdown.
*
* @return {Object{name, displayName}}
*/
exports.getBreakdownDisplayData = function () {
return exports.getBreakdownNames().map(({ name, tooltip }) => {
// If it's a preset use the display name value
let preset = breakdowns[name];
let displayName = name;
if (preset && preset.displayName) {
displayName = preset.displayName;
}
return { name, tooltip, displayName };
});
};
/**
* Returns an array of the unique names and tooltips for each breakdown in
* presets and custom pref.
*
* @return {Array<Object>}
*/
exports.getBreakdownNames = function () {
let custom = exports.getCustomBreakdowns();
return Object.keys(Object.assign({}, breakdowns, custom))
.map(key => {
return breakdowns[key]
? { name: key, tooltip: breakdowns[key].tooltip }
: { name: key };
});
};
/**
* Returns custom breakdowns defined in `devtools.memory.custom-breakdowns` pref.
*
* @return {Object}
*/
exports.getCustomBreakdowns = function () {
let customBreakdowns = Object.create(null);
function getCustomDisplaysHelper(pref) {
let customDisplays = Object.create(null);
try {
customBreakdowns = JSON.parse(Preferences.get(CUSTOM_BREAKDOWN_PREF)) || Object.create(null);
customDisplays = JSON.parse(Preferences.get(pref)) || Object.create(null);
} catch (e) {
DevToolsUtils.reportException(
`String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`);
`String stored in "${pref}" pref cannot be parsed by \`JSON.parse()\`.`);
}
return customBreakdowns;
return Object.freeze(customDisplays);
}
/**
* Converts a breakdown preset name, like "allocationStack", and returns the
* spec for the breakdown. Also checks properties of keys in the `devtools.memory.custom-breakdowns`
* pref. If not found, returns an empty object.
*
* @param {String} name
* @return {Object}
*/
exports.breakdownNameToSpec = function (name) {
let customBreakdowns = exports.getCustomBreakdowns();
// If breakdown is already a breakdown, use it
if (typeof name === "object") {
return name;
}
// If it's in our custom breakdowns, use it
else if (name in customBreakdowns) {
return customBreakdowns[name];
}
// If breakdown name is in our presets, use that
else if (name in breakdowns) {
return breakdowns[name].breakdown;
}
return Object.create(null);
};
/**
* Returns an array of objects with the unique key `name` and `displayName` for
* each breakdown for dominator trees.
*
* @return {Array<Object>}
*/
exports.getDominatorTreeBreakdownDisplayData = function () {
return exports.getDominatorTreeBreakdownNames().map(({ name, tooltip }) => {
// If it's a preset use the display name value
let preset = dominatorTreeBreakdowns[name];
let displayName = name;
if (preset && preset.displayName) {
displayName = preset.displayName;
}
return { name, tooltip, displayName };
});
};
/**
* Returns an array of the unique names for each breakdown in
* presets and custom pref.
*
* @return {Array<Breakdown>}
*/
exports.getDominatorTreeBreakdownNames = function () {
let custom = exports.getCustomDominatorTreeBreakdowns();
return Object.keys(Object.assign({}, dominatorTreeBreakdowns, custom))
.map(key => {
return dominatorTreeBreakdowns[key]
? { name: key, tooltip: dominatorTreeBreakdowns[key].tooltip }
: { name: key };
});
};
/**
* Returns custom breakdowns defined in `devtools.memory.custom-dominator-tree-breakdowns` pref.
* Returns custom displays defined in `devtools.memory.custom-census-displays`
* pref.
*
* @return {Object}
*/
exports.getCustomDominatorTreeBreakdowns = function () {
let customBreakdowns = Object.create(null);
try {
customBreakdowns = JSON.parse(Preferences.get(CUSTOM_DOMINATOR_TREE_BREAKDOWN_PREF)) || Object.create(null);
} catch (e) {
DevToolsUtils.reportException(
`String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`);
}
return customBreakdowns;
exports.getCustomCensusDisplays = function () {
return getCustomDisplaysHelper(CUSTOM_CENSUS_DISPLAY_PREF);
};
/**
* Converts a dominator tree breakdown preset name, like "allocationStack", and
* returns the spec for the breakdown. Also checks properties of keys in the
* `devtools.memory.custom-breakdowns` pref. If not found, returns an empty
* object.
* Returns custom displays defined in
* `devtools.memory.custom-dominator-tree-displays` pref.
*
* @param {String} name
* @return {Object}
*/
exports.dominatorTreeBreakdownNameToSpec = function (name) {
let customBreakdowns = exports.getCustomDominatorTreeBreakdowns();
// If breakdown is already a breakdown, use it.
if (typeof name === "object") {
return name;
}
// If it's in our custom breakdowns, use it.
if (name in customBreakdowns) {
return customBreakdowns[name];
}
// If breakdown name is in our presets, use that.
if (name in dominatorTreeBreakdowns) {
return dominatorTreeBreakdowns[name].breakdown;
}
return Object.create(null);
exports.getCustomDominatorTreeDisplays = function () {
return getCustomDisplaysHelper(CUSTOM_DOMINATOR_TREE_DISPLAY_PREF);
};
/**
@ -389,61 +264,19 @@ exports.createSnapshot = function createSnapshot(state) {
};
/**
* Takes two objects and compares them deeply, returning
* a boolean indicating if they're equal or not. Used for breakdown
* comparison.
* Return true if the census is up to date with regards to the current filtering
* and requested display, false otherwise.
*
* @param {Any} obj1
* @param {Any} obj2
* @return {Boolean}
*/
const breakdownEquals = exports.breakdownEquals = function (obj1, obj2) {
let type1 = typeof obj1;
let type2 = typeof obj2;
// Quick checks
if (type1 !== type2 || (Array.isArray(obj1) !== Array.isArray(obj2))) {
return false;
}
if (obj1 === obj2) {
return true;
}
if (Array.isArray(obj1)) {
if (obj1.length !== obj2.length) { return false; }
return obj1.every((_, i) => exports.breakdownEquals(obj[1], obj2[i]));
}
else if (type1 === "object") {
let k1 = Object.keys(obj1);
let k2 = Object.keys(obj2);
if (k1.length !== k2.length) {
return false;
}
return k1.every(k => exports.breakdownEquals(obj1[k], obj2[k]));
}
return false;
};
/**
* Return true if the census is up to date with regards to the current
* inversion/filtering/breakdown, false otherwise.
*
* @param {Boolean} inverted
* @param {String} filter
* @param {Object} breakdown
* @param {censusDisplayModel} display
* @param {censusModel} census
*
* @returns {Boolean}
*/
exports.censusIsUpToDate = function (inverted, filter, breakdown, census) {
exports.censusIsUpToDate = function (filter, display, census) {
return census
&& inverted === census.inverted
&& filter === census.filter
&& breakdownEquals(breakdown, census.breakdown);
&& display === census.display;
};
/**

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

@ -4,6 +4,7 @@
"use strict";
const { Cu, Ci } = require("chrome");
const Services = require("Services");
loader.lazyRequireGetter(this, "HarAutomation", "devtools/client/netmonitor/har/har-automation", true);

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

@ -13,7 +13,7 @@ const { Cu, Ci } = require("chrome");
const Services = require("Services");
const { L10N } = require("devtools/client/performance/modules/global");
const { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers");
const WebConsoleUtils = require("devtools/shared/webconsole/utils");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const SHOW_TRIGGER_FOR_GC_TYPES_PREF = "devtools.performance.ui.show-triggers-for-gc-types";
/**
@ -259,7 +259,7 @@ const DOM = {
let urlNode = doc.createElement("label");
urlNode.className = "filename";
urlNode.setAttribute("value", WebConsoleUtils.Utils.abbreviateSourceURL(url));
urlNode.setAttribute("value", getSourceNames(url).short);
let lineNode = doc.createElement("label");
lineNode.className = "line-number";
lineNode.setAttribute("value", `:${line}`);

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

@ -108,8 +108,8 @@ pref("devtools.debugger.ui.variables-searchbox-visible", false);
// Enable the Memory tools
pref("devtools.memory.enabled", false);
pref("devtools.memory.custom-breakdowns", "{}");
pref("devtools.memory.custom-dominator-tree-breakdowns", "{}");
pref("devtools.memory.custom-census-displays", "{}");
pref("devtools.memory.custom-dominator-tree-displays", "{}");
// Enable the Performance tools
pref("devtools.performance.enabled", true);

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

@ -5,7 +5,7 @@
"use strict";
/* global content, docShell, addEventListener, addMessageListener,
removeEventListener, removeMessageListener, sendAsyncMessage */
removeEventListener, removeMessageListener, sendAsyncMessage, Services */
var global = this;

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

@ -7,18 +7,22 @@
const Ci = Components.interfaces;
const Cu = Components.utils;
var { loader, require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var {loader, require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
var Telemetry = require("devtools/client/shared/telemetry");
var { showDoorhanger } = require("devtools/client/shared/doorhanger");
var { TouchEventSimulator } = require("devtools/shared/touch/simulator");
var { Task } = require("resource://gre/modules/Task.jsm");
var {showDoorhanger} = require("devtools/client/shared/doorhanger");
var {TouchEventSimulator} = require("devtools/shared/touch/simulator");
var {Task} = require("resource://gre/modules/Task.jsm");
var promise = require("promise");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var Services = require("Services");
var EventEmitter = require("devtools/shared/event-emitter");
var { ViewHelpers } = require("devtools/client/shared/widgets/ViewHelpers.jsm");
var {ViewHelpers} = require("devtools/client/shared/widgets/ViewHelpers.jsm");
loader.lazyImporter(this, "SystemAppProxy",
"resource://gre/modules/SystemAppProxy.jsm");
loader.lazyRequireGetter(this, "DebuggerClient",
"devtools/shared/client/main", true);
loader.lazyRequireGetter(this, "DebuggerServer",
"devtools/server/main", true);
this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
@ -175,6 +179,7 @@ function ResponsiveUI(aWindow, aTab)
this.bound_startResizing = this.startResizing.bind(this);
this.bound_stopResizing = this.stopResizing.bind(this);
this.bound_onDrag = this.onDrag.bind(this);
this.bound_changeUA = this.changeUA.bind(this);
this.bound_onContentResize = this.onContentResize.bind(this);
this.mm.addMessageListener("ResponsiveMode:OnContentResize",
@ -201,7 +206,6 @@ ResponsiveUI.prototype = {
init: Task.async(function*() {
debug("INIT BEGINS");
let ready = this.waitForMessage("ResponsiveMode:ChildScriptReady");
this.mm.loadFrameScript("resource://devtools/client/responsivedesign/responsivedesign-child.js", true);
yield ready;
@ -241,6 +245,9 @@ ResponsiveUI.prototype = {
this.touchEnableBefore = false;
this.touchEventSimulator = new TouchEventSimulator(this.browser);
yield this.connectToServer();
this.userAgentInput.hidden = false;
// Hook to display promotional Developer Edition doorhanger.
// Only displayed once.
showDoorhanger({
@ -254,6 +261,21 @@ ResponsiveUI.prototype = {
ResponsiveUIManager.emit("on", { tab: this.tab });
}),
connectToServer: Task.async(function*() {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
this.client = new DebuggerClient(DebuggerServer.connectPipe());
yield this.client.connect();
let {tab} = yield this.client.getTab();
let [response, tabClient] = yield this.client.attachTab(tab.actor);
this.tabClient = tabClient;
if (!tabClient) {
Cu.reportError("Responsive Mode: failed to attach tab");
}
}),
loadPresets: function() {
// Try to load presets from prefs
let presets = defaultPresets;
@ -359,7 +381,14 @@ ResponsiveUI.prototype = {
if (this.touchEventSimulator) {
this.touchEventSimulator.stop();
}
yield new Promise((resolve, reject) => {
this.client.close(resolve);
this.client = this.tabClient = null;
});
this._telemetry.toolClosed("responsive");
let stopped = this.waitForMessage("ResponsiveMode:Stop:Done");
this.tab.linkedBrowser.messageManager.sendAsyncMessage("ResponsiveMode:Stop");
yield stopped;
@ -510,6 +539,14 @@ ResponsiveUI.prototype = {
this.toolbar.appendChild(this.screenshotbutton);
this.userAgentInput = this.chromeDoc.createElement("textbox");
this.userAgentInput.className = "devtools-responsiveui-textinput";
this.userAgentInput.setAttribute("placeholder",
this.strings.GetStringFromName("responsiveUI.userAgentPlaceholder"));
this.userAgentInput.addEventListener("blur", this.bound_changeUA, true);
this.userAgentInput.hidden = true;
this.toolbar.appendChild(this.userAgentInput);
// Resizers
let resizerTooltip = this.strings.GetStringFromName("responsiveUI.resizerTooltip");
this.resizer = this.chromeDoc.createElement("box");
@ -905,6 +942,39 @@ ResponsiveUI.prototype = {
}
}),
waitForReload() {
let navigatedDeferred = promise.defer();
let onNavigated = (_, { state }) => {
if (state != "stop") {
return;
}
this.client.removeListener("tabNavigated", onNavigated);
navigatedDeferred.resolve();
};
this.client.addListener("tabNavigated", onNavigated);
return navigatedDeferred.promise;
},
/**
* Change the user agent string
*/
changeUA: Task.async(function*() {
let value = this.userAgentInput.value;
if (value) {
this.userAgentInput.setAttribute("attention", "true");
} else {
this.userAgentInput.removeAttribute("attention");
}
// Changing the UA triggers an automatic reload. Ensure we wait for this to
// complete before emitting the changed event, so that tests wait for the
// reload.
let reloaded = this.waitForReload();
yield this.tabClient.reconfigure({customUserAgent: value});
yield reloaded;
ResponsiveUIManager.emit("userAgentChanged", { tab: this.tab });
}),
/**
* Get the current width and height.
*/

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

@ -7,8 +7,11 @@ support-files =
[browser_responsive_cmd.js]
[browser_responsivecomputedview.js]
skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s
[browser_responsiveruleview.js]
skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s
[browser_responsiveui.js]
[browser_responsiveui_touch.js]
[browser_responsiveuiaddcustompreset.js]
[browser_responsive_devicewidth.js]
[browser_responsiveui_customuseragent.js]

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

@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URI = "data:text/html, Custom User Agent test";
const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
.getService(Ci.nsIHttpProtocolHandler)
.userAgent;
const CHROME_UA = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36" +
" (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";
add_task(function*() {
yield addTab(TEST_URI);
let {rdm, manager} = yield openRDM();
yield testUserAgent(DEFAULT_UA);
info("Setting UA to " + CHROME_UA);
yield setUserAgent(CHROME_UA, rdm, manager);
yield testUserAgent(CHROME_UA);
info("Resetting UA");
yield setUserAgent("", rdm, manager);
yield testUserAgent(DEFAULT_UA);
info("Setting UA to " + CHROME_UA);
yield setUserAgent(CHROME_UA, rdm, manager);
yield testUserAgent(CHROME_UA);
info("Closing responsive mode");
yield closeRDM(rdm);
yield testUserAgent(DEFAULT_UA);
});
function* setUserAgent(ua, rdm, manager) {
let input = rdm.userAgentInput;
input.focus();
input.value = ua;
let onUAChanged = once(manager, "userAgentChanged");
input.blur();
yield onUAChanged;
if (ua !== "") {
ok(input.hasAttribute("attention"), "UA input should be highlighted");
} else {
ok(!input.hasAttribute("attention"), "UA input shouldn't be highlighted");
}
}
function* testUserAgent(value) {
let ua = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
return content.navigator.userAgent;
});
is(ua, value, `UA should be set to ${value}`);
}

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

@ -2,13 +2,10 @@
* 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/. */
const { Cu } = require("chrome");
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
const STRINGS_URI = "chrome://devtools/locale/components.properties";
const L10N = new ViewHelpers.L10N(STRINGS_URI);
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const UNKNOWN_SOURCE_STRING = L10N.getStr("frame.unknownSource");
const { L10N } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm").ViewHelpers;
const l10n = new L10N("chrome://devtools/locale/components.properties");
const Frame = module.exports = createClass({
displayName: "Frame",
@ -39,7 +36,7 @@ const Frame = module.exports = createClass({
render() {
let { onClick, frame, showFunctionName, showHost } = this.props;
const { short, long, host } = getSourceNames(frame.source, UNKNOWN_SOURCE_STRING);
const { short, long, host } = getSourceNames(frame.source);
let tooltip = `${long}:${frame.line}`;
if (frame.column) {
@ -51,7 +48,7 @@ const Frame = module.exports = createClass({
sourceString += `:${frame.column}`;
}
let onClickTooltipString = L10N.getFormatStr("frame.viewsourceindebugger", sourceString);
let onClickTooltipString = l10n.getFormatStr("frame.viewsourceindebugger", sourceString);
let fields = [
dom.a({

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

@ -4,10 +4,16 @@
"use strict";
const { URL } = require("sdk/url");
const { Cu } = require("chrome");
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
const STRINGS_URI = "chrome://devtools/locale/components.properties";
const L10N = new ViewHelpers.L10N(STRINGS_URI);
const UNKNOWN_SOURCE_STRING = L10N.getStr("frame.unknownSource");
// Character codes used in various parsing helper functions.
const CHAR_CODE_A = "a".charCodeAt(0);
const CHAR_CODE_C = "c".charCodeAt(0);
const CHAR_CODE_D = "d".charCodeAt(0);
const CHAR_CODE_E = "e".charCodeAt(0);
const CHAR_CODE_F = "f".charCodeAt(0);
const CHAR_CODE_H = "h".charCodeAt(0);
@ -26,6 +32,8 @@ const CHAR_CODE_SLASH = "/".charCodeAt(0);
// The cache used in the `nsIURL` function.
const gURLStore = new Map();
// The cache used in the `getSourceNames` function.
const gSourceNamesStore = new Map();
/**
* Takes a string and returns an object containing all the properties
@ -81,17 +89,38 @@ function parseURL(location) {
*
* @param {String} source
* The source to parse. Can be a URI or names like "(eval)" or "self-hosted".
* @param {String} unknownSourceString
* The string to use if no valid source name can be generated.
* @return {Object}
* An object with the following properties:
* - {String} short: A short name for the source.
* - {String} long: The full, long name for the source.
* - "http://page.com/test.js#go?q=query" -> "test.js"
* - {String} long: The full, long name for the source, with hash/query stripped.
* - "http://page.com/test.js#go?q=query" -> "http://page.com/test.js"
* - {String?} host: If available, the host name for the source.
* - "http://page.com/test.js#go?q=query" -> "page.com"
*/
function getSourceNames (source, unknownSourceString) {
function getSourceNames (source) {
let data = gSourceNamesStore.get(source);
if (data) {
return data;
}
let short, long, host;
const sourceStr = source ? String(source) : "";
// If `data:...` uri
if (isDataScheme(sourceStr)) {
let commaIndex = sourceStr.indexOf(",");
if (commaIndex > -1) {
// The `short` name for a data URI becomes `data:` followed by the actual
// encoded content, omitting the MIME type, and charset.
let short = `data:${sourceStr.substring(commaIndex + 1)}`.slice(0, 100);
let result = { short, long: sourceStr };
gSourceNamesStore.set(source, result);
return result;
}
}
const parsedUrl = parseURL(sourceStr);
if (!parsedUrl) {
@ -99,19 +128,35 @@ function getSourceNames (source, unknownSourceString) {
long = sourceStr;
short = sourceStr.slice(0, 100);
} else {
short = parsedUrl.fileName;
long = parsedUrl.href;
host = parsedUrl.host;
long = parsedUrl.href;
if (parsedUrl.hash) {
long = long.replace(parsedUrl.hash, "");
}
if (parsedUrl.search) {
long = long.replace(parsedUrl.search, "");
}
short = parsedUrl.fileName;
// If `short` is just a slash, and we actually have a path,
// strip the slash and parse again to get a more useful short name.
// e.g. "http://foo.com/bar/" -> "bar", rather than "/"
if (short === "/" && parsedUrl.pathname !== "/") {
short = parseURL(long.replace(/\/$/, "")).fileName;
}
}
if (!short) {
if (!long) {
long = unknownSourceString;
long = UNKNOWN_SOURCE_STRING;
}
short = long.slice(0, 100);
}
return { short, long, host };
let result = { short, long, host };
gSourceNamesStore.set(source, result);
return result;
}
// For the functions below, we assume that we will never access the location
@ -126,6 +171,14 @@ function isColonSlashSlash(location, i=0) {
location.charCodeAt(++i) === CHAR_CODE_SLASH;
}
function isDataScheme(location, i=0) {
return location.charCodeAt(i) === CHAR_CODE_D &&
location.charCodeAt(++i) === CHAR_CODE_A &&
location.charCodeAt(++i) === CHAR_CODE_T &&
location.charCodeAt(++i) === CHAR_CODE_A &&
location.charCodeAt(++i) === CHAR_CODE_COLON;
}
function isContentScheme(location, i=0) {
let firstChar = location.charCodeAt(i);
@ -208,3 +261,4 @@ exports.parseURL = parseURL;
exports.getSourceNames = getSourceNames;
exports.isChromeScheme = isChromeScheme;
exports.isContentScheme = isContentScheme;
exports.isDataScheme = isDataScheme;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше