зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c, a=merge
MozReview-Commit-ID: DAm5uRF7n91
This commit is contained in:
Коммит
ef3cede9a5
|
@ -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",
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<Url type="application/x-suggestions+json" method="GET" template="https://completion.amazon.com/search/complete?q={searchTerms}&search-alias=aps&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"/>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
},
|
||||
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
|
||||
DevToolsModules(
|
||||
'grids.js',
|
||||
'highlighter-settings.js',
|
||||
'index.js',
|
||||
)
|
||||
|
|
|
@ -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 ],
|
||||
|
|
|
@ -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")
|
||||
);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
});
|
|
@ -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
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
@ -9,4 +9,6 @@ DevToolsModules(
|
|||
'Accordion.js',
|
||||
'App.js',
|
||||
'Grid.js',
|
||||
'GridDisplaySettings.js',
|
||||
'GridList.js',
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -5,3 +5,4 @@
|
|||
"use strict";
|
||||
|
||||
exports.grids = require("./grids");
|
||||
exports.highlighterSettings = require("./highlighter-settings");
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
|
||||
DevToolsModules(
|
||||
'grids.js',
|
||||
'highlighter-settings.js',
|
||||
'index.js',
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -8917,6 +8917,8 @@ nsDocShell::RestoreFromHistory()
|
|||
nsSubDocumentFrame* subDocFrame =
|
||||
do_QueryFrame(container->GetPrimaryFrame());
|
||||
rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
|
||||
} else {
|
||||
rootViewParent = nullptr;
|
||||
}
|
||||
if (sibling &&
|
||||
sibling->GetShell() &&
|
||||
|
|
|
@ -146,7 +146,8 @@ TabGroup::GetTopLevelWindows()
|
|||
nsTArray<nsPIDOMWindowOuter*> array;
|
||||
|
||||
for (nsPIDOMWindowOuter* outerWindow : mWindows) {
|
||||
if (!outerWindow->GetScriptableParentOrNull()) {
|
||||
if (outerWindow->GetDocShell() &&
|
||||
!outerWindow->GetScriptableParentOrNull()) {
|
||||
array.AppendElement(outerWindow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,11 @@ DOMInterfaces = {
|
|||
'concrete': False
|
||||
},
|
||||
|
||||
'AddonManagerPermissions': {
|
||||
'wrapperCache': False,
|
||||
'concrete': False
|
||||
},
|
||||
|
||||
'AnimationEffectReadOnly': {
|
||||
'concrete': False
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ ScaledFontDWrite::GetSkTypeface()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle);
|
||||
mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle, mForceGDIMode);
|
||||
}
|
||||
return mTypeface;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(); });
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 *() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче