Merge m-i to m-c, a=merge

MozReview-Commit-ID: DAm5uRF7n91
This commit is contained in:
Phil Ringnalda 2016-11-29 19:28:12 -08:00
commit ef3cede9a5
107 changed files with 2004 additions and 938 deletions

View File

@ -13,7 +13,7 @@ function test() {
let engine = Services.search.getEngineByName("Amazon.com");
ok(engine, "Amazon.com");
let base = "https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
let base = "https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&ie=UTF-8&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
let url;
// Test search URLs (including purposes).
@ -29,7 +29,7 @@ function test() {
name: "Amazon.com",
alias: null,
description: "Amazon.com Search",
searchForm: "https://www.amazon.com/exec/obidos/external-search/?field-keywords=&mode=blended&tag=mozilla-20&sourceid=Mozilla-search",
searchForm: "https://www.amazon.com/exec/obidos/external-search/?field-keywords=&ie=UTF-8&mode=blended&tag=mozilla-20&sourceid=Mozilla-search",
hidden: false,
wrappedJSObject: {
queryCharset: "UTF-8",
@ -51,6 +51,11 @@ function test() {
value: "{searchTerms}",
purpose: undefined,
},
{
name: "ie",
value: "{inputEncoding}",
purpose: undefined,
},
{
name: "mode",
value: "blended",

View File

@ -18,7 +18,7 @@ function test() {
Services.search.currentEngine = engine;
engine.alias = "a";
let base = "https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
let base = "https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&ie=UTF-8&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
let url;
// Test search URLs (including purposes).

View File

@ -5,10 +5,11 @@
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.co.uk</ShortName>
<Description>Amazon.co.uk Search</Description>
<InputEncoding>ISO-8859-1</InputEncoding>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAC0AQAAJgAAACAgAAAAAAAA6QIAANoBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAF7SURBVDjLlZPLasJAFIaFRF+iVV+h6hO0GF+gVB9AaHwDt64qCG03tQgtdCFIuyhUelmGli66MXThSt24kNiFBUlAYi6ezjnNxSuawB/ITP7v/HNmJgQAEaZzpgHs/gwcTyTEXuXl2U6nA8ViEbK5HKler28CVRAwnB9ptVrAh8MrQuCaZ4iA8fzIqSgCxwzpTIaSuN/RWGwdYLwCUBQFZFkGSZLgqdmEE7YEN8VOAKyaSKUW4nNBAFmnYiKZpDRX1WqwBBzP089n5f/NEQsFL4WqqtsBWJlzDAJr5PwSMM1awEzzdxIbGI3Hvc6jCZeVFgRQRwpY7Qcw3ktgfpR8wLRxCPaot/X4GS95MppfF6DX9n2A3f+kAZycaT8bAZjU6r6B/duD6d3BYg9wQq/tkYzHY1blEiz5lmQyGc95mrO6r2CxgpjCBXgNsJVviolpXJiraeOIjJRE10juUa4sR8V+mO17VvmGqtuOcdNlwut8zTQJcJ0njifyB2bgTdKh6w4BAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVRYw71XQWsTURBe2LQgeNKLB+tVemt6txcteNSD/QGC6VEIGDx5s+eKPQqFgJhLNdFLBWMP7cU0oSAWjB70koC9WHbVQ5SO8+XtS14mr7svyaYDH9m87Jv55puZt1nPi4yIzjMeMj7T9OwjI88455nGC1cZX+nsDESumJmPFDwIAqrX6z00Gg1qt9vjkJgFgUeuO16Vy3RjeZkyMzM9+MY1fsM9I9h9zyV7ZAznZrA4FAoFVwJ1z+WuOysrg1lnMolkHJX4k0igzI5sARYWF7vEZEk0rvO6iyUSuJfLJUqM7zYSqRDIra4OOUZPmNZsNrsl8UVTpkJAjh1GzmaSpJ8mAWmYeZB5urHRhW5SNOfUCCDo47W1bvPZsp2qAhipy3Nz1kaLG8dUCEBqM5AvpgElqFar01NgIZsdco7Zb7VasU2YigIYL5tjqCL7Q5YkFQXKlcqQ7DbHthIALk/IWAKor82xPIhshxWABCYioDMz51sexcVi0XoG4DPLIyvJjkTArK3scDQnRvO0MdTrUHGiKZCP4tNgO6BAEI08EQH9Z2Qow0hyPypJGIa9p6JWKCn4SA8jSKmJIDgyRvPJkcRxjfUwNGr/i8+Mo32iHzWiThBD4NM60bet9P77/ubA728RlTjMiwiH6zEEfvIrwdZFtQmMJ7W/ofIDBZD5m3mVZGwJcOP2kmILIlCkE45HoPWurwCSg0+UQRD4ZyXxId+T7gQb9+4q9sioY5ltrOG3L5vqXiiJffDx/aUi83ZJ7jr2ohcEu8Hh6/m+I7OWGiVxbWKHsz+O3vSOakqFQdsFgQeJUiKD7Wv9YKXBgCeSUC3v2kM5EJhlHDh3NcgcPlG1BXZu98sDmTuBa4fsMnz9fniJUaGzs+eMC540XuR0aDO2L8Y3qPyMcdOM+R/8XcqRA3qp9gAAAABJRU5ErkJggg==</Image>
<Url type="text/html" method="GET" template="https://www.amazon.co.uk/exec/obidos/external-search/" resultdomain="amazon.co.uk">
<Param name="field-keywords" value="{searchTerms}"/>
<Param name="ie" value="{inputEncoding}"/>
<Param name="mode" value="blended"/>
<Param name="tag" value="firefox-uk-21"/>
<Param name="sourceid" value="Mozilla-search"/>

View File

@ -5,10 +5,11 @@
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.fr</ShortName>
<Description>Recherche Amazon.fr</Description>
<InputEncoding>ISO-8859-1</InputEncoding>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAC0AQAAJgAAACAgAAAAAAAA6QIAANoBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAF7SURBVDjLlZPLasJAFIaFRF+iVV+h6hO0GF+gVB9AaHwDt64qCG03tQgtdCFIuyhUelmGli66MXThSt24kNiFBUlAYi6ezjnNxSuawB/ITP7v/HNmJgQAEaZzpgHs/gwcTyTEXuXl2U6nA8ViEbK5HKler28CVRAwnB9ptVrAh8MrQuCaZ4iA8fzIqSgCxwzpTIaSuN/RWGwdYLwCUBQFZFkGSZLgqdmEE7YEN8VOAKyaSKUW4nNBAFmnYiKZpDRX1WqwBBzP089n5f/NEQsFL4WqqtsBWJlzDAJr5PwSMM1awEzzdxIbGI3Hvc6jCZeVFgRQRwpY7Qcw3ktgfpR8wLRxCPaot/X4GS95MppfF6DX9n2A3f+kAZycaT8bAZjU6r6B/duD6d3BYg9wQq/tkYzHY1blEiz5lmQyGc95mrO6r2CxgpjCBXgNsJVviolpXJiraeOIjJRE10juUa4sR8V+mO17VvmGqtuOcdNlwut8zTQJcJ0njifyB2bgTdKh6w4BAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVRYw71XQWsTURBe2LQgeNKLB+tVemt6txcteNSD/QGC6VEIGDx5s+eKPQqFgJhLNdFLBWMP7cU0oSAWjB70koC9WHbVQ5SO8+XtS14mr7svyaYDH9m87Jv55puZt1nPi4yIzjMeMj7T9OwjI88455nGC1cZX+nsDESumJmPFDwIAqrX6z00Gg1qt9vjkJgFgUeuO16Vy3RjeZkyMzM9+MY1fsM9I9h9zyV7ZAznZrA4FAoFVwJ1z+WuOysrg1lnMolkHJX4k0igzI5sARYWF7vEZEk0rvO6iyUSuJfLJUqM7zYSqRDIra4OOUZPmNZsNrsl8UVTpkJAjh1GzmaSpJ8mAWmYeZB5urHRhW5SNOfUCCDo47W1bvPZsp2qAhipy3Nz1kaLG8dUCEBqM5AvpgElqFar01NgIZsdco7Zb7VasU2YigIYL5tjqCL7Q5YkFQXKlcqQ7DbHthIALk/IWAKor82xPIhshxWABCYioDMz51sexcVi0XoG4DPLIyvJjkTArK3scDQnRvO0MdTrUHGiKZCP4tNgO6BAEI08EQH9Z2Qow0hyPypJGIa9p6JWKCn4SA8jSKmJIDgyRvPJkcRxjfUwNGr/i8+Mo32iHzWiThBD4NM60bet9P77/ubA728RlTjMiwiH6zEEfvIrwdZFtQmMJ7W/ofIDBZD5m3mVZGwJcOP2kmILIlCkE45HoPWurwCSg0+UQRD4ZyXxId+T7gQb9+4q9sioY5ltrOG3L5vqXiiJffDx/aUi83ZJ7jr2ohcEu8Hh6/m+I7OWGiVxbWKHsz+O3vSOakqFQdsFgQeJUiKD7Wv9YKXBgCeSUC3v2kM5EJhlHDh3NcgcPlG1BXZu98sDmTuBa4fsMnz9fniJUaGzs+eMC540XuR0aDO2L8Y3qPyMcdOM+R/8XcqRA3qp9gAAAABJRU5ErkJggg==</Image>
<Url type="text/html" method="GET" template="https://www.amazon.fr/exec/obidos/external-search/" resultdomain="amazon.fr">
<Param name="field-keywords" value="{searchTerms}"/>
<Param name="ie" value="{inputEncoding}"/>
<Param name="mode" value="blended"/>
<Param name="tag" value="firefox-fr-21"/>
<Param name="sourceid" value="Mozilla-search"/>

View File

@ -5,10 +5,11 @@
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.it</ShortName>
<Description>Ricerca Amazon.it</Description>
<InputEncoding>ISO-8859-1</InputEncoding>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAC0AQAAJgAAACAgAAAAAAAA6QIAANoBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAF7SURBVDjLlZPLasJAFIaFRF+iVV+h6hO0GF+gVB9AaHwDt64qCG03tQgtdCFIuyhUelmGli66MXThSt24kNiFBUlAYi6ezjnNxSuawB/ITP7v/HNmJgQAEaZzpgHs/gwcTyTEXuXl2U6nA8ViEbK5HKler28CVRAwnB9ptVrAh8MrQuCaZ4iA8fzIqSgCxwzpTIaSuN/RWGwdYLwCUBQFZFkGSZLgqdmEE7YEN8VOAKyaSKUW4nNBAFmnYiKZpDRX1WqwBBzP089n5f/NEQsFL4WqqtsBWJlzDAJr5PwSMM1awEzzdxIbGI3Hvc6jCZeVFgRQRwpY7Qcw3ktgfpR8wLRxCPaot/X4GS95MppfF6DX9n2A3f+kAZycaT8bAZjU6r6B/duD6d3BYg9wQq/tkYzHY1blEiz5lmQyGc95mrO6r2CxgpjCBXgNsJVviolpXJiraeOIjJRE10juUa4sR8V+mO17VvmGqtuOcdNlwut8zTQJcJ0njifyB2bgTdKh6w4BAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVRYw71XQWsTURBe2LQgeNKLB+tVemt6txcteNSD/QGC6VEIGDx5s+eKPQqFgJhLNdFLBWMP7cU0oSAWjB70koC9WHbVQ5SO8+XtS14mr7svyaYDH9m87Jv55puZt1nPi4yIzjMeMj7T9OwjI88455nGC1cZX+nsDESumJmPFDwIAqrX6z00Gg1qt9vjkJgFgUeuO16Vy3RjeZkyMzM9+MY1fsM9I9h9zyV7ZAznZrA4FAoFVwJ1z+WuOysrg1lnMolkHJX4k0igzI5sARYWF7vEZEk0rvO6iyUSuJfLJUqM7zYSqRDIra4OOUZPmNZsNrsl8UVTpkJAjh1GzmaSpJ8mAWmYeZB5urHRhW5SNOfUCCDo47W1bvPZsp2qAhipy3Nz1kaLG8dUCEBqM5AvpgElqFar01NgIZsdco7Zb7VasU2YigIYL5tjqCL7Q5YkFQXKlcqQ7DbHthIALk/IWAKor82xPIhshxWABCYioDMz51sexcVi0XoG4DPLIyvJjkTArK3scDQnRvO0MdTrUHGiKZCP4tNgO6BAEI08EQH9Z2Qow0hyPypJGIa9p6JWKCn4SA8jSKmJIDgyRvPJkcRxjfUwNGr/i8+Mo32iHzWiThBD4NM60bet9P77/ubA728RlTjMiwiH6zEEfvIrwdZFtQmMJ7W/ofIDBZD5m3mVZGwJcOP2kmILIlCkE45HoPWurwCSg0+UQRD4ZyXxId+T7gQb9+4q9sioY5ltrOG3L5vqXiiJffDx/aUi83ZJ7jr2ohcEu8Hh6/m+I7OWGiVxbWKHsz+O3vSOakqFQdsFgQeJUiKD7Wv9YKXBgCeSUC3v2kM5EJhlHDh3NcgcPlG1BXZu98sDmTuBa4fsMnz9fniJUaGzs+eMC540XuR0aDO2L8Y3qPyMcdOM+R/8XcqRA3qp9gAAAABJRU5ErkJggg==</Image>
<Url type="text/html" method="GET" template="https://www.amazon.it/exec/obidos/external-search/" resultdomain="amazon.it">
<Param name="field-keywords" value="{searchTerms}"/>
<Param name="ie" value="{inputEncoding}"/>
<Param name="mode" value="blended"/>
<Param name="tag" value="firefoxit-21"/>
<Param name="sourceid" value="Mozilla-search"/>

View File

@ -5,10 +5,11 @@
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Amazon.de</ShortName>
<Description>Amazon.de Suche</Description>
<InputEncoding>ISO-8859-1</InputEncoding>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAC0AQAAJgAAACAgAAAAAAAA6QIAANoBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAF7SURBVDjLlZPLasJAFIaFRF+iVV+h6hO0GF+gVB9AaHwDt64qCG03tQgtdCFIuyhUelmGli66MXThSt24kNiFBUlAYi6ezjnNxSuawB/ITP7v/HNmJgQAEaZzpgHs/gwcTyTEXuXl2U6nA8ViEbK5HKler28CVRAwnB9ptVrAh8MrQuCaZ4iA8fzIqSgCxwzpTIaSuN/RWGwdYLwCUBQFZFkGSZLgqdmEE7YEN8VOAKyaSKUW4nNBAFmnYiKZpDRX1WqwBBzP089n5f/NEQsFL4WqqtsBWJlzDAJr5PwSMM1awEzzdxIbGI3Hvc6jCZeVFgRQRwpY7Qcw3ktgfpR8wLRxCPaot/X4GS95MppfF6DX9n2A3f+kAZycaT8bAZjU6r6B/duD6d3BYg9wQq/tkYzHY1blEiz5lmQyGc95mrO6r2CxgpjCBXgNsJVviolpXJiraeOIjJRE10juUa4sR8V+mO17VvmGqtuOcdNlwut8zTQJcJ0njifyB2bgTdKh6w4BAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVRYw71XQWsTURBe2LQgeNKLB+tVemt6txcteNSD/QGC6VEIGDx5s+eKPQqFgJhLNdFLBWMP7cU0oSAWjB70koC9WHbVQ5SO8+XtS14mr7svyaYDH9m87Jv55puZt1nPi4yIzjMeMj7T9OwjI88455nGC1cZX+nsDESumJmPFDwIAqrX6z00Gg1qt9vjkJgFgUeuO16Vy3RjeZkyMzM9+MY1fsM9I9h9zyV7ZAznZrA4FAoFVwJ1z+WuOysrg1lnMolkHJX4k0igzI5sARYWF7vEZEk0rvO6iyUSuJfLJUqM7zYSqRDIra4OOUZPmNZsNrsl8UVTpkJAjh1GzmaSpJ8mAWmYeZB5urHRhW5SNOfUCCDo47W1bvPZsp2qAhipy3Nz1kaLG8dUCEBqM5AvpgElqFar01NgIZsdco7Zb7VasU2YigIYL5tjqCL7Q5YkFQXKlcqQ7DbHthIALk/IWAKor82xPIhshxWABCYioDMz51sexcVi0XoG4DPLIyvJjkTArK3scDQnRvO0MdTrUHGiKZCP4tNgO6BAEI08EQH9Z2Qow0hyPypJGIa9p6JWKCn4SA8jSKmJIDgyRvPJkcRxjfUwNGr/i8+Mo32iHzWiThBD4NM60bet9P77/ubA728RlTjMiwiH6zEEfvIrwdZFtQmMJ7W/ofIDBZD5m3mVZGwJcOP2kmILIlCkE45HoPWurwCSg0+UQRD4ZyXxId+T7gQb9+4q9sioY5ltrOG3L5vqXiiJffDx/aUi83ZJ7jr2ohcEu8Hh6/m+I7OWGiVxbWKHsz+O3vSOakqFQdsFgQeJUiKD7Wv9YKXBgCeSUC3v2kM5EJhlHDh3NcgcPlG1BXZu98sDmTuBa4fsMnz9fniJUaGzs+eMC540XuR0aDO2L8Y3qPyMcdOM+R/8XcqRA3qp9gAAAABJRU5ErkJggg==</Image>
<Url type="text/html" method="GET" template="https://www.amazon.de/exec/obidos/external-search/" resultdomain="amazon.de">
<Param name="field-keywords" value="{searchTerms}"/>
<Param name="ie" value="{inputEncoding}"/>
<Param name="mode" value="blended"/>
<Param name="tag" value="firefox-de-21"/>
<Param name="sourceid" value="Mozilla-search"/>

View File

@ -10,6 +10,7 @@
<Url type="application/x-suggestions+json" method="GET" template="https://completion.amazon.com/search/complete?q={searchTerms}&amp;search-alias=aps&amp;mkt=1"/>
<Url type="text/html" method="GET" template="https://www.amazon.com/exec/obidos/external-search/" rel="searchform">
<Param name="field-keywords" value="{searchTerms}"/>
<Param name="ie" value="{inputEncoding}"/>
<Param name="mode" value="blended"/>
<Param name="tag" value="mozilla-20"/>
<Param name="sourceid" value="Mozilla-search"/>

View File

@ -614,7 +614,25 @@ Inspector.prototype = {
this.sidebar.addTab(id, title, panel, selected);
},
setupToolbar: function () {
/**
* Method to check whether the document is a HTML document and
* pickColorFromPage method is available or not.
* Returns a boolean value
*/
supportsEyeDropper: Task.async(function* () {
let isInHTMLDocument = this.selection.nodeFront &&
this.selection.nodeFront.isInHTMLDocument;
let pickColorAvailable = false;
try {
pickColorAvailable = yield this.target
.actorHasMethod("inspector", "pickColorFromPage");
} catch (e) {
console.error(e);
}
return isInHTMLDocument && pickColorAvailable;
}),
setupToolbar: Task.async(function* () {
this.teardownToolbar();
// Setup the sidebar toggle button.
@ -637,26 +655,21 @@ Inspector.prototype = {
this.addNodeButton.addEventListener("click", this.addNode);
// Setup the eye-dropper icon if we're in an HTML document and we have actor support.
if (this.selection.nodeFront && this.selection.nodeFront.isInHTMLDocument) {
this.target.actorHasMethod("inspector", "pickColorFromPage").then(value => {
if (!value) {
return;
}
this.onEyeDropperDone = this.onEyeDropperDone.bind(this);
this.onEyeDropperButtonClicked = this.onEyeDropperButtonClicked.bind(this);
this.eyeDropperButton = this.panelDoc
let canShowEyeDropper = yield this.supportsEyeDropper();
if (canShowEyeDropper) {
this.onEyeDropperDone = this.onEyeDropperDone.bind(this);
this.onEyeDropperButtonClicked = this.onEyeDropperButtonClicked.bind(this);
this.eyeDropperButton = this.panelDoc
.getElementById("inspector-eyedropper-toggle");
this.eyeDropperButton.disabled = false;
this.eyeDropperButton.title = INSPECTOR_L10N.getStr("inspector.eyedropper.label");
this.eyeDropperButton.addEventListener("click", this.onEyeDropperButtonClicked);
}, e => console.error(e));
this.eyeDropperButton.disabled = false;
this.eyeDropperButton.title = INSPECTOR_L10N.getStr("inspector.eyedropper.label");
this.eyeDropperButton.addEventListener("click", this.onEyeDropperButtonClicked);
} else {
let eyeDropperButton = this.panelDoc.getElementById("inspector-eyedropper-toggle");
eyeDropperButton.disabled = true;
eyeDropperButton.title = INSPECTOR_L10N.getStr("eyedropper.disabled.title");
}
},
}),
teardownToolbar: function () {
this._sidebarToggle = null;

View File

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
UPDATE_SHOW_GRID_LINE_NUMBERS,
UPDATE_SHOW_INFINITE_LINES,
} = require("./index");
module.exports = {
/**
* Update the grid highlighter's show grid line numbers preference.
*
* @param {Boolean} enabled
* Whether or not the grid highlighter should show the grid line numbers.
*/
updateShowGridLineNumbers(enabled) {
return {
type: UPDATE_SHOW_GRID_LINE_NUMBERS,
enabled,
};
},
/**
* Update the grid highlighter's show infinite lines preference.
*
* @param {Boolean} enabled
* Whether or not the grid highlighter should extend grid lines infinitely.
*/
updateShowInfiniteLines(enabled) {
return {
type: UPDATE_SHOW_INFINITE_LINES,
enabled,
};
},
};

View File

@ -14,4 +14,10 @@ createEnum([
// Update the entire grids state with the new list of grids.
"UPDATE_GRIDS",
// Update the grid highlighter's show grid line numbers state.
"UPDATE_SHOW_GRID_LINE_NUMBERS",
// Update the grid highlighter's show infinite lines state.
"UPDATE_SHOW_INFINITE_LINES",
], module.exports);

View File

@ -6,5 +6,6 @@
DevToolsModules(
'grids.js',
'highlighter-settings.js',
'index.js',
)

View File

@ -20,6 +20,10 @@ const App = createClass({
propTypes: {
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
onToggleShowInfiniteLines: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],

View File

@ -4,9 +4,12 @@
"use strict";
const { addons, createClass, DOM: dom, PropTypes } =
const { addons, createClass, createFactory, DOM: dom, PropTypes } =
require("devtools/client/shared/vendor/react");
const GridDisplaySettings = createFactory(require("./GridDisplaySettings"));
const GridList = createFactory(require("./GridList"));
const Types = require("../types");
const { getStr } = require("../utils/l10n");
@ -16,6 +19,10 @@ module.exports = createClass({
propTypes: {
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
onToggleShowInfiniteLines: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
@ -23,20 +30,34 @@ module.exports = createClass({
render() {
let {
grids,
highlighterSettings,
onToggleGridHighlighter,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
} = this.props;
return dom.div(
{
id: "layout-grid-container",
},
!grids.length ?
dom.div(
{
className: "layout-no-grids"
},
getStr("layout.noGrids")
) : null
);
return grids.length ?
dom.div(
{
id: "layout-grid-container",
},
GridList({
grids,
onToggleGridHighlighter,
}),
GridDisplaySettings({
highlighterSettings,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
})
)
:
dom.div(
{
className: "layout-no-grids",
},
getStr("layout.noGrids")
);
},
});

View File

@ -0,0 +1,90 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { addons, createClass, DOM: dom, PropTypes } =
require("devtools/client/shared/vendor/react");
const Types = require("../types");
const { getStr } = require("../utils/l10n");
module.exports = createClass({
displayName: "GridDisplaySettings",
propTypes: {
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
onToggleShowInfiniteLines: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
onShowGridLineNumbersCheckboxClick() {
let {
highlighterSettings,
onToggleShowGridLineNumbers,
} = this.props;
onToggleShowGridLineNumbers(!highlighterSettings.showGridLineNumbers);
},
onShowInfiniteLinesCheckboxClick() {
let {
highlighterSettings,
onToggleShowInfiniteLines,
} = this.props;
onToggleShowInfiniteLines(!highlighterSettings.showInfiniteLines);
},
render() {
let {
highlighterSettings,
} = this.props;
return dom.div(
{
className: "grid-container",
},
dom.span(
{},
getStr("layout.gridDisplaySettings")
),
dom.ul(
{},
dom.li(
{},
dom.label(
{},
dom.input(
{
type: "checkbox",
checked: highlighterSettings.showInfiniteLines,
onChange: this.onShowInfiniteLinesCheckboxClick,
}
),
getStr("layout.extendGridLinesInfinitely")
)
),
dom.li(
{},
dom.label(
{},
dom.input(
{
type: "checkbox",
checked: highlighterSettings.showGridLineNumbers,
onChange: this.onShowGridLineNumbersCheckboxClick,
}
),
getStr("layout.displayNumbersOnLines")
)
)
)
);
},
});

View File

@ -0,0 +1,87 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { addons, createClass, DOM: dom, PropTypes } =
require("devtools/client/shared/vendor/react");
const Types = require("../types");
const { getStr } = require("../utils/l10n");
module.exports = createClass({
displayName: "GridList",
propTypes: {
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
onGridCheckboxClick({ target }) {
let {
grids,
onToggleGridHighlighter,
} = this.props;
onToggleGridHighlighter(grids[target.value].nodeFront);
},
render() {
let {
grids,
} = this.props;
return dom.div(
{
className: "grid-container",
},
dom.span(
{},
getStr("layout.overlayMultipleGrids")
),
dom.ul(
{},
grids.map(grid => {
let { nodeFront } = grid;
let { displayName, attributes } = nodeFront;
let gridName = displayName;
let idIndex = attributes.findIndex(({ name }) => name === "id");
if (idIndex > -1 && attributes[idIndex].value) {
gridName += "#" + attributes[idIndex].value;
}
let classIndex = attributes.findIndex(({name}) => name === "class");
if (classIndex > -1 && attributes[classIndex].value) {
gridName += "." + attributes[classIndex].value.split(" ").join(".");
}
return dom.li(
{
key: grid.id,
},
dom.label(
{},
dom.input(
{
type: "checkbox",
value: grid.id,
checked: grid.highlighted,
onChange: this.onGridCheckboxClick,
}
),
gridName
)
);
})
)
);
},
});

View File

@ -9,4 +9,6 @@ DevToolsModules(
'Accordion.js',
'App.js',
'Grid.js',
'GridDisplaySettings.js',
'GridList.js',
)

View File

@ -9,7 +9,15 @@ const { Task } = require("devtools/shared/task");
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
const { updateGrids } = require("./actions/grids");
const {
updateGridHighlighted,
updateGrids,
} = require("./actions/grids");
const {
updateShowGridLineNumbers,
updateShowInfiniteLines,
} = require("./actions/highlighter-settings");
const App = createFactory(require("./components/App"));
const Store = require("./store");
@ -17,15 +25,22 @@ const { LocalizationHelper } = require("devtools/shared/l10n");
const INSPECTOR_L10N =
new LocalizationHelper("devtools/client/locales/inspector.properties");
const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
function LayoutView(inspector, window) {
this.document = window.document;
this.highlighters = inspector.highlighters;
this.inspector = inspector;
this.store = null;
this.walker = this.inspector.walker;
this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
this.onHighlighterChange = this.onHighlighterChange.bind(this);
this.onSidebarSelect = this.onSidebarSelect.bind(this);
this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
this.inspector.sidebar.on("select", this.onSidebarSelect);
this.init();
@ -43,14 +58,77 @@ LayoutView.prototype = {
}
this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
let store = this.store = Store();
this.loadHighlighterSettings();
let app = App({
/**
* Handler for a change in the input checkboxes in the GridList component.
* Toggles on/off the grid highlighter for the provided grid container element.
*
* @param {NodeFront} node
* The NodeFront of the grid container element for which the grid
* highlighter is toggled on/off for.
*/
onToggleGridHighlighter: node => {
let { highlighterSettings } = this.store.getState();
this.highlighters.toggleGridHighlighter(node, highlighterSettings);
},
/**
* Handler for a change in the show grid line numbers checkbox in the
* GridDisplaySettings component. TOggles on/off the option to show the grid line
* numbers in the grid highlighter. Refreshes the shown grid highlighter for the
* grids currently highlighted.
*
* @param {Boolean} enabled
* Whether or not the grid highlighter should show the grid line numbers.
*/
onToggleShowGridLineNumbers: enabled => {
this.store.dispatch(updateShowGridLineNumbers(enabled));
Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
let { grids, highlighterSettings } = this.store.getState();
for (let grid of grids) {
if (grid.highlighted) {
this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
}
}
},
/**
* Handler for a change in the extend grid lines infinitely checkbox in the
* GridDisplaySettings component. Toggles on/off the option to extend the grid
* lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
* for grids currently highlighted.
*
* @param {Boolean} enabled
* Whether or not the grid highlighter should extend grid lines infinitely.
*/
onToggleShowInfiniteLines: enabled => {
this.store.dispatch(updateShowInfiniteLines(enabled));
Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
let { grids, highlighterSettings } = this.store.getState();
for (let grid of grids) {
if (grid.highlighted) {
this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
}
}
},
});
let provider = createElement(Provider, {
store,
id: "layoutview",
title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle"),
key: "layoutview",
}, App());
}, app);
let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
@ -67,6 +145,8 @@ LayoutView.prototype = {
* and cleans up references.
*/
destroy() {
this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
this.inspector.sidebar.off("select", this.onSidebarSelect);
this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
@ -86,6 +166,19 @@ LayoutView.prototype = {
this.inspector.sidebar.getCurrentTabID() === "layoutview";
},
/**
* Load the grid highligher display settings into the store from the stored preferences.
*/
loadHighlighterSettings() {
let { dispatch } = this.store;
let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
dispatch(updateShowGridLineNumbers(showGridLineNumbers));
dispatch(updateShowInfiniteLines(showInfinteLines));
},
/**
* Refreshes the layout view by dispatching the new grid data. This is called when the
* layout view becomes visible or the view needs to be updated with new grid data.
@ -111,8 +204,9 @@ LayoutView.prototype = {
grids.push({
id: i,
gridFragments: grid.gridFragments,
highlighted: nodeFront == this.highlighters.gridHighlighterShown,
nodeFront,
gridFragments: grid.gridFragments
});
}
@ -120,7 +214,7 @@ LayoutView.prototype = {
}),
/**
* Handler for 'grid-layout-changed' events emitted from the LayoutActor.
* Handler for "grid-layout-changed" events emitted from the LayoutActor.
*
* @param {Array} grids
* Array of all GridFront in the current page.
@ -131,6 +225,21 @@ LayoutView.prototype = {
}
},
/**
* Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
* from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
*
* @param {Event} event
* Event that was triggered.
* @param {NodeFront} nodeFront
* The NodeFront of the grid container element for which the grid highlighter
* is shown for.
*/
onHighlighterChange(event, nodeFront) {
let highlighted = event === "grid-highlighter-shown";
this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
},
/**
* Handler for the inspector sidebar select event. Starts listening for
* "grid-layout-changed" if the layout panel is visible. Otherwise, stop

View File

@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
UPDATE_SHOW_GRID_LINE_NUMBERS,
UPDATE_SHOW_INFINITE_LINES
} = require("../actions/index");
const INITIAL_HIGHLIGHTER_SETTINGS = {
showGridLineNumbers: false,
showInfiniteLines: false,
};
let reducers = {
[UPDATE_SHOW_GRID_LINE_NUMBERS](highlighterSettings, { enabled }) {
return Object.assign({}, highlighterSettings, {
showGridLineNumbers: enabled,
});
},
[UPDATE_SHOW_INFINITE_LINES](highlighterSettings, { enabled }) {
return Object.assign({}, highlighterSettings, {
showInfiniteLines: enabled,
});
},
};
module.exports = function (highlighterSettings = INITIAL_HIGHLIGHTER_SETTINGS, action) {
let reducer = reducers[action.type];
if (!reducer) {
return highlighterSettings;
}
return reducer(highlighterSettings, action);
};

View File

@ -5,3 +5,4 @@
"use strict";
exports.grids = require("./grids");
exports.highlighterSettings = require("./highlighter-settings");

View File

@ -6,5 +6,6 @@
DevToolsModules(
'grids.js',
'highlighter-settings.js',
'index.js',
)

View File

@ -15,7 +15,7 @@ exports.grid = {
id: PropTypes.number,
// The grid fragment object of the grid container
gridFragments: PropTypes.object,
gridFragments: PropTypes.array,
// Whether or not the grid highlighter is highlighting the grid
highlighted: PropTypes.bool,
@ -24,3 +24,16 @@ exports.grid = {
nodeFront: PropTypes.object,
};
/**
* The grid highlighter settings on what to display in its grid overlay in the document.
*/
exports.highlighterSettings = {
// Whether or not the grid highlighter should show the grid line numbers
showGridLineNumbers: PropTypes.bool,
// Whether or not the grid highlighter extends the grid lines infinitely
showInfiniteLines: PropTypes.bool,
};

View File

@ -29,13 +29,13 @@ add_task(function* () {
let gridToggle = container.querySelector(".ruleview-grid");
info("Toggling ON the CSS grid highlighter from the rule-view.");
let onHighlighterShown = highlighters.once("highlighter-shown");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
gridToggle.click();
yield onHighlighterShown;
info("Edit the 'grid' property value to 'block'.");
let editor = yield focusEditableField(view, container);
let onHighlighterHidden = highlighters.once("highlighter-hidden");
let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
let onDone = view.once("ruleview-changed");
editor.input.value = "block;";
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);

View File

@ -30,7 +30,7 @@ add_task(function* () {
let gridToggle = container.querySelector(".ruleview-grid");
info("Toggling ON the CSS grid highlighter from the rule-view.");
let onHighlighterShown = highlighters.once("highlighter-shown");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
gridToggle.click();
yield onHighlighterShown;

View File

@ -39,7 +39,7 @@ add_task(function* () {
ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
info("Toggling ON the CSS grid highlighter from the rule-view.");
let onHighlighterShown = highlighters.once("highlighter-shown");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
gridToggle.click();
yield onHighlighterShown;
@ -52,7 +52,7 @@ add_task(function* () {
ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
info("Toggling OFF the CSS grid highlighter from the rule-view.");
let onHighlighterHidden = highlighters.once("highlighter-hidden");
let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
gridToggle.click();
yield onHighlighterHidden;

View File

@ -45,7 +45,7 @@ add_task(function* () {
ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
info("Toggling ON the CSS grid highlighter from the overridden rule in the rule-view.");
let onHighlighterShown = highlighters.once("highlighter-shown");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
overriddenGridToggle.click();
yield onHighlighterShown;
@ -60,7 +60,7 @@ add_task(function* () {
info("Toggling off the CSS grid highlighter from the normal grid declaration in the " +
"rule-view.");
let onHighlighterHidden = highlighters.once("highlighter-hidden");
let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
gridToggle.click();
yield onHighlighterHidden;

View File

@ -45,7 +45,7 @@ add_task(function* () {
info("Toggling ON the CSS grid highlighter for the first grid container from the " +
"rule-view.");
let onHighlighterShown = highlighters.once("highlighter-shown");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
gridToggle.click();
yield onHighlighterShown;
@ -72,7 +72,7 @@ add_task(function* () {
info("Toggling ON the CSS grid highlighter for the second grid container from the " +
"rule-view.");
onHighlighterShown = highlighters.once("highlighter-shown");
onHighlighterShown = highlighters.once("grid-highlighter-shown");
gridToggle.click();
yield onHighlighterShown;

View File

@ -736,7 +736,7 @@ TextPropertyEditor.prototype = {
}
if (this.isDisplayGrid()) {
this.ruleView.highlighters._hideGridHighlighter();
this.ruleView.highlighters.hideGridHighlighter();
}
// First, set this property value (common case, only modified a property)

View File

@ -6,12 +6,8 @@
"use strict";
/**
* The highlighter overlays are in-content highlighters that appear when hovering over
* property values.
*/
const promise = require("promise");
const {Task} = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const { VIEW_NODE_VALUE_TYPE } = require("devtools/client/inspector/shared/node-types");
@ -36,10 +32,10 @@ function HighlightersOverlay(inspector) {
// Name of the selector highlighter shown.
this.selectorHighlighterShown = null;
this._onClick = this._onClick.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseOut = this._onMouseOut.bind(this);
this._onWillNavigate = this._onWillNavigate.bind(this);
this.onClick = this.onClick.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseOut = this.onMouseOut.bind(this);
this.onWillNavigate = this.onWillNavigate.bind(this);
EventEmitter.decorate(this);
}
@ -63,12 +59,12 @@ HighlightersOverlay.prototype = {
}
let el = view.element;
el.addEventListener("click", this._onClick, true);
el.addEventListener("mousemove", this._onMouseMove, false);
el.addEventListener("mouseout", this._onMouseOut, false);
el.ownerDocument.defaultView.addEventListener("mouseout", this._onMouseOut, false);
el.addEventListener("click", this.onClick, true);
el.addEventListener("mousemove", this.onMouseMove, false);
el.addEventListener("mouseout", this.onMouseOut, false);
el.ownerDocument.defaultView.addEventListener("mouseout", this.onMouseOut, false);
this.inspector.target.on("will-navigate", this._onWillNavigate);
this.inspector.target.on("will-navigate", this.onWillNavigate);
},
/**
@ -85,49 +81,191 @@ HighlightersOverlay.prototype = {
}
let el = view.element;
el.removeEventListener("click", this._onClick, true);
el.removeEventListener("mousemove", this._onMouseMove, false);
el.removeEventListener("mouseout", this._onMouseOut, false);
el.removeEventListener("click", this.onClick, true);
el.removeEventListener("mousemove", this.onMouseMove, false);
el.removeEventListener("mouseout", this.onMouseOut, false);
this.inspector.target.off("will-navigate", this._onWillNavigate);
this.inspector.target.off("will-navigate", this.onWillNavigate);
},
_onClick: function (event) {
/**
* Toggle the grid highlighter for the given grid container element.
*
* @param {NodeFront} node
* The NodeFront of the grid container element to highlight.
* @param {Object} options
* Object used for passing options to the grid highlighter.
*/
toggleGridHighlighter: Task.async(function* (node, options = {}) {
if (node == this.gridHighlighterShown) {
yield this.hideGridHighlighter(node);
return;
}
yield this.showGridHighlighter(node, options);
}),
/**
* Show the grid highlighter for the given grid container element.
*
* @param {NodeFront} node
* The NodeFront of the grid container element to highlight.
* @param {Object} options
* Object used for passing options to the grid highlighter.
*/
showGridHighlighter: Task.async(function* (node, options) {
let highlighter = yield this._getHighlighter("CssGridHighlighter");
if (!highlighter) {
return;
}
let isShown = yield highlighter.show(node, options);
if (!isShown) {
return;
}
this._toggleRuleViewGridIcon(node, true);
// Emit the NodeFront of the grid container element that the grid highlighter was
// shown for.
this.emit("grid-highlighter-shown", node);
this.gridHighlighterShown = node;
}),
/**
* Hide the grid highlighter for the given grid container element.
*
* @param {NodeFront} node
* The NodeFront of the grid container element to unhighlight.
*/
hideGridHighlighter: Task.async(function* (node) {
if (!this.gridHighlighterShown || !this.highlighters.CssGridHighlighter) {
return;
}
this._toggleRuleViewGridIcon(node, false);
yield this.highlighters.CssGridHighlighter.hide();
// Emit the NodeFront of the grid container element that the grid highlighter was
// hidden for.
this.emit("grid-highlighter-hidden", this.gridHighlighterShown);
this.gridHighlighterShown = null;
}),
/**
* Get a highlighter front given a type. It will only be initialized once.
*
* @param {String} type
* The highlighter type. One of this.highlighters.
* @return {Promise} that resolves to the highlighter
*/
_getHighlighter: function (type) {
let utils = this.highlighterUtils;
if (this.highlighters[type]) {
return promise.resolve(this.highlighters[type]);
}
return utils.getHighlighterByType(type).then(highlighter => {
this.highlighters[type] = highlighter;
return highlighter;
});
},
/**
* Toggle all the grid icons in the rule view if the current inspector selection is the
* highlighted node.
*
* @param {NodeFront} node
* The NodeFront of the grid container element to highlight.
* @param {Boolean} active
* Whether or not the grid icon should be active.
*/
_toggleRuleViewGridIcon: function (node, active) {
if (this.inspector.selection.nodeFront != node) {
return;
}
let ruleViewEl = this.inspector.ruleview.view.element;
for (let gridIcon of ruleViewEl.querySelectorAll(".ruleview-grid")) {
gridIcon.classList.toggle("active", active);
}
},
/**
* Hide the currently shown hovered highlighter.
*/
_hideHoveredHighlighter: function () {
if (!this.hoveredHighlighterShown ||
!this.highlighters[this.hoveredHighlighterShown]) {
return;
}
// For some reason, the call to highlighter.hide doesn't always return a
// promise. This causes some tests to fail when trying to install a
// rejection handler on the result of the call. To avoid this, check
// whether the result is truthy before installing the handler.
let onHidden = this.highlighters[this.hoveredHighlighterShown].hide();
if (onHidden) {
onHidden.then(null, e => console.error(e));
}
this.hoveredHighlighterShown = null;
this.emit("highlighter-hidden");
},
/**
* Is the current hovered node a css transform property value in the
* computed-view.
*
* @param {Object} nodeInfo
* @return {Boolean}
*/
_isComputedViewTransform: function (nodeInfo) {
let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
nodeInfo.value.property === "transform";
return !this.isRuleView && isTransform;
},
/**
* Is the current clicked node a grid display property value in the
* rule-view.
*
* @param {DOMNode} node
* @return {Boolean}
*/
_isRuleViewDisplayGrid: function (node) {
return this.isRuleView && node.classList.contains("ruleview-grid");
},
/**
* Is the current hovered node a css transform property value in the rule-view.
*
* @param {Object} nodeInfo
* @return {Boolean}
*/
_isRuleViewTransform: function (nodeInfo) {
let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
nodeInfo.value.property === "transform";
let isEnabled = nodeInfo.value.enabled &&
!nodeInfo.value.overridden &&
!nodeInfo.value.pseudoElement;
return this.isRuleView && isTransform && isEnabled;
},
onClick: function (event) {
// Bail out if the target is not a grid property value.
if (!this._isRuleViewDisplayGrid(event.target)) {
return;
}
event.stopPropagation();
this._getHighlighter("CssGridHighlighter").then(highlighter => {
let node = this.inspector.selection.nodeFront;
// Toggle off the grid highlighter if the grid highlighter toggle is clicked
// for the current highlighted grid.
if (node === this.gridHighlighterShown) {
return highlighter.hide();
}
return highlighter.show(node);
}).then(isGridShown => {
// Toggle all the grid icons in the current rule view.
let ruleViewEl = this.inspector.ruleview.view.element;
for (let gridIcon of ruleViewEl.querySelectorAll(".ruleview-grid")) {
gridIcon.classList.toggle("active", isGridShown);
}
if (isGridShown) {
this.gridHighlighterShown = this.inspector.selection.nodeFront;
this.emit("highlighter-shown");
} else {
this.gridHighlighterShown = null;
this.emit("highlighter-hidden");
}
}).catch(e => console.error(e));
this.toggleGridHighlighter(this.inspector.selection.nodeFront);
},
_onMouseMove: function (event) {
onMouseMove: function (event) {
// Bail out if the target is the same as for the last mousemove.
if (event.target === this._lastHovered) {
return;
@ -165,7 +303,7 @@ HighlightersOverlay.prototype = {
}
},
_onMouseOut: function (event) {
onMouseOut: function (event) {
// Only hide the highlighter if the mouse leaves the currently hovered node.
if (!this._lastHovered ||
(event && this._lastHovered.contains(event.relatedTarget))) {
@ -180,110 +318,12 @@ HighlightersOverlay.prototype = {
/**
* Clear saved highlighter shown properties on will-navigate.
*/
_onWillNavigate: function () {
onWillNavigate: function () {
this.gridHighlighterShown = null;
this.hoveredHighlighterShown = null;
this.selectorHighlighterShown = null;
},
/**
* Is the current hovered node a css transform property value in the rule-view.
*
* @param {Object} nodeInfo
* @return {Boolean}
*/
_isRuleViewTransform: function (nodeInfo) {
let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
nodeInfo.value.property === "transform";
let isEnabled = nodeInfo.value.enabled &&
!nodeInfo.value.overridden &&
!nodeInfo.value.pseudoElement;
return this.isRuleView && isTransform && isEnabled;
},
/**
* Is the current hovered node a css transform property value in the
* computed-view.
*
* @param {Object} nodeInfo
* @return {Boolean}
*/
_isComputedViewTransform: function (nodeInfo) {
let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
nodeInfo.value.property === "transform";
return !this.isRuleView && isTransform;
},
/**
* Is the current clicked node a grid display property value in the
* rule-view.
*
* @param {DOMNode} node
* @return {Boolean}
*/
_isRuleViewDisplayGrid: function (node) {
return this.isRuleView && node.classList.contains("ruleview-grid");
},
/**
* Hide the currently shown grid highlighter.
*/
_hideGridHighlighter: function () {
if (!this.gridHighlighterShown || !this.highlighters.CssGridHighlighter) {
return;
}
let onHidden = this.highlighters.CssGridHighlighter.hide();
if (onHidden) {
onHidden.then(null, e => console.error(e));
}
this.gridHighlighterShown = null;
this.emit("highlighter-hidden");
},
/**
* Hide the currently shown hovered highlighter.
*/
_hideHoveredHighlighter: function () {
if (!this.hoveredHighlighterShown ||
!this.highlighters[this.hoveredHighlighterShown]) {
return;
}
// For some reason, the call to highlighter.hide doesn't always return a
// promise. This causes some tests to fail when trying to install a
// rejection handler on the result of the call. To avoid this, check
// whether the result is truthy before installing the handler.
let onHidden = this.highlighters[this.hoveredHighlighterShown].hide();
if (onHidden) {
onHidden.then(null, e => console.error(e));
}
this.hoveredHighlighterShown = null;
this.emit("highlighter-hidden");
},
/**
* Get a highlighter front given a type. It will only be initialized once.
*
* @param {String} type
* The highlighter type. One of this.highlighters.
* @return {Promise} that resolves to the highlighter
*/
_getHighlighter: function (type) {
let utils = this.highlighterUtils;
if (this.highlighters[type]) {
return promise.resolve(this.highlighters[type]);
}
return utils.getHighlighterByType(type).then(highlighter => {
this.highlighters[type] = highlighter;
return highlighter;
});
},
/**
* Destroy this overlay instance, removing it from the view and destroying
* all initialized highlighters.
@ -296,6 +336,8 @@ HighlightersOverlay.prototype = {
}
}
this._lastHovered = null;
this.inspector = null;
this.highlighters = null;
this.highlighterUtils = null;

View File

@ -28,13 +28,13 @@ add_task(function* () {
info("Faking a mousemove on a non-transform property");
let {valueSpan} = getRuleViewProperty(view, "body", "color");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
ok(!hs.highlighters[TYPE], "No highlighter exists in the rule-view (2)");
info("Faking a mousemove on a transform property");
({valueSpan} = getRuleViewProperty(view, "body", "transform"));
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
let onComputedViewReady = inspector.once("computed-view-refreshed");
@ -48,13 +48,13 @@ add_task(function* () {
info("Faking a mousemove on a non-transform property");
({valueSpan} = getComputedViewProperty(cView, "color"));
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
ok(!hs.highlighters[TYPE], "No highlighter exists in the computed-view (3)");
info("Faking a mousemove on a transform property");
({valueSpan} = getComputedViewProperty(cView, "transform"));
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.highlighters[TYPE],

View File

@ -56,11 +56,11 @@ add_task(function* () {
info("Checking that the HighlighterFront's show/hide methods are called");
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(HighlighterFront.isShown, "The highlighter is shown");
let onHighlighterHidden = hs.once("highlighter-hidden");
hs._onMouseOut();
hs.onMouseOut();
yield onHighlighterHidden;
ok(!HighlighterFront.isShown, "The highlighter is hidden");
@ -68,11 +68,11 @@ add_task(function* () {
" show the highlighter several times");
let nb = HighlighterFront.nbOfTimesShown;
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nbOfTimesShown, nb + 1, "The highlighter was shown once");
hs._onMouseMove({target: valueSpan});
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
is(HighlighterFront.nbOfTimesShown, nb + 1,
"The highlighter was shown once, after several mousemove");
@ -80,7 +80,7 @@ add_task(function* () {
yield selectNode("html", inspector);
({valueSpan} = getRuleViewProperty(view, "html", "transform"));
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nodeFront.tagName, "HTML",
"The right NodeFront is passed to the highlighter (1)");
@ -88,7 +88,7 @@ add_task(function* () {
yield selectNode("body", inspector);
({valueSpan} = getRuleViewProperty(view, "body", "transform"));
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nodeFront.tagName, "BODY",
"The right NodeFront is passed to the highlighter (2)");
@ -97,7 +97,7 @@ add_task(function* () {
"non-transform property");
({valueSpan} = getRuleViewProperty(view, "body", "color"));
onHighlighterHidden = hs.once("highlighter-hidden");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterHidden;
ok(!HighlighterFront.isShown, "The highlighter is hidden");
});

View File

@ -36,7 +36,7 @@ add_task(function* () {
info("Faking a mousemove on the overriden property");
let {valueSpan} = getRuleViewProperty(view, "div", "transform");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
ok(!hs.highlighters[TYPE],
"No highlighter was created for the overriden property");
@ -48,13 +48,13 @@ add_task(function* () {
info("Faking a mousemove on the disabled property");
({valueSpan} = getRuleViewProperty(view, ".test", "transform"));
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
ok(!hs.highlighters[TYPE],
"No highlighter was created for the disabled property");
info("Faking a mousemove on the now unoverriden property");
({valueSpan} = getRuleViewProperty(view, "div", "transform"));
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
hs.onMouseMove({target: valueSpan});
yield onHighlighterShown;
});

View File

@ -7,9 +7,25 @@
# The Layout Inspector may need to be enabled in about:config by setting
# devtools.layoutview.enabled to true.
# LOCALIZATION NOTE (layout.displayNumbersOnLines): Label of the display numbers on lines
# setting option in the CSS Grid pane.
layout.displayNumbersOnLines=Display numbers on lines
# LOCALIZATION NOTE (layout.extendGridLinesInfinitely): Label of the extend grid lines
# infinitely setting option in the CSS Grid pane.
layout.extendGridLinesInfinitely=Extend grid lines infinitely
# LOCALIZATION NOTE (layout.header): The accordion header for the CSS Grid pane.
layout.header=Grid
# LOCALIZATION NOTE (layout.gridDisplaySettings): The header for the grid display
# settings container in the CSS Grid pane.
layout.gridDisplaySettings=Grid Display Settings
# LOCALIZATION NOTE (layout.noGrids): In the case where there are no CSS grid
# containers to display.
layout.noGrids=No grids
# LOCALIZATION NOTE (layout.overlayMultipleGrids): The header for the list of grid
# container elements that can be highlighted in the CSS Grid pane.
layout.overlayMultipleGrids=Overlay Multiple Grids

View File

@ -71,6 +71,7 @@ pref("devtools.fontinspector.enabled", true);
pref("devtools.layoutview.enabled", false);
// Grid highlighter preferences
pref("devtools.gridinspector.showGridLineNumbers", false);
pref("devtools.gridinspector.showInfiniteLines", false);
// By how many times eyedropper will magnify pixels

View File

@ -95,20 +95,18 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
this.spectrum.updateUI();
}
let {target} = this.inspector;
target.actorHasMethod("inspector", "pickColorFromPage").then(value => {
let tooltipDoc = this.tooltip.doc;
let eyeButton = tooltipDoc.querySelector("#eyedropper-button");
if (value && this.inspector.selection.nodeFront.isInHTMLDocument) {
eyeButton.disabled = false;
eyeButton.removeAttribute("title");
eyeButton.addEventListener("click", this._openEyeDropper);
} else {
eyeButton.disabled = true;
eyeButton.title = L10N.getStr("eyedropper.disabled.title");
}
this.emit("ready");
}, e => console.error(e));
let tooltipDoc = this.tooltip.doc;
let eyeButton = tooltipDoc.querySelector("#eyedropper-button");
let canShowEyeDropper = yield this.inspector.supportsEyeDropper();
if (canShowEyeDropper) {
eyeButton.disabled = false;
eyeButton.removeAttribute("title");
eyeButton.addEventListener("click", this._openEyeDropper);
} else {
eyeButton.disabled = true;
eyeButton.title = L10N.getStr("eyedropper.disabled.title");
}
this.emit("ready");
}),
_onSpectrumColorChange: function (event, rgba, cssColor) {

View File

@ -5,8 +5,48 @@
#layout-container {
height: 100%;
width: 100%;
overflow: auto;
}
/**
* Common styles for shared components
*/
.grid-container {
display: flex;
flex-direction: column;
flex: 1;
align-items: center;
}
.grid-container > span {
font-weight: bold;
margin-bottom: 3px;
}
.grid-container > ul {
list-style: none;
margin: 0;
padding: 0;
}
.grid-container li {
padding: 4px 0;
}
/**
* Grid Container
*/
#layout-grid-container {
display: flex;
margin: 5px;
}
/**
* Container when no grids are present
*/
.layout-no-grids {
font-style: italic;
text-align: center;

View File

@ -8917,6 +8917,8 @@ nsDocShell::RestoreFromHistory()
nsSubDocumentFrame* subDocFrame =
do_QueryFrame(container->GetPrimaryFrame());
rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
} else {
rootViewParent = nullptr;
}
if (sibling &&
sibling->GetShell() &&

View File

@ -146,7 +146,8 @@ TabGroup::GetTopLevelWindows()
nsTArray<nsPIDOMWindowOuter*> array;
for (nsPIDOMWindowOuter* outerWindow : mWindows) {
if (!outerWindow->GetScriptableParentOrNull()) {
if (outerWindow->GetDocShell() &&
!outerWindow->GetScriptableParentOrNull()) {
array.AppendElement(outerWindow);
}
}

View File

@ -53,6 +53,11 @@ DOMInterfaces = {
'concrete': False
},
'AddonManagerPermissions': {
'wrapperCache': False,
'concrete': False
},
'AnimationEffectReadOnly': {
'concrete': False
},

View File

@ -91,6 +91,7 @@ void AudioInputCubeb::UpdateDeviceList()
// new device, add to the array
mDeviceIndexes->AppendElement(i);
mDeviceNames->AppendElement(devices->device[i]->device_id);
j = mDeviceIndexes->Length()-1;
}
if (devices->device[i]->preferred & CUBEB_DEVICE_PREF_VOICE) {
// There can be only one... we hope

View File

@ -83,3 +83,9 @@ interface AddonManager : EventTarget {
[ChromeOnly]
void eventListenerWasRemoved(DOMString type);
};
[ChromeOnly,Exposed=System,HeaderFile="mozilla/AddonManagerWebAPI.h"]
interface AddonManagerPermissions {
static boolean isHostPermitted(DOMString host);
};

View File

@ -144,7 +144,7 @@ ScaledFontDWrite::GetSkTypeface()
return nullptr;
}
mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle);
mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle, mForceGDIMode);
}
return mTypeface;
}

View File

@ -52,7 +52,8 @@ struct IDWriteFontFallback;
*/
SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
IDWriteFontFace* aFontFace,
SkFontStyle aStyle);
SkFontStyle aStyle,
bool aForceGDI);
SK_API SkFontMgr* SkFontMgr_New_GDI();
SK_API SkFontMgr* SkFontMgr_New_DirectWrite(IDWriteFactory* factory = NULL,

View File

@ -127,7 +127,14 @@ Interval::Interval(const Sk4f& c0, SkScalar p0,
, fZeroRamp((c0 == c1).allTrue()) {
SkASSERT(p0 != p1);
const Sk4f dc = (c1 - c0) / (p1 - p0);
// Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
SkASSERT(SkScalarIsFinite(p0) || SkScalarIsFinite(p1));
const auto dp = p1 - p0;
// Clamp edge intervals are always zero-ramp.
SkASSERT(SkScalarIsFinite(dp) || fZeroRamp);
const Sk4f dc = SkScalarIsFinite(dp) ? (c1 - c0) / dp : 0;
c0.store(&fC0.fVec);
dc.store(&fDc.fVec);
@ -223,7 +230,7 @@ GradientShaderBase4fContext::buildIntervals(const SkGradientShaderBase& shader,
// synthetic edge interval: -/+inf .. P0
const Sk4f clamp_color = pack_color(shader.fOrigColors[first_index],
fColorsArePremul, componentScale);
const SkScalar clamp_pos = reverse ? SK_ScalarMax : SK_ScalarMin;
const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
fIntervals.emplace_back(clamp_color, clamp_pos,
clamp_color, first_pos);
} else if (shader.fTileMode == SkShader::kMirror_TileMode && reverse) {
@ -248,7 +255,7 @@ GradientShaderBase4fContext::buildIntervals(const SkGradientShaderBase& shader,
// synthetic edge interval: Pn .. +/-inf
const Sk4f clamp_color = pack_color(shader.fOrigColors[last_index],
fColorsArePremul, componentScale);
const SkScalar clamp_pos = reverse ? SK_ScalarMin : SK_ScalarMax;
const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
fIntervals.emplace_back(clamp_color, last_pos,
clamp_color, clamp_pos);
} else if (shader.fTileMode == SkShader::kMirror_TileMode && !reverse) {

View File

@ -322,11 +322,16 @@ public:
private:
void compute_interval_props(SkScalar t) {
const Sk4f dC = DstTraits<dstType>::load(fInterval->fDc);
fCc = DstTraits<dstType>::load(fInterval->fC0);
fCc = fCc + dC * Sk4f(t);
fDcDx = dC * fDx;
fZeroRamp = fIsVertical || fInterval->isZeroRamp();
fCc = DstTraits<dstType>::load(fInterval->fC0);
if (fInterval->isZeroRamp()) {
fDcDx = 0;
} else {
const Sk4f dC = DstTraits<dstType>::load(fInterval->fDc);
fCc = fCc + dC * Sk4f(t);
fDcDx = dC * fDx;
}
}
const Interval* next_interval(const Interval* i) const {

View File

@ -335,9 +335,10 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
IDWriteFontFace* aFontFace,
SkFontStyle aStyle)
SkFontStyle aStyle,
bool aForceGDI)
{
return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle);
return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle, aForceGDI);
}
/**

View File

@ -288,7 +288,7 @@ SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
// If we can use a bitmap, use gdi classic rendering and measurement.
// This will not always provide a bitmap, but matches expected behavior.
} else if (treatLikeBitmap && axisAlignedBitmap) {
} else if ((treatLikeBitmap && axisAlignedBitmap) || typeface->ForceGDI()) {
fTextSizeRender = gdiTextSize;
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;

View File

@ -53,6 +53,7 @@ private:
, fDWriteFontFamily(SkSafeRefComPtr(fontFamily))
, fDWriteFont(SkSafeRefComPtr(font))
, fDWriteFontFace(SkRefComPtr(fontFace))
, fForceGDI(false)
{
#if SK_HAS_DWRITE_1_H
if (!SUCCEEDED(fDWriteFontFace->QueryInterface(&fDWriteFontFace1))) {
@ -76,10 +77,14 @@ public:
static DWriteFontTypeface* Create(IDWriteFactory* factory,
IDWriteFontFace* fontFace,
SkFontStyle aStyle) {
return new DWriteFontTypeface(aStyle, factory, fontFace,
nullptr, nullptr,
nullptr, nullptr);
SkFontStyle aStyle,
bool aForceGDI) {
DWriteFontTypeface* typeface =
new DWriteFontTypeface(aStyle, factory, fontFace,
nullptr, nullptr,
nullptr, nullptr);
typeface->fForceGDI = aForceGDI;
return typeface;
}
static DWriteFontTypeface* Create(IDWriteFactory* factory,
@ -92,6 +97,8 @@ public:
fontFileLoader, fontCollectionLoader);
}
bool ForceGDI() { return fForceGDI; }
protected:
void weak_dispose() const override {
if (fDWriteFontCollectionLoader.get()) {
@ -124,6 +131,7 @@ protected:
private:
typedef SkTypeface INHERITED;
bool fForceGDI;
};
#endif

View File

@ -11,6 +11,7 @@
#include "nsNetUtil.h"
#include "mozilla/HashFunctions.h"
#include "nsHashKeys.h"
#include "nsProxyRelease.h"
namespace mozilla {
namespace image {
@ -33,6 +34,7 @@ class ImageURL
{
public:
explicit ImageURL(nsIURI* aURI, nsresult& aRv)
: mURI(new nsMainThreadPtrHolder<nsIURI>(aURI))
{
MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
@ -97,10 +99,7 @@ public:
already_AddRefed<nsIURI> ToIURI()
{
MOZ_ASSERT(NS_IsMainThread(),
"Convert to nsIURI on main thread only; it is not threadsafe.");
nsCOMPtr<nsIURI> newURI;
NS_NewURI(getter_AddRefs(newURI), mSpec);
nsCOMPtr<nsIURI> newURI = mURI.get();
return newURI.forget();
}
@ -133,6 +132,8 @@ private:
return HashString(mSpec);
}
nsMainThreadPtrHandle<nsIURI> mURI;
// Since this is a basic storage class, no duplication of spec parsing is
// included in the functionality. Instead, the class depends upon the
// parsing implementation in the nsIURI class used in object construction.

View File

@ -60,7 +60,7 @@ namespace oom {
enum ThreadType {
THREAD_TYPE_NONE = 0, // 0
THREAD_TYPE_MAIN, // 1
THREAD_TYPE_ASMJS, // 2
THREAD_TYPE_WASM, // 2
THREAD_TYPE_ION, // 3
THREAD_TYPE_PARSE, // 4
THREAD_TYPE_COMPRESS, // 5

View File

@ -6,6 +6,8 @@
#include "jit/MacroAssembler-inl.h"
#include "mozilla/CheckedInt.h"
#include "jsfriendapi.h"
#include "jsprf.h"
@ -34,6 +36,8 @@ using namespace js::jit;
using JS::GenericNaN;
using JS::ToInt32;
using mozilla::CheckedUint32;
template <typename Source> void
MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
Register scratch, Label* miss)
@ -1060,6 +1064,9 @@ JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
MOZ_CRASH("Unsupported TypedArray type");
}
if (!(CheckedUint32(nbytes) + sizeof(Value)).isValid())
return;
nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
Nursery& nursery = cx->runtime()->gc.nursery;
void* buf = nursery.allocateBuffer(obj, nbytes);

View File

@ -424,7 +424,7 @@ MacroAssembler::subFromStackPtr(Imm32 imm32)
// On windows, we cannot skip very far down the stack without touching the
// memory pages in-between. This is a corner-case code for situations where the
// Ion frame data for a piece of code is very large. To handle this special case,
// for frames over 1k in size we allocate memory on the stack incrementally, touching
// for frames over 4k in size we allocate memory on the stack incrementally, touching
// it as we go.
//
// When the amount is quite large, which it can be, we emit an actual loop, in order

View File

@ -331,7 +331,7 @@ MacroAssembler::subFromStackPtr(Imm32 imm32)
// On windows, we cannot skip very far down the stack without touching the
// memory pages in-between. This is a corner-case code for situations where the
// Ion frame data for a piece of code is very large. To handle this special case,
// for frames over 1k in size we allocate memory on the stack incrementally, touching
// for frames over 4k in size we allocate memory on the stack incrementally, touching
// it as we go.
//
// When the amount is quite large, which it can be, we emit an actual loop, in order

View File

@ -329,7 +329,10 @@ JSObject*
Wrapper::wrappedObject(JSObject* wrapper)
{
MOZ_ASSERT(wrapper->is<WrapperObject>());
return wrapper->as<ProxyObject>().target();
JSObject* target = wrapper->as<ProxyObject>().target();
if (target)
JS::ExposeObjectToActiveJS(target);
return target;
}
JS_FRIEND_API(JSObject*)

View File

@ -20,7 +20,6 @@
#include "vm/SharedImmutableStringsCache.h"
#include "vm/Time.h"
#include "vm/TraceLogging.h"
#include "wasm/WasmIonCompile.h"
#include "jscntxtinlines.h"
#include "jscompartmentinlines.h"
@ -84,7 +83,7 @@ js::SetFakeCPUCount(size_t count)
}
bool
js::StartOffThreadWasmCompile(wasm::IonCompileTask* task)
js::StartOffThreadWasmCompile(wasm::CompileTask* task)
{
AutoLockHelperThreadState lock;
@ -867,7 +866,7 @@ GlobalHelperThreadState::maxUnpausedIonCompilationThreads() const
size_t
GlobalHelperThreadState::maxWasmCompilationThreads() const
{
if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_ASMJS))
if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_WASM))
return 1;
if (cpuCount < 2)
return 2;
@ -920,7 +919,7 @@ GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lo
// Honor the maximum allowed threads to compile wasm jobs at once,
// to avoid oversaturating the machine.
if (!checkTaskThreadLimit<wasm::IonCompileTask*>(maxWasmCompilationThreads()))
if (!checkTaskThreadLimit<wasm::CompileTask*>(maxWasmCompilationThreads()))
return false;
return true;
@ -1419,7 +1418,7 @@ HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked)
currentTask.emplace(HelperThreadState().wasmWorklist(locked).popCopy());
bool success = false;
wasm::IonCompileTask* task = wasmTask();
wasm::CompileTask* task = wasmTask();
{
AutoUnlockHelperThreadState unlock(locked);
success = wasm::CompileFunction(task);
@ -1872,7 +1871,7 @@ HelperThread::threadLoop()
js::oom::SetThreadType(js::oom::THREAD_TYPE_ION);
handleIonWorkload(lock);
} else if (HelperThreadState().canStartWasmCompile(lock)) {
js::oom::SetThreadType(js::oom::THREAD_TYPE_ASMJS);
js::oom::SetThreadType(js::oom::THREAD_TYPE_WASM);
handleWasmWorkload(lock);
} else if (HelperThreadState().canStartPromiseTask(lock)) {
js::oom::SetThreadType(js::oom::THREAD_TYPE_PROMISE_TASK);

View File

@ -13,6 +13,7 @@
#ifndef vm_HelperThreads_h
#define vm_HelperThreads_h
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/PodOperations.h"
#include "mozilla/TimeStamp.h"
@ -42,8 +43,8 @@ namespace jit {
namespace wasm {
class FuncIR;
class FunctionCompileResults;
class IonCompileTask;
typedef Vector<IonCompileTask*, 0, SystemAllocPolicy> IonCompileTaskPtrVector;
class CompileTask;
typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
} // namespace wasm
enum class ParseTaskKind
@ -84,7 +85,7 @@ class GlobalHelperThreadState
IonBuilderVector ionWorklist_, ionFinishedList_;
// wasm worklist and finished jobs.
wasm::IonCompileTaskPtrVector wasmWorklist_, wasmFinishedList_;
wasm::CompileTaskPtrVector wasmWorklist_, wasmFinishedList_;
public:
// For now, only allow a single parallel wasm compilation to happen at a
@ -163,10 +164,10 @@ class GlobalHelperThreadState
return ionFinishedList_;
}
wasm::IonCompileTaskPtrVector& wasmWorklist(const AutoLockHelperThreadState&) {
wasm::CompileTaskPtrVector& wasmWorklist(const AutoLockHelperThreadState&) {
return wasmWorklist_;
}
wasm::IonCompileTaskPtrVector& wasmFinishedList(const AutoLockHelperThreadState&) {
wasm::CompileTaskPtrVector& wasmFinishedList(const AutoLockHelperThreadState&) {
return wasmFinishedList_;
}
@ -308,7 +309,7 @@ struct HelperThread
/* The current task being executed by this thread, if any. */
mozilla::Maybe<mozilla::Variant<jit::IonBuilder*,
wasm::IonCompileTask*,
wasm::CompileTask*,
PromiseTask*,
ParseTask*,
SourceCompressionTask*,
@ -325,8 +326,8 @@ struct HelperThread
}
/* Any wasm data currently being optimized on this thread. */
wasm::IonCompileTask* wasmTask() {
return maybeCurrentTaskAs<wasm::IonCompileTask*>();
wasm::CompileTask* wasmTask() {
return maybeCurrentTaskAs<wasm::CompileTask*>();
}
/* Any source being parsed/emitted on this thread. */
@ -395,9 +396,17 @@ SetFakeCPUCount(size_t count);
void
PauseCurrentHelperThread();
/* Perform MIR optimization and LIR generation on a single function. */
// Enqueues a wasm compilation task.
bool
StartOffThreadWasmCompile(wasm::IonCompileTask* task);
StartOffThreadWasmCompile(wasm::CompileTask* task);
namespace wasm {
// Performs MIR optimization and LIR generation on one or several functions.
MOZ_MUST_USE bool
CompileFunction(CompileTask* task);
}
/*
* If helper threads are available, start executing the given PromiseTask on a

View File

@ -556,38 +556,32 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
return nullptr;
}
if (proto.isObject()) {
RootedObject obj(cx, proto.toObject());
if (associated) {
if (associated->is<JSFunction>()) {
if (!TypeNewScript::make(cx->asJSContext(), group, &associated->as<JSFunction>()))
return nullptr;
} else {
group->setTypeDescr(&associated->as<TypeDescr>());
}
if (associated) {
if (associated->is<JSFunction>()) {
if (!TypeNewScript::make(cx->asJSContext(), group, &associated->as<JSFunction>()))
return nullptr;
} else {
group->setTypeDescr(&associated->as<TypeDescr>());
}
}
/*
* Some builtin objects have slotful native properties baked in at
* creation via the Shape::{insert,get}initialShape mechanism. Since
* these properties are never explicitly defined on new objects, update
* the type information for them here.
*/
/*
* Some builtin objects have slotful native properties baked in at
* creation via the Shape::{insert,get}initialShape mechanism. Since
* these properties are never explicitly defined on new objects, update
* the type information for them here.
*/
const JSAtomState& names = cx->names();
const JSAtomState& names = cx->names();
if (StandardProtoKeyOrNull(obj) == JSProto_RegExp)
AddTypePropertyId(cx, group, nullptr, NameToId(names.lastIndex), TypeSet::Int32Type());
if (obj->is<StringObject>())
AddTypePropertyId(cx, group, nullptr, NameToId(names.length), TypeSet::Int32Type());
if (IsErrorProtoKey(StandardProtoKeyOrNull(obj))) {
AddTypePropertyId(cx, group, nullptr, NameToId(names.fileName), TypeSet::StringType());
AddTypePropertyId(cx, group, nullptr, NameToId(names.lineNumber), TypeSet::Int32Type());
AddTypePropertyId(cx, group, nullptr, NameToId(names.columnNumber), TypeSet::Int32Type());
}
if (clasp == &RegExpObject::class_) {
AddTypePropertyId(cx, group, nullptr, NameToId(names.lastIndex), TypeSet::Int32Type());
} else if (clasp == &StringObject::class_) {
AddTypePropertyId(cx, group, nullptr, NameToId(names.length), TypeSet::Int32Type());
} else if (ErrorObject::isErrorClass(clasp)) {
AddTypePropertyId(cx, group, nullptr, NameToId(names.fileName), TypeSet::StringType());
AddTypePropertyId(cx, group, nullptr, NameToId(names.lineNumber), TypeSet::Int32Type());
AddTypePropertyId(cx, group, nullptr, NameToId(names.columnNumber), TypeSet::Int32Type());
}
return group;

View File

@ -7602,9 +7602,9 @@ js::wasm::BaselineCanCompile(const FunctionGenerator* fg)
}
bool
js::wasm::BaselineCompileFunction(IonCompileTask* task)
js::wasm::BaselineCompileFunction(CompileTask* task)
{
MOZ_ASSERT(task->mode() == IonCompileTask::CompileMode::Baseline);
MOZ_ASSERT(task->mode() == CompileTask::CompileMode::Baseline);
const FuncBytes& func = task->func();
FuncCompileResults& results = task->results();

View File

@ -19,12 +19,11 @@
#ifndef asmjs_wasm_baseline_compile_h
#define asmjs_wasm_baseline_compile_h
#include "wasm/WasmIonCompile.h"
namespace js {
namespace wasm {
class FunctionGenerator;
class CompileTask;
// Return true if BaselineCompileFunction can generate code for the
// function held in the FunctionGenerator. If false is returned a
@ -40,7 +39,7 @@ BaselineCanCompile(const FunctionGenerator* fg);
// Generate adequate code quickly.
bool
BaselineCompileFunction(IonCompileTask* task);
BaselineCompileFunction(CompileTask* task);
} // namespace wasm
} // namespace js

View File

@ -72,7 +72,7 @@ DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
return false;
if (sectionStart == Decoder::NotStarted) {
if (mg.numFuncDefs() != 0)
if (mg.env().numFuncDefs() != 0)
return d.fail("expected function bodies");
return mg.finishFuncDefs();
@ -82,11 +82,11 @@ DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
if (!d.readVarU32(&numFuncDefs))
return d.fail("expected function body count");
if (numFuncDefs != mg.numFuncDefs())
if (numFuncDefs != mg.env().numFuncDefs())
return d.fail("function body count does not match function signature count");
for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
if (!DecodeFunctionBody(d, mg, mg.numFuncImports() + funcDefIndex))
if (!DecodeFunctionBody(d, mg, mg.env().numFuncImports() + funcDefIndex))
return false;
}

View File

@ -70,12 +70,12 @@ ModuleGenerator::~ModuleGenerator()
if (outstanding_) {
AutoLockHelperThreadState lock;
while (true) {
IonCompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock);
CompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock);
MOZ_ASSERT(outstanding_ >= worklist.length());
outstanding_ -= worklist.length();
worklist.clear();
IonCompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock);
CompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock);
MOZ_ASSERT(outstanding_ >= finished.length());
outstanding_ -= finished.length();
finished.clear();
@ -224,7 +224,7 @@ ModuleGenerator::finishOutstandingTask()
{
MOZ_ASSERT(parallel_);
IonCompileTask* task = nullptr;
CompileTask* task = nullptr;
{
AutoLockHelperThreadState lock;
while (true) {
@ -385,7 +385,7 @@ ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits)
}
bool
ModuleGenerator::finishTask(IonCompileTask* task)
ModuleGenerator::finishTask(CompileTask* task)
{
const FuncBytes& func = task->func();
FuncCompileResults& results = task->results();
@ -419,8 +419,10 @@ ModuleGenerator::finishTask(IonCompileTask* task)
return false;
MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
UniqueBytes recycled;
task->reset(&recycled);
freeTasks_.infallibleAppend(task);
return true;
return freeBytes_.emplaceBack(Move(recycled));
}
bool
@ -802,15 +804,6 @@ ModuleGenerator::numFuncImports() const
return metadata_->funcImports.length();
}
uint32_t
ModuleGenerator::numFuncDefs() const
{
// asm.js overallocates the length of funcSigs and in general does not know
// the number of function definitions until it's done compiling.
MOZ_ASSERT(!isAsmJS());
return env_->funcSigs.length() - numFuncImports();
}
const SigWithId&
ModuleGenerator::funcSig(uint32_t funcIndex) const
{
@ -864,6 +857,15 @@ ModuleGenerator::startFuncDefs()
for (size_t i = 0; i < numTasks; i++)
freeTasks_.infallibleAppend(&tasks_[i]);
if (!freeBytes_.reserve(numTasks))
return false;
for (size_t i = 0; i < numTasks; i++) {
auto bytes = js::MakeUnique<Bytes>();
if (!bytes)
return false;
freeBytes_.infallibleAppend(Move(bytes));
}
startedFuncDefs_ = true;
MOZ_ASSERT(!finishedFuncDefs_);
return true;
@ -876,16 +878,17 @@ ModuleGenerator::startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg)
MOZ_ASSERT(!activeFuncDef_);
MOZ_ASSERT(!finishedFuncDefs_);
if (freeTasks_.empty() && !finishOutstandingTask())
return false;
if (!freeBytes_.empty()) {
fg->bytes_ = Move(freeBytes_.back());
freeBytes_.popBack();
} else {
fg->bytes_ = js::MakeUnique<Bytes>();
if (!fg->bytes_)
return false;
}
IonCompileTask* task = freeTasks_.popCopy();
task->reset(&fg->bytes_);
fg->bytes_.clear();
fg->lineOrBytecode_ = lineOrBytecode;
fg->m_ = this;
fg->task_ = task;
activeFuncDef_ = fg;
return true;
}
@ -904,24 +907,27 @@ ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
return false;
auto mode = alwaysBaseline_ && BaselineCanCompile(fg)
? IonCompileTask::CompileMode::Baseline
: IonCompileTask::CompileMode::Ion;
? CompileTask::CompileMode::Baseline
: CompileTask::CompileMode::Ion;
fg->task_->init(Move(func), mode);
if (freeTasks_.empty() && !finishOutstandingTask())
return false;
CompileTask* task = freeTasks_.popCopy();
task->init(Move(func), mode);
if (parallel_) {
if (!StartOffThreadWasmCompile(fg->task_))
if (!StartOffThreadWasmCompile(task))
return false;
outstanding_++;
} else {
if (!CompileFunction(fg->task_))
if (!CompileFunction(task))
return false;
if (!finishTask(fg->task_))
if (!finishTask(task))
return false;
}
fg->m_ = nullptr;
fg->task_ = nullptr;
activeFuncDef_ = nullptr;
numFinishedFuncDefs_++;
return true;
@ -975,7 +981,7 @@ ModuleGenerator::finishFuncDefs()
for (uint32_t i = AsmJSFirstDefFuncIndex; i < numFinishedFuncDefs_; i++)
MOZ_ASSERT(funcCodeRange(i).funcIndex() == i);
} else {
MOZ_ASSERT(numFinishedFuncDefs_ == numFuncDefs());
MOZ_ASSERT(numFinishedFuncDefs_ == env_->numFuncDefs());
for (uint32_t i = 0; i < env_->numFuncs(); i++)
MOZ_ASSERT(funcCodeRange(i).funcIndex() == i);
}
@ -1127,3 +1133,21 @@ ModuleGenerator::finish(const ShareableBytes& bytecode, DataSegmentVector&& data
*metadata_,
bytecode));
}
bool
wasm::CompileFunction(CompileTask* task)
{
TraceLoggerThread* logger = TraceLoggerForCurrentThread();
AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
switch (task->mode()) {
case wasm::CompileTask::CompileMode::Ion:
return wasm::IonCompileFunction(task);
case wasm::CompileTask::CompileMode::Baseline:
return wasm::BaselineCompileFunction(task);
case wasm::CompileTask::CompileMode::None:
break;
}
MOZ_CRASH("Uninitialized task");
}

View File

@ -26,10 +26,137 @@
namespace js {
namespace wasm {
struct ModuleEnvironment;
typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
struct CompileArgs;
class FunctionGenerator;
typedef Vector<UniqueBytes, 0, SystemAllocPolicy> UniqueBytesVector;
// The FuncBytes class represents a single, concurrently-compilable function.
// A FuncBytes object is composed of the wasm function body bytes along with the
// ambient metadata describing the function necessary to compile it.
class FuncBytes
{
UniqueBytes bytes_;
uint32_t index_;
const SigWithId& sig_;
uint32_t lineOrBytecode_;
Uint32Vector callSiteLineNums_;
public:
FuncBytes(UniqueBytes bytes,
uint32_t index,
const SigWithId& sig,
uint32_t lineOrBytecode,
Uint32Vector&& callSiteLineNums)
: bytes_(Move(bytes)),
index_(index),
sig_(sig),
lineOrBytecode_(lineOrBytecode),
callSiteLineNums_(Move(callSiteLineNums))
{}
Bytes& bytes() { return *bytes_; }
const Bytes& bytes() const { return *bytes_; }
UniqueBytes recycle() { return Move(bytes_); }
uint32_t index() const { return index_; }
const SigWithId& sig() const { return sig_; }
uint32_t lineOrBytecode() const { return lineOrBytecode_; }
const Uint32Vector& callSiteLineNums() const { return callSiteLineNums_; }
};
typedef UniquePtr<FuncBytes> UniqueFuncBytes;
// The FuncCompileResults class contains the results of compiling a single
// function body, ready to be merged into the whole-module MacroAssembler.
class FuncCompileResults
{
jit::TempAllocator alloc_;
jit::MacroAssembler masm_;
FuncOffsets offsets_;
FuncCompileResults(const FuncCompileResults&) = delete;
FuncCompileResults& operator=(const FuncCompileResults&) = delete;
public:
explicit FuncCompileResults(LifoAlloc& lifo)
: alloc_(&lifo),
masm_(jit::MacroAssembler::WasmToken(), alloc_)
{}
jit::TempAllocator& alloc() { return alloc_; }
jit::MacroAssembler& masm() { return masm_; }
FuncOffsets& offsets() { return offsets_; }
};
// A CompileTask represents the task of compiling a single function body. An
// CompileTask is filled with the wasm code to be compiled on the main
// validation thread, sent off to a compilation helper thread which creates
// the FuncCompileResults, and finally sent back to the validation thread. To
// save time allocating and freeing memory, CompileTasks are reset() and
// reused.
class CompileTask
{
public:
enum class CompileMode { None, Baseline, Ion };
private:
const ModuleEnvironment& env_;
LifoAlloc lifo_;
UniqueFuncBytes func_;
CompileMode mode_;
Maybe<FuncCompileResults> results_;
CompileTask(const CompileTask&) = delete;
CompileTask& operator=(const CompileTask&) = delete;
public:
CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize)
: env_(env), lifo_(defaultChunkSize), func_(nullptr), mode_(CompileMode::None)
{}
LifoAlloc& lifo() {
return lifo_;
}
const ModuleEnvironment& env() const {
return env_;
}
void init(UniqueFuncBytes func, CompileMode mode) {
MOZ_ASSERT(!func_);
func_ = Move(func);
results_.emplace(lifo_);
mode_ = mode;
}
CompileMode mode() const {
return mode_;
}
const FuncBytes& func() const {
MOZ_ASSERT(func_);
return *func_;
}
FuncCompileResults& results() {
return *results_;
}
void reset(UniqueBytes* recycled) {
if (func_) {
*recycled = Move(func_->recycle());
(*recycled)->clear();
}
func_.reset(nullptr);
results_.reset();
lifo_.releaseAll();
mode_ = CompileMode::None;
}
};
// A ModuleGenerator encapsulates the creation of a wasm module. During the
// lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
// and destroyed to compile the individual function bodies. After generating all
@ -39,8 +166,8 @@ class FunctionGenerator;
class MOZ_STACK_CLASS ModuleGenerator
{
typedef HashSet<uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> Uint32Set;
typedef Vector<IonCompileTask, 0, SystemAllocPolicy> IonCompileTaskVector;
typedef Vector<IonCompileTask*, 0, SystemAllocPolicy> IonCompileTaskPtrVector;
typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
typedef EnumeratedArray<Trap, Trap::Limit, ProfilingOffsets> TrapExitOffsetArray;
// Constant parameters
@ -67,8 +194,9 @@ class MOZ_STACK_CLASS ModuleGenerator
// Parallel compilation
bool parallel_;
uint32_t outstanding_;
IonCompileTaskVector tasks_;
IonCompileTaskPtrVector freeTasks_;
CompileTaskVector tasks_;
CompileTaskPtrVector freeTasks_;
UniqueBytesVector freeBytes_;
// Assertions
DebugOnly<FunctionGenerator*> activeFuncDef_;
@ -80,7 +208,7 @@ class MOZ_STACK_CLASS ModuleGenerator
const CodeRange& funcCodeRange(uint32_t funcIndex) const;
MOZ_MUST_USE bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr);
MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits);
MOZ_MUST_USE bool finishTask(IonCompileTask* task);
MOZ_MUST_USE bool finishTask(CompileTask* task);
MOZ_MUST_USE bool finishOutstandingTask();
MOZ_MUST_USE bool finishFuncExports();
MOZ_MUST_USE bool finishCodegen();
@ -92,6 +220,9 @@ class MOZ_STACK_CLASS ModuleGenerator
MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
MOZ_MUST_USE bool initWasm();
// Functions declarations:
uint32_t numFuncImports() const;
public:
explicit ModuleGenerator();
~ModuleGenerator();
@ -121,10 +252,6 @@ class MOZ_STACK_CLASS ModuleGenerator
// Globals:
const GlobalDescVector& globals() const { return env_->globals; }
// Functions declarations:
uint32_t numFuncImports() const;
uint32_t numFuncDefs() const;
// Function definitions:
MOZ_MUST_USE bool startFuncDefs();
MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
@ -160,20 +287,19 @@ class MOZ_STACK_CLASS FunctionGenerator
friend class ModuleGenerator;
ModuleGenerator* m_;
IonCompileTask* task_;
bool usesSimd_;
bool usesAtomics_;
// Data created during function generation, then handed over to the
// FuncBytes in ModuleGenerator::finishFunc().
Bytes bytes_;
UniqueBytes bytes_;
Uint32Vector callSiteLineNums_;
uint32_t lineOrBytecode_;
public:
FunctionGenerator()
: m_(nullptr), task_(nullptr), usesSimd_(false), usesAtomics_(false), lineOrBytecode_(0)
: m_(nullptr), usesSimd_(false), usesAtomics_(false), bytes_(nullptr), lineOrBytecode_(0)
{}
bool usesSimd() const {
@ -191,7 +317,7 @@ class MOZ_STACK_CLASS FunctionGenerator
}
Bytes& bytes() {
return bytes_;
return *bytes_;
}
MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) {
return callSiteLineNums_.append(lineno);

View File

@ -3697,9 +3697,9 @@ EmitExpr(FunctionCompiler& f)
}
bool
wasm::IonCompileFunction(IonCompileTask* task)
wasm::IonCompileFunction(CompileTask* task)
{
MOZ_ASSERT(task->mode() == IonCompileTask::CompileMode::Ion);
MOZ_ASSERT(task->mode() == CompileTask::CompileMode::Ion);
const FuncBytes& func = task->func();
FuncCompileResults& results = task->results();
@ -3779,21 +3779,3 @@ wasm::IonCompileFunction(IonCompileTask* task)
return true;
}
bool
wasm::CompileFunction(IonCompileTask* task)
{
TraceLoggerThread* logger = TraceLoggerForCurrentThread();
AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
switch (task->mode()) {
case wasm::IonCompileTask::CompileMode::Ion:
return wasm::IonCompileFunction(task);
case wasm::IonCompileTask::CompileMode::Baseline:
return wasm::BaselineCompileFunction(task);
case wasm::IonCompileTask::CompileMode::None:
break;
}
MOZ_CRASH("Uninitialized task");
}

View File

@ -19,139 +19,16 @@
#ifndef wasm_ion_compile_h
#define wasm_ion_compile_h
#include "jit/MacroAssembler.h"
#include "wasm/WasmTypes.h"
#include "mozilla/Attributes.h"
namespace js {
namespace wasm {
struct ModuleEnvironment;
typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
// The FuncBytes class represents a single, concurrently-compilable function.
// A FuncBytes object is composed of the wasm function body bytes along with the
// ambient metadata describing the function necessary to compile it.
class FuncBytes
{
Bytes bytes_;
uint32_t index_;
const SigWithId& sig_;
uint32_t lineOrBytecode_;
Uint32Vector callSiteLineNums_;
public:
FuncBytes(Bytes&& bytes,
uint32_t index,
const SigWithId& sig,
uint32_t lineOrBytecode,
Uint32Vector&& callSiteLineNums)
: bytes_(Move(bytes)),
index_(index),
sig_(sig),
lineOrBytecode_(lineOrBytecode),
callSiteLineNums_(Move(callSiteLineNums))
{}
Bytes& bytes() { return bytes_; }
const Bytes& bytes() const { return bytes_; }
uint32_t index() const { return index_; }
const SigWithId& sig() const { return sig_; }
uint32_t lineOrBytecode() const { return lineOrBytecode_; }
const Uint32Vector& callSiteLineNums() const { return callSiteLineNums_; }
};
typedef UniquePtr<FuncBytes> UniqueFuncBytes;
// The FuncCompileResults class contains the results of compiling a single
// function body, ready to be merged into the whole-module MacroAssembler.
class FuncCompileResults
{
jit::TempAllocator alloc_;
jit::MacroAssembler masm_;
FuncOffsets offsets_;
FuncCompileResults(const FuncCompileResults&) = delete;
FuncCompileResults& operator=(const FuncCompileResults&) = delete;
public:
explicit FuncCompileResults(LifoAlloc& lifo)
: alloc_(&lifo),
masm_(jit::MacroAssembler::WasmToken(), alloc_)
{}
jit::TempAllocator& alloc() { return alloc_; }
jit::MacroAssembler& masm() { return masm_; }
FuncOffsets& offsets() { return offsets_; }
};
// An IonCompileTask represents the task of compiling a single function body. An
// IonCompileTask is filled with the wasm code to be compiled on the main
// validation thread, sent off to an Ion compilation helper thread which creates
// the FuncCompileResults, and finally sent back to the validation thread. To
// save time allocating and freeing memory, IonCompileTasks are reset() and
// reused.
class IonCompileTask
{
public:
enum class CompileMode { None, Baseline, Ion };
private:
const ModuleEnvironment& env_;
LifoAlloc lifo_;
UniqueFuncBytes func_;
CompileMode mode_;
Maybe<FuncCompileResults> results_;
IonCompileTask(const IonCompileTask&) = delete;
IonCompileTask& operator=(const IonCompileTask&) = delete;
public:
IonCompileTask(const ModuleEnvironment& env, size_t defaultChunkSize)
: env_(env), lifo_(defaultChunkSize), func_(nullptr), mode_(CompileMode::None)
{}
LifoAlloc& lifo() {
return lifo_;
}
const ModuleEnvironment& env() const {
return env_;
}
void init(UniqueFuncBytes func, CompileMode mode) {
MOZ_ASSERT(!func_);
func_ = Move(func);
results_.emplace(lifo_);
mode_ = mode;
}
CompileMode mode() const {
return mode_;
}
const FuncBytes& func() const {
MOZ_ASSERT(func_);
return *func_;
}
FuncCompileResults& results() {
return *results_;
}
void reset(Bytes* recycled) {
if (func_)
*recycled = Move(func_->bytes());
func_.reset(nullptr);
results_.reset();
lifo_.releaseAll();
mode_ = CompileMode::None;
}
};
class CompileTask;
// Generates very fast code at the expense of compilation time.
MOZ_MUST_USE bool
IonCompileFunction(IonCompileTask* task);
bool
CompileFunction(IonCompileTask* task);
IonCompileFunction(CompileTask* task);
} // namespace wasm
} // namespace js

View File

@ -1683,14 +1683,14 @@ ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise
return promise->resolve(cx, resolutionValue);
}
struct CompileTask : PromiseTask
struct CompilePromiseTask : PromiseTask
{
MutableBytes bytecode;
CompileArgs compileArgs;
UniqueChars error;
SharedModule module;
CompileTask(JSContext* cx, Handle<PromiseObject*> promise)
CompilePromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
: PromiseTask(cx, promise)
{}
@ -1758,7 +1758,7 @@ WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp)
if (!promise)
return false;
auto task = cx->make_unique<CompileTask>(cx, promise);
auto task = cx->make_unique<CompilePromiseTask>(cx, promise);
if (!task)
return false;
@ -1806,12 +1806,12 @@ ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
return promise->resolve(cx, val);
}
struct InstantiateTask : CompileTask
struct InstantiatePromiseTask : CompilePromiseTask
{
PersistentRootedObject importObj;
InstantiateTask(JSContext* cx, Handle<PromiseObject*> promise, HandleObject importObj)
: CompileTask(cx, promise),
InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise, HandleObject importObj)
: CompilePromiseTask(cx, promise),
importObj(cx, importObj)
{}
@ -1878,7 +1878,7 @@ WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp)
if (!promise->resolve(cx, resolutionValue))
return false;
} else {
auto task = cx->make_unique<InstantiateTask>(cx, promise, importObj);
auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, importObj);
if (!task)
return false;

View File

@ -21,7 +21,7 @@
#include "mozilla/ArrayUtils.h"
#include "wasm/WasmCode.h"
#include "wasm/WasmIonCompile.h"
#include "wasm/WasmGenerator.h"
#include "jit/MacroAssembler-inl.h"

View File

@ -83,6 +83,7 @@ using mozilla::Unused;
typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytes;
typedef UniquePtr<Bytes> UniqueBytes;
typedef int8_t I8x16[16];
typedef int16_t I16x8[8];

View File

@ -53,6 +53,12 @@ SERVO_BINDING_FUNC(Servo_CssRules_ListTypes, void,
nsTArrayBorrowed_uintptr_t result)
SERVO_BINDING_FUNC(Servo_CssRules_GetStyleRuleAt, RawServoStyleRuleStrong,
ServoCssRulesBorrowed rules, uint32_t index)
SERVO_BINDING_FUNC(Servo_CssRules_InsertRule, nsresult,
ServoCssRulesBorrowed rules,
RawServoStyleSheetBorrowed sheet, const nsACString* rule,
uint32_t index, bool nested, uint16_t* rule_type)
SERVO_BINDING_FUNC(Servo_CssRules_DeleteRule, nsresult,
ServoCssRulesBorrowed rules, uint32_t index)
// CSS Rules
SERVO_BINDING_FUNC(Servo_StyleRule_Debug, void,

View File

@ -24,14 +24,9 @@ ServoCSSRuleList::ServoCSSRuleList(ServoStyleSheet* aStyleSheet,
// stylesheet goes away.
}
nsIDOMCSSRule*
ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
css::Rule*
ServoCSSRuleList::GetRule(uint32_t aIndex)
{
if (aIndex >= mRules.Length()) {
aFound = false;
return nullptr;
}
aFound = true;
uintptr_t rule = mRules[aIndex];
if (rule <= kMaxRuleType) {
RefPtr<css::Rule> ruleObj = nullptr;
@ -54,7 +49,21 @@ ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
rule = CastToUint(ruleObj.forget().take());
mRules[aIndex] = rule;
}
return CastToPtr(rule)->GetDOMRule();
return CastToPtr(rule);
}
nsIDOMCSSRule*
ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
{
if (aIndex >= mRules.Length()) {
aFound = false;
return nullptr;
}
aFound = true;
if (css::Rule* rule = GetRule(aIndex)) {
return rule->GetDOMRule();
}
return nullptr;
}
template<typename Func>
@ -77,6 +86,32 @@ ServoCSSRuleList::DropReference()
});
}
nsresult
ServoCSSRuleList::InsertRule(const nsAString& aRule, uint32_t aIndex)
{
NS_ConvertUTF16toUTF8 rule(aRule);
// XXX This needs to actually reflect whether it is nested when we
// support using CSSRuleList in CSSGroupingRules.
bool nested = false;
uint16_t type;
nsresult rv = Servo_CssRules_InsertRule(mRawRules, mStyleSheet->RawSheet(),
&rule, aIndex, nested, &type);
if (!NS_FAILED(rv)) {
mRules.InsertElementAt(type);
}
return rv;
}
nsresult
ServoCSSRuleList::DeleteRule(uint32_t aIndex)
{
nsresult rv = Servo_CssRules_DeleteRule(mRawRules, aIndex);
if (!NS_FAILED(rv)) {
mRules.RemoveElementAt(aIndex);
}
return rv;
}
ServoCSSRuleList::~ServoCSSRuleList()
{
EnumerateInstantiatedRules([](css::Rule* rule) { rule->Release(); });

View File

@ -32,6 +32,10 @@ public:
void DropReference();
css::Rule* GetRule(uint32_t aIndex);
nsresult InsertRule(const nsAString& aRule, uint32_t aIndex);
nsresult DeleteRule(uint32_t aIndex);
private:
virtual ~ServoCSSRuleList();

View File

@ -10,6 +10,8 @@
#include "mozilla/ServoCSSRuleList.h"
#include "mozilla/dom/CSSRuleList.h"
#include "mozAutoDocUpdate.h"
namespace mozilla {
ServoStyleSheet::ServoStyleSheet(css::SheetParsingMode aParsingMode,
@ -139,14 +141,45 @@ uint32_t
ServoStyleSheet::InsertRuleInternal(const nsAString& aRule,
uint32_t aIndex, ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return 0;
// Ensure mRuleList is constructed.
GetCssRulesInternal(aRv);
mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
aRv = mRuleList->InsertRule(aRule, aIndex);
if (aRv.Failed()) {
return 0;
}
if (mDocument) {
// XXX When we support @import rules, we should not notify here,
// but rather when the sheet the rule is importing is loaded.
// XXX We may not want to get the rule when stylesheet change event
// is not enabled.
mDocument->StyleRuleAdded(this, mRuleList->GetRule(aIndex));
}
return aIndex;
}
void
ServoStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
// Ensure mRuleList is constructed.
GetCssRulesInternal(aRv);
if (aIndex > mRuleList->Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
// Hold a strong ref to the rule so it doesn't die when we remove it
// from the list. XXX We may not want to hold it if stylesheet change
// event is not enabled.
RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
aRv = mRuleList->DeleteRule(aIndex);
MOZ_ASSERT(!aRv.ErrorCodeIs(NS_ERROR_DOM_INDEX_SIZE_ERR),
"IndexSizeError should have been handled earlier");
if (!aRv.Failed() && mDocument) {
mDocument->StyleRuleRemoved(this, rule);
}
}
} // namespace mozilla

View File

@ -4400,11 +4400,27 @@ CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
return false;
}
if (mToken.mType != eCSSToken_Ident) {
if (mToken.mType != eCSSToken_Ident && mToken.mType != eCSSToken_String) {
REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
UngetToken();
return false;
}
if (mToken.mType == eCSSToken_Ident) {
// Check for keywords that are not allowed as custom-ident for the
// keyframes-name: standard CSS-wide keywords, plus 'none'.
static const nsCSSKeyword excludedKeywords[] = {
eCSSKeyword_none,
eCSSKeyword_UNKNOWN
};
nsCSSValue value;
if (!ParseCustomIdent(value, mToken.mIdent, excludedKeywords)) {
REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
UngetToken();
return false;
}
}
nsString name(mToken.mIdent);
if (!ExpectSymbol('{', true)) {

View File

@ -434,7 +434,7 @@ CSS_PROP_DISPLAY(
"",
// FIXME: The spec should say something about 'inherit' and 'initial'
// not being allowed.
VARIANT_NONE | VARIANT_IDENTIFIER_NO_INHERIT, // used by list parsing
VARIANT_NONE | VARIANT_IDENTIFIER_NO_INHERIT | VARIANT_STRING, // used by list parsing
nullptr,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)

View File

@ -5741,6 +5741,7 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
animation->SetName(EmptyString());
} else if (animName.list) {
switch (animName.list->mValue.GetUnit()) {
case eCSSUnit_String:
case eCSSUnit_Ident: {
nsDependentString
nameStr(animName.list->mValue.GetStringBufferValue());

View File

@ -163,6 +163,38 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=435442
0% { opacity: 0.2 }
100% { opacity: 0.8 }
}
@keyframes "string name 1" { /* using string for keyframes name */
0%, 100% { left: 1px }
}
@keyframes "string name 2" {
0%, 100% { left: 2px }
}
@keyframes custom\ ident\ 1 {
0%, 100% { left: 3px }
}
@keyframes custom\ ident\ 2 {
0%, 100% { left: 4px }
}
@keyframes "initial" {
0%, 100% { left: 5px }
}
@keyframes initial { /* illegal as an identifier, should be dropped */
0%, 100% { left: 6px }
}
@keyframes "none" {
0%, 100% { left: 7px }
}
@keyframes none { /* illegal as an identifier, should be dropped */
0%, 100% { left: 8px }
}
</style>
</head>
<body>
@ -2039,6 +2071,33 @@ advance_clock(500);
is(cs.getPropertyValue("opacity"), "0.35", "opacity animation overriding transition at 0.5s");
done_div();
/*
* Bug 1320474 - keyframes-name may be a string, allows names that would otherwise be excluded
*/
new_div("position: relative; animation: \"string name 1\" 1s linear");
advance_clock(0);
is(cs.getPropertyValue("left"), "1px", "animation name as a string");
div.style.animation = "string\\ name\\ 2 1s linear";
is(cs.getPropertyValue("left"), "2px", "animation name specified as string, referenced using custom ident");
div.style.animation = "custom\\ ident\\ 1 1s linear";
is(cs.getPropertyValue("left"), "3px", "animation name specified as custom-ident");
div.style.animation = "\"custom ident 2\" 1s linear";
is(cs.getPropertyValue("left"), "4px", "animation name specified as custom-ident, referenced using string");
div.style.animation = "unset";
div.style.animation = "initial 1s linear";
is(cs.getPropertyValue("left"), "0px", "animation name 'initial' as identifier is ignored");
div.style.animation = "unset";
div.style.animation = "\"initial\" 1s linear";
is(cs.getPropertyValue("left"), "5px", "animation name 'initial' as string is accepted");
div.style.animation = "unset";
div.style.animation = "none 1s linear";
is(cs.getPropertyValue("left"), "0px", "animation name 'none' as identifier is ignored");
div.style.animation = "unset";
div.style.animation = "\"none\" 1s linear";
is(cs.getPropertyValue("left"), "7px", "animation name 'none' as string is accepted");
done_div();
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
</script>

View File

@ -2237,6 +2237,12 @@ addAsyncAnimTest(function *() {
done_div();
});
// Bug 1320474 - keyframes-name may be a string, allows names that would
// otherwise be excluded.
// These tests don't need to be duplicated here as they relate purely to
// the animation setup which is common to both main-thread and compositor
// animations.
// Bug 847287 - Test that changes of when an animation is dynamically
// overridden work correctly.
addAsyncAnimTest(function *() {

View File

@ -98,9 +98,11 @@ import org.mozilla.gecko.updater.PostUpdateHandler;
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.IntentUtils;
@ -200,7 +202,8 @@ public class BrowserApp extends GeckoApp
OnUrlOpenInBackgroundListener,
AnchoredPopup.OnVisibilityChangeListener,
ActionModeCompat.Presenter,
LayoutInflater.Factory {
LayoutInflater.Factory,
BundleEventListener {
private static final String LOGTAG = "GeckoBrowserApp";
private static final int TABS_ANIMATION_DURATION = 450;
@ -729,7 +732,6 @@ public class BrowserApp extends GeckoApp
"Menu:Update",
"LightweightTheme:Update",
"Search:Keyword",
"Prompt:ShowTop",
"Tab:Added",
"Video:Play");
@ -751,6 +753,8 @@ public class BrowserApp extends GeckoApp
"Updater:Launch",
"Website:Metadata");
getAppEventDispatcher().registerUiThreadListener(this, "Prompt:ShowTop");
final GeckoProfile profile = getProfile();
// We want to upload the telemetry core ping as soon after startup as possible. It relies on the
@ -1060,8 +1064,7 @@ public class BrowserApp extends GeckoApp
}
if (!mHasResumed) {
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
"Prompt:ShowTop");
getAppEventDispatcher().unregisterUiThreadListener(this, "Prompt:ShowTop");
mHasResumed = true;
}
@ -1081,8 +1084,7 @@ public class BrowserApp extends GeckoApp
if (mHasResumed) {
// Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this,
"Prompt:ShowTop");
getAppEventDispatcher().registerUiThreadListener(this, "Prompt:ShowTop");
mHasResumed = false;
}
@ -1434,7 +1436,6 @@ public class BrowserApp extends GeckoApp
"Menu:Update",
"LightweightTheme:Update",
"Search:Keyword",
"Prompt:ShowTop",
"Tab:Added",
"Video:Play");
@ -1456,6 +1457,8 @@ public class BrowserApp extends GeckoApp
"Updater:Launch",
"Website:Metadata");
getAppEventDispatcher().unregisterUiThreadListener(this, "Prompt:ShowTop");
if (AppConstants.MOZ_ANDROID_BEAM) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
if (nfc != null) {
@ -1700,6 +1703,21 @@ public class BrowserApp extends GeckoApp
mBrowserToolbar.refresh();
}
@Override // BundleEventListener
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
switch (event) {
case "Prompt:ShowTop":
// Bring this activity to front so the prompt is visible..
Intent bringToFrontIntent = new Intent();
bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(bringToFrontIntent);
break;
}
}
@Override
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
@ -2111,14 +2129,6 @@ public class BrowserApp extends GeckoApp
}
break;
case "Prompt:ShowTop":
// Bring this activity to front so the prompt is visible..
Intent bringToFrontIntent = new Intent();
bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(bringToFrontIntent);
break;
case "Tab:Added":
if (message.getBoolean("cancelEditMode")) {
ThreadUtils.postToUiThread(new Runnable() {
@ -3823,24 +3833,22 @@ public class BrowserApp extends GeckoApp
final Prompt ps = new Prompt(this, new Prompt.PromptCallback() {
@Override
public void onPromptFinished(String result) {
try {
int itemId = new JSONObject(result).getInt("button");
if (itemId == 0) {
final Context context = GeckoAppShell.getApplicationContext();
if (type == GuestModeDialog.ENTERING) {
GeckoProfile.enterGuestMode(context);
} else {
GeckoProfile.leaveGuestMode(context);
// Now's a good time to make sure we're not displaying the
// Guest Browsing notification.
GuestSession.hideNotification(context);
}
doRestart();
}
} catch (JSONException ex) {
Log.e(LOGTAG, "Exception reading guest mode prompt result", ex);
public void onPromptFinished(final GeckoBundle result) {
final int itemId = result.getInt("button", -1);
if (itemId != 0) {
return;
}
final Context context = GeckoAppShell.getApplicationContext();
if (type == GuestModeDialog.ENTERING) {
GeckoProfile.enterGuestMode(context);
} else {
GeckoProfile.leaveGuestMode(context);
// Now's a good time to make sure we're not displaying the
// Guest Browsing notification.
GuestSession.hideNotification(context);
}
doRestart();
}
});

View File

@ -189,14 +189,13 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
GeckoAppShell.notifyObservers("AndroidCastDevice:Removed", route.getId());
}
@SuppressWarnings("unused")
public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo route) {
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
updatePresentation();
}
// These methods aren't used by the support version Media Router
@SuppressWarnings("unused")
public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
@Override
public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
updatePresentation();
}

View File

@ -35,6 +35,7 @@ import org.mozilla.gecko.promotion.SimpleHelperUI;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.util.DrawableUtil;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import java.lang.ref.WeakReference;
@ -162,19 +163,14 @@ public class BookmarkStateChangeDelegate extends BrowserAppDelegateWithReference
final Resources res = browserApp.getResources();
final Tab tab = Tabs.getInstance().getSelectedTab();
if (tab == null) {
return;
}
final Prompt ps = new Prompt(browserApp, new Prompt.PromptCallback() {
@Override
public void onPromptFinished(String result) {
int itemId = -1;
try {
itemId = new JSONObject(result).getInt("button");
} catch (JSONException ex) {
Log.e(LOGTAG, "Exception reading bookmark prompt result", ex);
}
if (tab == null) {
return;
}
public void onPromptFinished(final GeckoBundle result) {
final int itemId = result.getInt("button", -1);
if (itemId == 0) {
final String extrasId = res.getResourceEntryName(R.string.contextmenu_edit_bookmark);
@ -182,6 +178,7 @@ public class BookmarkStateChangeDelegate extends BrowserAppDelegateWithReference
TelemetryContract.Method.DIALOG, extrasId);
new EditBookmarkDialog(browserApp).show(tab.getURL());
} else if (itemId == 1) {
final String extrasId = res.getResourceEntryName(R.string.contextmenu_add_to_launcher);
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION,

View File

@ -5,8 +5,8 @@
package org.mozilla.gecko.prompts;
import org.json.JSONObject;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.widget.BasicColorPicker;
import android.content.Context;
@ -21,9 +21,9 @@ public class ColorPickerInput extends PromptInput {
private final boolean mShowAdvancedButton = true;
private final int mInitialColor;
public ColorPickerInput(JSONObject obj) {
public ColorPickerInput(GeckoBundle obj) {
super(obj);
String init = obj.optString("value");
String init = obj.getString("value");
mInitialColor = Color.rgb(Integer.parseInt(init.substring(1, 3), 16),
Integer.parseInt(init.substring(3, 5), 16),
Integer.parseInt(init.substring(5, 7), 16));

View File

@ -8,10 +8,9 @@ package org.mozilla.gecko.prompts;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ResourceDrawableUtils;
import android.content.Context;
@ -39,11 +38,12 @@ public class IconGridInput extends PromptInput implements OnItemClickListener {
private static int mMaxColumns = -1; // The maximum number of columns to show
private static int mIconSize = -1; // Size of icons in the grid
private int mSelected; // Current selection
private final JSONArray mArray;
private final GeckoBundle[] mArray;
public IconGridInput(JSONObject obj) {
public IconGridInput(GeckoBundle obj) {
super(obj);
mArray = obj.optJSONArray("items");
final GeckoBundle[] array = obj.getBundleArray("items");
mArray = array != null ? array : new GeckoBundle[0];
}
@Override
@ -70,9 +70,9 @@ public class IconGridInput extends PromptInput implements OnItemClickListener {
final GridView view = (GridView) LayoutInflater.from(context).inflate(R.layout.icon_grid, null, false);
view.setColumnWidth(mColumnWidth);
final ArrayList<IconGridItem> items = new ArrayList<IconGridItem>(mArray.length());
for (int i = 0; i < mArray.length(); i++) {
IconGridItem item = new IconGridItem(context, mArray.optJSONObject(i));
final ArrayList<IconGridItem> items = new ArrayList<IconGridItem>(mArray.length);
for (int i = 0; i < mArray.length; i++) {
IconGridItem item = new IconGridItem(context, mArray[i]);
items.add(item);
if (item.selected) {
mSelected = i;
@ -151,11 +151,11 @@ public class IconGridInput extends PromptInput implements OnItemClickListener {
final boolean selected;
Drawable icon;
public IconGridItem(final Context context, final JSONObject obj) {
label = obj.optString("name");
final String iconUrl = obj.optString("iconUri");
description = obj.optString("description");
selected = obj.optBoolean("selected");
public IconGridItem(final Context context, final GeckoBundle obj) {
label = obj.getString("name");
final String iconUrl = obj.getString("iconUri");
description = obj.getString("description");
selected = obj.getBoolean("selected");
ResourceDrawableUtils.getDrawable(context, iconUrl, new ResourceDrawableUtils.BitmapLoader() {
@Override

View File

@ -5,6 +5,7 @@
package org.mozilla.gecko.prompts;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.GeckoActionProvider;
@ -16,9 +17,6 @@ import android.content.pm.ResolveInfo;
import android.widget.ListView;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
@ -66,17 +64,12 @@ public class IntentChooserPrompt {
final Prompt prompt = new Prompt(context, new Prompt.PromptCallback() {
@Override
public void onPromptFinished(String promptServiceResult) {
public void onPromptFinished(final GeckoBundle result) {
if (handler == null) {
return;
}
int itemId = -1;
try {
itemId = new JSONObject(promptServiceResult).getInt("button");
} catch (JSONException e) {
Log.e(LOGTAG, "result from promptservice was invalid: ", e);
}
final int itemId = result.getInt("button", -1);
if (itemId == -1) {
handler.onCancelled();

View File

@ -5,12 +5,10 @@
package org.mozilla.gecko.prompts;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.R;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
@ -78,27 +76,25 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
return view;
}
public void show(JSONObject message) {
String title = message.optString("title");
String text = message.optString("text");
mGuid = message.optString("guid");
public void show(GeckoBundle message) {
String title = message.getString("title");
String text = message.getString("text");
mGuid = message.getString("guid");
mButtons = getStringArray(message, "buttons");
mButtons = message.getStringArray("buttons");
final int buttonCount = mButtons == null ? 0 : mButtons.length;
mDoubleTapButtonType = convertIndexToButtonType(message.optInt("doubleTapButton", -1), buttonCount);
mDoubleTapButtonType = convertIndexToButtonType(message.getInt("doubleTapButton", -1), buttonCount);
mPreviousInputValue = null;
JSONArray inputs = getSafeArray(message, "inputs");
mInputs = new PromptInput[inputs.length()];
GeckoBundle[] inputs = message.getBundleArray("inputs");
mInputs = new PromptInput[inputs != null ? inputs.length : 0];
for (int i = 0; i < mInputs.length; i++) {
try {
mInputs[i] = PromptInput.getInput(inputs.getJSONObject(i));
mInputs[i].setListener(this);
} catch (Exception ex) { }
mInputs[i] = PromptInput.getInput(inputs[i]);
mInputs[i].setListener(this);
}
PromptListItem[] menuitems = PromptListItem.getArray(message.optJSONArray("listitems"));
String selected = message.optString("choiceMode");
PromptListItem[] menuitems = PromptListItem.getArray(message.getBundleArray("listitems"));
String selected = message.getString("choiceMode");
int choiceMode = ListView.CHOICE_MODE_NONE;
if ("single".equals(selected)) {
@ -107,9 +103,7 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
choiceMode = ListView.CHOICE_MODE_MULTIPLE;
}
if (message.has("tabId")) {
mTabId = message.optInt("tabId", Tabs.INVALID_TAB_ID);
}
mTabId = message.getInt("tabId", Tabs.INVALID_TAB_ID);
show(title, text, menuitems, choiceMode);
}
@ -226,63 +220,71 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
* object that's passed in. If this is a multi-select dialog, sets a
* selected attribute to an array of booleans.
*/
private void addListResult(final JSONObject result, int which) {
private void addListResult(final GeckoBundle result, int which) {
if (mAdapter == null) {
return;
}
try {
JSONArray selected = new JSONArray();
// If the button has already been filled in
final ArrayList<Integer> selected = mAdapter.getSelected();
// If the button has already been filled in
ArrayList<Integer> selectedItems = mAdapter.getSelected();
for (Integer item : selectedItems) {
selected.put(item);
// If we haven't assigned a button yet, or we assigned it to -1, assign the which
// parameter to both selected and the button.
if (result.getInt("button", -1) == -1) {
if (!selected.contains(which)) {
selected.add(which);
}
// If we haven't assigned a button yet, or we assigned it to -1, assign the which
// parameter to both selected and the button.
if (!result.has("button") || result.optInt("button") == -1) {
if (!selectedItems.contains(which)) {
selected.put(which);
}
result.putInt("button", which);
}
result.put("button", which);
}
result.put("list", selected);
} catch (JSONException ex) { }
result.putIntArray("list", selected);
}
/* Adds to a result value from the inputs that can be shown in dialogs.
* Each input will set its own value in the result.
*/
private void addInputValues(final JSONObject result) {
try {
if (mInputs != null) {
for (int i = 0; i < mInputs.length; i++) {
if (mInputs[i] != null) {
result.put(mInputs[i].getId(), mInputs[i].getValue());
}
}
private void addInputValues(final GeckoBundle result) {
if (mInputs == null) {
return;
}
for (final PromptInput input : mInputs) {
if (input == null) {
continue;
}
} catch (JSONException ex) { }
final String id = input.getId();
final Object value = input.getValue();
if (value instanceof Boolean) {
result.putBoolean(id, (Boolean) value);
} else if (value instanceof Double) {
result.putDouble(id, (Double) value);
} else if (value instanceof Integer) {
result.putInt(id, (Integer) value);
} else if (value instanceof String) {
result.putString(id, (String) value);
} else if (value instanceof GeckoBundle) {
result.putBundle(id, (GeckoBundle) value);
} else {
throw new UnsupportedOperationException();
}
}
}
/* Adds the selected button to a result. This should only be called if there
* are no lists shown on the dialog, since they also write their results to the button
* attribute.
*/
private void addButtonResult(final JSONObject result, int which) {
private void addButtonResult(final GeckoBundle result, int which) {
int button = -1;
switch (which) {
case DialogInterface.BUTTON_POSITIVE : button = 0; break;
case DialogInterface.BUTTON_NEUTRAL : button = 1; break;
case DialogInterface.BUTTON_NEGATIVE : button = 2; break;
}
try {
result.put("button", button);
} catch (JSONException ex) { }
result.putInt("button", button);
}
@Override
@ -475,14 +477,13 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
}
/* Called in situations where we want to cancel the dialog . This can happen if the user hits back,
* or if the dialog can't be created because of invalid JSON.
* or if the dialog can't be created because of invalid input.
*/
private void cancelDialog() {
JSONObject ret = new JSONObject();
try {
ret.put("button", -1);
} catch (Exception ex) { }
final GeckoBundle ret = new GeckoBundle();
ret.putInt("button", -1);
addInputValues(ret);
notifyClosing(ret);
}
@ -490,7 +491,7 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
* is closing.
*/
private void closeDialog(int which) {
JSONObject ret = new JSONObject();
final GeckoBundle ret = new GeckoBundle();
mDialog.dismiss();
addButtonResult(ret, which);
@ -503,17 +504,15 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
/* Called any time we're closing the dialog to cleanup and notify listeners that the dialog
* is closing.
*/
private void notifyClosing(JSONObject aReturn) {
try {
aReturn.put("guid", mGuid);
} catch (JSONException ex) { }
private void notifyClosing(final GeckoBundle ret) {
ret.putString("guid", mGuid);
if (mTabId != Tabs.INVALID_TAB_ID) {
Tabs.unregisterOnTabsChangedListener(this);
}
if (mCallback != null) {
mCallback.onPromptFinished(aReturn.toString());
mCallback.onPromptFinished(ret);
}
}
@ -540,47 +539,11 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
return false;
}
private static JSONArray getSafeArray(JSONObject json, String key) {
try {
return json.getJSONArray(key);
} catch (Exception e) {
return new JSONArray();
}
}
public static String[] getStringArray(JSONObject aObject, String aName) {
JSONArray items = getSafeArray(aObject, aName);
int length = items.length();
String[] list = new String[length];
for (int i = 0; i < length; i++) {
try {
list[i] = items.getString(i);
} catch (Exception ex) { }
}
return list;
}
private static boolean[] getBooleanArray(JSONObject aObject, String aName) {
JSONArray items = new JSONArray();
try {
items = aObject.getJSONArray(aName);
} catch (Exception ex) { return null; }
int length = items.length();
boolean[] list = new boolean[length];
for (int i = 0; i < length; i++) {
try {
list[i] = items.getBoolean(i);
} catch (Exception ex) { }
}
return list;
}
public interface PromptCallback {
/**
* Called when the Prompt has been completed (i.e. when the user has selected an item or action in the Prompt).
* This callback is run on the UI thread.
*/
public void onPromptFinished(String jsonResult);
public void onPromptFinished(GeckoBundle result);
}
}

View File

@ -9,8 +9,8 @@ import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.json.JSONObject;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.widget.AllCapsTextView;
import org.mozilla.gecko.widget.DateTimePicker;
@ -59,10 +59,10 @@ public abstract class PromptInput {
protected final boolean mAutofocus;
public static final String INPUT_TYPE = "textbox";
public EditInput(JSONObject object) {
public EditInput(GeckoBundle object) {
super(object);
mHint = object.optString("hint");
mAutofocus = object.optBoolean("autofocus");
mHint = object.getString("hint");
mAutofocus = object.getBoolean("autofocus");
}
@Override
@ -103,7 +103,7 @@ public abstract class PromptInput {
public static class NumberInput extends EditInput {
public static final String INPUT_TYPE = "number";
public NumberInput(JSONObject obj) {
public NumberInput(GeckoBundle obj) {
super(obj);
}
@ -120,7 +120,7 @@ public abstract class PromptInput {
public static class PasswordInput extends EditInput {
public static final String INPUT_TYPE = "password";
public PasswordInput(JSONObject obj) {
public PasswordInput(GeckoBundle obj) {
super(obj);
}
@ -138,9 +138,9 @@ public abstract class PromptInput {
public static final String INPUT_TYPE = "checkbox";
private final boolean mChecked;
public CheckboxInput(JSONObject obj) {
public CheckboxInput(GeckoBundle obj) {
super(obj);
mChecked = obj.optBoolean("checked");
mChecked = obj.getBoolean("checked");
}
@Override
@ -170,7 +170,7 @@ public abstract class PromptInput {
"month"
};
public DateTimeInput(JSONObject obj) {
public DateTimeInput(GeckoBundle obj) {
super(obj);
}
@ -265,16 +265,17 @@ public abstract class PromptInput {
public static class MenulistInput extends PromptInput {
public static final String INPUT_TYPE = "menulist";
private static String[] mListitems;
private static int mSelected;
private final String[] mListitems;
private final int mSelected;
public Spinner spinner;
public AllCapsTextView textView;
public MenulistInput(JSONObject obj) {
public MenulistInput(GeckoBundle obj) {
super(obj);
mListitems = Prompt.getStringArray(obj, "values");
mSelected = obj.optInt("selected");
final String[] listitems = obj.getStringArray("values");
mListitems = listitems != null ? listitems : new String[0];
mSelected = obj.getInt("selected");
}
@Override
@ -314,7 +315,7 @@ public abstract class PromptInput {
public static class LabelInput extends PromptInput {
public static final String INPUT_TYPE = "label";
public LabelInput(JSONObject obj) {
public LabelInput(GeckoBundle obj) {
super(obj);
}
@ -328,18 +329,18 @@ public abstract class PromptInput {
}
}
public PromptInput(JSONObject obj) {
mLabel = obj.optString("label");
mType = obj.optString("type");
String id = obj.optString("id");
public PromptInput(GeckoBundle obj) {
mLabel = obj.getString("label");
mType = obj.getString("type");
String id = obj.getString("id");
mId = TextUtils.isEmpty(id) ? mType : id;
mValue = obj.optString("value");
mMaxValue = obj.optString("max");
mMinValue = obj.optString("min");
mValue = obj.getString("value");
mMaxValue = obj.getString("max");
mMinValue = obj.getString("min");
}
public static PromptInput getInput(JSONObject obj) {
String type = obj.optString("type");
public static PromptInput getInput(GeckoBundle obj) {
String type = obj.getString("type");
switch (type) {
case EditInput.INPUT_TYPE:
return new EditInput(obj);

View File

@ -1,15 +1,12 @@
package org.mozilla.gecko.prompts;
import org.json.JSONException;
import org.mozilla.gecko.IntentHelper;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.ThumbnailHelper;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ResourceDrawableUtils;
import org.mozilla.gecko.widget.GeckoActionProvider;
import org.json.JSONArray;
import org.json.JSONObject;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@ -32,32 +29,32 @@ public class PromptListItem {
public boolean mSelected;
public Drawable mIcon;
PromptListItem(JSONObject aObject) {
PromptListItem(GeckoBundle aObject) {
Context context = GeckoAppShell.getContext();
label = aObject.isNull("label") ? "" : aObject.optString("label");
isGroup = aObject.optBoolean("isGroup");
inGroup = aObject.optBoolean("inGroup");
disabled = aObject.optBoolean("disabled");
id = aObject.optInt("id");
mSelected = aObject.optBoolean("selected");
label = aObject.getString("label", "");
isGroup = aObject.getBoolean("isGroup");
inGroup = aObject.getBoolean("inGroup");
disabled = aObject.getBoolean("disabled");
id = aObject.getInt("id");
mSelected = aObject.getBoolean("selected");
JSONObject obj = aObject.optJSONObject("showAsActions");
GeckoBundle obj = aObject.getBundle("showAsActions");
if (obj != null) {
showAsActions = true;
String uri = obj.isNull("uri") ? "" : obj.optString("uri");
String type = obj.isNull("type") ? GeckoActionProvider.DEFAULT_MIME_TYPE :
obj.optString("type", GeckoActionProvider.DEFAULT_MIME_TYPE);
String uri = obj.getString("uri", "");
String type = obj.getString("type", GeckoActionProvider.DEFAULT_MIME_TYPE);
mIntent = IntentHelper.getShareIntent(context, uri, type, "");
isParent = true;
} else {
mIntent = null;
showAsActions = false;
// Support both "isParent" (backwards compat for older consumers), and "menu" for the new Tabbed prompt ui.
isParent = aObject.optBoolean("isParent") || aObject.optBoolean("menu");
// Support both "isParent" (backwards compat for older consumers), and "menu"
// for the new Tabbed prompt ui.
isParent = aObject.getBoolean("isParent") || aObject.getBoolean("menu");
}
final String iconStr = aObject.optString("icon");
final String iconStr = aObject.getString("icon");
if (iconStr != null) {
final ResourceDrawableUtils.BitmapLoader loader = new ResourceDrawableUtils.BitmapLoader() {
@Override
@ -109,18 +106,16 @@ public class PromptListItem {
showAsActions = false;
}
static PromptListItem[] getArray(JSONArray items) {
static PromptListItem[] getArray(GeckoBundle[] items) {
if (items == null) {
return new PromptListItem[0];
}
int length = items.length();
int length = items.length;
List<PromptListItem> list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
try {
PromptListItem item = new PromptListItem(items.getJSONObject(i));
list.add(item);
} catch (JSONException ex) { }
PromptListItem item = new PromptListItem(items[i]);
list.add(item);
}
return list.toArray(new PromptListItem[length]);

View File

@ -5,68 +5,46 @@
package org.mozilla.gecko.prompts;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.Context;
import android.util.Log;
public class PromptService implements GeckoEventListener {
public class PromptService implements BundleEventListener {
private static final String LOGTAG = "GeckoPromptService";
private final Context mContext;
public PromptService(Context context) {
GeckoApp.getEventDispatcher().registerGeckoThreadListener(this,
GeckoApp.getEventDispatcher().registerUiThreadListener(this,
"Prompt:Show",
"Prompt:ShowTop");
mContext = context;
}
public void destroy() {
GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
GeckoApp.getEventDispatcher().unregisterUiThreadListener(this,
"Prompt:Show",
"Prompt:ShowTop");
}
public void show(final String aTitle, final String aText, final PromptListItem[] aMenuList,
final int aChoiceMode, final Prompt.PromptCallback callback) {
// The dialog must be created on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
Prompt p;
p = new Prompt(mContext, callback);
p.show(aTitle, aText, aMenuList, aChoiceMode);
}
});
}
// GeckoEventListener implementation
// BundleEventListener implementation
@Override
public void handleMessage(String event, final JSONObject message) {
// The dialog must be created on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
Prompt p;
p = new Prompt(mContext, new Prompt.PromptCallback() {
@Override
public void run() {
Prompt p;
p = new Prompt(mContext, new Prompt.PromptCallback() {
@Override
public void onPromptFinished(String jsonResult) {
try {
EventDispatcher.sendResponse(message, new JSONObject(jsonResult));
} catch (JSONException ex) {
Log.i(LOGTAG, "Error building json response", ex);
}
}
});
p.show(message);
public void onPromptFinished(final GeckoBundle result) {
callback.sendSuccess(result);
}
});
p.show(message);
}
}

View File

@ -7,11 +7,9 @@ package org.mozilla.gecko.prompts;
import java.util.LinkedHashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.Context;
@ -27,25 +25,21 @@ public class TabInput extends PromptInput implements AdapterView.OnItemClickList
public static final String INPUT_TYPE = "tabs";
public static final String LOGTAG = "GeckoTabInput";
/* Keeping the order of this in sync with the JSON is important. */
/* Keeping the order of this in sync with the input is important. */
final private LinkedHashMap<String, PromptListItem[]> mTabs;
private TabHost mHost;
private int mPosition;
public TabInput(JSONObject obj) {
public TabInput(GeckoBundle obj) {
super(obj);
mTabs = new LinkedHashMap<String, PromptListItem[]>();
try {
JSONArray tabs = obj.getJSONArray("items");
for (int i = 0; i < tabs.length(); i++) {
JSONObject tab = tabs.getJSONObject(i);
String title = tab.getString("label");
JSONArray items = tab.getJSONArray("items");
mTabs.put(title, PromptListItem.getArray(items));
}
} catch (JSONException ex) {
Log.e(LOGTAG, "Exception", ex);
GeckoBundle[] tabs = obj.getBundleArray("items");
for (int i = 0; i < (tabs != null ? tabs.length : 0); i++) {
GeckoBundle tab = tabs[i];
String title = tab.getString("label");
GeckoBundle[] items = tab.getBundleArray("items");
mTabs.put(title, PromptListItem.getArray(items));
}
}
@ -78,12 +72,9 @@ public class TabInput extends PromptInput implements AdapterView.OnItemClickList
@Override
public Object getValue() {
JSONObject obj = new JSONObject();
try {
obj.put("tab", mHost.getCurrentTab());
obj.put("item", mPosition);
} catch (JSONException ex) { }
final GeckoBundle obj = new GeckoBundle(2);
obj.putInt("tab", mHost.getCurrentTab());
obj.putInt("item", mPosition);
return obj;
}

View File

@ -15,6 +15,7 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.prompts.PromptInput;
import org.mozilla.gecko.util.GeckoBundle;
import org.json.JSONArray;
import org.json.JSONException;
@ -109,7 +110,8 @@ public class DefaultDoorHanger extends DoorHanger {
for (int i = 0; i < inputs.length(); i++) {
try {
PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
PromptInput input = PromptInput.getInput(
GeckoBundle.fromJSONObject(inputs.getJSONObject(i)));
mInputs.add(input);
final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_section_padding_medium);

View File

@ -6887,10 +6887,15 @@ var Tabs = {
};
function ContextMenuItem(args) {
this.id = uuidgen.generateUUID().toString();
this.id = ContextMenuItem._nextId;
this.args = args;
// Limit to Java int range.
ContextMenuItem._nextId = (ContextMenuItem._nextId + 1) & 0x7fffffff;
}
ContextMenuItem._nextId = 1;
ContextMenuItem.prototype = {
get order() {
return this.args.order || 0;

View File

@ -366,7 +366,7 @@ public final class EventDispatcher extends JNIObject {
final GeckoBundle messageAsBundle;
try {
messageAsBundle = jsMessage != null ?
convertBundle(jsMessage.toBundle()) : bundleMessage;
convertBundle(jsMessage.toBundle(), jsMessage) : bundleMessage;
} catch (final NativeJSObject.InvalidPropertyException e) {
Log.e(LOGTAG, "Exception occurred while handling " + type, e);
return true;
@ -425,7 +425,7 @@ public final class EventDispatcher extends JNIObject {
}
// XXX: temporary helper for converting Bundle to GeckoBundle.
private GeckoBundle convertBundle(final Bundle bundle) {
private GeckoBundle convertBundle(final Bundle bundle, final NativeJSObject jsObj) {
if (bundle == null) {
return null;
}
@ -437,15 +437,25 @@ public final class EventDispatcher extends JNIObject {
final Object value = bundle.get(key);
if (value instanceof Bundle) {
out.putBundle(key, convertBundle((Bundle) value));
final Bundle bundleValue = (Bundle) value;
try {
// XXX: NativeJSObject.toBundle doesn't support object arrays, and
// instead converts it to a Bundle with integer members; correct that.
final NativeJSObject[] objs = jsObj.getObjectArray(key);
final GeckoBundle[] outArray = new GeckoBundle[objs.length];
for (int i = 0; i < objs.length; i++) {
outArray[i] = convertBundle(
bundleValue.getBundle(String.valueOf(i)), objs[i]);
}
out.putBundleArray(key, outArray);
} catch (final Exception e) {
// Not an array
out.putBundle(key, convertBundle(bundleValue, jsObj.getObject(key)));
}
} else if (value instanceof Bundle[]) {
final Bundle[] inArray = (Bundle[]) value;
final GeckoBundle[] outArray = new GeckoBundle[inArray.length];
for (int i = 0; i < inArray.length; i++) {
outArray[i] = convertBundle(inArray[i]);
}
out.putBundleArray(key, outArray);
throw new IllegalStateException("toBundle should not have generated Bundle[] values");
} else {
out.put(key, value);
@ -480,7 +490,7 @@ public final class EventDispatcher extends JNIObject {
final GeckoBundle messageAsBundle;
try {
messageAsBundle = jsMessage != null ?
convertBundle(jsMessage.toBundle()) : bundleMessage;
convertBundle(jsMessage.toBundle(), jsMessage) : bundleMessage;
} catch (final NativeJSObject.InvalidPropertyException e) {
Log.e(LOGTAG, "Exception occurred while handling " + type, e);
return true;

View File

@ -8,9 +8,15 @@ package org.mozilla.gecko.util;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.support.v4.util.SimpleArrayMap;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
@ -138,7 +144,8 @@ public final class GeckoBundle {
*/
public boolean[] getBooleanArray(final String key) {
final Object value = mMap.get(key);
return Array.getLength(value) == 0 ? EMPTY_BOOLEAN_ARRAY : (boolean[]) value;
return value == null ? null :
Array.getLength(value) == 0 ? EMPTY_BOOLEAN_ARRAY : (boolean[]) value;
}
/**
@ -183,7 +190,7 @@ public final class GeckoBundle {
*/
public double[] getDoubleArray(final String key) {
final Object value = mMap.get(key);
return Array.getLength(value) == 0 ? EMPTY_DOUBLE_ARRAY :
return value == null ? null : Array.getLength(value) == 0 ? EMPTY_DOUBLE_ARRAY :
value instanceof int[] ? getDoubleArray((int[]) value) : (double[]) value;
}
@ -229,7 +236,7 @@ public final class GeckoBundle {
*/
public int[] getIntArray(final String key) {
final Object value = mMap.get(key);
return Array.getLength(value) == 0 ? EMPTY_INT_ARRAY :
return value == null ? null : Array.getLength(value) == 0 ? EMPTY_INT_ARRAY :
value instanceof double[] ? getIntArray((double[]) value) : (int[]) value;
}
@ -278,7 +285,7 @@ public final class GeckoBundle {
*/
public String[] getStringArray(final String key) {
final Object value = mMap.get(key);
return Array.getLength(value) == 0 ? EMPTY_STRING_ARRAY :
return value == null ? null : Array.getLength(value) == 0 ? EMPTY_STRING_ARRAY :
!(value instanceof String[]) ? new String[getNullArrayLength(value)] :
(String[]) value;
}
@ -303,7 +310,7 @@ public final class GeckoBundle {
*/
public GeckoBundle[] getBundleArray(final String key) {
final Object value = mMap.get(key);
return Array.getLength(value) == 0 ? EMPTY_BUNDLE_ARRAY :
return value == null ? null : Array.getLength(value) == 0 ? EMPTY_BUNDLE_ARRAY :
!(value instanceof GeckoBundle[]) ? new GeckoBundle[getNullArrayLength(value)] :
(GeckoBundle[]) value;
}
@ -370,6 +377,43 @@ public final class GeckoBundle {
mMap.put(key, value);
}
/**
* Map a key to a boolean array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putBooleanArray(final String key, final Boolean[] value) {
if (value == null) {
mMap.put(key, null);
return;
}
final boolean[] array = new boolean[value.length];
for (int i = 0; i < value.length; i++) {
array[i] = value[i];
}
mMap.put(key, array);
}
/**
* Map a key to a boolean array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putBooleanArray(final String key, final Collection<Boolean> value) {
if (value == null) {
mMap.put(key, null);
return;
}
final boolean[] array = new boolean[value.size()];
int i = 0;
for (final Boolean element : value) {
array[i++] = element;
}
mMap.put(key, array);
}
/**
* Map a key to a double value.
*
@ -390,6 +434,43 @@ public final class GeckoBundle {
mMap.put(key, value);
}
/**
* Map a key to a double array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putDoubleArray(final String key, final Double[] value) {
if (value == null) {
mMap.put(key, null);
return;
}
final double[] array = new double[value.length];
for (int i = 0; i < value.length; i++) {
array[i] = value[i];
}
mMap.put(key, array);
}
/**
* Map a key to a double array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putDoubleArray(final String key, final Collection<Double> value) {
if (value == null) {
mMap.put(key, null);
return;
}
final double[] array = new double[value.size()];
int i = 0;
for (final Double element : value) {
array[i++] = element;
}
mMap.put(key, array);
}
/**
* Map a key to an int value.
*
@ -410,6 +491,43 @@ public final class GeckoBundle {
mMap.put(key, value);
}
/**
* Map a key to a int array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putIntArray(final String key, final Integer[] value) {
if (value == null) {
mMap.put(key, null);
return;
}
final int[] array = new int[value.length];
for (int i = 0; i < value.length; i++) {
array[i] = value[i];
}
mMap.put(key, array);
}
/**
* Map a key to a int array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putIntArray(final String key, final Collection<Integer> value) {
if (value == null) {
mMap.put(key, null);
return;
}
final int[] array = new int[value.size()];
int i = 0;
for (final Integer element : value) {
array[i++] = element;
}
mMap.put(key, array);
}
/**
* Map a key to a String value.
*
@ -430,6 +548,25 @@ public final class GeckoBundle {
mMap.put(key, value);
}
/**
* Map a key to a String array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putStringArray(final String key, final Collection<String> value) {
if (value == null) {
mMap.put(key, null);
return;
}
final String[] array = new String[value.size()];
int i = 0;
for (final String element : value) {
array[i++] = element;
}
mMap.put(key, array);
}
/**
* Map a key to a GeckoBundle value.
*
@ -450,6 +587,25 @@ public final class GeckoBundle {
mMap.put(key, value);
}
/**
* Map a key to a GeckoBundle array value.
*
* @param key Key to map.
* @param value Value to map to.
*/
public void putBundleArray(final String key, final Collection<GeckoBundle> value) {
if (value == null) {
mMap.put(key, null);
return;
}
final GeckoBundle[] array = new GeckoBundle[value.size()];
int i = 0;
for (final GeckoBundle element : value) {
array[i++] = element;
}
mMap.put(key, array);
}
/**
* Remove a mapping.
*
@ -467,4 +623,68 @@ public final class GeckoBundle {
public int size() {
return mMap.size();
}
private static Object fromJSONValue(Object value) throws JSONException {
if (value instanceof JSONObject || value == JSONObject.NULL) {
return fromJSONObject((JSONObject) value);
}
if (value instanceof JSONArray) {
final JSONArray array = (JSONArray) value;
final int len = array.length();
if (len == 0) {
return EMPTY_BOOLEAN_ARRAY;
}
Object out = null;
for (int i = 0; i < len; i++) {
final Object element = fromJSONValue(array.opt(0));
if (element == null) {
continue;
}
if (out == null) {
Class<?> type = element.getClass();
if (type == Boolean.class) {
type = boolean.class;
} else if (type == Integer.class) {
type = int.class;
} else if (type == Double.class) {
type = double.class;
}
out = Array.newInstance(type, len);
}
Array.set(out, i, element);
}
if (out == null) {
// Treat all-null arrays as String arrays.
return new String[len];
}
return out;
}
if (value instanceof Boolean) {
return value;
}
if (value instanceof Byte || value instanceof Short || value instanceof Integer) {
return ((Number) value).intValue();
}
if (value instanceof Float || value instanceof Double || value instanceof Long) {
return ((Number) value).doubleValue();
}
return value != null ? value.toString() : null;
}
public static GeckoBundle fromJSONObject(final JSONObject obj) throws JSONException {
if (obj == null || obj == JSONObject.NULL) {
return null;
}
final String[] keys = new String[obj.length()];
final Object[] values = new String[obj.length()];
final Iterator<String> iter = obj.keys();
for (int i = 0; iter.hasNext(); i++) {
final String key = iter.next();
keys[i] = key;
values[i] = fromJSONValue(obj.opt(key));
}
return new GeckoBundle(keys, values);
}
}

View File

@ -8,7 +8,6 @@ var Cc = Components.classes;
var Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/Messaging.jsm");
this.EXPORTED_SYMBOLS = ["Prompt"];
@ -176,7 +175,8 @@ Prompt.prototype = {
},
_innerShow: function() {
Messaging.sendRequestForResult(this.msg).then((data) => {
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequestForResult(this.msg).then((data) => {
if (this.callback)
this.callback(data);
});

View File

@ -211,8 +211,32 @@ public class testEventDispatcher extends JavascriptBridgeTest
checkBundle(message);
checkBundle(message.getBundle("object"));
fAssertSame("Bundle null object has correct value", null, message.getBundle("nullObject"));
fAssertSame("Bundle nonexistent object returns null",
null, message.getBundle("nonexistentObject"));
// XXX add objectArray check when we remove NativeJSObject
final GeckoBundle[] objectArray = message.getBundleArray("objectArray");
fAssertNotNull("Bundle object array should exist", objectArray);
fAssertEquals("Bundle object array has correct length", 2, objectArray.length);
fAssertSame("Bundle object array index 0 has correct value", null, objectArray[0]);
checkBundle(objectArray[1]);
final GeckoBundle[] objectArrayOfNull = message.getBundleArray("objectArrayOfNull");
fAssertNotNull("Bundle object array of null should exist", objectArrayOfNull);
fAssertEquals("Bundle object array of null has correct length", 2, objectArrayOfNull.length);
fAssertSame("Bundle object array of null index 0 has correct value",
null, objectArrayOfNull[0]);
fAssertSame("Bundle object array of null index 1 has correct value",
null, objectArrayOfNull[1]);
final GeckoBundle[] emptyObjectArray = message.getBundleArray("emptyObjectArray");
fAssertNotNull("Bundle empty object array should exist", emptyObjectArray);
fAssertEquals("Bundle empty object array has correct length", 0, emptyObjectArray.length);
fAssertSame("Bundle null object array exists",
null, message.getBundleArray("nullObjectArray"));
fAssertSame("Bundle nonexistent object array returns null",
null, message.getBundleArray("nonexistentObjectArray"));
} else if (UI_RESPONSE_EVENT.equals(event) || BACKGROUND_RESPONSE_EVENT.equals(event)) {
final String response = message.getString("response");
@ -390,6 +414,24 @@ public class testEventDispatcher extends JavascriptBridgeTest
fAssertEquals("Bundle double has correct value", 0.5, bundle.getDouble("double"));
fAssertEquals("Bundle string has correct value", "foo", bundle.getString("string"));
fAssertEquals("Bundle default boolean has correct value",
true, bundle.getBoolean("nonexistentBoolean", true));
fAssertEquals("Bundle default int has correct value",
1, bundle.getInt("nonexistentInt", 1));
fAssertEquals("Bundle default double has correct value",
0.5, bundle.getDouble("nonexistentDouble", 0.5));
fAssertEquals("Bundle default string has correct value",
"foo", bundle.getString("nonexistentString", "foo"));
fAssertEquals("Bundle nonexistent boolean returns false",
false, bundle.getBoolean("nonexistentBoolean"));
fAssertEquals("Bundle nonexistent int returns 0",
0, bundle.getInt("nonexistentInt"));
fAssertEquals("Bundle nonexistent double returns 0.0",
0.0, bundle.getDouble("nonexistentDouble"));
fAssertSame("Bundle nonexistent string returns null",
null, bundle.getString("nonexistentString"));
fAssertSame("Bundle null string has correct value", null, bundle.getString("nullString"));
fAssertEquals("Bundle empty string has correct value", "", bundle.getString("emptyString"));
fAssertEquals("Bundle default null string is correct", "foo",
@ -421,6 +463,14 @@ public class testEventDispatcher extends JavascriptBridgeTest
fAssertEquals("Bundle string array index 0 has correct value", "bar", stringArray[0]);
fAssertEquals("Bundle string array index 1 has correct value", "baz", stringArray[1]);
final String[] stringArrayOfNull = bundle.getStringArray("stringArrayOfNull");
fAssertNotNull("Bundle string array of null should exist", stringArrayOfNull);
fAssertEquals("Bundle string array of null has correct length", 2, stringArrayOfNull.length);
fAssertSame("Bundle string array of null index 0 has correct value",
null, stringArrayOfNull[0]);
fAssertSame("Bundle string array of null index 1 has correct value",
null, stringArrayOfNull[1]);
final boolean[] emptyBooleanArray = bundle.getBooleanArray("emptyBooleanArray");
fAssertNotNull("Bundle empty boolean array should exist", emptyBooleanArray);
fAssertEquals("Bundle empty boolean array has correct length", 0, emptyBooleanArray.length);
@ -437,6 +487,24 @@ public class testEventDispatcher extends JavascriptBridgeTest
fAssertNotNull("Bundle empty String array should exist", emptyStringArray);
fAssertEquals("Bundle empty String array has correct length", 0, emptyStringArray.length);
fAssertSame("Bundle null boolean array exists",
null, bundle.getBooleanArray("nullBooleanArray"));
fAssertSame("Bundle null int array exists",
null, bundle.getIntArray("nullIntArray"));
fAssertSame("Bundle null double array exists",
null, bundle.getDoubleArray("nullDoubleArray"));
fAssertSame("Bundle null string array exists",
null, bundle.getStringArray("nullStringArray"));
fAssertSame("Bundle nonexistent boolean array returns null",
null, bundle.getBooleanArray("nonexistentBooleanArray"));
fAssertSame("Bundle nonexistent int array returns null",
null, bundle.getIntArray("nonexistentIntArray"));
fAssertSame("Bundle nonexistent double array returns null",
null, bundle.getDoubleArray("nonexistentDoubleArray"));
fAssertSame("Bundle nonexistent string array returns null",
null, bundle.getStringArray("nonexistentStringArray"));
// XXX add mixedArray check when we remove NativeJSObject
}
@ -580,12 +648,18 @@ public class testEventDispatcher extends JavascriptBridgeTest
bundle.putString("nullString", null);
bundle.putString("emptyString", "");
bundle.putStringArray("stringArray", new String[] {"bar", "baz"});
bundle.putStringArray("stringArrayOfNull", new String[2]);
bundle.putBooleanArray("emptyBooleanArray", new boolean[0]);
bundle.putIntArray("emptyIntArray", new int[0]);
bundle.putDoubleArray("emptyDoubleArray", new double[0]);
bundle.putStringArray("emptyStringArray", new String[0]);
bundle.putBooleanArray("nullBooleanArray", (boolean[]) null);
bundle.putIntArray("nullIntArray", (int[]) null);
bundle.putDoubleArray("nullDoubleArray", (double[]) null);
bundle.putStringArray("nullStringArray", (String[]) null);
return bundle;
}
@ -596,7 +670,9 @@ public class testEventDispatcher extends JavascriptBridgeTest
outer.putBundle("object", inner);
outer.putBundle("nullObject", null);
outer.putBundleArray("objectArray", new GeckoBundle[] {null, inner});
outer.putBundleArray("objectArrayOfNull", new GeckoBundle[2]);
outer.putBundleArray("emptyObjectArray", new GeckoBundle[0]);
outer.putBundleArray("nullObjectArray", (GeckoBundle[]) null);
return outer;
}

View File

@ -21,10 +21,15 @@ function get_test_message() {
nullString: null,
emptyString: "",
stringArray: ["bar", "baz"],
stringArrayOfNull: [null, null],
emptyBooleanArray: [],
emptyIntArray: [],
emptyDoubleArray: [],
emptyStringArray: [],
nullBooleanArray: null,
nullIntArray: null,
nullDoubleArray: null,
nullStringArray: null,
// XXX enable when we remove NativeJSObject
// mixedArray: [1, 1.5],
};
@ -35,7 +40,9 @@ function get_test_message() {
outerObject.object = innerObject;
outerObject.nullObject = null;
outerObject.objectArray = [null, innerObject];
outerObject.objectArrayOfNull = [null, null];
outerObject.emptyObjectArray = [];
outerObject.nullObjectArray = null;
return outerObject;
}
@ -104,10 +111,19 @@ let listener = {
do_check_eq(obj.stringArray[0], "bar");
do_check_eq(obj.stringArray[1], "baz");
do_check_eq(obj.stringArrayOfNull.length, 2);
do_check_eq(obj.stringArrayOfNull[0], null);
do_check_eq(obj.stringArrayOfNull[1], null);
do_check_eq(obj.emptyBooleanArray.length, 0);
do_check_eq(obj.emptyIntArray.length, 0);
do_check_eq(obj.emptyDoubleArray.length, 0);
do_check_eq(obj.emptyStringArray.length, 0);
do_check_eq(obj.nullBooleanArray, null);
do_check_eq(obj.nullIntArray, null);
do_check_eq(obj.nullDoubleArray, null);
do_check_eq(obj.nullStringArray, null);
},
onEvent: function (event, data, callback) {
@ -122,7 +138,13 @@ let listener = {
do_check_eq(data.objectArray.length, 2);
do_check_eq(data.objectArray[0], null);
this._checkObject(data.objectArray[1]);
do_check_eq(data.objectArrayOfNull.length, 2);
do_check_eq(data.objectArrayOfNull[0], null);
do_check_eq(data.objectArrayOfNull[1], null);
do_check_eq(data.emptyObjectArray.length, 0);
do_check_eq(data.nullObjectArray, null);
}
};

View File

@ -13,7 +13,6 @@ NS_IMPL_ISUPPORTS(ArrayBufferInputStream, nsIArrayBufferInputStream, nsIInputStr
ArrayBufferInputStream::ArrayBufferInputStream()
: mBufferLength(0)
, mOffset(0)
, mPos(0)
, mClosed(false)
{
@ -33,11 +32,16 @@ ArrayBufferInputStream::SetData(JS::Handle<JS::Value> aBuffer,
return NS_ERROR_FAILURE;
}
mArrayBuffer.emplace(aCx, arrayBuffer);
uint32_t buflen = JS_GetArrayBufferByteLength(arrayBuffer);
mOffset = std::min(buflen, aByteOffset);
mBufferLength = std::min(buflen - mOffset, aLength);
uint32_t offset = std::min(buflen, aByteOffset);
mBufferLength = std::min(buflen - offset, aLength);
mArrayBuffer = mozilla::MakeUnique<char[]>(mBufferLength);
JS::AutoCheckCannotGC nogc;
bool isShared;
char* src = (char*) JS_GetArrayBufferData(arrayBuffer, &isShared, nogc) + offset;
memcpy(&mArrayBuffer[0], src, mBufferLength);
return NS_OK;
}
@ -55,8 +59,7 @@ ArrayBufferInputStream::Available(uint64_t* aCount)
return NS_BASE_STREAM_CLOSED;
}
if (mArrayBuffer) {
uint32_t buflen = JS_GetArrayBufferByteLength(mArrayBuffer->get());
*aCount = buflen ? buflen - mPos : 0;
*aCount = mBufferLength ? mBufferLength - mPos : 0;
} else {
*aCount = 0;
}
@ -86,34 +89,14 @@ ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
while (mPos < mBufferLength) {
uint32_t remaining = mBufferLength - mPos;
MOZ_ASSERT(mArrayBuffer);
uint32_t byteLength = JS_GetArrayBufferByteLength(mArrayBuffer->get());
if (byteLength == 0) {
mClosed = true;
return NS_BASE_STREAM_CLOSED;
}
// If you change the size of this buffer, please also remember to
// update test_arraybufferinputstream.html.
char buffer[8192];
uint32_t count = std::min(std::min(aCount, remaining), uint32_t(mozilla::ArrayLength(buffer)));
uint32_t count = std::min(aCount, remaining);
if (count == 0) {
break;
}
// It is just barely possible that writer() will detach the ArrayBuffer's
// data, setting its length to zero. Or move the data to a different memory
// area. (This would only happen in a subclass that passed something other
// than NS_CopySegmentToBuffer as 'writer'). So copy the data out into a
// holding area before passing it to writer().
{
JS::AutoCheckCannotGC nogc;
bool isShared;
char* src = (char*) JS_GetArrayBufferData(mArrayBuffer->get(), &isShared, nogc) + mOffset + mPos;
MOZ_ASSERT(!isShared); // Because ArrayBuffer
memcpy(buffer, src, count);
}
uint32_t written;
nsresult rv = writer(this, closure, buffer, *result, count, &written);
nsresult rv = writer(this, closure, &mArrayBuffer[0] + mPos, *result, count, &written);
if (NS_FAILED(rv)) {
// InputStreams do not propagate errors to caller.
return NS_OK;

View File

@ -9,6 +9,7 @@
#include "nsIArrayBufferInputStream.h"
#include "js/Value.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#define NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID "@mozilla.org/io/arraybuffer-input-stream;1"
#define NS_ARRAYBUFFERINPUTSTREAM_CID \
@ -28,10 +29,9 @@ public:
private:
virtual ~ArrayBufferInputStream() {}
mozilla::Maybe<JS::PersistentRooted<JSObject*> > mArrayBuffer;
uint32_t mBufferLength; // length of slice
uint32_t mOffset; // permanent offset from start of actual buffer
uint32_t mPos; // offset from start of slice
mozilla::UniquePtr<char[]> mArrayBuffer;
uint32_t mBufferLength;
uint32_t mPos;
bool mClosed;
};

View File

@ -1714,6 +1714,9 @@ nsHttpConnection::OnSocketWritable()
NS_SUCCEEDED(mSocketOutCondition)) {
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
}
} else if (!mTransaction) {
rv = NS_ERROR_FAILURE;
LOG((" No Transaction In OnSocketWritable\n"));
} else {
// for non spdy sessions let the connection manager know

View File

@ -19,6 +19,7 @@ function test()
var ab = new ArrayBuffer(4000);
var ta = new Uint8Array(ab);
ta[0] = 'a'.charCodeAt(0);
ta[1] = 'b'.charCodeAt(0);
const Cc = SpecialPowers.Cc, Ci = SpecialPowers.Ci, Cr = SpecialPowers.Cr;
var abis = Cc["@mozilla.org/io/arraybuffer-input-stream;1"]
@ -41,13 +42,11 @@ function test()
try
{
sis.read(1);
ok(false, "reading from stream shouldn't have worked");
is(sis.read(1), "b", "should read 'b' after detaching buffer");
}
catch (e)
{
ok(e.result === Cr.NS_BASE_STREAM_CLOSED,
"detaching underneath an input stream should close it");
ok(false, "reading from stream should have worked");
}
// A regression test for bug 1265076. Previously, overflowing

View File

@ -20,13 +20,10 @@
// include_subdomains: (optional bool) whether subdomains of |name| are also covered
// pins: (string) the |name| member of an object in |pinsets|
//
// "extra_certs" is a list of base64-encoded certificates. These are used in
// "extra_certificates" is a list of base64-encoded certificates. These are used in
// pinsets that reference certificates not in our root program (for example,
// Facebook).
// Facebook or intermediate CA certs).
// equifax -> aus3
// Geotrust Primary -> www.mozilla.org
// Geotrust Global -> *. addons.mozilla.org
{
"chromium_data" : {
"cert_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.pins?format=TEXT",
@ -71,40 +68,15 @@
]
},
"pinsets": [
{
// From bug 772756, mozilla uses GeoTrust, Digicert and Thawte. Our
// cdn sites use Verisign and Baltimore. We exclude 1024-bit root certs
// from all providers. geotrust ca info:
// http://www.geotrust.com/resources/root-certificates/index.html
"name": "mozilla",
"sha256_hashes": [
"Baltimore CyberTrust Root",
"DigiCert Assured ID Root CA",
"DigiCert Global Root CA",
"DigiCert High Assurance EV Root CA",
"GeoTrust Global CA",
"GeoTrust Global CA 2",
"GeoTrust Primary Certification Authority",
"GeoTrust Primary Certification Authority - G2",
"GeoTrust Primary Certification Authority - G3",
"GeoTrust Universal CA",
"GeoTrust Universal CA 2",
"thawte Primary Root CA",
"thawte Primary Root CA - G2",
"thawte Primary Root CA - G3",
"Verisign Class 1 Public Primary Certification Authority - G3",
"Verisign Class 2 Public Primary Certification Authority - G3",
"Verisign Class 3 Public Primary Certification Authority - G3",
"VeriSign Class 3 Public Primary Certification Authority - G4",
"VeriSign Class 3 Public Primary Certification Authority - G5",
// "Verisign Class 4 Public Primary Certification Authority - G3",
"VeriSign Universal Root Certification Authority"
]
},
{
"name": "mozilla_services",
"sha256_hashes": [
"DigiCert Global Root CA"
"DigiCert Global Root CA",
"DigiCert High Assurance EV Root CA",
// Backup intermediates with Let's Encrypt are not normally
// in use and require disabling Mozilla's sites blacklisting
"Let's Encrypt Authority X3",
"Let's Encrypt Authority X4"
]
},
// For pinning tests on pinning.example.com, the certificate must be 'End
@ -185,21 +157,46 @@
// Only domains that are operationally crucial to Firefox can have per-host
// telemetry reporting (the "id") field
{ "name": "addons.mozilla.org", "include_subdomains": true,
"pins": "mozilla", "test_mode": false, "id": 1 },
"pins": "mozilla_services", "test_mode": false, "id": 1 },
{ "name": "addons.mozilla.net", "include_subdomains": true,
"pins": "mozilla", "test_mode": false, "id": 2 },
"pins": "mozilla_services", "test_mode": false, "id": 2 },
// AUS servers MUST remain in test mode
// see: https://bugzilla.mozilla.org/show_bug.cgi?id=1301956#c23
{ "name": "aus4.mozilla.org", "include_subdomains": true,
"pins": "mozilla", "test_mode": true, "id": 3 },
"pins": "mozilla_services", "test_mode": true, "id": 3 },
{ "name": "aus5.mozilla.org", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": true, "id": 7 },
// Firefox Accounts & sync
{ "name": "accounts.firefox.com", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": false, "id": 4 },
{ "name": "api.accounts.firefox.com", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": false, "id": 5 },
{ "name": "sync.services.mozilla.com", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": false, "id": 13 },
// Catch-all for all CDN resources, including product delivery
{ "name": "cdn.mozilla.net", "include_subdomains": true,
"pins": "mozilla", "test_mode": false },
"pins": "mozilla_services", "test_mode": false },
{ "name": "cdn.mozilla.org", "include_subdomains": true,
"pins": "mozilla", "test_mode": false },
"pins": "mozilla_services", "test_mode": false },
{ "name": "download.mozilla.org", "include_subdomains": false,
"pins": "mozilla_services", "test_mode": false, "id": 14 },
// Catch-all for everything hosted under services.mozilla.com
{ "name": "services.mozilla.com", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": false, "id": 6 },
// Catch-all for everything hosted under telemetry.mozilla.org
// MUST remain in test mode in order to receive telemetry on broken pins
{ "name": "telemetry.mozilla.org", "include_subdomains": true,
"pins": "mozilla_services", "test_mode": true, "id": 8 },
// Test Pilot
{ "name": "testpilot.firefox.com", "include_subdomains": false,
"pins": "mozilla_services", "test_mode": false, "id": 9 },
// Crash report sites
{ "name": "crash-reports.mozilla.com", "include_subdomains": false,
"pins": "mozilla_services", "test_mode": false, "id": 10 },
{ "name": "crash-reports-xpsp2.mozilla.com", "include_subdomains": false,
"pins": "mozilla_services", "test_mode": false, "id": 11 },
{ "name": "crash-stats.mozilla.com", "include_subdomains": false,
"pins": "mozilla_services", "test_mode": false, "id": 12 },
{ "name": "include-subdomains.pinning.example.com",
"include_subdomains": true, "pins": "mozilla_test",
"test_mode": false },
@ -213,10 +210,17 @@
// twitterCDN. More specific rules take precedence because we search for
// exact domain name first.
{ "name": "twitter.com", "include_subdomains": true,
"pins": "twitterCDN", "test_mode": false },
{ "name": "aus5.mozilla.org", "include_subdomains": true,
"pins": "mozilla", "test_mode": true, "id": 7 }
"pins": "twitterCDN", "test_mode": false }
],
"extra_certificates": []
// When pinning to non-root certs, like intermediates,
// place the PEM of the pinned certificate in this array
// so Firefox can find the subject DN and public key
"extra_certificates": [
// Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
// Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1
"MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrXNSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHlNpi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7DcGu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgzuEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGxA/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRMUM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOuOsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vwp7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKRPB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5brUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt",
// Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X4
// Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1
"MIIFjTCCA3WgAwIBAgIRAJObmZ6kjhYNW0JZtD0gE9owDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0NDM0WhcNMjExMDA2MTU0NDM0WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhJHRCe7eRMdlz/ziq2M5EXLc5CtxErg29RbmXN2evvVBPX9MQVGv3QdqOY+ZtW8DoQKmMQfzRA4n/YmEJYNYHBXiakL0aZD5P3M93L4lry2evQU3FjQDAa/6NhNy18pUxqOj2kKBDSpN0XLM+Q2lLiSJHdFE+mWTDzSQB+YQvKHcXIqfdw2wITGYvN3TFb5OOsEY3FmHRUJjIsA9PWFN8rPbaLZZhUK1D3AqmT561Urmcju9O30azMdwg/GnCoyB1Puw4GzZOZmbS3/VmpJMve6YOlD5gPUpLHG+6tE0cPJFYbi9NxNpw2+0BOXbASefpNbUUBpDB5ZLiEP1rubSFAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBTFsatOTLHNZDCTfsGEmQWr5gPiJTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBAF4tI1yGjZgld9lP01+zftU3aSV0un0d2GKUMO7GxvwTLWAKQz/eT+u3J4+GvpD+BMfopIxkJcDCzMChjjZtZZwJpIY7BatVrO6OkEmaRNITtbZ/hCwNkUnbk3C7EG3OGJZlo9b2wzA8v9WBsPzHpTvLfOr+dS57LLPZBhp3ArHaLbdk33lIONRPt9sseDEkmdHnVmGmBRf4+J0Wy67mddOvz5rHH8uzY94raOayf20gzzcmqmot4hPXtDG4Y49MoFMMT2kcWck3EOTAH6QiGWkGJ7cxMfSL3S0niA6wgFJtfETETOZu8AVDgENgCJ3DS0bz/dhVKvs3WRkaKuuR/W0nnC2VDdaFj4+CRF8LGtn/8ERaH48TktH5BDyDVcF9zfJ75Scxcy23jAL2N6w3n/t3nnqoXt9Im4FprDr+mP1g2Z6Lf2YA0jE3kZalgZ6lNHu4CmvJYoOTSJw9X2qlGl1K+B4U327rG1tRxgjM76pN6lIS02PMECoyKJigpOSBu4V8+LVaUMezCJH9Qf4EKeZTHddQ1t96zvNd2s9ewSKx/DblXbKsBDzIdHJ+qi6+F9DIVM5/ICdtDdulOO+dr/BXB+pBZ3uVxjRANvJKKpdxkePyluITSNZHbanWRN07gMvwBWOL060i4VrL9er1sBQrRjU9iNpZQGTnLVAxQVFu"
]
}

View File

@ -73,11 +73,15 @@
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/HangMonitor.h"
#if defined(MOZ_ENABLE_PROFILER_SPS)
#include "shared-libraries.h"
#if defined(MOZ_STACKWALKING)
#define ENABLE_STACK_CAPTURE
#include "mozilla/StackWalk.h"
#include "nsPrintfCString.h"
#endif
#endif // MOZ_STACKWALKING
#endif // MOZ_ENABLE_PROFILER_SPS
namespace {
@ -395,7 +399,7 @@ HangReports::GetAnnotationInfo() const {
return mAnnotationInfo;
}
#if defined(MOZ_ENABLE_PROFILER_SPS)
#if defined(ENABLE_STACK_CAPTURE)
const uint8_t kMaxKeyLength = 50;
@ -883,6 +887,8 @@ public:
int32_t aSystemUptime,
int32_t aFirefoxUptime,
HangAnnotationsPtr aAnnotations);
#endif
#if defined(ENABLE_STACK_CAPTURE)
static void DoStackCapture(const nsACString& aKey);
#endif
static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
@ -931,7 +937,7 @@ private:
HangReports mHangReports;
Mutex mHangReportsMutex;
#if defined(MOZ_ENABLE_PROFILER_SPS)
#if defined(ENABLE_STACK_CAPTURE)
// Stores data about stacks captured on demand.
KeyedStackCapturer mStackCapturer;
#endif
@ -1528,7 +1534,7 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
NS_IMETHODIMP
TelemetryImpl::SnapshotCapturedStacks(bool clear, JSContext *cx, JS::MutableHandle<JS::Value> ret)
{
#if defined(MOZ_ENABLE_PROFILER_SPS)
#if defined(ENABLE_STACK_CAPTURE)
nsresult rv = mStackCapturer.ReflectCapturedStacks(cx, ret);
if (clear) {
mStackCapturer.Clear();
@ -2461,6 +2467,7 @@ TelemetryImpl::RecordChromeHang(uint32_t aDuration,
Move(annotations));
}
#if defined(ENABLE_STACK_CAPTURE)
void
TelemetryImpl::DoStackCapture(const nsACString& aKey) {
if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) {
@ -2468,10 +2475,11 @@ TelemetryImpl::DoStackCapture(const nsACString& aKey) {
}
}
#endif
#endif
nsresult
TelemetryImpl::CaptureStack(const nsACString& aKey) {
#if defined(MOZ_ENABLE_PROFILER_SPS)
#if defined(ENABLE_STACK_CAPTURE)
TelemetryImpl::DoStackCapture(aKey);
#endif
return NS_OK;
@ -3148,7 +3156,9 @@ void RecordChromeHang(uint32_t duration,
void CaptureStack(const nsACString& aKey)
{
#if defined(ENABLE_STACK_CAPTURE)
TelemetryImpl::DoStackCapture(aKey);
#endif
}
#endif

View File

@ -4,6 +4,10 @@
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
Cu.import("resource://gre/modules/AppConstants.jsm", this);
// We need both in order to capture stacks.
const ENABLE_TESTS = AppConstants.MOZ_ENABLE_PROFILER_SPS &&
AppConstants.MOZ_STACKWALKING;
/**
* Ensures that the sctucture of the javascript object used for capturing stacks
* is as intended. The structure is expected to be as in this example:
@ -66,7 +70,7 @@ const TEST_STACK_KEYS = ["TEST-KEY1", "TEST-KEY2"];
* Ensures that captured stacks appear in pings, if any were captured.
*/
add_task({
skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
skip_if: () => !ENABLE_TESTS
}, function* test_capturedStacksAppearInPings() {
yield TelemetryController.testSetup();
captureStacks("DOES-NOT-MATTER", false);
@ -83,7 +87,7 @@ add_task({
* of captured stacks and adds a new entry to captures.
*/
add_task({
skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
skip_if: () => !ENABLE_TESTS
}, function* test_CaptureStacksIncreasesNumberOfCapturedStacks() {
// Construct a unique key for this test.
let key = TEST_STACK_KEYS[0] + "-UNIQUE-KEY-1";
@ -111,7 +115,7 @@ add_task({
* more than once for the key, the length of stacks does not increase.
*/
add_task({
skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
skip_if: () => !ENABLE_TESTS
}, function* test_CaptureStacksGroupsDuplicateStacks() {
// Make sure that there are initial captures for TEST_STACK_KEYS[0].
let stacks = captureStacks(TEST_STACK_KEYS[0], false);
@ -142,7 +146,7 @@ add_task({
* for other keys.
*/
add_task({
skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
skip_if: () => !ENABLE_TESTS
}, function* test_CaptureStacksSeparatesInformationByKeys() {
// Make sure that there are initial captures for TEST_STACK_KEYS[0].
let stacks = captureStacks(TEST_STACK_KEYS[0], false);
@ -169,7 +173,7 @@ add_task({
* Ensure that Telemetry does not allow weird keys.
*/
add_task({
skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
skip_if: () => !ENABLE_TESTS
}, function* test_CaptureStacksDoesNotAllowBadKey() {
for (let badKey of [null, "KEY-!@\"#$%^&*()_"]) {
let stacks = captureStacks(badKey);

Some files were not shown because too many files have changed in this diff Show More