Merge mozilla central to inbound. r=merge a=merge on a CLOSED TREE

This commit is contained in:
Bogdan Tara 2018-01-09 00:18:26 +02:00
commit 251971ad8e
85 changed files with 1514 additions and 531 deletions

View File

@ -180,11 +180,6 @@ var gPage = {
*/
_handleUnloadEvent: function Page_handleUnloadEvent() {
gAllPages.unregister(this);
// compute page life-span and send telemetry probe: using milli-seconds will leave
// many low buckets empty. Instead we use half-second precision to make low end
// of histogram linear and not lose the change in user attention
let delta = Math.round((Date.now() - this._firstVisibleTime) / 500);
Services.telemetry.getHistogramById("NEWTAB_PAGE_LIFE_SPAN").add(delta);
},
/**
@ -233,18 +228,12 @@ var gPage = {
},
onPageFirstVisible: function () {
// Record another page impression.
Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true);
for (let site of gGrid.sites) {
if (site) {
site.captureIfMissing();
}
}
// save timestamp to compute page life-span delta
this._firstVisibleTime = Date.now();
if (document.readyState == "complete") {
this.onPageVisibleAndLoaded();
} else {

View File

@ -242,21 +242,6 @@ Site.prototype = {
} catch (e) {}
},
/**
* Record interaction with site using telemetry.
*/
_recordSiteClicked: function Site_recordSiteClicked(aIndex) {
if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") ||
Services.prefs.prefHasUserValue("browser.newtabpage.columns") ||
aIndex > 8) {
// We only want to get indices for the default configuration, everything
// else goes in the same bucket.
aIndex = 9;
}
Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED")
.add(aIndex);
},
/**
* Handles site click events.
*/
@ -265,16 +250,8 @@ Site.prototype = {
let tileIndex = this.cell.index;
let {button, target} = aEvent;
// Handle tile/thumbnail link click
if (target.classList.contains("newtab-link") ||
target.parentElement.classList.contains("newtab-link")) {
// Record for primary and middle clicks
if (button == 0 || button == 1) {
this._recordSiteClicked(tileIndex);
}
}
// Only handle primary clicks for the remaining targets
else if (button == 0) {
if (button == 0) {
aEvent.preventDefault();
if (target.classList.contains("newtab-control-block")) {
this.block();

View File

@ -534,17 +534,56 @@ this.FormAutofillHeuristics = {
*/
_parseAddressFields(fieldScanner) {
let parsedFields = false;
let addressLines = ["address-line1", "address-line2", "address-line3"];
for (let i = 0; !fieldScanner.parsingFinished && i < addressLines.length; i++) {
const addressLines = ["address-line1", "address-line2", "address-line3"];
// TODO: These address-line* regexps are for the lines with numbers, and
// they are the subset of the regexps in `heuristicsRegexp.js`. We have to
// find a better way to make them consistent.
const addressLineRegexps = {
"address-line1": new RegExp(
"address[_-]?line(1|one)|address1|addr1" +
"|addrline1|address_1" + // Extra rules by Firefox
"|indirizzo1" + // it-IT
"|住所1" + // ja-JP
"|地址1" + // zh-CN
"|주소.?1", // ko-KR
"iu"
),
"address-line2": new RegExp(
"address[_-]?line(2|two)|address2|addr2" +
"|addrline2|address_2" + // Extra rules by Firefox
"|indirizzo2" + // it-IT
"|住所2" + // ja-JP
"|地址2" + // zh-CN
"|주소.?2", // ko-KR
"iu"
),
"address-line3": new RegExp(
"address[_-]?line(3|three)|address3|addr3" +
"|addrline3|address_3" + // Extra rules by Firefox
"|indirizzo3" + // it-IT
"|住所3" + // ja-JP
"|地址3" + // zh-CN
"|주소.?3", // ko-KR
"iu"
),
};
while (!fieldScanner.parsingFinished) {
let detail = fieldScanner.getFieldDetailByIndex(fieldScanner.parsingIndex);
if (!detail || !addressLines.includes(detail.fieldName)) {
// When the field is not related to any address-line[1-3] fields, it
// means the parsing process can be terminated.
if (!detail || !addressLines.includes(detail.fieldName) || detail._reason == "autocomplete") {
// When the field is not related to any address-line[1-3] fields or
// determined by autocomplete attr, it means the parsing process can be
// terminated.
break;
}
fieldScanner.updateFieldName(fieldScanner.parsingIndex, addressLines[i]);
const elem = detail.elementWeakRef.get();
for (let regexp of Object.keys(addressLineRegexps)) {
if (this._matchRegexp(elem, addressLineRegexps[regexp])) {
fieldScanner.updateFieldName(fieldScanner.parsingIndex, regexp);
parsedFields = true;
}
}
fieldScanner.parsingIndex++;
parsedFields = true;
}
return parsedFields;

View File

@ -66,6 +66,7 @@ var HeuristicsRegExp = {
),
"address-line1": new RegExp(
"^address$|address[_-]?line(one)?|address1|addr1|street" +
"|addrline1|address_1" + // Extra rules by Firefox
"|(?:shipping|billing)address$" +
"|strasse|straße|hausnummer|housenumber" + // de-DE
"|house.?name" + // en-GB
@ -81,6 +82,7 @@ var HeuristicsRegExp = {
),
"address-line2": new RegExp(
"address[_-]?line(2|two)|address2|addr2|street|suite|unit" +
"|addrline2|address_2" + // Extra rules by Firefox
"|adresszusatz|ergänzende.?angaben" + // de-DE
"|direccion2|colonia|adicional" + // es
"|addresssuppl|complementnom|appartement" + // fr-FR
@ -94,6 +96,7 @@ var HeuristicsRegExp = {
),
"address-line3": new RegExp(
"address[_-]?line(3|three)|address3|addr3|street|suite|unit" +
"|addrline3|address_3" + // Extra rules by Firefox
"|adresszusatz|ergänzende.?angaben" + // de-DE
"|direccion3|colonia|adicional" + // es
"|addresssuppl|complementnom|appartement" + // fr-FR

View File

@ -40,6 +40,14 @@
<p><button type="reset">Reset</button></p>
</form>
<form id="formC">
<p><label><input type="text" name="someprefixAddrLine1" /></label></p>
<p><label>City: <input type="text" name="address-level2" /></label></p>
<p><label><input type="text" name="someprefixAddrLine2" /></label></p>
<p><label>Organization: <input type="text" name="organization" /></label></p>
<p><label><input type="text" name="someprefixAddrLine3" /></label></p>
</form>
</body>
</html>

View File

@ -32,6 +32,13 @@ runHeuristicsTest([
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
]],
[[
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "organization"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-line3"},
]],
],
},
], "../../fixtures/");

View File

@ -120,7 +120,8 @@ const TESTCASES = [
}],
},
{
description: "Address form with street-address, address-line[1, 3]",
description: "Address form with street-address, address-line[1, 3]" +
", determined by autocomplete attr",
document: `<form>
<input id="street-addr" autocomplete="street-address">
<input id="line1" autocomplete="address-line1">
@ -131,8 +132,34 @@ const TESTCASES = [
"guid": "123",
"street-address": "2 Harrison St line2 line3",
"-moz-street-address-one-line": "2 Harrison St line2 line3",
"address-line1": "2 Harrison St",
"address-line2": "line2 line3",
// Since the form is missing address-line2 field, the value of
// address-line1 should contain line2 value as well.
"address-line1": "2 Harrison St line2",
"address-line2": "line2",
"address-line3": "line3",
"address-level1": "CA",
"country": "US",
"tel": "+19876543210",
"tel-national": "9876543210",
}],
},
{
description: "Address form with street-address, address-line[1, 3]" +
", determined by heuristics",
document: `<form>
<input id="street-address">
<input id="address-line1">
<input id="address-line3">
</form>`,
profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
expectedResult: [{
"guid": "123",
"street-address": "2 Harrison St line2 line3",
"-moz-street-address-one-line": "2 Harrison St line2 line3",
// Since the form is missing address-line2 field, the value of
// address-line1 should contain line2 value as well.
"address-line1": "2 Harrison St line2",
"address-line2": "line2",
"address-line3": "line3",
"address-level1": "CA",
"country": "US",

View File

@ -149,7 +149,7 @@ public:
// Adjust code sections offsets according to their size
std::vector<ElfSection *>::iterator c = code.begin();
(*c)->getShdr().sh_addr = 0;
for(ElfSection *last = *(c++); c != code.end(); c++) {
for(ElfSection *last = *(c++); c != code.end(); ++c) {
unsigned int addr = last->getShdr().sh_addr + last->getSize();
if (addr & ((*c)->getAddrAlign() - 1))
addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
@ -162,7 +162,7 @@ public:
shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
data = new char[shdr.sh_size];
char *buf = data;
for (c = code.begin(); c != code.end(); c++) {
for (c = code.begin(); c != code.end(); ++c) {
memcpy(buf, (*c)->getData(), (*c)->getSize());
buf += (*c)->getSize();
}
@ -176,11 +176,11 @@ public:
void serialize(std::ofstream &file, char ei_class, char ei_data)
{
// Readjust code offsets
for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++)
for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); ++c)
(*c)->getShdr().sh_addr += getAddr();
// Apply relocations
for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) {
for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); ++c) {
for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext())
if (((rel->getType() == SHT_REL) ||
(rel->getType() == SHT_RELA)) &&
@ -237,7 +237,7 @@ private:
void scan_relocs_for_code(ElfRel_Section<Rel_Type> *rel)
{
ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) {
for (auto r = rel->rels.begin(); r != rel->rels.end(); ++r) {
ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
add_code_section(section);
}
@ -350,7 +350,7 @@ private:
char *buf = data + (the_code->getAddr() - code.front()->getAddr());
// TODO: various checks on the sections
ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) {
for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); ++r) {
// TODO: various checks on the symbol
const char *name = symtab->syms[ELF32_R_SYM(r->r_info)].name;
unsigned int addr;
@ -476,7 +476,7 @@ void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill)
for (; it != segment->end(); ++it) {
newSegment->addSection(*it);
}
for (it = newSegment->begin(); it != newSegment->end(); it++) {
for (it = newSegment->begin(); it != newSegment->end(); ++it) {
segment->removeSection(*it);
}
// Fill the virtual address space gap left between the two PT_LOADs
@ -576,7 +576,7 @@ int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type
relhack_entry.r_offset = relhack_entry.r_info = 0;
size_t init_array_reloc = 0;
for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
i != section->rels.end(); i++) {
i != section->rels.end(); ++i) {
// We don't need to keep R_*_NONE relocations
if (!ELF32_R_TYPE(i->r_info))
continue;

View File

@ -9,7 +9,7 @@
* Functions handling details about a single recorded animation frame snapshot
* (the calls list, rendering preview, thumbnails filmstrip etc.).
*/
var CallsListView = Heritage.extend(WidgetMethods, {
var CallsListView = extend(WidgetMethods, {
/**
* Initialization function, called when the tool is started.
*/

View File

@ -14,10 +14,11 @@ const EventEmitter = require("devtools/shared/old-event-emitter");
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
const { CanvasFront } = require("devtools/shared/fronts/canvas");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { extend } = require("devtools/shared/extend");
const flags = require("devtools/shared/flags");
const { LocalizationHelper } = require("devtools/shared/l10n");
const { PluralForm } = require("devtools/shared/plural-form");
const { Heritage, WidgetMethods, setNamedTimeout, clearNamedTimeout,
const { WidgetMethods, setNamedTimeout, clearNamedTimeout,
setConditionalTimeout } = require("devtools/client/shared/widgets/view-helpers");
// Use privileged promise in panel documents to prevent having them to freeze

View File

@ -8,7 +8,7 @@
/**
* Functions handling the recorded animation frame snapshots UI.
*/
var SnapshotsListView = Heritage.extend(WidgetMethods, {
var SnapshotsListView = extend(WidgetMethods, {
/**
* Initialization function, called when the tool is started.
*/

View File

@ -8,7 +8,8 @@
const actions = require("../actions/event-listeners");
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { Heritage, WidgetMethods } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const { WidgetMethods } = require("devtools/client/shared/widgets/view-helpers");
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
/**
@ -26,7 +27,7 @@ function EventListenersView(controller) {
controller.onChange("event-listeners", this.renderListeners.bind(this));
}
EventListenersView.prototype = Heritage.extend(WidgetMethods, {
EventListenersView.prototype = extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/

View File

@ -20,8 +20,8 @@ const actions = Object.assign(
require("../actions/breakpoints")
);
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { extend } = require("devtools/shared/extend");
const {
Heritage,
WidgetMethods,
setNamedTimeout
} = require("devtools/client/shared/widgets/view-helpers");
@ -80,7 +80,7 @@ function SourcesView(controller, DebuggerView) {
this._onConditionalPopupHidden = this._onConditionalPopupHidden.bind(this);
}
SourcesView.prototype = Heritage.extend(WidgetMethods, {
SourcesView.prototype = extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/
@ -350,13 +350,13 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
}
// Create the element node and menu popup for the breakpoint item.
let breakpointArgs = Heritage.extend(breakpoint.asMutable(), options);
let breakpointArgs = extend(breakpoint.asMutable(), options);
let breakpointView = this._createBreakpointView.call(this, breakpointArgs);
let contextMenu = this._createContextMenu.call(this, breakpointArgs);
// Append a breakpoint child item to the corresponding source item.
sourceItem.append(breakpointView.container, {
attachment: Heritage.extend(breakpointArgs, {
attachment: extend(breakpointArgs, {
actor: location.actor,
line: location.line,
view: breakpointView,

View File

@ -109,8 +109,9 @@ const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/Si
const { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
const { VariablesViewController, StackFrameUtils } = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
const EventEmitter = require("devtools/shared/old-event-emitter");
const { extend } = require("devtools/shared/extend");
const { gDevTools } = require("devtools/client/framework/devtools");
const { ViewHelpers, Heritage, WidgetMethods, setNamedTimeout,
const { ViewHelpers, WidgetMethods, setNamedTimeout,
clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
// Use privileged promise in panel documents to prevent having them to freeze

View File

@ -846,7 +846,7 @@ var DebuggerView = {
function ResultsPanelContainer() {
}
ResultsPanelContainer.prototype = Heritage.extend(WidgetMethods, {
ResultsPanelContainer.prototype = extend(WidgetMethods, {
/**
* Sets the anchor node for this container panel.
* @param nsIDOMNode aNode

View File

@ -551,7 +551,7 @@ function FilteredSourcesView(DebuggerView) {
this._onSelect = this._onSelect.bind(this);
}
FilteredSourcesView.prototype = Heritage.extend(ResultsPanelContainer.prototype, {
FilteredSourcesView.prototype = extend(ResultsPanelContainer.prototype, {
/**
* Initialization function, called when the debugger is started.
*/
@ -714,7 +714,7 @@ function FilteredFunctionsView(SourceScripts, Parser, DebuggerView) {
this._onSelect = this._onSelect.bind(this);
}
FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototype, {
FilteredFunctionsView.prototype = extend(ResultsPanelContainer.prototype, {
/**
* Initialization function, called when the debugger is started.
*/

View File

@ -23,7 +23,7 @@ function GlobalSearchView(DebuggerController, DebuggerView) {
this._onMatchClick = this._onMatchClick.bind(this);
}
GlobalSearchView.prototype = Heritage.extend(WidgetMethods, {
GlobalSearchView.prototype = extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/

View File

@ -20,7 +20,7 @@ function StackFramesClassicListView(DebuggerController, DebuggerView) {
this._onSelect = this._onSelect.bind(this);
}
StackFramesClassicListView.prototype = Heritage.extend(WidgetMethods, {
StackFramesClassicListView.prototype = extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/

View File

@ -25,7 +25,7 @@ function StackFramesView(DebuggerController, DebuggerView) {
this._getStackAsString = this._getStackAsString.bind(this);
}
StackFramesView.prototype = Heritage.extend(WidgetMethods, {
StackFramesView.prototype = extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/

View File

@ -29,7 +29,7 @@ function WatchExpressionsView(DebuggerController, DebuggerView) {
this._onKeyPress = this._onKeyPress.bind(this);
}
WatchExpressionsView.prototype = Heritage.extend(WidgetMethods, {
WatchExpressionsView.prototype = extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/

View File

@ -13,7 +13,7 @@ function WorkersView() {
this._onWorkerSelect = this._onWorkerSelect.bind(this);
}
WorkersView.prototype = Heritage.extend(WidgetMethods, {
WorkersView.prototype = extend(WidgetMethods, {
initialize: function () {
if (!Prefs.workersEnabled) {
return;

View File

@ -159,6 +159,7 @@
display: block;
height: 100%;
flex: 1;
overflow-x: auto;
}
.network-monitor .source-editor-mount {

View File

@ -11,7 +11,7 @@ add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
// Set a higher panel height in order to get full CodeMirror content
Services.prefs.setIntPref("devtools.toolbox.footer.height", 400);
Services.prefs.setIntPref("devtools.toolbox.footer.height", 600);
let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
info("Starting test... ");
@ -139,7 +139,13 @@ add_task(function* () {
is(labels.length, 3, "There should be 3 param values displayed in this tabpanel.");
let text = document.querySelector(".CodeMirror-code").textContent;
// Collect code lines and combine into one text for checking
let text = "";
let lines = [...document.querySelectorAll(".CodeMirror-line")];
lines.forEach((line) => {
text += line.textContent + "\n";
});
ok(text.includes("Content-Disposition: form-data; name=\"text\""),
"The text shown in the source editor is incorrect (1.1).");

View File

@ -8,7 +8,7 @@
*/
const { Task } = require("devtools/shared/task");
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGraphWidget");
const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
@ -59,7 +59,7 @@ function PerformanceGraph(parent, metric) {
this.setTheme();
}
PerformanceGraph.prototype = Heritage.extend(LineGraphWidget.prototype, {
PerformanceGraph.prototype = extend(LineGraphWidget.prototype, {
strokeWidth: STROKE_WIDTH,
dampenValuesFactor: DAMPEN_VALUES,
fixedHeight: HEIGHT,
@ -108,7 +108,7 @@ function FramerateGraph(parent) {
PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.fps"));
}
FramerateGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
FramerateGraph.prototype = extend(PerformanceGraph.prototype, {
mainColor: FRAMERATE_GRAPH_COLOR_NAME,
setPerformanceData: function ({ duration, ticks }, resolution) {
this.dataDuration = duration;
@ -126,7 +126,7 @@ function MemoryGraph(parent) {
PerformanceGraph.call(this, parent, ProfilerGlobal.L10N.getStr("graphs.memory"));
}
MemoryGraph.prototype = Heritage.extend(PerformanceGraph.prototype, {
MemoryGraph.prototype = extend(PerformanceGraph.prototype, {
mainColor: MEMORY_GRAPH_COLOR_NAME,
setPerformanceData: function ({ duration, memory }) {
this.dataDuration = duration;
@ -138,7 +138,7 @@ function TimelineGraph(parent, filter) {
MarkersOverview.call(this, parent, filter);
}
TimelineGraph.prototype = Heritage.extend(MarkersOverview.prototype, {
TimelineGraph.prototype = extend(MarkersOverview.prototype, {
headerHeight: MARKERS_GRAPH_HEADER_HEIGHT,
rowHeight: MARKERS_GRAPH_ROW_HEIGHT,
groupPadding: MARKERS_GROUP_VERTICAL_PADDING,
@ -439,7 +439,7 @@ function OptimizationsGraph(parent) {
this.setTheme();
}
OptimizationsGraph.prototype = Heritage.extend(MountainGraphWidget.prototype, {
OptimizationsGraph.prototype = extend(MountainGraphWidget.prototype, {
render: Task.async(function* (threadNode, frameNode) {
// Regardless if we draw or clear the graph, wait

View File

@ -9,7 +9,7 @@
* markers are visible in the "waterfall".
*/
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const { AbstractCanvasGraph } = require("devtools/client/shared/widgets/Graphs");
const { colorUtils } = require("devtools/shared/css/color");
@ -48,7 +48,7 @@ function MarkersOverview(parent, filter = [], ...args) {
this.setFilter(filter);
}
MarkersOverview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
MarkersOverview.prototype = extend(AbstractCanvasGraph.prototype, {
clipheadLineColor: OVERVIEW_CLIPHEAD_LINE_COLOR,
selectionLineColor: OVERVIEW_SELECTION_LINE_COLOR,
headerHeight: OVERVIEW_HEADER_HEIGHT,

View File

@ -9,7 +9,7 @@
*/
const { L10N } = require("devtools/client/performance/modules/global");
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const { AbstractTreeItem } = require("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm");
const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
@ -174,7 +174,7 @@ function CallView({
this._onUrlClick = this._onUrlClick.bind(this);
}
CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
CallView.prototype = extend(AbstractTreeItem.prototype, {
/**
* Creates the view for this tree node.
* @param nsIDOMNode document

View File

@ -14,10 +14,11 @@ var { loader, require } = BrowserLoaderModule.BrowserLoader({
window
});
var { Task } = require("devtools/shared/task");
/* exported Heritage, ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout */
var { Heritage, ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
/* exported ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout */
var { ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
var { PrefObserver } = require("devtools/client/shared/prefs");
/* exported extend */
const { extend } = require("devtools/shared/extend");
// Use privileged promise in panel documents to prevent having them to freeze
// during toolbox destruction. See bug 1402779.
var Promise = require("Promise");

View File

@ -55,14 +55,14 @@ exports.synthesizeProfile = () => {
exports.synthesizeCustomTreeClass = () => {
const { Cu } = require("chrome");
const { AbstractTreeItem } = Cu.import("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm", {});
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
function MyCustomTreeItem(dataSrc, properties) {
AbstractTreeItem.call(this, properties);
this.itemDataSrc = dataSrc;
}
MyCustomTreeItem.prototype = Heritage.extend(AbstractTreeItem.prototype, {
MyCustomTreeItem.prototype = extend(AbstractTreeItem.prototype, {
_displaySelf: function (document, arrowNode) {
let node = document.createElement("hbox");
node.style.marginInlineStart = (this.level * 10) + "px";

View File

@ -9,7 +9,7 @@
/**
* CallTree view containing profiler call tree, controlled by DetailsView.
*/
var JsCallTreeView = Heritage.extend(DetailsSubview, {
var JsCallTreeView = extend(DetailsSubview, {
rerenderPrefs: [
"invert-call-tree",

View File

@ -10,7 +10,7 @@
* FlameGraph view containing a pyramid-like visualization of a profile,
* controlled by DetailsView.
*/
var JsFlameGraphView = Heritage.extend(DetailsSubview, {
var JsFlameGraphView = extend(DetailsSubview, {
shouldUpdateWhileMouseIsActive: true,

View File

@ -9,7 +9,7 @@
/**
* CallTree view containing memory allocation sites, controlled by DetailsView.
*/
var MemoryCallTreeView = Heritage.extend(DetailsSubview, {
var MemoryCallTreeView = extend(DetailsSubview, {
rerenderPrefs: [
"invert-call-tree"

View File

@ -10,7 +10,7 @@
* FlameGraph view containing a pyramid-like visualization of memory allocation
* sites, controlled by DetailsView.
*/
var MemoryFlameGraphView = Heritage.extend(DetailsSubview, {
var MemoryFlameGraphView = extend(DetailsSubview, {
shouldUpdateWhileMouseIsActive: true,

View File

@ -15,7 +15,7 @@ const { TickUtils } = require("devtools/client/performance/modules/waterfall-tic
/**
* Waterfall view containing the timeline markers, controlled by DetailsView.
*/
var WaterfallView = Heritage.extend(DetailsSubview, {
var WaterfallView = extend(DetailsSubview, {
// Smallest unit of time between two markers. Larger by 10x^3 than Number.EPSILON.
MARKER_EPSILON: 0.000000000001,

View File

@ -55,7 +55,7 @@ const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
const {Heritage} = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
@ -2177,7 +2177,7 @@ function ScratchpadWindow() {}
ScratchpadWindow.consoleFor = ScratchpadTab.consoleFor;
ScratchpadWindow.prototype = Heritage.extend(ScratchpadTab.prototype, {
ScratchpadWindow.prototype = extend(ScratchpadTab.prototype, {
/**
* Attach to this window.
*
@ -2207,7 +2207,7 @@ function ScratchpadTarget(aTarget)
ScratchpadTarget.consoleFor = ScratchpadTab.consoleFor;
ScratchpadTarget.prototype = Heritage.extend(ScratchpadTab.prototype, {
ScratchpadTarget.prototype = extend(ScratchpadTab.prototype, {
_attach: function ST__attach()
{
if (this._target.isRemote) {

View File

@ -15,7 +15,8 @@ const EventEmitter = require("devtools/shared/old-event-emitter");
const Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
const Editor = require("devtools/client/sourceeditor/editor");
const {LocalizationHelper} = require("devtools/shared/l10n");
const {Heritage, WidgetMethods, setNamedTimeout} =
const {extend} = require("devtools/shared/extend");
const {WidgetMethods, setNamedTimeout} =
require("devtools/client/shared/widgets/view-helpers");
const {Task} = require("devtools/shared/task");
@ -198,7 +199,7 @@ var EventsHandler = {
/**
* Functions handling the sources UI.
*/
var ShadersListView = Heritage.extend(WidgetMethods, {
var ShadersListView = extend(WidgetMethods, {
/**
* Initialization function, called when the tool is started.
*/

View File

@ -41,7 +41,7 @@ this.EXPORTED_SYMBOLS = ["AbstractTreeItem"];
* this.itemDataSrc = dataSrc;
* }
*
* MyCustomTreeItem.prototype = Heritage.extend(AbstractTreeItem.prototype, {
* MyCustomTreeItem.prototype = extend(AbstractTreeItem.prototype, {
* _displaySelf: function(document, arrowNode) {
* let node = document.createElement("hbox");
* ...

View File

@ -1,6 +1,7 @@
"use strict";
const { Heritage, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const { setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
const HTML_NS = "http://www.w3.org/1999/xhtml";
@ -77,7 +78,7 @@ this.BarGraphWidget = function (parent, ...args) {
});
};
BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
BarGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,

View File

@ -1,7 +1,7 @@
"use strict";
const { Task } = require("devtools/shared/task");
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
const { LocalizationHelper } = require("devtools/shared/l10n");
@ -89,7 +89,7 @@ this.LineGraphWidget = function (parent, options = {}, ...args) {
});
};
LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
LineGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
backgroundColor: GRAPH_BACKGROUND_COLOR,
backgroundGradientStart: GRAPH_BACKGROUND_GRADIENT_START,
backgroundGradientEnd: GRAPH_BACKGROUND_GRADIENT_END,

View File

@ -1,6 +1,6 @@
"use strict";
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
const { extend } = require("devtools/shared/extend");
const { AbstractCanvasGraph } = require("devtools/client/shared/widgets/Graphs");
// Bar graph constants.
@ -57,7 +57,7 @@ this.MountainGraphWidget = function (parent, ...args) {
AbstractCanvasGraph.apply(this, [parent, "mountain-graph", ...args]);
};
MountainGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
MountainGraphWidget.prototype = extend(AbstractCanvasGraph.prototype, {
backgroundColor: GRAPH_BACKGROUND_COLOR,
strokeColor: GRAPH_STROKE_COLOR,
strokeWidth: GRAPH_STROKE_WIDTH,

View File

@ -24,7 +24,8 @@ const Services = require("Services");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const { Heritage, ViewHelpers, setNamedTimeout } =
const { extend } = require("devtools/shared/extend");
const { ViewHelpers, setNamedTimeout } =
require("devtools/client/shared/widgets/view-helpers");
const { Task } = require("devtools/shared/task");
const nodeConstants = require("devtools/shared/dom-node-constants");
@ -2179,7 +2180,7 @@ function Variable(aScope, aName, aDescriptor, aOptions) {
this.setGrip(aDescriptor.value);
}
Variable.prototype = Heritage.extend(Scope.prototype, {
Variable.prototype = extend(Scope.prototype, {
/**
* Whether this Variable should be prefetched when it is remoted.
*/
@ -3072,7 +3073,7 @@ function Property(aVar, aName, aDescriptor, aOptions) {
Variable.call(this, aVar, aName, aDescriptor, aOptions);
}
Property.prototype = Heritage.extend(Variable.prototype, {
Property.prototype = extend(Variable.prototype, {
/**
* The class name applied to this property's target element.
*/
@ -3160,7 +3161,7 @@ VariablesView.prototype.commitHierarchy = function () {
// Some variables are likely to contain a very large number of properties.
// It would be a bad idea to re-expand them or perform expensive operations.
VariablesView.prototype.commitHierarchyIgnoredItems = Heritage.extend(null, {
VariablesView.prototype.commitHierarchyIgnoredItems = extend(null, {
"window": true,
"this": true
});
@ -4116,7 +4117,7 @@ function EditableName(aVariable, aOptions) {
EditableName.create = Editable.create;
EditableName.prototype = Heritage.extend(Editable.prototype, {
EditableName.prototype = extend(Editable.prototype, {
className: "element-name-input",
get label() {
@ -4138,7 +4139,7 @@ function EditableValue(aVariable, aOptions) {
EditableValue.create = Editable.create;
EditableValue.prototype = Heritage.extend(Editable.prototype, {
EditableValue.prototype = extend(Editable.prototype, {
className: "element-value-input",
get label() {
@ -4160,7 +4161,7 @@ function EditableNameAndValue(aVariable, aOptions) {
EditableNameAndValue.create = Editable.create;
EditableNameAndValue.prototype = Heritage.extend(EditableName.prototype, {
EditableNameAndValue.prototype = extend(EditableName.prototype, {
_reset: function (e) {
// Hide the Variable or Property if the user presses escape.
this._variable.remove();

View File

@ -13,29 +13,6 @@ const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
var namedTimeoutsStore = new Map();
/**
* Inheritance helpers from the addon SDK's core/heritage.
* Remove these when all devtools are loadered.
*/
exports.Heritage = {
/**
* @see extend in sdk/core/heritage.
*/
extend: function (prototype, properties = {}) {
return Object.create(prototype, this.getOwnPropertyDescriptors(properties));
},
/**
* @see getOwnPropertyDescriptors in sdk/core/heritage.
*/
getOwnPropertyDescriptors: function (object) {
return Object.getOwnPropertyNames(object).reduce((descriptor, name) => {
descriptor[name] = Object.getOwnPropertyDescriptor(object, name);
return descriptor;
}, {});
}
};
/**
* Helper for draining a rapid succession of events and invoking a callback
* once everything settles down.
@ -490,7 +467,7 @@ Item.prototype = {
* this.widget = new MyWidget(document.querySelector(".my-node"));
* }
*
* MyView.prototype = Heritage.extend(WidgetMethods, {
* MyView.prototype = extend(WidgetMethods, {
* myMethod: function() {},
* ...
* });

View File

@ -1,6 +1,6 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// A test to ensure Style Editor doesn't bybass cache when loading style sheet
@ -42,8 +42,6 @@ add_task(function* () {
}
}
is(items.length, 2,
"Got two requests for doc_uncached.css after Style Editor was loaded.");
ok(items[1].fromCache,
"Second request was loaded from browser cache");
is(items.length, 1,
"Got one request for doc_uncached.css after Style Editor was loaded.");
});

View File

@ -125,10 +125,38 @@ describe("ConsoleAPICall component:", () => {
describe("console.count", () => {
it("renders", () => {
const message = stubPreparedMessages.get("console.count('bar')");
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
const messages = [{
key: "console.count('bar')",
expectedBodyText: "bar: 1",
}, {
key: "console.count | default: 1",
expectedBodyText: "default: 1",
}, {
key: "console.count | default: 2",
expectedBodyText: "default: 2",
}, {
key: "console.count | test counter: 1",
expectedBodyText: "test counter: 1",
}, {
key: "console.count | test counter: 2",
expectedBodyText: "test counter: 2",
}, {
key: "console.count | default: 3",
expectedBodyText: "default: 3",
}, {
key: "console.count | default: 4",
expectedBodyText: "default: 4",
}, {
key: "console.count | test counter: 3",
expectedBodyText: "test counter: 3",
}];
expect(wrapper.find(".message-body").text()).toBe("bar: 1");
for (const {key, expectedBodyText} of messages) {
const message = stubPreparedMessages.get(key);
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
expect(wrapper.find(".message-body").text()).toBe(expectedBodyText);
}
});
});

View File

@ -135,6 +135,28 @@ consoleApi.set("console.dir({C, M, Y, K})", {
code: "console.dir({cyan: 'C', magenta: 'M', yellow: 'Y', black: 'K'});"
});
consoleApi.set("console.count", {
keys: [
"console.count | default: 1",
"console.count | default: 2",
"console.count | test counter: 1",
"console.count | test counter: 2",
"console.count | default: 3",
"console.count | clear",
"console.count | default: 4",
"console.count | test counter: 3",
],
code: `
console.count();
console.count();
console.count("test counter");
console.count("test counter");
console.count();
console.clear();
console.count();
console.count("test counter");
`});
// CSS messages
const cssMessage = new Map();

View File

@ -1297,6 +1297,200 @@ stubPreparedMessages.set("console.dir({C, M, Y, K})", new ConsoleMessage({
"indent": 0
}));
stubPreparedMessages.set("console.count | default: 1", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913333,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "default: 1",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 2,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | default: 2", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913334,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "default: 2",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 2\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 3,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | test counter: 1", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913334,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "test counter: 1",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":4,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 4,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | test counter: 2", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913334,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "test counter: 2",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 2\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 5,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | default: 3", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913334,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "default: 3",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 3\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 6,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | clear", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913334,
"type": "clear",
"helperType": null,
"level": "log",
"messageText": null,
"parameters": [
"Console was cleared."
],
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":7,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"source\":\"console-api\",\"type\":\"clear\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 7,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | default: 4", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913335,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "default: 4",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":8,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 4\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 8,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPreparedMessages.set("console.count | test counter: 3", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1511365913335,
"type": "log",
"helperType": null,
"level": "log",
"messageText": "test counter: 3",
"parameters": null,
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":9,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 3\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 9,
"column": 5
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
stubPackets.set("console.log('foobar', 'test')", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
@ -2524,6 +2718,217 @@ stubPackets.set("console.dir({C, M, Y, K})", {
}
});
stubPackets.set("console.count | default: 1", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"default"
],
"columnNumber": 5,
"counter": {
"count": 1,
"label": "default"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 2,
"private": false,
"timeStamp": 1511365913333,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | default: 2", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"default"
],
"columnNumber": 5,
"counter": {
"count": 2,
"label": "default"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 3,
"private": false,
"timeStamp": 1511365913334,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | test counter: 1", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"test counter"
],
"columnNumber": 5,
"counter": {
"count": 1,
"label": "test counter"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 4,
"private": false,
"timeStamp": 1511365913334,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | test counter: 2", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"test counter"
],
"columnNumber": 5,
"counter": {
"count": 2,
"label": "test counter"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 5,
"private": false,
"timeStamp": 1511365913334,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | default: 3", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"default"
],
"columnNumber": 5,
"counter": {
"count": 3,
"label": "default"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 6,
"private": false,
"timeStamp": 1511365913334,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | clear", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [],
"columnNumber": 5,
"counter": null,
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "clear",
"lineNumber": 7,
"private": false,
"timeStamp": 1511365913334,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | default: 4", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"default"
],
"columnNumber": 5,
"counter": {
"count": 4,
"label": "default"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 8,
"private": false,
"timeStamp": 1511365913335,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
stubPackets.set("console.count | test counter: 3", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"test counter"
],
"columnNumber": 5,
"counter": {
"count": 3,
"label": "test counter"
},
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "count",
"lineNumber": 9,
"private": false,
"timeStamp": 1511365913335,
"timer": null,
"workerType": "none",
"styles": [],
"category": "webdev"
}
});
module.exports = {
stubPreparedMessages,
stubPackets,

View File

@ -272,9 +272,7 @@ skip-if = true # Bug 1403907
[browser_webconsole_errors_after_page_reload.js]
[browser_webconsole_eval_in_debugger_stackframe.js]
[browser_webconsole_eval_in_debugger_stackframe2.js]
skip-if = true # Bug 1408893
[browser_webconsole_execution_scope.js]
skip-if = true # Bug 1405333
[browser_webconsole_external_script_errors.js]
[browser_webconsole_file_uri.js]
skip-if = true # Bug 1404382

View File

@ -10,62 +10,56 @@
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/test-eval-in-stackframe.html";
"new-console-output/test/mochitest/test-eval-in-stackframe.html";
// Force the old debugger UI since it's directly used (see Bug 1301705)
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
registerCleanupFunction(function* () {
Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
});
add_task(async function () {
// Force the old debugger UI since it's directly used (see Bug 1301705).
await pushPref("devtools.debugger.new-debugger-frontend", false);
add_task(function* () {
yield loadTab(TEST_URI);
info("open the web console");
let hud = yield openConsole();
let {jsterm} = hud;
info("open the console");
const hud = await openNewTabAndConsole(TEST_URI);
const {jsterm} = hud;
info("open the debugger");
let {panelWin} = yield openDebugger();
let {DebuggerController} = panelWin;
let {activeThread} = DebuggerController;
let {panel} = await openDebugger();
let {activeThread} = panel.panelWin.DebuggerController;
let firstCall = defer();
let frameAdded = defer();
executeSoon(() => {
info("Executing firstCall");
activeThread.addOneTimeListener("framesadded", () => {
executeSoon(frameAdded.resolve);
});
jsterm.execute("firstCall()").then(firstCall.resolve);
const onFirstCallFramesAdded = activeThread.addOneTimeListener("framesadded");
// firstCall calls secondCall, which has a debugger statement, so we'll be paused.
const onFirstCallMessageReceived = waitForMessage(hud, "undefined");
const unresolvedSymbol = Symbol();
let firstCallEvaluationResult = unresolvedSymbol;
onFirstCallMessageReceived.then(message => {
firstCallEvaluationResult = message;
});
jsterm.execute("firstCall()");
info("Waiting for a frame to be added");
yield frameAdded.promise;
await onFirstCallFramesAdded;
info("frames added, select the console again");
await openConsole();
info("Executing basic command while paused");
yield executeAndConfirm(jsterm, "1 + 2", "3");
let onMessageReceived = waitForMessage(hud, "3");
jsterm.execute("1 + 2");
let message = await onMessageReceived;
ok(message, "`1 + 2` was evaluated whith debugger paused");
info("Executing command using scoped variables while paused");
yield executeAndConfirm(jsterm, "foo + foo2",
'"globalFooBug783499foo2SecondCall"');
onMessageReceived = waitForMessage(hud, `"globalFooBug783499foo2SecondCall"`);
jsterm.execute("foo + foo2");
message = await onMessageReceived;
ok(message, "`foo + foo2` was evaluated as expected with debugger paused");
info("Checking the first command, which is the last to resolve since it paused");
ok(firstCallEvaluationResult === unresolvedSymbol, "firstCall was not evaluated yet");
info("Resuming the thread");
activeThread.resume();
info("Checking the first command, which is the last to resolve since it " +
"paused");
let node = yield firstCall.promise;
is(node.querySelector(".message-body").textContent,
"undefined",
"firstCall() returned correct value");
message = await onFirstCallMessageReceived;
ok(firstCallEvaluationResult !== unresolvedSymbol,
"firstCall() returned correct value");
});
function* executeAndConfirm(jsterm, input, output) {
info("Executing command `" + input + "`");
let node = yield jsterm.execute(input);
is(node.querySelector(".message-body").textContent, output,
"Expected result from call to " + input);
}

View File

@ -8,30 +8,20 @@
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/test-console.html";
"new-console-output/test/mochitest/test-console.html";
add_task(function* () {
yield loadTab(TEST_URI);
let hud = yield openConsole();
hud.jsterm.clearOutput();
hud.jsterm.execute("window.location.href;");
add_task(async function () {
const hud = await openNewTabAndConsole(TEST_URI);
const {jsterm} = hud;
jsterm.clearOutput();
let [input, output] = yield waitForMessages({
webconsole: hud,
messages: [{
text: "window.location.href;",
category: CATEGORY_INPUT,
},
{
text: TEST_URI,
category: CATEGORY_OUTPUT,
}],
});
const onInputMessage = waitForMessage(hud, "window.location.href;", ".message.command");
const onEvaluationResultMessage = waitForMessage(hud, TEST_URI, ".message.result");
jsterm.execute("window.location.href;");
let inputNode = [...input.matched][0];
let outputNode = [...output.matched][0];
is(inputNode.getAttribute("category"), "input",
"input node category is correct");
is(outputNode.getAttribute("category"), "output",
"output node category is correct");
let message = await onInputMessage;
ok(message, "Input message is displayed with the expected class");
message = await onEvaluationResultMessage;
ok(message, "EvaluationResult message is displayed with the expected class");
});

View File

@ -698,7 +698,7 @@ describe("Message reducer:", () => {
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length).toBe(1);
expect(Object.keys(networkUpdates).length > 0).toBe(true);
dispatch(actions.messagesClear());

View File

@ -18,7 +18,14 @@ describe("Testing UI", () => {
describe("Toggle sidebar", () => {
it("sidebar is toggled on and off", () => {
store.dispatch(actions.sidebarClose());
const packet = stubPackets.get("inspect({a: 1})");
const message = stubPreparedMessages.get("inspect({a: 1})");
store.dispatch(actions.messageAdd(packet));
const actorId = message.parameters[0].actor;
const messageId = getFirstMessage(store.getState()).id;
store.dispatch(actions.showObjectInSidebar(actorId, messageId));
expect(store.getState().ui.sidebarVisible).toEqual(true);
store.dispatch(actions.sidebarClose());
expect(store.getState().ui.sidebarVisible).toEqual(false);
@ -27,7 +34,14 @@ describe("Testing UI", () => {
describe("Hide sidebar on clear", () => {
it("sidebar is hidden on clear", () => {
store.dispatch(actions.sidebarClose());
const packet = stubPackets.get("inspect({a: 1})");
const message = stubPreparedMessages.get("inspect({a: 1})");
store.dispatch(actions.messageAdd(packet));
const actorId = message.parameters[0].actor;
const messageId = getFirstMessage(store.getState()).id;
store.dispatch(actions.showObjectInSidebar(actorId, messageId));
expect(store.getState().ui.sidebarVisible).toEqual(true);
store.dispatch(actions.messagesClear());
expect(store.getState().ui.sidebarVisible).toEqual(false);

View File

@ -14,6 +14,7 @@
* [Creating and sending patches](./contributing/making-prs.md)
* [Code reviews](./contributing/code-reviews.md)
* [Filing good bugs](./contributing/filing-good-bugs.md)
* [Investigating performance issues](./contributing/performance.md)
* [Bugs and issue trackers](bugs-issues.md)
* [Files and directories](files/README.md)
* [Adding New Files](files/adding-files.md)

View File

@ -0,0 +1,152 @@
# Writing efficient code
When debugging a page, tools get to slow down the website because of the added instrumentation.
While working on Developer Tools we should strive to be the less impactful.
First, because it is painful to work with laggy UI, but also because some tools record timings.
For example, the network monitor records HTTP request timings.
If the tools are slowing down Firefox significantly, it will make these measurements be wrong.
To be efficient while working on performance, you should always focus on one precise user scenario.
It could be:
* a bug report where someone reports a precise interaction being slow,
* or you could be trying to improve overall tools performance by looking at the most common usages.
The important point here is to have some steps to reproduce, that you can redo manually in order to record a profile.
And also, it is even better if you can replay via a test script. Test script that you can save as a new performance test.
## Don't guess — profile.
The very first thing to do is to record a profile while reproducing the scenario.
Here's the Firefox documentation for [how to install the profiler and record a profile](https://developer.mozilla.org/docs/Mozilla/Performance/Reporting_a_Performance_Problem) and also [how to interpret the profiles](https://developer.mozilla.org/docs/Mozilla/Performance/Profiling_with_the_Built-in_Profiler#Understanding_Profiles)
There are some peculiarities about DevTools architecture that are worth knowing about when looking at a profile:
### Tweak profiler default settings
The default buffer size (9MB) is too small. If you don't increase it, you may easily miss data and only see last couple of seconds of your recording.
To increase the buffer size, click on the profiler add-on icon, in the Firefox toolbar, and set it to 360MB, like this:
<img src="performance/profiler-buffer-size.png" alt="Profiler buffer size" style="width: 300px" />
The other setting worth mentioning for DevTools debugging is the `Interval`
The profiler records only samples, based on this `Interval`.
If you want to see more fine-grained stack traces, you may reduce this interval to 0.1ms,
but do that only if you really need it, as it will make Firefox much even slower when recording,
and the measured times will be even slower.
### The DevTools UI runs on the parent process
When you are debugging tool front-ends (e.g. panels), always ensure you select the `Main Thread` line.
It should have a light blue background like this:
<img src="performance/profiler-main-thread.png" alt="Select main process" style="width: 300px" />
Otherwise, the vast majority of DevTools backend (DebuggerServer, actors, ...) lives in content processes.
So if you are debugging them, you should select one of the `Content` lines.
### Most of the DevTools codebase is in Javascript
In the call tree, it is easier to filter by `JS`, via this menu list:
<img src="performance/profiler-filter-js.png" alt="JS Filtering" style="width: 200px" />
But note that you may have to switch back to `Combined` in order to understand why some particular Javascript method is slow.
### Handy filter strings for DevTools:
* `require`
Helps highlighting the cost of module loading
![modules](performance/profiler-filter-require.png)
* DevTools uses two kind of URLs:
* `chrome://devtools/` for all panel documents. Filter with this to see the cost of all panel documents:
![panels documents](performance/profiler-chrome-url.png)
* `resource://devtools/` for all javascript modules. Filter with this to see the cost of all modules:
![modules](performance/profiler-resource-url.png)
### Record durations manually
Sometimes it is handy to focus on a very precise piece of code and record its time manually.
For example when you identified one slow running method and think you can speed it up.
It saves your from having to: record the profile, wait for the profiler to display and search for the precise method durations.
#### Print durations in your Terminal and in the Browser Console
You can use the [`Performance`](https://developer.mozilla.org/docs/Web/API/Performance) API, like this:
```
let start = window.performance.now();
// Run the code you want to measure
// Once it is done, do:
console.log("my function took", window.performance.now() - start, "ms");
```
#### Use markers
The Performance API also allows recording markers, like this:
```
window.performance.mark("my-function-start");
// Run the code you want to measure
// Once it is done, do:
window.performance.measure("my-function", "my-function-start");
```
This marker will appear in the `Marker Chart` section in perf-html, in the `UserTiming` lines:
![custom markers](performance/profiler-custom-markers.png)
You can double click on it to make perf-html display the record during this precise moment in time,
and the call tree will only display what was executed during this measurement.
### Prototype quickly
Sometimes the best way to find what is slow is to comment blocks of code out
and uncomment them one by one until you identify the culprit. And then focus on it.
There are few things worse than spending a long time refactoring the piece of code that was not slow to begin with!
## Assess your improvement.
Once you have a patch that you think improves the performance, you have to assess whether it actually improves it.
### Record another profile
Compare the two profiles, without and with your patch.
Then see if the call tree reports a significant difference:
* A function call completely disappears in the new profile, with your fix.
For example you were loading a big module, and you got a frame for `require("my/big/module")` call, and no longer see it.
* The same function call takes xxx ms less with your patch.
This [lazy loading of modules in netmonitor](https://bugzilla.mozilla.org/show_bug.cgi?id=1420289) is a good example.
Without this patch, App.js was loading in 91ms and was loading MonitorPanel.js and StatisticsPanel.js as dependencies:
![netmonitor without patch](performance/profiler-netmonitor-open.png)
With the patch, App.js loads in 47ms and only loads MonitorPanel.js:
![netmonitor with patch](performance/profiler-netmon-open-fixed.png)
It highlights that:
* we no longer load StatisticsPanel,
* App is faster to load.
### Run performance tests
See if any subtest reports a improvement. Ensure that the improvement makes any sense.
For example, if the test is 50% faster, maybe you broke the performance test.
This might happen if the test no longer waits for all the operations to finish executing before completing.
To push your current patch to try, execute:
```
./mach try -b o -p linux64 -u none -t g2-e10s --rebuild-talos 5 --artifact
```
It will print in your Terminal a link to perfherder like this one:
[https://treeherder.mozilla.org/perf.html#/comparechooser?newProject=try&newRevision=9bef6cb13c43bbce21d40ffaea595e082a4c28db](https://treeherder.mozilla.org/perf.html#/comparechooser?newProject=try&newRevision=9bef6cb13c43bbce21d40ffaea595e082a4c28db)
Running performance tests takes time, so you should open it 30 minutes up to 2 hours later to see your results.
See [Performance tests (DAMP)](../tests/performance-tests.md) for more information about PerfHerder/try.
Let's look at how to interpret an actual real-life [set of perfherder results](https://treeherder.mozilla.org/perf.html#/comparesubtest?originalProject=mozilla-central&newProject=try&newRevision=9bef6cb13c43bbce21d40ffaea595e082a4c28db&originalSignature=edaec66500db21d37602c99daa61ac983f21a6ac&newSignature=edaec66500db21d37602c99daa61ac983f21a6ac&showOnlyImportant=1&framework=1&selectedTimeRange=172800):
![perfherder results](performance/perfherder-results.png)
These results are related to [lazy loading of modules in netmonitor](https://bugzilla.mozilla.org/show_bug.cgi?id=1420289).
It is interesting to see that this patch is a trade-off. It makes netmonitor opening significantly faster, by preventing loading many modules during its opening.
But it makes the page reload a bit slower as some modules that used to be loaded during netmonitor open, now have to be loaded during page reload.

View File

@ -423,6 +423,12 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
* If an error occurs, the promise is rejected with that error.
*/
fetchStylesheet: Task.async(function* (href) {
// Check if network monitor observed this load, and if so, use that.
let result = this.fetchStylesheetFromNetworkMonitor(href);
if (result) {
return result;
}
let options = {
loadFromCache: true,
policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
@ -442,7 +448,6 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
options.principal = this.ownerDocument.nodePrincipal;
}
let result;
try {
result = yield fetch(this.href, options);
} catch (e) {
@ -458,6 +463,43 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
return result;
}),
/**
* Try to locate the console actor if it exists via our parent actor (the tab).
*/
get _consoleActor() {
if (this.parentActor.exited) {
return null;
}
let form = this.parentActor.form();
return this.conn._getOrCreateActor(form.consoleActor);
},
/**
* Try to fetch the stylesheet text from the network monitor. If it was enabled during
* the load, it should have a copy of the text saved.
*
* @param string href
* The URL of the sheet to fetch.
*/
fetchStylesheetFromNetworkMonitor(href) {
let consoleActor = this._consoleActor;
if (!consoleActor) {
return null;
}
let request = consoleActor.getNetworkEventActorForURL(href);
if (!request) {
return null;
}
let content = request._response.content;
if (request._discardResponseBody || !content) {
return null;
}
return {
content: content.text,
contentType: content.mimeType,
};
},
/**
* Protocol method to get the media rules for the stylesheet.
*/

View File

@ -68,6 +68,7 @@ function WebConsoleActor(connection, parentActor) {
this.dbg = this.parentActor.makeDebugger();
this._netEvents = new Map();
this._networkEventActorsByURL = new Map();
this._gripDepth = 0;
this._listeners = new Set();
this._lastConsoleInputEvaluation = undefined;
@ -124,13 +125,23 @@ WebConsoleActor.prototype =
/**
* Holds a map between nsIChannel objects and NetworkEventActors for requests
* created with sendHTTPRequest.
* created with sendHTTPRequest or found via the network listener.
*
* @private
* @type Map
*/
_netEvents: null,
/**
* Holds a map from URL to NetworkEventActors for requests noticed by the network
* listener. Requests are added when they start, so the actor might not yet have all
* data for the request until it has completed.
*
* @private
* @type Map
*/
_networkEventActorsByURL: null,
/**
* Holds a set of all currently registered listeners.
*
@ -1632,6 +1643,8 @@ WebConsoleActor.prototype =
let actor = this.getNetworkEventActor(event.channelId);
actor.init(event);
this._networkEventActorsByURL.set(actor._request.url, actor);
let packet = {
from: this.actorID,
type: "networkEvent",
@ -1665,6 +1678,18 @@ WebConsoleActor.prototype =
return actor;
},
/**
* Get the NetworkEventActor for a given URL that may have been noticed by the network
* listener. Requests are added when they start, so the actor might not yet have all
* data for the request until it has completed.
*
* @param string url
* The URL of the request to search for.
*/
getNetworkEventActorForURL(url) {
return this._networkEventActorsByURL.get(url);
},
/**
* Send a new HTTP request from the target's window.
*
@ -1992,6 +2017,9 @@ NetworkEventActor.prototype =
}
this._longStringActors = new Set();
if (this._request.url) {
this.parent._networkEventActorsByURL.delete(this._request.url);
}
if (this.channel) {
this.parent._netEvents.delete(this.channel);
}

View File

@ -2685,7 +2685,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
// Copy aParsedValue for later use since it will be lost when we call
// SetAndSwapMappedAttr below
nsAttrValue valueForAfterSetAttr;
if (aCallAfterSetAttr) {
if (aCallAfterSetAttr || GetCustomElementData()) {
valueForAfterSetAttr.SetTo(aParsedValue);
}

View File

@ -86,7 +86,7 @@ nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
// getting a new stylerule here. And we can't compare the stringvalues of
// the old and the new rules since both will point to the same declaration
// and thus will be the same.
if (hasListeners) {
if (hasListeners || GetCustomElementData()) {
// save the old attribute so we can set up the mutation event properly
nsAutoString oldValueStr;
modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style,

View File

@ -28,7 +28,7 @@ def generate(output, idlFilename, preprocessorHeader):
continue
# Unfortunately, even some of the getters here are fallible
# (e.g. on nsComputedDOMStyle).
extendedAttrs = ["Throws", "TreatNullAs=EmptyString",
extendedAttrs = ["CEReactions", "Throws", "TreatNullAs=EmptyString",
"SetterNeedsSubjectPrincipal=NonSystem"]
if pref is not "":
extendedAttrs.append('Pref="%s"' % pref)

View File

@ -1364,6 +1364,7 @@ OggTrackDemuxer::NextSample()
// This will also update mSharedAudioTrackInfo.
mParent->ReadOggChain(data->GetEndTime());
}
data->mOffset = mParent->Resource(mType)->Tell();
// We adjust the start time of the sample to account for the potential ogg chaining.
data->mTime += totalDuration;
return data;

View File

@ -292,6 +292,48 @@ function testAttributeChangedExtended() {
elem.setAttribute("foo", "bar");
}
function testStyleAttributeChange() {
var expectedCallbackArguments = [
// [name, oldValue, newValue]
// This is an additional attributeChangedCallback from *first* style
// attribute change, see https://bugzilla.mozilla.org/show_bug.cgi?id=1428246.
["style", null, ""],
["style", "", "font-size: 10px;"],
["style", "font-size: 10px;", "font-size: 20px;"],
["style", "font-size: 20px;", "font-size: 30px;"],
];
customElements.define("x-style-attribute-change", class extends HTMLElement {
attributeChangedCallback(name, oldValue, newValue) {
if (expectedCallbackArguments.length === 0) {
ok(false, "Got unexpected attributeChangedCallback?");
return;
}
let expectedArgument = expectedCallbackArguments.shift();
is(name, expectedArgument[0],
"The name argument should match the expected value.");
is(oldValue, expectedArgument[1],
"The old value argument should match the expected value.");
is(newValue, expectedArgument[2],
"The new value argument should match the expected value.");
}
static get observedAttributes() {
return ["style"];
}
});
var elem = document.createElement("x-style-attribute-change");
elem.style.fontSize = "10px";
elem.style.fontSize = "20px";
elem.style.fontSize = "30px";
ok(expectedCallbackArguments.length === 0,
"The attributeChangedCallback should be fired synchronously.");
runNextTest();
}
// Creates a custom element that is an upgrade candidate (no registration)
// and mutate the element in ways that would call callbacks for registered
// elements.
@ -368,6 +410,7 @@ var testFunctions = [
testRegisterResolved,
testAttributeChanged,
testAttributeChangedExtended,
testStyleAttributeChange,
testUpgradeCandidate,
testChangingCallback,
testNotInDocEnterLeave,

View File

@ -107,7 +107,9 @@ ExpectedOwnerForChild(const nsIFrame& aFrame)
// Handle :-moz-table and :-moz-inline-table.
parent = IsAnonBox(*tableFrame) ? parent->GetParent() : tableFrame;
} else {
parent = parent->GetParent();
// We get the in-flow parent here so that we can handle the OOF anonymous
// boxed to get the correct parent.
parent = parent->GetInFlowParent();
}
parent = FirstContinuationOrPartOfIBSplit(parent);
}

View File

@ -1099,7 +1099,10 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
int32_t budget = maxHeightScreenPx - screenRect.height;
// Scale the margins down to fit into the budget if necessary, maintaining
// their relative ratio.
float scale = std::min(1.0f, float(budget) / margins.TopBottom());
float scale = 1.0f;
if (float(budget) < margins.TopBottom()) {
scale = float(budget) / margins.TopBottom();
}
float top = margins.top * scale;
float bottom = margins.bottom * scale;
screenRect.y -= top;
@ -1107,7 +1110,10 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
}
if (screenRect.width < maxWidthScreenPx) {
int32_t budget = maxWidthScreenPx - screenRect.width;
float scale = std::min(1.0f, float(budget) / margins.LeftRight());
float scale = 1.0f;
if (float(budget) < margins.LeftRight()) {
scale = float(budget) / margins.LeftRight();
}
float left = margins.left * scale;
float right = margins.right * scale;
screenRect.x -= left;

View File

@ -888,7 +888,7 @@ public:
* Gets the parent of a frame, using the parent of the placeholder for
* out-of-flow frames.
*/
inline nsContainerFrame* GetInFlowParent();
inline nsContainerFrame* GetInFlowParent() const;
/**
* Gets the primary frame of the Content's flattened tree

View File

@ -170,7 +170,7 @@ nsIFrame::PropagateRootElementWritingMode(mozilla::WritingMode aRootElemWM)
}
nsContainerFrame*
nsIFrame::GetInFlowParent()
nsIFrame::GetInFlowParent() const
{
if (GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
nsIFrame* ph = FirstContinuation()->GetProperty(nsIFrame::PlaceholderFrameProperty());

View File

@ -1786,6 +1786,7 @@ public abstract class GeckoApp extends GeckoActivity
});
} else if (Intent.ACTION_ASSIST.equals(action)) {
Tabs.getInstance().addTab(Tabs.LOADURL_START_EDITING | Tabs.LOADURL_EXTERNAL);
autoHideTabs();
} else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
final GeckoBundle data = new GeckoBundle(2);
data.putString("uri", uri);

View File

@ -260,7 +260,6 @@ pub unsafe fn new_rt_and_cx() -> Runtime {
if let Some(val) = PREFS.get("js.mem.gc.dynamic_mark_slice.enabled").as_boolean() {
JS_SetGCParameter(runtime.rt(), JSGCParamKey::JSGC_DYNAMIC_MARK_SLICE, val as u32);
}
// TODO: handle js.mem.gc.refresh_frame_slices.enabled
if let Some(val) = PREFS.get("js.mem.gc.dynamic_heap_growth.enabled").as_boolean() {
JS_SetGCParameter(runtime.rt(), JSGCParamKey::JSGC_DYNAMIC_HEAP_GROWTH, val as u32);
}

View File

@ -45,7 +45,6 @@
"js.mem.gc.incremental.slice_ms": 10,
"js.mem.gc.low_frequency_heap_growth": 150,
"js.mem.gc.per_compartment.enabled": true,
"js.mem.gc.refresh_frame_slices.enabled": true,
"js.mem.gc.zeal.frequency": 100,
"js.mem.gc.zeal.level": 0,
"js.mem.high_water_mark": 128,

View File

@ -35,6 +35,8 @@ jobs:
symbol: I(idx)
funsize-update-generator:
symbol: I(pg)
google-play-strings:
symbol: I(gps)
funsize-balrog-submitter:
symbol: I(fbs)
beet-mover:

View File

@ -0,0 +1,54 @@
# 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/.
loader: taskgraph.loader.transform:loader
transforms:
- taskgraph.transforms.google_play_strings:transforms
- taskgraph.transforms.task:transforms
jobs:
google-play-strings:
description: Download strings to display on Google Play from https://l10n.mozilla-community.org/stores_l10n/
attributes:
build_type: google_play_strings
build_platform: android-nightly
nightly: true
shipping-phase: promote
shipping-product: fennec
worker-type: aws-provisioner-v1/gecko-{level}-b-android
worker:
implementation: docker-worker
os: linux
docker-image: {in-tree: google-play-strings}
chain-of-trust: true
max-run-time: 600
artifacts:
- name: 'public/google_play_strings.json'
# XXX The folder depends on the one defined in the Dockerfile
path: /builds/worker/google_play_strings.json
type: 'file'
env:
# TODO Use the branch name instead of the android package name
PACKAGE_NAME:
by-project:
mozilla-central: org.mozilla.fennec_aurora
mozilla-beta: org.mozilla.firefox_beta
mozilla-release: org.mozilla.firefox_beta
default: org.mozilla.fennec_aurora # Fetches strings for mozilla-central
# XXX The folder depends on the one defined in the Dockerfile
GOOGLE_PLAY_STRING_FILE: /builds/worker/google_play_strings.json
command:
- bash
- -cx
- >
python3 ./mozapkpublisher/get_l10n_strings.py
--package-name "${PACKAGE_NAME}"
--output-file "${GOOGLE_PLAY_STRING_FILE}"
treeherder:
symbol: pub(gps)
platform: Android/opt
tier: 2
kind: other
run-on-projects: ['maple', 'mozilla-central', 'mozilla-beta', 'mozilla-release']

View File

@ -5,39 +5,39 @@
loader: taskgraph.loader.push_apk:loader
transforms:
- taskgraph.transforms.push_apk:transforms
- taskgraph.transforms.task:transforms
- taskgraph.transforms.push_apk:transforms
- taskgraph.transforms.task:transforms
kind-dependencies:
- build-signing
- push-apk-breakpoint
- build-signing
- google-play-strings
- push-apk-breakpoint
jobs:
push-apk/opt:
description: Publishes APK onto Google Play Store
attributes:
build_platform: android-nightly
nightly: true
shipping-phase: ship
shipping-product: fennec
worker-type:
by-project:
mozilla-central: scriptworker-prov-v1/pushapk-v1
mozilla-beta: scriptworker-prov-v1/pushapk-v1
mozilla-release: scriptworker-prov-v1/pushapk-v1
default: scriptworker-prov-v1/dep-pushapk
worker:
upstream-artifacts: # see transforms
google-play-track: # see transforms
implementation: push-apk
commit: # see transforms
scopes: # see transforms
treeherder:
symbol: pub(gp)
platform: Android/opt
tier: 2
kind: other
run-on-projects: ['mozilla-central', 'mozilla-beta', 'mozilla-release']
deadline-after: 5 days
extra:
product: fennec
push-apk/opt:
description: Publishes APK onto Google Play Store
attributes:
build_platform: android-nightly
nightly: true
shipping-phase: ship
shipping-product: fennec
worker-type:
by-project:
mozilla-central: scriptworker-prov-v1/pushapk-v1
mozilla-beta: scriptworker-prov-v1/pushapk-v1
mozilla-release: scriptworker-prov-v1/pushapk-v1
default: scriptworker-prov-v1/dep-pushapk
worker:
upstream-artifacts: # see transforms
google-play-track: # see transforms
implementation: push-apk
commit: # see transforms
requires: all-resolved
scopes: # see transforms
treeherder:
symbol: pub(gp)
platform: Android/opt
tier: 2
kind: other
run-on-projects: ['mozilla-central', 'mozilla-beta', 'mozilla-release', 'maple']
deadline-after: 5 days

View File

@ -3,6 +3,7 @@ version: @VERSION@-@BUILD_NUMBER@
summary: Mozilla Firefox web browser
description: Firefox is a powerful, extensible web browser with support for modern web application technologies.
confinement: strict
grade: stable
apps:
firefox:
@ -11,8 +12,10 @@ apps:
environment:
DISABLE_WAYLAND: 1
plugs:
- avahi-observe
- browser-sandbox
- camera
- cups-control
- desktop
- desktop-legacy
- gsettings
@ -41,4 +44,7 @@ parts:
- libgl1-mesa-dri
- libgl1-mesa-glx
- libmirclient9
- desktop-file-utils
- xdg-utils
- ffmpeg
after: [desktop-gtk3]

View File

@ -2,4 +2,6 @@ mar==2.1.2
backports.lzma==0.0.8
datadog==0.17.0
redo==1.6
aiohttp==2.3.6
awscli==1.14.10
scriptworker==6.0.0

View File

@ -18,7 +18,8 @@ curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json \
S3_BUCKET_AND_PATH=$(jq -r '.scopes[] | select(contains ("auth:aws-s3"))' /home/worker/task.json | awk -F: '{print $4}')
# Will be empty if there's no scope for AWS S3.
if [ -n "${S3_BUCKET_AND_PATH}" ]; then
if [ -n "${S3_BUCKET_AND_PATH}" ] && getent hosts taskcluster
then
# Does this parse as we expect?
S3_PATH=${S3_BUCKET_AND_PATH#*/}
AWS_BUCKET_NAME=${S3_BUCKET_AND_PATH%/${S3_PATH}*}

View File

@ -5,6 +5,8 @@
from __future__ import absolute_import, division, print_function
import asyncio
import aiohttp
import configparser
import argparse
import hashlib
@ -18,6 +20,7 @@ import requests
import sh
import redo
from scriptworker.utils import retry_async
from mardor.reader import MarReader
from mardor.signing import get_keysize
@ -25,6 +28,12 @@ from datadog import initialize, ThreadStats
log = logging.getLogger(__name__)
# Create this even when not sending metrics, so the context manager
# statements work.
ddstats = ThreadStats(namespace='releng.releases.partials')
ALLOWED_URL_PREFIXES = [
"http://download.cdn.mozilla.net/pub/mozilla.org/firefox/nightly/",
"http://download.cdn.mozilla.net/pub/firefox/nightly/",
@ -71,48 +80,87 @@ def get_secret(secret_name):
return r.json().get('secret', {})
@redo.retriable()
def download(url, dest, mode=None):
log.debug("Downloading %s to %s", url, dest)
r = requests.get(url)
r.raise_for_status()
async def retry_download(*args, **kwargs): # noqa: E999
"""Retry download() calls."""
await retry_async(
download,
retry_exceptions=(
aiohttp.ClientError
),
args=args,
kwargs=kwargs
)
async def download(url, dest, mode=None): # noqa: E999
log.info("Downloading %s to %s", url, dest)
bytes_downloaded = 0
with open(dest, 'wb') as fd:
for chunk in r.iter_content(4096):
fd.write(chunk)
bytes_downloaded += len(chunk)
log.debug('Downloaded %s bytes', bytes_downloaded)
if 'content-length' in r.headers:
log.debug('Content-Length: %s bytes', r.headers['content-length'])
if bytes_downloaded != int(r.headers['content-length']):
raise IOError('Unexpected number of bytes downloaded')
async with aiohttp.ClientSession(raise_for_status=True) as session:
async with session.get(url) as resp:
with open(dest, 'wb') as fd:
while True:
chunk = await resp.content.read(4096)
if not chunk:
break
fd.write(chunk)
bytes_downloaded += len(chunk)
if mode:
log.debug("chmod %o %s", mode, dest)
os.chmod(dest, mode)
log.debug('Downloaded %s bytes', bytes_downloaded)
if 'content-length' in resp.headers:
log.debug('Content-Length: %s bytes', resp.headers['content-length'])
if bytes_downloaded != int(resp.headers['content-length']):
raise IOError('Unexpected number of bytes downloaded')
if mode:
log.debug("chmod %o %s", mode, dest)
os.chmod(dest, mode)
def unpack(work_env, mar, dest_dir):
async def run_command(cmd, cwd='/', env=None, label=None, silent=False):
if not env:
env = dict()
process = await asyncio.create_subprocess_shell(cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
cwd=cwd, env=env)
stdout, stderr = await process.communicate()
await process.wait()
if silent:
return
if not stderr:
stderr = ""
if not stdout:
stdout = ""
label = "{}: ".format(label)
for line in stdout.splitlines():
log.debug("%s%s", label, line.decode('utf-8'))
for line in stderr.splitlines():
log.warn("%s%s", label, line.decode('utf-8'))
async def unpack(work_env, mar, dest_dir):
os.mkdir(dest_dir)
unwrap_cmd = sh.Command(os.path.join(work_env.workdir,
"unwrap_full_update.pl"))
log.debug("Unwrapping %s", mar)
env = work_env.env
if not is_lzma_compressed_mar(mar):
env['MAR_OLD_FORMAT'] = '1'
elif 'MAR_OLD_FORMAT' in env:
del env['MAR_OLD_FORMAT']
out = unwrap_cmd(mar, _cwd=dest_dir, _env=env, _timeout=240,
_err_to_out=True)
if out:
log.debug(out)
cmd = "{} {}".format(work_env.paths['unwrap_full_update.pl'], mar)
await run_command(cmd, cwd=dest_dir, env=env, label=dest_dir)
def find_file(directory, filename):
log.debug("Searching for %s in %s", filename, directory)
for root, dirs, files in os.walk(directory):
for root, _, files in os.walk(directory):
if filename in files:
f = os.path.join(root, filename)
log.debug("Found %s", f)
@ -120,7 +168,7 @@ def find_file(directory, filename):
def get_option(directory, filename, section, option):
log.debug("Exctracting [%s]: %s from %s/**/%s", section, option, directory,
log.debug("Extracting [%s]: %s from %s/**/%s", section, option, directory,
filename)
f = find_file(directory, filename)
config = configparser.ConfigParser()
@ -130,9 +178,9 @@ def get_option(directory, filename, section, option):
return rv
def generate_partial(work_env, from_dir, to_dir, dest_mar, channel_ids,
version, use_old_format):
log.debug("Generating partial %s", dest_mar)
async def generate_partial(work_env, from_dir, to_dir, dest_mar, channel_ids,
version, use_old_format):
log.info("Generating partial %s", dest_mar)
env = work_env.env
env["MOZ_PRODUCT_VERSION"] = version
env["MOZ_CHANNEL_ID"] = channel_ids
@ -142,11 +190,9 @@ def generate_partial(work_env, from_dir, to_dir, dest_mar, channel_ids,
del env['MAR_OLD_FORMAT']
make_incremental_update = os.path.join(work_env.workdir,
"make_incremental_update.sh")
out = sh.bash(make_incremental_update, dest_mar, from_dir, to_dir,
_cwd=work_env.workdir, _env=env, _timeout=900,
_err_to_out=True)
if out:
log.debug(out)
cmd = " ".join([make_incremental_update, dest_mar, from_dir, to_dir])
await run_command(cmd, cwd=work_env.workdir, env=env, label=dest_mar.split('/')[-1])
def get_hash(path, hash_type="sha512"):
@ -160,32 +206,42 @@ class WorkEnv(object):
def __init__(self):
self.workdir = tempfile.mkdtemp()
self.paths = {
'unwrap_full_update.pl': os.path.join(self.workdir, 'unwrap_full_update.pl'),
'mar': os.path.join(self.workdir, 'mar'),
'mbsdiff': os.path.join(self.workdir, 'mbsdiff')
}
def setup(self):
self.download_unwrap()
self.download_martools()
async def setup(self):
await self.download_unwrap()
await self.download_martools()
def download_unwrap(self):
async def clone(self, workenv):
for path in workenv.paths:
if os.path.exists(self.paths[path]):
os.unlink(self.paths[path])
os.link(workenv.paths[path], self.paths[path])
async def download_unwrap(self):
# unwrap_full_update.pl is not too sensitive to the revision
url = "https://hg.mozilla.org/mozilla-central/raw-file/default/" \
"tools/update-packaging/unwrap_full_update.pl"
download(url, dest=os.path.join(self.workdir, "unwrap_full_update.pl"),
mode=0o755)
await retry_download(url, dest=self.paths['unwrap_full_update.pl'], mode=0o755)
def download_buildsystem_bits(self, repo, revision):
async def download_buildsystem_bits(self, repo, revision):
prefix = "{repo}/raw-file/{revision}/tools/update-packaging"
prefix = prefix.format(repo=repo, revision=revision)
for f in ("make_incremental_update.sh", "common.sh"):
for f in ('make_incremental_update.sh', 'common.sh'):
url = "{prefix}/{f}".format(prefix=prefix, f=f)
download(url, dest=os.path.join(self.workdir, f), mode=0o755)
await retry_download(url, dest=os.path.join(self.workdir, f), mode=0o755)
def download_martools(self):
async def download_martools(self):
# TODO: check if the tools have to be branch specific
prefix = "https://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/" \
"latest-mozilla-central/mar-tools/linux64"
for f in ("mar", "mbsdiff"):
for f in ('mar', 'mbsdiff'):
url = "{prefix}/{f}".format(prefix=prefix, f=f)
download(url, dest=os.path.join(self.workdir, f), mode=0o755)
await retry_download(url, dest=self.paths[f], mode=0o755)
def cleanup(self):
shutil.rmtree(self.workdir)
@ -206,6 +262,151 @@ def verify_allowed_url(mar):
))
async def manage_partial(partial_def, work_env, filename_template, artifacts_dir, signing_certs):
"""Manage the creation of partial mars based on payload."""
for mar in (partial_def["from_mar"], partial_def["to_mar"]):
verify_allowed_url(mar)
complete_mars = {}
use_old_format = False
for mar_type, f in (("from", partial_def["from_mar"]), ("to", partial_def["to_mar"])):
dest = os.path.join(work_env.workdir, "{}.mar".format(mar_type))
unpack_dir = os.path.join(work_env.workdir, mar_type)
with ddstats.timer('mar.download.time'):
await retry_download(f, dest)
if not os.getenv("MOZ_DISABLE_MAR_CERT_VERIFICATION"):
verify_signature(dest, signing_certs)
complete_mars["%s_size" % mar_type] = os.path.getsize(dest)
complete_mars["%s_hash" % mar_type] = get_hash(dest)
with ddstats.timer('mar.unpack.time'):
await unpack(work_env, dest, unpack_dir)
if mar_type == 'from':
version = get_option(unpack_dir, filename="application.ini",
section="App", option="Version")
major = int(version.split(".")[0])
# The updater for versions less than 56.0 requires BZ2
# compressed MAR files
if major < 56:
use_old_format = True
log.info("Forcing BZ2 compression for %s", f)
log.info("AV-scanning %s ...", unpack_dir)
metric_tags = [
"platform:{}".format(partial_def['platform']),
]
with ddstats.timer('mar.clamscan.time', tags=metric_tags):
await run_command("clamscan -r {}".format(unpack_dir), label='clamscan')
log.info("Done.")
to_path = os.path.join(work_env.workdir, "to")
from_path = os.path.join(work_env.workdir, "from")
mar_data = {
"ACCEPTED_MAR_CHANNEL_IDS": get_option(
to_path, filename="update-settings.ini", section="Settings",
option="ACCEPTED_MAR_CHANNEL_IDS"),
"version": get_option(to_path, filename="application.ini",
section="App", option="Version"),
"to_buildid": get_option(to_path, filename="application.ini",
section="App", option="BuildID"),
"from_buildid": get_option(from_path, filename="application.ini",
section="App", option="BuildID"),
"appName": get_option(from_path, filename="application.ini",
section="App", option="Name"),
# Use Gecko repo and rev from platform.ini, not application.ini
"repo": get_option(to_path, filename="platform.ini", section="Build",
option="SourceRepository"),
"revision": get_option(to_path, filename="platform.ini",
section="Build", option="SourceStamp"),
"from_mar": partial_def["from_mar"],
"to_mar": partial_def["to_mar"],
"platform": partial_def["platform"],
"locale": partial_def["locale"],
}
# Override ACCEPTED_MAR_CHANNEL_IDS if needed
if "ACCEPTED_MAR_CHANNEL_IDS" in os.environ:
mar_data["ACCEPTED_MAR_CHANNEL_IDS"] = os.environ["ACCEPTED_MAR_CHANNEL_IDS"]
for field in ("update_number", "previousVersion", "previousBuildNumber",
"toVersion", "toBuildNumber"):
if field in partial_def:
mar_data[field] = partial_def[field]
mar_data.update(complete_mars)
# if branch not set explicitly use repo-name
mar_data['branch'] = partial_def.get('branch', mar_data['repo'].rstrip('/').split('/')[-1])
if 'dest_mar' in partial_def:
mar_name = partial_def['dest_mar']
else:
# default to formatted name if not specified
mar_name = filename_template.format(**mar_data)
mar_data['mar'] = mar_name
dest_mar = os.path.join(work_env.workdir, mar_name)
# TODO: download these once
await work_env.download_buildsystem_bits(repo=mar_data["repo"],
revision=mar_data["revision"])
metric_tags = [
"branch:{}".format(mar_data['branch']),
"platform:{}".format(mar_data['platform']),
# If required. Shouldn't add much useful info, but increases
# cardinality of metrics substantially, so avoided.
# "locale:{}".format(mar_data['locale']),
]
with ddstats.timer('generate_partial.time', tags=metric_tags):
await generate_partial(work_env, from_path, to_path, dest_mar,
mar_data["ACCEPTED_MAR_CHANNEL_IDS"],
mar_data["version"],
use_old_format)
mar_data["size"] = os.path.getsize(dest_mar)
metric_tags.append("unit:bytes")
# Allows us to find out how many releases there were between the two,
# making buckets of the file sizes easier.
metric_tags.append("update_number:{}".format(mar_data.get('update_number', 0)))
ddstats.gauge('partial_mar_size', mar_data['size'], tags=metric_tags)
mar_data["hash"] = get_hash(dest_mar)
shutil.copy(dest_mar, artifacts_dir)
work_env.cleanup()
return mar_data
async def async_main(args, signing_certs):
tasks = []
master_env = WorkEnv()
await master_env.setup()
task = json.load(args.task_definition)
# TODO: verify task["extra"]["funsize"]["partials"] with jsonschema
for definition in task["extra"]["funsize"]["partials"]:
workenv = WorkEnv()
await workenv.clone(master_env)
tasks.append(asyncio.ensure_future(manage_partial(
partial_def=definition,
filename_template=args.filename_template,
artifacts_dir=args.artifacts_dir,
work_env=workenv,
signing_certs=signing_certs)
))
manifest = await asyncio.gather(*tasks)
master_env.cleanup()
return manifest
def main():
start = time.time()
@ -227,8 +428,6 @@ def main():
logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s")
log.setLevel(args.log_level)
task = json.load(args.task_definition)
# TODO: verify task["extra"]["funsize"]["partials"] with jsonschema
signing_certs = {
'sha1': open(args.sha1_signing_cert, 'rb').read(),
@ -244,10 +443,6 @@ def main():
if not dd_api_key and os.environ.get('DATADOG_API_SECRET'):
dd_api_key = get_secret(os.environ.get('DATADOG_API_SECRET')).get('key')
# Create this even when not sending metrics, so the context manager
# statements work.
ddstats = ThreadStats(namespace='releng.releases.partials')
if dd_api_key:
dd_options = {
'api_key': dd_api_key,
@ -269,133 +464,29 @@ def main():
except sh.ErrorReturnCode:
log.warning("Freshclam failed, skipping DB update")
manifest = []
for e in task["extra"]["funsize"]["partials"]:
for mar in (e["from_mar"], e["to_mar"]):
verify_allowed_url(mar)
work_env = WorkEnv()
# TODO: run setup once
work_env.setup()
complete_mars = {}
use_old_format = False
for mar_type, f in (("from", e["from_mar"]), ("to", e["to_mar"])):
dest = os.path.join(work_env.workdir, "{}.mar".format(mar_type))
unpack_dir = os.path.join(work_env.workdir, mar_type)
with ddstats.timer('mar.download.time'):
download(f, dest)
if not os.getenv("MOZ_DISABLE_MAR_CERT_VERIFICATION"):
verify_signature(dest, signing_certs)
complete_mars["%s_size" % mar_type] = os.path.getsize(dest)
complete_mars["%s_hash" % mar_type] = get_hash(dest)
with ddstats.timer('mar.unpack.time'):
unpack(work_env, dest, unpack_dir)
if mar_type == 'from':
version = get_option(unpack_dir, filename="application.ini",
section="App", option="Version")
major = int(version.split(".")[0])
# The updater for versions less than 56.0 requires BZ2
# compressed MAR files
if major < 56:
use_old_format = True
log.info("Forcing BZ2 compression for %s", f)
log.info("AV-scanning %s ...", unpack_dir)
metric_tags = [
"platform:{}".format(e['platform']),
]
with ddstats.timer('mar.clamscan.time', tags=metric_tags):
sh.clamscan("-r", unpack_dir, _timeout=600, _err_to_out=True)
log.info("Done.")
path = os.path.join(work_env.workdir, "to")
from_path = os.path.join(work_env.workdir, "from")
mar_data = {
"ACCEPTED_MAR_CHANNEL_IDS": get_option(
path, filename="update-settings.ini", section="Settings",
option="ACCEPTED_MAR_CHANNEL_IDS"),
"version": get_option(path, filename="application.ini",
section="App", option="Version"),
"to_buildid": get_option(path, filename="application.ini",
section="App", option="BuildID"),
"from_buildid": get_option(from_path, filename="application.ini",
section="App", option="BuildID"),
"appName": get_option(from_path, filename="application.ini",
section="App", option="Name"),
# Use Gecko repo and rev from platform.ini, not application.ini
"repo": get_option(path, filename="platform.ini", section="Build",
option="SourceRepository"),
"revision": get_option(path, filename="platform.ini",
section="Build", option="SourceStamp"),
"from_mar": e["from_mar"],
"to_mar": e["to_mar"],
"platform": e["platform"],
"locale": e["locale"],
}
# Override ACCEPTED_MAR_CHANNEL_IDS if needed
if "ACCEPTED_MAR_CHANNEL_IDS" in os.environ:
mar_data["ACCEPTED_MAR_CHANNEL_IDS"] = os.environ["ACCEPTED_MAR_CHANNEL_IDS"]
for field in ("update_number", "previousVersion",
"previousBuildNumber", "toVersion",
"toBuildNumber"):
if field in e:
mar_data[field] = e[field]
mar_data.update(complete_mars)
# if branch not set explicitly use repo-name
mar_data["branch"] = e.get("branch",
mar_data["repo"].rstrip("/").split("/")[-1])
if 'dest_mar' in e:
mar_name = e['dest_mar']
else:
# default to formatted name if not specified
mar_name = args.filename_template.format(**mar_data)
mar_data["mar"] = mar_name
dest_mar = os.path.join(work_env.workdir, mar_name)
# TODO: download these once
work_env.download_buildsystem_bits(repo=mar_data["repo"],
revision=mar_data["revision"])
metric_tags = [
"branch:{}".format(mar_data['branch']),
"platform:{}".format(mar_data['platform']),
# If required. Shouldn't add much useful info, but increases
# cardinality of metrics substantially, so avoided.
# "locale:{}".format(mar_data['locale']),
]
with ddstats.timer('generate_partial.time', tags=metric_tags):
generate_partial(work_env, from_path, path, dest_mar,
mar_data["ACCEPTED_MAR_CHANNEL_IDS"],
mar_data["version"],
use_old_format)
mar_data["size"] = os.path.getsize(dest_mar)
metric_tags.append("unit:bytes")
# Allows us to find out how many releases there were between the two,
# making buckets of the file sizes easier.
metric_tags.append("update_number:{}".format(mar_data.get('update_number', 0)))
ddstats.gauge('partial_mar_size', mar_data['size'], tags=metric_tags)
mar_data["hash"] = get_hash(dest_mar)
shutil.copy(dest_mar, args.artifacts_dir)
work_env.cleanup()
manifest.append(mar_data)
loop = asyncio.get_event_loop()
manifest = loop.run_until_complete(async_main(args, signing_certs))
loop.close()
manifest_file = os.path.join(args.artifacts_dir, "manifest.json")
with open(manifest_file, "w") as fp:
json.dump(manifest, fp, indent=2, sort_keys=True)
log.debug("{}".format(json.dumps(manifest, indent=2, sort_keys=True)))
# Warning: Assumption that one partials task will always be for one branch.
metric_tags = [
"branch:{}".format(mar_data['branch']),
"branch:{}".format(manifest[0]['branch']),
]
ddstats.timing('task_duration', time.time() - start,
start, tags=metric_tags)
# Wait for all the metrics to flush. If the program ends before
# they've been sent, they'll be dropped.
# Should be more than the flush_interval for the ThreadStats object
time.sleep(10)
if dd_api_key:
time.sleep(10)
if __name__ == '__main__':

View File

@ -0,0 +1,19 @@
FROM ubuntu:16.04
MAINTAINER Johan Lorenzo <jlorenzo+tc@mozilla.com>
RUN mkdir /builds
RUN groupadd -g 500 worker
RUN useradd -u 500 -g 500 -d /builds/worker -s /bin/bash -m worker
RUN apt-get update
RUN apt-get install --yes git python3-setuptools build-essential libssl-dev libffi-dev python3-dev
WORKDIR /builds/worker/
RUN git clone https://github.com/mozilla-releng/mozapkpublisher
WORKDIR /builds/worker/mozapkpublisher
RUN python3 setup.py develop
RUN chown -R worker:worker /builds/worker
# Set a default command useful for debugging
CMD ["/bin/bash", "--login"]

View File

@ -217,6 +217,11 @@ Beetmover, takes specific artifact checksums and pushes it to a location outside
of Taskcluster's task artifacts (archive.mozilla.org as one place) and in the
process determines the final location and "pretty" names it (version product name)
google-play-strings
-------------------
Download strings to display on Google Play from https://l10n.mozilla-community.org/stores_l10n/.
Artifact is then used by push-apk.
push-apk-breakpoint
-------------------
Decides whether or not APKs should be published onto Google Play Store. Jobs of this

View File

@ -0,0 +1,64 @@
# 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/.
"""
Transform the push-apk kind into an actual task description.
"""
from __future__ import absolute_import, print_function, unicode_literals
import functools
from taskgraph.transforms.base import TransformSequence
from taskgraph.transforms.task import task_description_schema
from taskgraph.util.schema import resolve_keyed_by, Schema
from taskgraph.util.push_apk import fill_labels_tranform, validate_jobs_schema_transform_partial
from voluptuous import Required
transforms = TransformSequence()
# Voluptuous uses marker objects as dictionary *keys*, but they are not
# comparable, so we cast all of the keys back to regular strings
task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
google_play_description_schema = Schema({
Required('name'): basestring,
Required('label'): task_description_schema['label'],
Required('description'): task_description_schema['description'],
Required('job-from'): task_description_schema['job-from'],
Required('attributes'): task_description_schema['attributes'],
Required('treeherder'): task_description_schema['treeherder'],
Required('run-on-projects'): task_description_schema['run-on-projects'],
Required('shipping-phase'): task_description_schema['shipping-phase'],
Required('shipping-product'): task_description_schema['shipping-product'],
Required('worker-type'): task_description_schema['worker-type'],
Required('worker'): object,
})
validate_jobs_schema_transform = functools.partial(
validate_jobs_schema_transform_partial,
google_play_description_schema,
'GooglePlayStrings'
)
transforms.add(fill_labels_tranform)
transforms.add(validate_jobs_schema_transform)
@transforms.add
def set_worker_data(config, jobs):
for job in jobs:
worker = job['worker']
env = worker.setdefault('env', {})
resolve_keyed_by(
env, 'PACKAGE_NAME', item_name=job['name'],
project=config.params['project']
)
cot = job.setdefault('extra', {}).setdefault('chainOfTrust', {})
cot.setdefault('inputs', {})['docker-image'] = {'task-reference': '<docker-image>'}
yield job

View File

@ -28,22 +28,22 @@ task_description_schema = {str(k): v for k, v in task_description_schema.schema.
push_apk_description_schema = Schema({
# the dependent task (object) for this beetmover job, used to inform beetmover.
Required('dependent-tasks'): object,
Required('name'): basestring,
Required('label'): basestring,
Required('description'): basestring,
Required('job-from'): basestring,
Required('attributes'): object,
Required('treeherder'): object,
Required('run-on-projects'): list,
Required('label'): task_description_schema['label'],
Required('description'): task_description_schema['description'],
Required('job-from'): task_description_schema['job-from'],
Required('attributes'): task_description_schema['attributes'],
Required('treeherder'): task_description_schema['treeherder'],
Required('run-on-projects'): task_description_schema['run-on-projects'],
Required('worker-type'): optionally_keyed_by('project', basestring),
Required('worker'): object,
Required('scopes'): None,
Required('requires'): task_description_schema['requires'],
Required('deadline-after'): basestring,
Required('shipping-phase'): task_description_schema['shipping-phase'],
Required('shipping-product'): task_description_schema['shipping-product'],
Optional('extra'): object,
Optional('extra'): task_description_schema['extra'],
})
validate_jobs_schema_transform = functools.partial(
@ -83,8 +83,21 @@ transforms.add(delete_non_required_fields_transform)
def generate_upstream_artifacts(dependencies):
return [{
apks = [{
'taskId': {'task-reference': '<{}>'.format(task_kind)},
'taskType': 'signing',
'paths': ['public/build/target.apk'],
} for task_kind in dependencies.keys() if 'breakpoint' not in task_kind]
} for task_kind in dependencies.keys()
if task_kind not in ('push-apk-breakpoint', 'google-play-strings')
]
google_play_strings = [{
'taskId': {'task-reference': '<{}>'.format(task_kind)},
'taskType': 'build',
'paths': ['public/google_play_strings.json'],
'optional': True,
} for task_kind in dependencies.keys()
if 'google-play-strings' in task_kind
]
return apks + google_play_strings

View File

@ -93,6 +93,8 @@ task_description_schema = Schema({
# method.
Optional('dependencies'): {basestring: object},
Optional('requires'): Any('all-completed', 'all-resolved'),
# expiration and deadline times, relative to task creation, with units
# (e.g., "14 days"). Defaults are set based on the project.
Optional('expires-after'): basestring,
@ -571,6 +573,9 @@ task_description_schema = Schema({
# Paths to the artifacts to sign
Required('paths'): [basestring],
# Artifact is optional to run the task
Optional('optional', default=False): bool,
}],
# "Invalid" is a noop for try and other non-supported branches
@ -1471,6 +1476,9 @@ def build_task(config, tasks):
'priority': task['priority'],
}
if task.get('requires', None):
task_def['requires'] = task['requires']
if task_th:
# link back to treeherder in description
th_push_link = 'https://treeherder.mozilla.org/#/jobs?repo={}&revision={}'.format(

View File

@ -278,18 +278,20 @@ GeckoDriver.prototype.QueryInterface = XPCOMUtils.generateQI([
]);
GeckoDriver.prototype.init = function() {
this.mm.addMessageListener("Marionette:WebDriver:GetCapabilities", this);
this.mm.addMessageListener("Marionette:GetLogLevel", this);
this.mm.addMessageListener("Marionette:getVisibleCookies", this);
this.mm.addMessageListener("Marionette:listenersAttached", this);
this.mm.addMessageListener("Marionette:register", this);
this.mm.addMessageListener("Marionette:ListenersAttached", this);
this.mm.addMessageListener("Marionette:Register", this);
this.mm.addMessageListener("Marionette:switchedToFrame", this);
};
GeckoDriver.prototype.uninit = function() {
this.mm.removeMessageListener("Marionette:WebDriver:GetCapabilities", this);
this.mm.removeMessageListener("Marionette:GetLogLevel", this);
this.mm.removeMessageListener("Marionette:getVisibleCookies", this);
this.mm.removeMessageListener("Marionette:listenersAttached", this);
this.mm.removeMessageListener("Marionette:register", this);
this.mm.removeMessageListener("Marionette:ListenersAttached", this);
this.mm.removeMessageListener("Marionette:Register", this);
this.mm.removeMessageListener("Marionette:switchedToFrame", this);
};
@ -443,7 +445,6 @@ GeckoDriver.prototype.startBrowser = function(window, isNewSession = false) {
this.mainFrame = window;
this.curFrame = null;
this.addBrowser(window);
this.curBrowser.isNewSession = isNewSession;
this.whenBrowserStarted(window, isNewSession);
};
@ -536,26 +537,20 @@ GeckoDriver.prototype.registerBrowser = function(id, be) {
this.wins.set(id, listenerWindow);
if (nullPrevious && (this.curBrowser.curFrameId !== null)) {
this.sendAsync(
"newSession",
this.capabilities,
this.newSessionCommandId);
if (this.curBrowser.isNewSession) {
this.newSessionCommandId = null;
}
this.sendAsync("newSession");
}
return [id, this.capabilities.toJSON()];
return id;
};
GeckoDriver.prototype.registerPromise = function() {
const li = "Marionette:register";
const li = "Marionette:Register";
return new Promise(resolve => {
let cb = msg => {
let wid = msg.json.value;
let be = msg.target;
let rv = this.registerBrowser(wid, be);
let outerWindowID = this.registerBrowser(wid, be);
if (this.curBrowser.frameRegsPending > 0) {
this.curBrowser.frameRegsPending--;
@ -567,18 +562,18 @@ GeckoDriver.prototype.registerPromise = function() {
}
// this is a sync message and listeners expect the ID back
return rv;
return outerWindowID;
};
this.mm.addMessageListener(li, cb);
});
};
GeckoDriver.prototype.listeningPromise = function() {
const li = "Marionette:listenersAttached";
const li = "Marionette:ListenersAttached";
return new Promise(resolve => {
let cb = msg => {
if (msg.json.listenerId === this.curBrowser.curFrameId) {
if (msg.json.outerWindowID === this.curBrowser.curFrameId) {
this.mm.removeMessageListener(li, cb);
resolve();
}
@ -699,7 +694,6 @@ GeckoDriver.prototype.newSession = async function(cmd) {
throw new SessionNotCreatedError("Maximum number of active sessions");
}
this.sessionID = WebElement.generateUUID();
this.newSessionCommandId = cmd.id;
try {
this.capabilities = session.Capabilities.fromJSON(cmd.parameters);
@ -3343,22 +3337,25 @@ GeckoDriver.prototype.receiveMessage = function(message) {
}
break;
case "Marionette:register":
case "Marionette:Register":
let wid = message.json.value;
let be = message.target;
let rv = this.registerBrowser(wid, be);
return rv;
let outerWindowID = this.registerBrowser(wid, be);
return {outerWindowID};
case "Marionette:listenersAttached":
if (message.json.listenerId === this.curBrowser.curFrameId) {
case "Marionette:ListenersAttached":
if (message.json.outerWindowID === this.curBrowser.curFrameId) {
// If the frame script gets reloaded we need to call newSession.
// In the case of desktop this just sets up a small amount of state
// that doesn't change over the course of a session.
this.sendAsync("newSession", this.capabilities);
this.sendAsync("newSession");
this.curBrowser.flushPendingCommands();
}
break;
case "Marionette:WebDriver:GetCapabilities":
return this.capabilities.toJSON();
case "Marionette:GetLogLevel":
return logger.level;
}

View File

@ -47,7 +47,7 @@ Cu.import("chrome://marionette/content/session.js");
Cu.importGlobalProperties(["URL"]);
let listenerId = null; // unique ID of this listener
let outerWindowID = null;
let curContainer = {frame: content, shadowRoot: null};
// Listen for click event to indicate one click has happened, so actions
@ -69,7 +69,13 @@ const SUPPORTED_STRATEGIES = new Set([
element.Strategy.XPath,
]);
let capabilities;
Object.defineProperty(this, "capabilities", {
get() {
let payload = sendSyncMessage("Marionette:WebDriver:GetCapabilities");
return session.Capabilities.fromJSON(payload[0]);
},
configurable: true,
});
let legacyactions = new legacyaction.Chain();
@ -447,15 +453,11 @@ function registerSelf() {
// register will have the ID and a boolean describing if this is the
// main process or not
let register = sendSyncMessage("Marionette:register", msg);
let register = sendSyncMessage("Marionette:Register", msg);
if (register[0]) {
listenerId = register[0][0];
capabilities = session.Capabilities.fromJSON(register[0][1]);
if (typeof listenerId != "undefined") {
startListeners();
sendAsyncMessage("Marionette:listenersAttached",
{"listenerId": listenerId});
}
outerWindowID = register[0].outerWindowID;
startListeners();
sendAsyncMessage("Marionette:ListenersAttached", {outerWindowID});
}
}
@ -490,18 +492,12 @@ function dispatch(fn) {
};
}
/**
* Add a message listener that's tied to our listenerId.
*/
function addMessageListenerId(messageName, handler) {
addMessageListener(messageName + listenerId, handler);
addMessageListener(messageName + outerWindowID, handler);
}
/**
* Remove a message listener that's tied to our listenerId.
*/
function removeMessageListenerId(messageName, handler) {
removeMessageListener(messageName + listenerId, handler);
removeMessageListener(messageName + outerWindowID, handler);
}
let getPageSourceFn = dispatch(getPageSource);
@ -582,9 +578,12 @@ function startListeners() {
* Called when we start a new session. It registers the
* current environment, and resets all values
*/
function newSession(msg) {
capabilities = session.Capabilities.fromJSON(msg.json);
resetValues();
function newSession() {
sandboxes.clear();
curContainer = {frame: content, shadowRoot: null};
legacyactions.mouseEventsOnly = false;
action.inputStateMap = new Map();
action.inputsToCancel = [];
}
/**
@ -706,17 +705,6 @@ function sendError(err, uuid) {
sendToServer(uuid, err);
}
/**
* Clear test values after completion of test
*/
function resetValues() {
sandboxes.clear();
curContainer = {frame: content, shadowRoot: null};
legacyactions.mouseEventsOnly = false;
action.inputStateMap = new Map();
action.inputsToCancel = [];
}
async function execute(script, args, timeout, opts) {
opts.timeout = timeout;
let sb = sandbox.createMutable(curContainer.frame);

View File

@ -12,9 +12,6 @@
[setProperty on CSSStyleDeclaration must enqueue an attributeChanged reaction when it mutates the observed style attribute]
expected: FAIL
[setProperty on CSSStyleDeclaration must enqueue an attributeChanged reaction when it makes a property important and the style attribute is observed]
expected: FAIL
[setPropertyValue on CSSStyleDeclaration must enqueue an attributeChanged reaction when it adds the observed style attribute]
expected: FAIL
@ -33,9 +30,6 @@
[setPropertyPriority on CSSStyleDeclaration must enqueue an attributeChanged reaction when it makes a property important but the style attribute is not observed]
expected: FAIL
[removeProperty on CSSStyleDeclaration must enqueue an attributeChanged reaction when it removes a property from the observed style attribute]
expected: FAIL
[cssFloat on CSSStyleDeclaration must enqueue an attributeChanged reaction when it adds the observed style attribute]
expected: FAIL

View File

@ -5244,6 +5244,8 @@
"expires_in_version" : "never",
"kind": "enumerated",
"n_values": 4,
"alert_emails": ["fx-search@mozilla.com"],
"bug_numbers": [1053467],
"description": "The possible types of the 'sizes' attribute for <link rel=icon>. 0: Attribute not specified, 1: 'any', 2: Integer dimensions, 3: Invalid value."
},
"LINK_ICON_SIZES_ATTR_DIMENSION": {
@ -5252,6 +5254,8 @@
"kind": "linear",
"high": 513,
"n_buckets" : 64,
"alert_emails": ["fx-search@mozilla.com"],
"bug_numbers": [1053467],
"description": "The width dimension of the 'sizes' attribute for <link rel=icon>."
},
"PAGE_METADATA_SIZE": {
@ -7812,14 +7816,6 @@
"kind": "boolean",
"description": "New tab page is enhanced (showing suggestions)."
},
"NEWTAB_PAGE_LIFE_SPAN": {
"record_in_processes": ["main", "content"],
"expires_in_version": "default",
"kind": "exponential",
"high": 1200,
"n_buckets": 100,
"description": "Life-span of a new tab without suggested tile: time delta between first-visible and unload events (half-seconds)."
},
"NEWTAB_PAGE_PINNED_SITES_COUNT": {
"record_in_processes": ["main", "content"],
"expires_in_version": "default",
@ -7835,19 +7831,6 @@
"n_buckets": 10,
"description": "Number of sites blocked from the new tab page."
},
"NEWTAB_PAGE_SHOWN": {
"record_in_processes": ["main", "content"],
"expires_in_version": "35",
"kind": "boolean",
"description": "Number of times about:newtab was shown from opening a new tab or window. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
},
"NEWTAB_PAGE_SITE_CLICKED": {
"record_in_processes": ["main", "content"],
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track click count on about:newtab tiles per index (0-8). For non-default row or column configurations all clicks into the '9' bucket. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
},
"BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS": {
"record_in_processes": ["main", "content"],
"expires_in_version": "default",

View File

@ -288,8 +288,6 @@
"IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB",
"IPV4_AND_IPV6_ADDRESS_CONNECTIVITY",
"JS_TELEMETRY_ADDON_EXCEPTIONS",
"LINK_ICON_SIZES_ATTR_DIMENSION",
"LINK_ICON_SIZES_ATTR_USAGE",
"LOCALDOMSTORAGE_CLEAR_BLOCKING_MS",
"LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS",
"LOCALDOMSTORAGE_GETKEY_BLOCKING_MS",
@ -371,10 +369,7 @@
"NEWTAB_PAGE_BLOCKED_SITES_COUNT",
"NEWTAB_PAGE_ENABLED",
"NEWTAB_PAGE_ENHANCED",
"NEWTAB_PAGE_LIFE_SPAN",
"NEWTAB_PAGE_PINNED_SITES_COUNT",
"NEWTAB_PAGE_SHOWN",
"NEWTAB_PAGE_SITE_CLICKED",
"NTLM_MODULE_USED_2",
"ONBEFOREUNLOAD_PROMPT_ACTION",
"ONBEFOREUNLOAD_PROMPT_COUNT",
@ -959,8 +954,6 @@
"IPV4_AND_IPV6_ADDRESS_CONNECTIVITY",
"JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT",
"JS_TELEMETRY_ADDON_EXCEPTIONS",
"LINK_ICON_SIZES_ATTR_DIMENSION",
"LINK_ICON_SIZES_ATTR_USAGE",
"LOCALDOMSTORAGE_CLEAR_BLOCKING_MS",
"LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS",
"LOCALDOMSTORAGE_GETKEY_BLOCKING_MS",
@ -1059,10 +1052,7 @@
"NEWTAB_PAGE_BLOCKED_SITES_COUNT",
"NEWTAB_PAGE_ENABLED",
"NEWTAB_PAGE_ENHANCED",
"NEWTAB_PAGE_LIFE_SPAN",
"NEWTAB_PAGE_PINNED_SITES_COUNT",
"NEWTAB_PAGE_SHOWN",
"NEWTAB_PAGE_SITE_CLICKED",
"NTLM_MODULE_USED_2",
"ONBEFOREUNLOAD_PROMPT_ACTION",
"ONBEFOREUNLOAD_PROMPT_COUNT",
@ -1591,7 +1581,6 @@
"NEWTAB_PAGE_ENABLED",
"MOZ_SQLITE_OPEN_MS",
"SHOULD_TRANSLATION_UI_APPEAR",
"NEWTAB_PAGE_LIFE_SPAN",
"FX_TOTAL_TOP_VISITS",
"FX_SESSION_RESTORE_NUMBER_OF_EAGER_TABS_RESTORED",
"CACHE_DISK_SEARCH_2",

View File

@ -531,7 +531,8 @@ XRE_InitChildProcess(int aArgc,
printf_stderr("Could not allow ptrace from any process.\n");
}
#endif
printf_stderr("\n\nCHILDCHILDCHILDCHILD\n debug me @ %d\n\n",
printf_stderr("\n\nCHILDCHILDCHILDCHILD (process type %s)\n debug me @ %d\n\n",
XRE_ChildProcessTypeToString(XRE_GetProcessType()),
base::GetCurrentProcId());
sleep(GetDebugChildPauseTime());
}
@ -541,7 +542,8 @@ XRE_InitChildProcess(int aArgc,
"Invoking NS_DebugBreak() to debug child process",
nullptr, __FILE__, __LINE__);
} else if (PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
printf_stderr("\n\nCHILDCHILDCHILDCHILD\n debug me @ %d\n\n",
printf_stderr("\n\nCHILDCHILDCHILDCHILD (process type %s)\n debug me @ %d\n\n",
XRE_ChildProcessTypeToString(XRE_GetProcessType()),
base::GetCurrentProcId());
::Sleep(GetDebugChildPauseTime());
}

View File

@ -34,6 +34,7 @@ py2:
- security
- services/common/tests/mach_commands.py
- servo
- taskcluster/docker/funsize-update-generator
- testing/awsy
- testing/firefox-ui
- testing/geckodriver