Merge m-c to m-i

MozReview-Commit-ID: B5Nf2oiDcqG
This commit is contained in:
Phil Ringnalda 2016-10-10 19:13:45 -07:00
commit fe9d9c45d7
57 changed files with 11841 additions and 735 deletions

View File

@ -87,11 +87,12 @@ function testToggleToolboxButtons() {
let toolboxButtonNodes = [...doc.querySelectorAll(".command-button")];
let toggleableTools = toolbox.toolboxButtons;
// The noautohide button is only displayed in the browser toolbox
// The noautohide button is only displayed in the browser toolbox, and the element
// picker button is not toggleable.
toggleableTools = toggleableTools.filter(
tool => tool.id != "command-button-noautohide");
tool => tool.id != "command-button-noautohide" && tool.id != "command-button-pick");
toolboxButtonNodes = toolboxButtonNodes.filter(
btn => btn.id != "command-button-noautohide");
btn => btn.id != "command-button-noautohide" && btn.id != "command-button-pick");
is(checkNodes.length, toggleableTools.length,
"All of the buttons are toggleable.");

View File

@ -71,10 +71,6 @@ loader.lazyGetter(this, "registerHarOverlay", () => {
// addons that have manually inserted toolbarbuttons into DOM.
// (By default, supported target is only local tab)
const ToolboxButtons = exports.ToolboxButtons = [
{ id: "command-button-pick",
isTargetSupported: target =>
target.getTrait("highlightable")
},
{ id: "command-button-frames",
isTargetSupported: target => {
return target.activeTab && target.activeTab.traits.frames;
@ -958,7 +954,7 @@ Toolbox.prototype = {
* Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
*/
_buildButtons: function () {
if (!this.target.isAddon || this.target.isWebExtension) {
if (this.target.getTrait("highlightable")) {
this._buildPickerButton();
}
@ -1003,7 +999,6 @@ Toolbox.prototype = {
this._pickerButton.className =
"command-button command-button-invertable devtools-button";
this._pickerButton.setAttribute("title", L10N.getStr("pickButton.tooltip"));
this._pickerButton.setAttribute("hidden", "true");
let container = this.doc.querySelector("#toolbox-picker-container");
container.appendChild(this._pickerButton);

View File

@ -42,7 +42,6 @@ const TOOLBOX_L10N = new LocalizationHelper("devtools/locale/toolbox.properties"
// Sidebar dimensions
const INITIAL_SIDEBAR_SIZE = 350;
const MIN_SIDEBAR_SIZE = 50;
// If the toolbox width is smaller than given amount of pixels,
// the sidebar automatically switches from 'landscape' to 'portrait' mode.
@ -457,7 +456,6 @@ Inspector.prototype = {
className: "inspector-sidebar-splitter",
initialWidth: INITIAL_SIDEBAR_SIZE,
initialHeight: INITIAL_SIDEBAR_SIZE,
minSize: MIN_SIDEBAR_SIZE,
splitterSize: 1,
endPanelControl: true,
startPanel: this.InspectorTabPanel({

View File

@ -14,7 +14,6 @@ devtools.jar:
content/projecteditor/chrome/content/projecteditor-test.xul (projecteditor/chrome/content/projecteditor-test.xul)
content/projecteditor/chrome/content/projecteditor-loader.js (projecteditor/chrome/content/projecteditor-loader.js)
content/netmonitor/netmonitor.xul (netmonitor/netmonitor.xul)
content/netmonitor/netmonitor.css (netmonitor/netmonitor.css)
content/netmonitor/netmonitor-controller.js (netmonitor/netmonitor-controller.js)
content/netmonitor/netmonitor-view.js (netmonitor/netmonitor-view.js)
content/webconsole/webconsole.xul (webconsole/webconsole.xul)

View File

@ -0,0 +1,129 @@
"use strict";
/**
* Predicates used when filtering items.
*
* @param object item
* The filtered item.
* @return boolean
* True if the item should be visible, false otherwise.
*/
function all() {
return true;
}
function isHtml({ mimeType }) {
return mimeType && mimeType.includes("/html");
}
function isCss({ mimeType }) {
return mimeType && mimeType.includes("/css");
}
function isJs({ mimeType }) {
return mimeType && (
mimeType.includes("/ecmascript") ||
mimeType.includes("/javascript") ||
mimeType.includes("/x-javascript"));
}
function isXHR(item) {
// Show the request it is XHR, except if the request is a WS upgrade
return item.isXHR && !isWS(item);
}
function isFont({ url, mimeType }) {
// Fonts are a mess.
return (mimeType && (
mimeType.includes("font/") ||
mimeType.includes("/font"))) ||
url.includes(".eot") ||
url.includes(".ttf") ||
url.includes(".otf") ||
url.includes(".woff");
}
function isImage({ mimeType }) {
return mimeType && mimeType.includes("image/");
}
function isMedia({ mimeType }) {
// Not including images.
return mimeType && (
mimeType.includes("audio/") ||
mimeType.includes("video/") ||
mimeType.includes("model/"));
}
function isFlash({ url, mimeType }) {
// Flash is a mess.
return (mimeType && (
mimeType.includes("/x-flv") ||
mimeType.includes("/x-shockwave-flash"))) ||
url.includes(".swf") ||
url.includes(".flv");
}
function isWS({ requestHeaders, responseHeaders }) {
// Detect a websocket upgrade if request has an Upgrade header with value 'websocket'
if (!requestHeaders || !Array.isArray(requestHeaders.headers)) {
return false;
}
// Find the 'upgrade' header.
let upgradeHeader = requestHeaders.headers.find(header => {
return (header.name == "Upgrade");
});
// If no header found on request, check response - mainly to get
// something we can unit test, as it is impossible to set
// the Upgrade header on outgoing XHR as per the spec.
if (!upgradeHeader && responseHeaders &&
Array.isArray(responseHeaders.headers)) {
upgradeHeader = responseHeaders.headers.find(header => {
return (header.name == "Upgrade");
});
}
// Return false if there is no such header or if its value isn't 'websocket'.
if (!upgradeHeader || upgradeHeader.value != "websocket") {
return false;
}
return true;
}
function isOther(item) {
let tests = [isHtml, isCss, isJs, isXHR, isFont, isImage, isMedia, isFlash, isWS];
return tests.every(is => !is(item));
}
function isFreetextMatch({ url }, text) {
let lowerCaseUrl = url.toLowerCase();
let lowerCaseText = text.toLowerCase();
let textLength = text.length;
// Support negative filtering
if (text.startsWith("-") && textLength > 1) {
lowerCaseText = lowerCaseText.substring(1, textLength);
return !lowerCaseUrl.includes(lowerCaseText);
}
// no text is a positive match
return !text || lowerCaseUrl.includes(lowerCaseText);
}
exports.Filters = {
all: all,
html: isHtml,
css: isCss,
js: isJs,
xhr: isXHR,
fonts: isFont,
images: isImage,
media: isMedia,
flash: isFlash,
ws: isWS,
other: isOther,
};
exports.isFreetextMatch = isFreetextMatch;

View File

@ -7,7 +7,6 @@ const { Ci } = require("chrome");
const { Class } = require("sdk/core/heritage");
const { resolve } = require("promise");
const Services = require("Services");
const { Task } = require("devtools/shared/task");
loader.lazyRequireGetter(this, "HarCollector", "devtools/client/netmonitor/har/har-collector", true);
loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/har/har-exporter", true);
@ -200,51 +199,6 @@ var HarAutomation = Class({
getString: function (stringGrip) {
return this.webConsoleClient.getString(stringGrip);
},
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
*
* @param object headers
* The "requestHeaders".
* @param object uploadHeaders
* The "requestHeadersFromUploadStream".
* @param object postData
* The "requestPostData".
* @return array
* A promise that is resolved with the extracted form data.
*/
_getFormDataSections: Task.async(function* (headers, uploadHeaders,
postData) {
let formDataSections = [];
let { headers: requestHeaders } = headers;
let { headers: payloadHeaders } = uploadHeaders;
let allHeaders = [...payloadHeaders, ...requestHeaders];
let contentTypeHeader = allHeaders.find(e => {
return e.name.toLowerCase() == "content-type";
});
let contentTypeLongString = contentTypeHeader ?
contentTypeHeader.value : "";
let contentType = yield this.getString(contentTypeLongString);
if (contentType.includes("x-www-form-urlencoded")) {
let postDataLongString = postData.postData.text;
let data = yield this.getString(postDataLongString);
for (let section of data.split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
// isn't a line containing upload stream headers.
if (payloadHeaders.every(header => !section.startsWith(header.name))) {
formDataSections.push(section);
}
}
}
return formDataSections;
}),
});
// Helpers

View File

@ -8,6 +8,7 @@ const { LocalizationHelper } = require("devtools/shared/l10n");
const Services = require("Services");
const appInfo = Services.appinfo;
const { CurlUtils } = require("devtools/client/shared/curl");
const { getFormDataSections } = require("devtools/client/netmonitor/request-utils");
loader.lazyRequireGetter(this, "NetworkHelper", "devtools/shared/webconsole/network-helper");
@ -272,16 +273,19 @@ HarBuilder.prototype = {
postData.mimeType = "application/x-www-form-urlencoded";
// Extract form parameters and produce nice HAR array.
this._options.view._getFormDataSections(file.requestHeaders,
getFormDataSections(
file.requestHeaders,
file.requestHeadersFromUploadStream,
file.requestPostData).then(formDataSections => {
formDataSections.forEach(section => {
let paramsArray = NetworkHelper.parseQueryString(section);
if (paramsArray) {
postData.params = [...postData.params, ...paramsArray];
}
});
file.requestPostData,
this._options.getString
).then(formDataSections => {
formDataSections.forEach(section => {
let paramsArray = NetworkHelper.parseQueryString(section);
if (paramsArray) {
postData.params = [...postData.params, ...paramsArray];
}
});
});
}
});

View File

@ -8,7 +8,9 @@ DIRS += [
]
DevToolsModules(
'panel.js'
'filter-predicates.js',
'panel.js',
'request-utils.js',
)
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']

View File

@ -32,7 +32,8 @@ const {ViewHelpers, Heritage, WidgetMethods, setNamedTimeout} =
require("devtools/client/shared/widgets/view-helpers");
const {gDevTools} = require("devtools/client/framework/devtools");
const {Curl, CurlUtils} = require("devtools/client/shared/curl");
const {Filters, isFreetextMatch} = require("devtools/client/netmonitor/filter-predicates");
const {getFormDataSections} = require("devtools/client/netmonitor/request-utils");
/**
* Localization convenience methods.
*/
@ -759,65 +760,19 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
clipboardHelper.copyString(string);
},
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
*
* @param object headers
* The "requestHeaders".
* @param object uploadHeaders
* The "requestHeadersFromUploadStream".
* @param object postData
* The "requestPostData".
* @return array
* A promise that is resolved with the extracted form data.
*/
_getFormDataSections: Task.async(function* (headers, uploadHeaders,
postData) {
let formDataSections = [];
let { headers: requestHeaders } = headers;
let { headers: payloadHeaders } = uploadHeaders;
let allHeaders = [...payloadHeaders, ...requestHeaders];
let contentTypeHeader = allHeaders.find(e => {
return e.name.toLowerCase() == "content-type";
});
let contentTypeLongString = contentTypeHeader ?
contentTypeHeader.value : "";
let contentType = yield gNetwork.getString(contentTypeLongString);
if (contentType.includes("x-www-form-urlencoded")) {
let postDataLongString = postData.postData.text;
let text = yield gNetwork.getString(postDataLongString);
for (let section of text.split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
// isn't a line containing upload stream headers.
if (payloadHeaders.every(header => !section.startsWith(header.name))) {
formDataSections.push(section);
}
}
}
return formDataSections;
}),
/**
* Copy the request form data parameters (or raw payload) from
* the currently selected item.
*/
copyPostData: Task.async(function* () {
let selected = this.selectedItem.attachment;
let view = this;
// Try to extract any form data parameters.
let formDataSections = yield view._getFormDataSections(
let formDataSections = yield getFormDataSections(
selected.requestHeaders,
selected.requestHeadersFromUploadStream,
selected.requestPostData);
selected.requestPostData,
gNetwork.getString.bind(gNetwork));
let params = [];
formDataSections.forEach(section => {
@ -1130,7 +1085,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
*/
_enableFilter: function (type) {
// Make sure this is a valid filter type.
if (Object.keys(this._allFilterPredicates).indexOf(type) == -1) {
if (!Object.keys(Filters).includes(type)) {
return;
}
@ -1152,35 +1107,12 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
* the active filters.
*/
get _filterPredicate() {
let filterPredicates = this._allFilterPredicates;
let currentFreetextFilter = this._currentFreetextFilter;
return requestItem => {
return this._activeFilters.some(filterName => {
return filterPredicates[filterName].call(this, requestItem) &&
filterPredicates.freetext.call(this, requestItem,
currentFreetextFilter);
});
};
},
/**
* Returns an object with all the filter predicates as [key: function] pairs.
*/
get _allFilterPredicates() {
return {
all: () => true,
html: this.isHtml,
css: this.isCss,
js: this.isJs,
xhr: this.isXHR,
fonts: this.isFont,
images: this.isImage,
media: this.isMedia,
flash: this.isFlash,
ws: this.isWS,
other: this.isOther,
freetext: this.isFreetextMatch
const { attachment } = requestItem;
return this._activeFilters.some(filterName => Filters[filterName](attachment)) &&
isFreetextMatch(attachment, currentFreetextFilter);
};
},
@ -1303,125 +1235,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.refreshSummary();
},
/**
* Predicates used when filtering items.
*
* @param object item
* The filtered item.
* @return boolean
* True if the item should be visible, false otherwise.
*/
isHtml: function ({ attachment: { mimeType } }) {
return mimeType && mimeType.includes("/html");
},
isCss: function ({ attachment: { mimeType } }) {
return mimeType && mimeType.includes("/css");
},
isJs: function ({ attachment: { mimeType } }) {
return mimeType && (
mimeType.includes("/ecmascript") ||
mimeType.includes("/javascript") ||
mimeType.includes("/x-javascript"));
},
isXHR: function (item) {
// Show the request it is XHR, except
// if the request is a WS upgrade
return item.attachment.isXHR && !this.isWS(item);
},
isFont: function ({ attachment: { url, mimeType } }) {
// Fonts are a mess.
return (mimeType && (
mimeType.includes("font/") ||
mimeType.includes("/font"))) ||
url.includes(".eot") ||
url.includes(".ttf") ||
url.includes(".otf") ||
url.includes(".woff");
},
isImage: function ({ attachment: { mimeType } }) {
return mimeType && mimeType.includes("image/");
},
isMedia: function ({ attachment: { mimeType } }) {
// Not including images.
return mimeType && (
mimeType.includes("audio/") ||
mimeType.includes("video/") ||
mimeType.includes("model/"));
},
isFlash: function ({ attachment: { url, mimeType } }) {
// Flash is a mess.
return (mimeType && (
mimeType.includes("/x-flv") ||
mimeType.includes("/x-shockwave-flash"))) ||
url.includes(".swf") ||
url.includes(".flv");
},
isWS: function ({ attachment: { requestHeaders, responseHeaders } }) {
// Detect a websocket upgrade if request has an Upgrade header
// with value 'websocket'
if (!requestHeaders || !Array.isArray(requestHeaders.headers)) {
return false;
}
// Find the 'upgrade' header.
let upgradeHeader = requestHeaders.headers.find(header => {
return (header.name == "Upgrade");
});
// If no header found on request, check response - mainly to get
// something we can unit test, as it is impossible to set
// the Upgrade header on outgoing XHR as per the spec.
if (!upgradeHeader && responseHeaders &&
Array.isArray(responseHeaders.headers)) {
upgradeHeader = responseHeaders.headers.find(header => {
return (header.name == "Upgrade");
});
}
// Return false if there is no such header or if its value isn't
// 'websocket'.
if (!upgradeHeader || upgradeHeader.value != "websocket") {
return false;
}
return true;
},
isOther: function (e) {
return !this.isHtml(e) &&
!this.isCss(e) &&
!this.isJs(e) &&
!this.isXHR(e) &&
!this.isFont(e) &&
!this.isImage(e) &&
!this.isMedia(e) &&
!this.isFlash(e) &&
!this.isWS(e);
},
isFreetextMatch: function ({ attachment: { url } }, text) {
let lowerCaseUrl = url.toLowerCase();
let lowerCaseText = text.toLowerCase();
let textLength = text.length;
// Support negative filtering
if (text.startsWith("-") && textLength > 1) {
lowerCaseText = lowerCaseText.substring(1, textLength);
return !lowerCaseUrl.includes(lowerCaseText);
}
// no text is a positive match
return !text || lowerCaseUrl.includes(lowerCaseText);
},
/**
* Predicates used when sorting items.
*
@ -2947,7 +2760,7 @@ NetworkDetailsView.prototype = {
$("#raw-headers").hidden = true;
$("#response-content-image-box").hidden = true;
let isHtml = RequestsMenuView.prototype.isHtml({ attachment: data });
let isHtml = Filters.html(data);
// Show the "Preview" tabpanel only for plain HTML responses.
this.sidebar.toggleTab(isHtml, "preview-tab");
@ -3293,8 +3106,11 @@ NetworkDetailsView.prototype = {
return;
}
let formDataSections = yield RequestsMenuView.prototype
._getFormDataSections(headers, uploadHeaders, postData);
let formDataSections = yield getFormDataSections(
headers,
uploadHeaders,
postData,
gNetwork.getString.bind(gNetwork));
this._params.onlyEnumVisible = false;
@ -3838,8 +3654,7 @@ PerformanceStatisticsView.prototype = {
*/
_sanitizeChartDataSource: function (items, emptyCache) {
let data = [
"html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws",
"other"
"html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws", "other"
].map(e => ({
cached: 0,
count: 0,
@ -3852,31 +3667,31 @@ PerformanceStatisticsView.prototype = {
let details = requestItem.attachment;
let type;
if (RequestsMenuView.prototype.isHtml(requestItem)) {
if (Filters.html(details)) {
// "html"
type = 0;
} else if (RequestsMenuView.prototype.isCss(requestItem)) {
} else if (Filters.css(details)) {
// "css"
type = 1;
} else if (RequestsMenuView.prototype.isJs(requestItem)) {
} else if (Filters.js(details)) {
// "js"
type = 2;
} else if (RequestsMenuView.prototype.isFont(requestItem)) {
} else if (Filters.fonts(details)) {
// "fonts"
type = 4;
} else if (RequestsMenuView.prototype.isImage(requestItem)) {
} else if (Filters.images(details)) {
// "images"
type = 5;
} else if (RequestsMenuView.prototype.isMedia(requestItem)) {
} else if (Filters.media(details)) {
// "media"
type = 6;
} else if (RequestsMenuView.prototype.isFlash(requestItem)) {
} else if (Filters.flash(details)) {
// "flash"
type = 7;
} else if (RequestsMenuView.prototype.isWS(requestItem)) {
} else if (Filters.ws(details)) {
// "ws"
type = 8;
} else if (RequestsMenuView.prototype.isXHR(requestItem)) {
} else if (Filters.xhr(details)) {
// Verify XHR last, to categorize other mime types in their own blobs.
// "xhr"
type = 3;

View File

@ -1,47 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
#toolbar-labels {
overflow: hidden;
}
/**
* Collapsed details pane needs to be truly hidden to prevent both accessibility
* tools and keyboard from accessing its contents.
*/
#details-pane.pane-collapsed {
visibility: hidden;
}
#details-pane-toggle[disabled] {
display: none;
}
#custom-pane {
overflow: auto;
}
#response-content-image-box {
overflow: auto;
}
#network-statistics-charts {
overflow: auto;
}
.cropped-textbox .textbox-input {
/* workaround for textbox not supporting the @crop attribute */
text-overflow: ellipsis;
}
/* Responsive sidebar */
@media (max-width: 700px) {
#toolbar-spacer,
#details-pane-toggle,
.requests-menu-waterfall,
#requests-menu-network-summary-button > .toolbarbutton-text {
display: none;
}
}

View File

@ -4,7 +4,6 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/netmonitor/netmonitor.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/netmonitor.css" type="text/css"?>
<!DOCTYPE window [

View File

@ -0,0 +1,50 @@
"use strict";
const { Task } = require("devtools/shared/task");
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
*
* @param object headers
* The "requestHeaders".
* @param object uploadHeaders
* The "requestHeadersFromUploadStream".
* @param object postData
* The "requestPostData".
* @param object getString
Callback to retrieve a string from a LongStringGrip.
* @return array
* A promise that is resolved with the extracted form data.
*/
exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
getString) {
let formDataSections = [];
let { headers: requestHeaders } = headers;
let { headers: payloadHeaders } = uploadHeaders;
let allHeaders = [...payloadHeaders, ...requestHeaders];
let contentTypeHeader = allHeaders.find(e => {
return e.name.toLowerCase() == "content-type";
});
let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
let contentType = yield getString(contentTypeLongString);
if (contentType.includes("x-www-form-urlencoded")) {
let postDataLongString = postData.postData.text;
let text = yield getString(postDataLongString);
for (let section of text.split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
// isn't a line containing upload stream headers.
if (payloadHeaders.every(header => !section.startsWith(header.name))) {
formDataSections.push(section);
}
}
}
return formDataSections;
});

View File

@ -37,7 +37,6 @@ pref("devtools.toolbox.splitconsoleEnabled", false);
pref("devtools.toolbox.splitconsoleHeight", 100);
// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);
pref("devtools.command-button-frames.enabled", true);
pref("devtools.command-button-splitconsole.enabled", true);
pref("devtools.command-button-paintflashing.enabled", false);

View File

@ -59,7 +59,7 @@ define(function (require, exports, module) {
);
});
let ownProperties = object.preview ? object.preview.ownProperties : [];
let ownProperties = object.preview ? object.preview.ownProperties : {};
let indexes = this.getPropIndexes(ownProperties, max, isInterestingProp);
if (indexes.length < max && indexes.length < object.ownPropertyLength) {
// There are not enough props yet. Then add uninteresting props to display them.
@ -70,24 +70,17 @@ define(function (require, exports, module) {
);
}
let props = this.getProps(ownProperties, indexes);
if (props.length < object.ownPropertyLength) {
const truncate = Object.keys(ownProperties).length > max;
let props = this.getProps(ownProperties, indexes, truncate);
if (truncate) {
// There are some undisplayed props. Then display "more...".
let objectLink = this.props.objectLink || span;
props.push(Caption({
object: objectLink({
object: object
}, ((object ? object.ownPropertyLength : 0) - max) + " more…")
}, `${object.ownPropertyLength - max} more…`)
}));
} else if (props.length > 0) {
// Remove the last comma.
// NOTE: do not change comp._store.props directly to update a property,
// it should be re-rendered or cloned with changed props
let last = props.length - 1;
props[last] = React.cloneElement(props[last], {
delim: ""
});
}
return props;
@ -98,9 +91,10 @@ define(function (require, exports, module) {
*
* @param {Object} ownProperties Props object.
* @param {Array} indexes Indexes of props.
* @param {Boolean} truncate true if the grip will be truncated.
* @return {Array} Props.
*/
getProps: function (ownProperties, indexes) {
getProps: function (ownProperties, indexes, truncate) {
let props = [];
// Make indexes ordered by ascending.
@ -117,7 +111,7 @@ define(function (require, exports, module) {
name: name,
object: value,
equal: ": ",
delim: ", ",
delim: i !== indexes.length - 1 || truncate ? ", " : "",
defaultRep: Grip
})));
});
@ -169,7 +163,7 @@ define(function (require, exports, module) {
(this.props.mode == "long") ? 100 : 3);
let objectLink = this.props.objectLink || span;
if (this.props.mode == "tiny" || !props.length) {
if (this.props.mode == "tiny") {
return (
span({className: "objectBox objectBox-object"},
this.getTitle(object),

View File

@ -27,6 +27,7 @@ window.onload = Task.async(function* () {
yield testMaxProps();
yield testMoreThanMaxProps();
yield testUninterestingProps();
yield testNonEnumerableProps();
// Test that properties are rendered as expected by PropRep
yield testNestedObject();
@ -50,7 +51,7 @@ window.onload = Task.async(function* () {
is(renderedRep.type, Grip.rep, `Rep correctly selects ${Grip.rep.displayName}`);
// Test rendering
const defaultOutput = `Object`;
const defaultOutput = `Object { }`;
const modeTests = [
{
@ -59,7 +60,7 @@ window.onload = Task.async(function* () {
},
{
mode: "tiny",
expectedOutput: defaultOutput,
expectedOutput: `Object`,
},
{
mode: "short",
@ -146,6 +147,40 @@ window.onload = Task.async(function* () {
const expectedOutput = `Object { a: undefined, b: undefined, c: "c", 1 more… }`;
}
function testNonEnumerableProps() {
// Test object: `Object.defineProperty({}, "foo", {enumerable : false});`
const testName = "testNonEnumerableProps";
// Test that correct rep is chosen
const gripStub = getGripStub("testNonEnumerableProps");
const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
is(renderedRep.type, Grip.rep, `Rep correctly selects ${Grip.rep.displayName}`);
// Test rendering
const defaultOutput = `Object { }`;
const modeTests = [
{
mode: undefined,
expectedOutput: defaultOutput,
},
{
mode: "tiny",
expectedOutput: `Object`,
},
{
mode: "short",
expectedOutput: defaultOutput,
},
{
mode: "long",
expectedOutput: defaultOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testNestedObject() {
// Test object: `{objProp: {id: 1}, strProp: "test string"}`
const testName = "testNestedObject";
@ -362,7 +397,22 @@ window.onload = Task.async(function* () {
"safeGetterValues": {}
}
};
case "testNonEnumerableProps":
return {
"type": "object",
"actor": "server1.conn1.child1/obj30",
"class": "Object",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 1,
"preview": {
"kind": "Object",
"ownProperties": {},
"ownPropertiesLength": 1,
"safeGetterValues": {}
}
};
case "testNestedObject":
return {
"type": "object",

View File

@ -27,6 +27,7 @@
#font-showall {
cursor: pointer;
flex-shrink: 0;
}
#font-showall:hover {

View File

@ -31,11 +31,14 @@ window {
/* The main panel layout. This area consists of a toolbar, markup view
and breadcrumbs bar. */
.devtools-main-content {
#inspector-main-content {
/* Subtract 1 pixel from the panel height. It's puzzling why this
is needed, but if not presented the entire Inspector panel
content jumps 1 pixel up when the Toolbox is opened. */
height: calc(100% - 1px);
/* This min-width avoids a visual glitch when moving the splitter quickly to the left.
See bug 1307408 comment #12. */
min-width: 125px;
display: flex;
flex-direction: column;
flex: 1 1 auto;
@ -49,10 +52,19 @@ window {
position: fixed;
}
/* Minimum width for the Inspector main (uncontrolled) area. */
#inspector-splitter-box .uncontrolled {
/* Minimum dimensions for the Inspector splitter areas. */
#inspector-splitter-box .uncontrolled,
#inspector-splitter-box .controlled {
min-height: 50px;
min-width: 50px;
overflow-x: hidden;
}
/* Set a minimum width of 200px for tab content to avoid breaking the layout when resizing
the sidebar tab to small width. If a specific panel supports smaller width, this should
be overridden on a panel-by-panel basis */
.inspector-tabpanel {
min-width: 200px;
}
#inspector-splitter-box .controlled.pane-collapsed {
@ -171,12 +183,8 @@ window {
font: message-box;
}
/* Set the minimum width for the side bar so, all tabs are
properly visible. The value can be decreased when bug 1281789
is fixed and the all-tabs-menu is available again. */
#inspector-sidebar-container {
overflow: hidden;
min-width: 50px;
position: relative;
height: 100%;
}

View File

@ -3,6 +3,49 @@
* 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/. */
#toolbar-labels {
overflow: hidden;
}
/**
* Collapsed details pane needs to be truly hidden to prevent both accessibility
* tools and keyboard from accessing its contents.
*/
#details-pane.pane-collapsed {
visibility: hidden;
}
#details-pane-toggle[disabled] {
display: none;
}
#custom-pane {
overflow: auto;
}
#response-content-image-box {
overflow: auto;
}
#network-statistics-charts {
overflow: auto;
}
.cropped-textbox .textbox-input {
/* workaround for textbox not supporting the @crop attribute */
text-overflow: ellipsis;
}
/* Responsive sidebar */
@media (max-width: 700px) {
#toolbar-spacer,
#details-pane-toggle,
.requests-menu-waterfall,
#requests-menu-network-summary-button > .toolbarbutton-text {
display: none;
}
}
:root.theme-dark {
--table-splitter-color: rgba(255,255,255,0.15);
--table-zebra-background: rgba(255,255,255,0.05);

View File

@ -30,6 +30,9 @@
flex-direction: column;
width: 100%;
height: 100%;
/* Override the min-width from .inspector-tabpanel, as the rule panel can support small
widths */
min-width: 100px;
}
/* Rule View Toolbar */

View File

@ -29,6 +29,10 @@ def resolve_path(start, relativePath):
"""Helper to resolve a path from a start, and a relative path"""
return os.path.normpath(os.path.join(start, relativePath))
def stringify(obj):
"""Helper to stringify to JSON"""
return json.dumps(obj, sort_keys=True, indent=2, separators=(',', ': '))
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
@ -42,9 +46,9 @@ class MachCommands(MachCommandBase):
db = self.get_properties_db_from_xpcshell()
self.output_template({
'preferences': json.dumps(preferences),
'cssProperties': json.dumps(db['cssProperties']),
'pseudoElements': json.dumps(db['pseudoElements'])})
'preferences': stringify(preferences),
'cssProperties': stringify(db['cssProperties']),
'pseudoElements': stringify(db['pseudoElements'])})
def get_preferences(self):
"""Get all of the preferences associated with enabling and disabling a property."""

File diff suppressed because one or more lines are too long

View File

@ -9,8 +9,6 @@
* to generate these files can be found at devtools/shared/css/generate-properties-db.js.
*/
/* eslint-disable max-len */
/**
* A list of CSS Properties and their various characteristics.
*/
@ -26,5 +24,3 @@ exports.PSEUDO_ELEMENTS = ${pseudoElements};
* exposed for testing purposes.
*/
exports.PREFERENCES = ${preferences};
/* eslint-enable max-len */

View File

@ -268,28 +268,22 @@ function normalizeCssData(db) {
// Fill in any missing DB information from the static database.
db = Object.assign({}, CSS_PROPERTIES_DB, db);
// Add "supports" information to the css properties if it's missing.
if (!db.properties.color.supports) {
for (let name in db.properties) {
if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
}
for (let name in db.properties) {
// Skip the current property if we can't find it in CSS_PROPERTIES_DB.
if (typeof CSS_PROPERTIES_DB.properties[name] !== "object") {
continue;
}
}
// Add "values" information to the css properties if it's missing.
if (!db.properties.color.values) {
for (let name in db.properties) {
if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
}
// Add "supports" information to the css properties if it's missing.
if (!db.properties.color.supports) {
db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
}
}
// Add "subproperties" information to the css properties if it's
// missing.
if (!db.properties.background.subproperties) {
for (let name in db.properties) {
// Add "values" information to the css properties if it's missing.
if (!db.properties.color.values) {
db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
}
// Add "subproperties" information to the css properties if it's missing.
if (!db.properties.background.subproperties) {
db.properties[name].subproperties =
CSS_PROPERTIES_DB.properties[name].subproperties;
}
@ -311,7 +305,8 @@ function reattachCssColorValues(db) {
for (let name in db.properties) {
const property = db.properties[name];
if (property.values[0] === "COLOR") {
// "values" can be undefined if {name} was not found in CSS_PROPERTIES_DB.
if (property.values && property.values[0] === "COLOR") {
property.values.shift();
property.values = property.values.concat(colors).sort();
}

View File

@ -3,7 +3,7 @@
/**
* Test that the devtool's client-side CSS properties database is in sync with the values
* on the platform. If they are not, then `mach generate-css-db` needs to be run to
* on the platform. If they are not, then `mach devtools-css-db` needs to be run to
* make everything up to date. Nightly, aurora, beta, and release may have different
* preferences for what CSS values are enabled. The static CSS properties database can
* be slightly different from the target platform as long as there is a preference that
@ -23,7 +23,7 @@ function run_test() {
const propertiesErrorMessage = "If this assertion fails, then the client side CSS " +
"properties list in devtools is out of sync with the " +
"CSS properties on the platform. To fix this " +
"assertion run `mach generate-css-db` to re-generate " +
"assertion run `mach devtools-css-db` to re-generate " +
"the client side properties.";
// Check that the platform and client match for pseudo elements.

View File

@ -672,10 +672,6 @@ nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix,
break;
}
}
// if both have been found we don't need to search further
if (bIncludesDouble && bIncludesSingle) {
break;
}
}
// Delimiter and escaping is according to the following table

View File

@ -165,12 +165,14 @@ ReportOnCallerUTF8(JSContext* callerContext,
char* buf = JS_vsmprintf(format, ap);
if (!buf) {
va_end(ap);
return NS_ERROR_OUT_OF_MEMORY;
}
JS_ReportErrorUTF8(callerContext, "%s", buf);
JS_smprintf_free(buf);
va_end(ap);
return NS_OK;
}
@ -183,11 +185,12 @@ ReportOnCallerUTF8(JSCLContextHelper& helper,
char* buf = JS_vsmprintf(format, ap);
if (!buf) {
va_end(ap);
return NS_ERROR_OUT_OF_MEMORY;
}
helper.reportErrorAfterPop(buf);
va_end(ap);
return NS_OK;
}

View File

@ -7659,7 +7659,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
frame = nullptr;
}
// Implicit pointer capture for touch
if (sPointerEventImplicitCapture &&
if (frame && sPointerEventImplicitCapture &&
pointerEvent->mMessage == ePointerDown &&
pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
nsCOMPtr<nsIContent> targetContent;

View File

@ -245,10 +245,10 @@ fuzzy-if(skiaContent,1,5) pref(svg.marker-improvements.enabled,true) == marker-o
# fuzzy because of the differences between clipPath and mask clipping
== mask-and-clipPath.html mask-and-clipPath.html
== mask-and-clipPath-2.svg mask-and-clipPath-2.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-01.svg mask-type-01.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-02.svg mask-type-02.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-03.svg mask-type-03.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-04.svg mask-type-04.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-01.svg mask-type-01.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-02.svg mask-type-02.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-03.svg mask-type-03.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-04.svg mask-type-04.svg
== nested-viewBox-01.svg nested-viewBox-01.svg
fuzzy-if(skiaContent,3,448000) == nesting-invalid-01.svg nesting-invalid-01.svg
fuzzy-if(d2d&&/^Windows\x20NT\x20(6\.1|10\.0)/.test(http.oscpu),63,168) fuzzy-if(cocoaWidget,1,122) fuzzy-if(skiaContent,2,1000) == non-scaling-stroke-01.svg non-scaling-stroke-01.svg

View File

@ -220,10 +220,10 @@ fuzzy-if(skiaContent,1,10000) == mask-basic-02.svg mask-basic-02-ref.svg
# fuzzy because of the differences between clipPath and mask clipping
fuzzy(27,28) == mask-and-clipPath.html mask-and-clipPath-ref.html
== mask-and-clipPath-2.svg pass.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-01.svg mask-type-01-ref.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-02.svg mask-type-01-ref.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-03.svg mask-type-01-ref.svg
pref(layout.css.masking.enabled,true) fuzzy-if(d2d||skiaContent,1,6400) == mask-type-04.svg mask-type-01-ref.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-01.svg mask-type-01-ref.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-02.svg mask-type-01-ref.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-03.svg mask-type-01-ref.svg
fuzzy-if(d2d||skiaContent,1,6400) == mask-type-04.svg mask-type-01-ref.svg
== nested-viewBox-01.svg pass.svg
fuzzy-if(skiaContent,3,448000) == nesting-invalid-01.svg nesting-invalid-01-ref.svg
fuzzy-if(d2d&&/^Windows\x20NT\x20(6\.1|10\.0)/.test(http.oscpu),63,168) fuzzy-if(cocoaWidget,1,122) fuzzy-if(skiaContent,2,1000) == non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg # bug 1074161 for Win7 and OSX 10.8

View File

@ -2792,7 +2792,7 @@ CSS_PROP_SVGRESET(
mask_type,
MaskType,
CSS_PROPERTY_PARSE_VALUE,
"layout.css.masking.enabled",
"",
VARIANT_HK,
kMaskTypeKTable,
CSS_PROP_NO_OFFSET,

View File

@ -1801,6 +1801,14 @@ var gCSSProperties = {
],
invalid_values: [ "5", "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
},
"mask-type": {
domProp: "maskType",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "luminance" ],
other_values: [ "alpha" ],
invalid_values: [],
},
"-moz-outline-radius": {
domProp: "MozOutlineRadius",
inherited: false,
@ -5561,17 +5569,6 @@ if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright.enabled")) {
}
}
if (IsCSSPropertyPrefEnabled("layout.css.masking.enabled")) {
gCSSProperties["mask-type"] = {
domProp: "maskType",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "luminance" ],
other_values: [ "alpha" ],
invalid_values: []
};
}
if (IsCSSPropertyPrefEnabled("svg.paint-order.enabled")) {
gCSSProperties["paint-order"] = {
domProp: "paintOrder",

View File

@ -72,7 +72,6 @@ var gProps = {
"layout.css.image-orientation.enabled": ["image-orientation"],
"layout.css.mix-blend-mode.enabled": ["mix-blend-mode"],
"layout.css.isolation.enabled": [ "isolation"],
"layout.css.masking.enabled": ["mask-type"],
"layout.css.touch_action.enabled": ["touch-action"],
"svg.transform-box.enabled": ["transform-box"]
};

View File

@ -67,7 +67,9 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
mStatusText = (TextView) content.findViewById(R.id.find_status);
mInflated = true;
GeckoApp.getEventDispatcher().registerGeckoThreadListener(this, "TextSelection:Data");
GeckoApp.getEventDispatcher().registerGeckoThreadListener(this,
"FindInPage:MatchesCountResult",
"TextSelection:Data");
}
public void show() {
@ -110,7 +112,34 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
if (!mInflated) {
return;
}
GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this, "TextSelection:Data");
GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
"FindInPage:MatchesCountResult",
"TextSelection:Data");
}
private void onMatchesCountResult(final int total, final int current, final int limit, final String searchString) {
if (total == -1) {
updateResult(Integer.toString(limit) + "+");
} else if (total > 0) {
updateResult(Integer.toString(current) + "/" + Integer.toString(total));
} else if (TextUtils.isEmpty(searchString)) {
updateResult("");
} else {
// We display 0/0, when there were no
// matches found, or if matching has been turned off by setting
// pref accessibility.typeaheadfind.matchesCountLimit to 0.
updateResult("0/0");
}
}
private void updateResult(final String statusText) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mStatusText.setVisibility(statusText.isEmpty() ? View.GONE : View.VISIBLE);
mStatusText.setText(statusText);
}
});
}
// TextWatcher implementation
@ -160,6 +189,14 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
@Override
public void handleMessage(String event, JSONObject message) {
if (event.equals("FindInPage:MatchesCountResult")) {
onMatchesCountResult(message.optInt("total", 0),
message.optInt("current", 0),
message.optInt("limit", 0),
message.optString("searchString"));
return;
}
if (!event.equals("TextSelection:Data") || !REQUEST_ID.equals(message.optString("requestId"))) {
return;
}
@ -203,21 +240,8 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
GeckoAppShell.sendRequestToGecko(new GeckoRequest(request, searchString) {
@Override
public void onResponse(NativeJSObject nativeJSObject) {
final int total = nativeJSObject.optInt("total", 0);
if (total == -1) {
final int limit = nativeJSObject.optInt("limit", 0);
updateResult(Integer.toString(limit) + "+");
} else if (total > 0) {
final int current = nativeJSObject.optInt("current", 0);
updateResult(Integer.toString(current) + "/" + Integer.toString(total));
} else if (TextUtils.isEmpty(searchString)) {
updateResult("");
} else {
// We display 0/0, when there were no
// matches found, or if matching has been turned off by setting
// pref accessibility.typeaheadfind.matchesCountLimit to 0.
updateResult("0/0");
}
// We don't care about the return value, because `onMatchesCountResult`
// does the heavy lifting.
}
@Override
@ -227,16 +251,6 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
searchString + "]");
updateResult("");
}
private void updateResult(final String statusText) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mStatusText.setVisibility(statusText.isEmpty() ? View.GONE : View.VISIBLE);
mStatusText.setText(statusText);
}
});
}
});
}
}

View File

@ -9,7 +9,8 @@ var FindHelper = {
_initialViewport: null,
_viewportChanged: false,
_result: null,
_limit: 0,
// Start of nsIObserver implementation.
observe: function(aMessage, aTopic, aData) {
switch(aTopic) {
@ -31,34 +32,25 @@ var FindHelper = {
}
},
/**
* When the FindInPageBar opens/ becomes visible, it's time to:
* 1. Add listeners for other message types sent from the FindInPageBar
* 2. initialize the Finder instance, if necessary.
*/
_findOpened: function() {
try {
this._limit = Services.prefs.getIntPref("accessibility.typeaheadfind.matchesCountLimit");
} catch (e) {
// Pref not available, assume 0, no match counting.
this._limit = 0;
}
Messaging.addListener((data) => {
this.doFind(data);
return this._getMatchesCountResult(data);
}, "FindInPage:Find");
Messaging.addListener((data) => {
this.findAgain(data, false);
return this._getMatchesCountResult(data);
}, "FindInPage:Next");
Messaging.addListener((data) => {
this.findAgain(data, true);
return this._getMatchesCountResult(data);
}, "FindInPage:Prev");
Messaging.addListener(data => this.doFind(data), "FindInPage:Find");
Messaging.addListener(data => this.findAgain(data, false), "FindInPage:Next");
Messaging.addListener(data => this.findAgain(data, true), "FindInPage:Prev");
// Initialize the finder component for the current page by performing a fake find.
this._init();
this._finder.requestMatchesCount("", 1);
this._finder.requestMatchesCount("");
},
/**
* Fetch the Finder instance from the active tabs' browser and start tracking
* the active viewport.
*/
_init: function() {
// If there's no find in progress, start one.
if (this._finder) {
@ -78,6 +70,10 @@ var FindHelper = {
this._viewportChanged = false;
},
/**
* Detach from the Finder instance (so stop listening for messages) and stop
* tracking the active viewport.
*/
_uninit: function() {
// If there's no find in progress, there's nothing to clean up.
if (!this._finder) {
@ -92,6 +88,9 @@ var FindHelper = {
this._viewportChanged = false;
},
/**
* When the FindInPageBar closes, it's time to stop listening for its messages.
*/
_findClosed: function() {
Messaging.removeListener("FindInPage:Find");
Messaging.removeListener("FindInPage:Next");
@ -99,47 +98,86 @@ var FindHelper = {
},
/**
* Request, wait for, and return the current matchesCount results for a string.
* Start an asynchronous find-in-page operation, using the current Finder
* instance and request to count the amount of matches.
* If no Finder instance is currently active, we'll lazily initialize it here.
*
* @param {String} searchString Word to search for in the current document
* @return {Object} Echo of the current find action
*/
_getMatchesCountResult: function(findString) {
// Count matches up to any provided limit.
if (this._limit <= 0) {
return { total: 0, current: 0, limit: 0 };
}
// Sync call to Finder, results available immediately.
this._finder.requestMatchesCount(findString, this._limit);
return this._result;
},
/**
* Pass along the count results to FindInPageBar for display.
*/
onMatchesCountResult: function(result) {
this._result = result;
this._result.limit = this._limit;
},
doFind: function(searchString) {
if (!this._finder) {
this._init();
}
this._finder.fastFind(searchString, false);
return { searchString, findBackwards: false };
},
/**
* Restart the same find-in-page operation as before via `doFind()`. If we
* haven't called `doFind()`, we simply kick off a regular find.
*
* @param {String} searchString Word to search for in the current document
* @param {Boolean} findBackwards Direction to search in
* @return {Object} Echo of the current find action
*/
findAgain: function(searchString, findBackwards) {
// This always happens if the user taps next/previous after re-opening the
// search bar, and not only forces _init() but also an initial fastFind(STRING)
// before any findAgain(DIRECTION).
if (!this._finder) {
this.doFind(searchString);
return;
return this.doFind(searchString);
}
this._finder.findAgain(findBackwards, false, false);
return { searchString, findBackwards };
},
// Start of Finder.jsm listener implementation.
/**
* Pass along the count results to FindInPageBar for display. The result that
* is sent to the FindInPageBar is augmented with the current find-in-page count
* limit.
*
* @param {Object} result Result coming from the Finder instance that contains
* the following properties:
* - {Number} total The total amount of matches found
* - {Number} current The index of current found range
* in the document
*/
onMatchesCountResult: function(result) {
this._result = result;
Messaging.sendRequest(Object.assign({
type: "FindInPage:MatchesCountResult"
}, this._result));
},
/**
* When a find-in-page action finishes, this method is invoked. This is mainly
* used at the moment to detect if the current viewport has changed, which might
* be indicated by not finding a string in the current page.
*
* @param {Object} aData A dictionary, representing the find result, which
* contains the following properties:
* - {String} searchString Word that was searched for
* in the current document
* - {Number} result One of the following
* Ci.nsITypeAheadFind.* result
* indicators: FIND_FOUND,
* FIND_NOTFOUND, FIND_WRAPPED,
* FIND_PENDING
* - {Boolean} findBackwards Whether the search direction
* was backwards
* - {Boolean} findAgain Whether the previous search
* was repeated
* - {Boolean} drawOutline Whether we may (re-)draw the
* outline of a hyperlink
* - {Boolean} linksOnly Whether links-only mode was
* active
*/
onFindResult: function(aData) {
if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
if (this._viewportChanged) {

View File

@ -2468,9 +2468,6 @@ pref("layout.css.devPixelsPerPx", "-1.0");
// Is support for CSS initial-letter property enabled?
pref("layout.css.initial-letter.enabled", false);
// Is support for CSS Masking features enabled?
pref("layout.css.masking.enabled", true);
// Is support for mix-blend-mode enabled?
pref("layout.css.mix-blend-mode.enabled", true);

View File

@ -8,7 +8,6 @@
#include <stdint.h>
#include "BRNameMatchingPolicy.h"
#include "CTKnownLogs.h"
#include "ExtendedValidation.h"
#include "MultiLogCTVerifier.h"
@ -322,7 +321,7 @@ CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode)
static const unsigned int MIN_RSA_BITS = 2048;
static const unsigned int MIN_RSA_BITS_WEAK = 1024;
SECStatus
Result
CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
Time time, void* pinArg, const char* hostname,
/*out*/ UniqueCERTCertList& builtChain,
@ -348,41 +347,34 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
}
if (ocspStaplingStatus) {
if (usage != certificateUsageSSLServer) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
return Result::FATAL_ERROR_INVALID_ARGS;
}
*ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED;
}
if (keySizeStatus) {
if (usage != certificateUsageSSLServer) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
return Result::FATAL_ERROR_INVALID_ARGS;
}
*keySizeStatus = KeySizeStatus::NeverChecked;
}
if (sha1ModeResult) {
if (usage != certificateUsageSSLServer) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
return Result::FATAL_ERROR_INVALID_ARGS;
}
*sha1ModeResult = SHA1ModeResult::NeverChecked;
}
if (!cert ||
(usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
return Result::FATAL_ERROR_INVALID_ARGS;
}
Result rv;
Input certDER;
rv = certDER.Init(cert->derCert.data, cert->derCert.len);
Result rv = certDER.Init(cert->derCert.data, cert->derCert.len);
if (rv != Success) {
PR_SetError(MapResultToPRErrorCode(rv), 0);
return SECFailure;
return rv;
}
// We configure the OCSP fetching modes separately for EV and non-EV
@ -404,8 +396,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
stapledOCSPResponseSECItem->len);
if (rv != Success) {
// The stapled OCSP response was too big.
PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
return SECFailure;
return Result::ERROR_OCSP_MALFORMED_RESPONSE;
}
stapledOCSPResponse = &stapledOCSPResponseInput;
}
@ -797,14 +788,13 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
}
if (rv != Success) {
PR_SetError(MapResultToPRErrorCode(rv), 0);
return SECFailure;
return rv;
}
return SECSuccess;
return Success;
}
SECStatus
Result
CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional*/ const SECItem* sctsFromTLS,
@ -831,84 +821,74 @@ CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
}
if (!hostname || !hostname[0]) {
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
return SECFailure;
return Result::ERROR_BAD_CERT_DOMAIN;
}
// CreateCertErrorRunnable assumes that CheckCertHostname is only called
// if VerifyCert succeeded.
SECStatus rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
pinarg, hostname, builtChain, flags,
stapledOCSPResponse, sctsFromTLS,
evOidPolicy, ocspStaplingStatus, keySizeStatus,
sha1ModeResult, pinningTelemetryInfo,
ctInfo);
if (rv != SECSuccess) {
Result rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
pinarg, hostname, builtChain, flags,
stapledOCSPResponse, sctsFromTLS, evOidPolicy,
ocspStaplingStatus, keySizeStatus, sha1ModeResult,
pinningTelemetryInfo, ctInfo);
if (rv != Success) {
return rv;
}
Input peerCertInput;
Result result = peerCertInput.Init(peerCert->derCert.data,
peerCert->derCert.len);
if (result != Success) {
PR_SetError(MapResultToPRErrorCode(result), 0);
return SECFailure;
rv = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len);
if (rv != Success) {
return rv;
}
Input stapledOCSPResponseInput;
Input* responseInputPtr = nullptr;
if (stapledOCSPResponse) {
result = stapledOCSPResponseInput.Init(stapledOCSPResponse->data,
stapledOCSPResponse->len);
if (result != Success) {
rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->data,
stapledOCSPResponse->len);
if (rv != Success) {
// The stapled OCSP response was too big.
PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
return SECFailure;
return Result::ERROR_OCSP_MALFORMED_RESPONSE;
}
responseInputPtr = &stapledOCSPResponseInput;
}
if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
result = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
if (result != Success) {
PR_SetError(MapResultToPRErrorCode(result), 0);
return SECFailure;
rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
if (rv != Success) {
return rv;
}
}
Input hostnameInput;
result = hostnameInput.Init(BitwiseCast<const uint8_t*, const char*>(hostname),
strlen(hostname));
if (result != Success) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
rv = hostnameInput.Init(BitwiseCast<const uint8_t*, const char*>(hostname),
strlen(hostname));
if (rv != Success) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
bool isBuiltInRoot;
result = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
if (result != Success) {
PR_SetError(MapResultToPRErrorCode(result), 0);
return SECFailure;
rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
if (rv != Success) {
return rv;
}
BRNameMatchingPolicy nameMatchingPolicy(
isBuiltInRoot ? mNameMatchingMode
: BRNameMatchingPolicy::Mode::DoNotEnforce);
result = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
if (result != Success) {
rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
if (rv != Success) {
// Treat malformed name information as a domain mismatch.
if (result == Result::ERROR_BAD_DER) {
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
} else {
PR_SetError(MapResultToPRErrorCode(result), 0);
if (rv == Result::ERROR_BAD_DER) {
return Result::ERROR_BAD_CERT_DOMAIN;
}
return SECFailure;
return rv;
}
if (saveIntermediatesInPermanentDatabase) {
SaveIntermediateCerts(builtChain);
}
return SECSuccess;
return Success;
}
} } // namespace mozilla::psm

View File

@ -97,23 +97,24 @@ public:
// *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
// Only one usage per verification is supported.
SECStatus VerifyCert(CERTCertificate* cert,
SECCertificateUsage usage,
mozilla::pkix::Time time,
void* pinArg,
const char* hostname,
/*out*/ UniqueCERTCertList& builtChain,
Flags flags = 0,
/*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
/*optional in*/ const SECItem* sctsFromTLS = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
/*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
/*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
mozilla::pkix::Result VerifyCert(
CERTCertificate* cert,
SECCertificateUsage usage,
mozilla::pkix::Time time,
void* pinArg,
const char* hostname,
/*out*/ UniqueCERTCertList& builtChain,
Flags flags = 0,
/*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
/*optional in*/ const SECItem* sctsFromTLS = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
/*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
/*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
SECStatus VerifySSLServerCert(
mozilla::pkix::Result VerifySSLServerCert(
const UniqueCERTCertificate& peerCert,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional*/ const SECItem* sctsFromTLS,

View File

@ -1309,8 +1309,6 @@ AuthCertificate(CertVerifier& certVerifier,
MOZ_ASSERT(infoObject);
MOZ_ASSERT(cert);
SECStatus rv;
// We want to avoid storing any intermediate cert information when browsing
// in private, transient contexts.
bool saveIntermediates =
@ -1331,20 +1329,18 @@ AuthCertificate(CertVerifier& certVerifier,
flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
}
rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
sctsFromTLSExtension, time, infoObject,
infoObject->GetHostNameRaw(),
certList, saveIntermediates, flags,
&evOidPolicy, &ocspStaplingStatus,
&keySizeStatus, &sha1ModeResult,
&pinningTelemetryInfo,
&certificateTransparencyInfo);
PRErrorCode savedErrorCode;
if (rv != SECSuccess) {
savedErrorCode = PR_GetError();
}
Result rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
sctsFromTLSExtension, time,
infoObject,
infoObject->GetHostNameRaw(),
certList, saveIntermediates,
flags, &evOidPolicy,
&ocspStaplingStatus,
&keySizeStatus, &sha1ModeResult,
&pinningTelemetryInfo,
&certificateTransparencyInfo);
uint32_t evStatus = (rv != SECSuccess) ? 0 // 0 = Failure
uint32_t evStatus = (rv != Success) ? 0 // 0 = Failure
: (evOidPolicy == SEC_OID_UNKNOWN) ? 1 // 1 = DV
: 2; // 2 = EV
Telemetry::Accumulate(Telemetry::CERT_EV_STATUS, evStatus);
@ -1379,15 +1375,14 @@ AuthCertificate(CertVerifier& certVerifier,
RefPtr<nsNSSCertificate> nsc;
if (!status || !status->HasServerCert()) {
if( rv == SECSuccess ){
if (rv == Success) {
nsc = nsNSSCertificate::Create(cert.get(), &evOidPolicy);
}
else {
} else {
nsc = nsNSSCertificate::Create(cert.get());
}
}
if (rv == SECSuccess) {
if (rv == Success) {
GatherSuccessfulValidationTelemetry(certList);
GatherCertificateTransparencyTelemetry(certList,
certificateTransparencyInfo);
@ -1400,13 +1395,13 @@ AuthCertificate(CertVerifier& certVerifier,
infoObject->SetSSLStatus(status);
}
if (rv == SECSuccess) {
if (rv == Success) {
// Certificate verification succeeded delete any potential record
// of certificate error bits.
RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
nullptr, rv);
}
else {
nullptr,
SECSuccess);
} else {
// Certificate verification failed, update the status' bits.
RememberCertErrorsTable::GetInstance().LookupCertErrorBits(
infoObject, status);
@ -1414,7 +1409,7 @@ AuthCertificate(CertVerifier& certVerifier,
if (status && !status->HasServerCert()) {
nsNSSCertificate::EVStatus evStatus;
if (evOidPolicy == SEC_OID_UNKNOWN || rv != SECSuccess) {
if (evOidPolicy == SEC_OID_UNKNOWN || rv != Success) {
evStatus = nsNSSCertificate::ev_status_invalid;
} else {
evStatus = nsNSSCertificate::ev_status_valid;
@ -1426,14 +1421,14 @@ AuthCertificate(CertVerifier& certVerifier,
}
}
if (rv != SECSuccess) {
if (rv != Success) {
// Certificate validation failed; store the peer certificate chain on
// infoObject so it can be used for error reporting.
infoObject->SetFailedCertChain(Move(peerCertChain));
PR_SetError(savedErrorCode, 0);
PR_SetError(MapResultToPRErrorCode(rv), 0);
}
return rv;
return rv == Success ? SECSuccess : SECFailure;
}
/*static*/ SECStatus

View File

@ -12,6 +12,7 @@
#include "nsCOMPtr.h"
#include "nsNSSComponent.h"
#include "nssb64.h"
#include "pkix/pkixnss.h"
#include "pkix/pkixtypes.h"
#include "ScopedNSSTypes.h"
#include "secerr.h"
@ -256,11 +257,16 @@ VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
return MapSECStatus(certVerifier->VerifyCert(cert,
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
context->builtChain));
Result result = certVerifier->VerifyCert(cert,
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
context->builtChain);
if (result != Success) {
return GetXPCOMFromNSSError(MapResultToPRErrorCode(result));
}
return NS_OK;
}
} // namespace

View File

@ -681,7 +681,8 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
nullptr, /*XXX fixme*/
nullptr, /* hostname */
nssChain,
CertVerifier::FLAG_LOCAL_ONLY) != SECSuccess) {
CertVerifier::FLAG_LOCAL_ONLY)
!= mozilla::pkix::Success) {
nssChain = nullptr;
// keep going
}
@ -707,7 +708,8 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
nullptr, /*XXX fixme*/
nullptr, /*hostname*/
nssChain,
CertVerifier::FLAG_LOCAL_ONLY) != SECSuccess) {
CertVerifier::FLAG_LOCAL_ONLY)
!= mozilla::pkix::Success) {
nssChain = nullptr;
// keep going
}
@ -1153,7 +1155,7 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
UniqueCERTCertList unusedBuiltChain;
SECStatus rv = certVerifier->VerifyCert(mCert.get(),
mozilla::pkix::Result result = certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, mozilla::pkix::Now(),
nullptr /* XXX pinarg */,
nullptr /* hostname */,
@ -1163,7 +1165,7 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
nullptr /* sctsFromTLSExtension */,
&resultOidTag);
if (rv != SECSuccess) {
if (result != mozilla::pkix::Success) {
resultOidTag = SEC_OID_UNKNOWN;
}
if (resultOidTag != SEC_OID_UNKNOWN) {

View File

@ -39,6 +39,7 @@
#include "nsThreadUtils.h"
#include "nspr.h"
#include "pkix/Time.h"
#include "pkix/pkixnss.h"
#include "pkix/pkixtypes.h"
#include "secasn1.h"
#include "secder.h"
@ -613,17 +614,17 @@ nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
}
UniqueCERTCertList certChain;
SECStatus srv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
mozilla::pkix::Now(), ctx,
nullptr, certChain);
if (srv != SECSuccess) {
mozilla::pkix::Result result =
certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient,
mozilla::pkix::Now(), ctx, nullptr, certChain);
if (result != mozilla::pkix::Success) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
continue;
}
srv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient,
false);
SECStatus srv = ImportCertsIntoPermanentStorage(certChain,
certUsageEmailRecipient,
false);
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
@ -669,18 +670,18 @@ nsNSSCertificateDB::ImportValidCACertsInList(const UniqueCERTCertList& filteredC
!CERT_LIST_END(node, filteredCerts.get());
node = CERT_LIST_NEXT(node)) {
UniqueCERTCertList certChain;
SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageVerifyCA,
mozilla::pkix::Now(), ctx,
nullptr, certChain);
if (rv != SECSuccess) {
mozilla::pkix::Result result =
certVerifier->VerifyCert(node->cert, certificateUsageVerifyCA,
mozilla::pkix::Now(), ctx, nullptr, certChain);
if (result != mozilla::pkix::Success) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
continue;
}
rv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
if (rv != SECSuccess) {
SECStatus srv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA,
true);
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
}
@ -1140,13 +1141,13 @@ nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress,
node = CERT_LIST_NEXT(node)) {
UniqueCERTCertList unusedCertChain;
SECStatus srv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
mozilla::pkix::Now(),
nullptr /*XXX pinarg*/,
nullptr /*hostname*/,
unusedCertChain);
if (srv == SECSuccess) {
mozilla::pkix::Result result =
certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient,
mozilla::pkix::Now(),
nullptr /*XXX pinarg*/,
nullptr /*hostname*/,
unusedCertChain);
if (result == mozilla::pkix::Success) {
break;
}
}
@ -1488,45 +1489,38 @@ VerifyCertAtTime(nsIX509Cert* aCert,
UniqueCERTCertList resultChain;
SECOidTag evOidPolicy;
SECStatus srv;
mozilla::pkix::Result result;
if (aHostname && aUsage == certificateUsageSSLServer) {
srv = certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
aTime,
nullptr, // Assume no context
aHostname,
resultChain,
false, // don't save intermediates
aFlags,
&evOidPolicy);
result = certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
aTime,
nullptr, // Assume no context
aHostname,
resultChain,
false, // don't save intermediates
aFlags,
&evOidPolicy);
} else {
srv = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime,
nullptr, // Assume no context
aHostname,
resultChain,
aFlags,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
&evOidPolicy);
result = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime,
nullptr, // Assume no context
aHostname,
resultChain,
aFlags,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
&evOidPolicy);
}
PRErrorCode error = PR_GetError();
nsCOMPtr<nsIX509CertList> nssCertList;
// This adopts the list
nssCertList = new nsNSSCertList(Move(resultChain), locker);
NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE);
if (srv == SECSuccess) {
if (evOidPolicy != SEC_OID_UNKNOWN) {
*aHasEVPolicy = true;
}
*_retval = 0;
} else {
NS_ENSURE_TRUE(error != 0, NS_ERROR_FAILURE);
*_retval = error;
*_retval = mozilla::pkix::MapResultToPRErrorCode(result);
if (result == mozilla::pkix::Success && evOidPolicy != SEC_OID_UNKNOWN) {
*aHasEVPolicy = true;
}
nssCertList.forget(aVerifiedChain);

View File

@ -379,6 +379,10 @@ nsNSSSocketInfo::DriveHandshake()
NS_IMETHODIMP
nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
{
NS_ENSURE_ARG(_retval);
*_retval = false;
// If this is the same hostname then the certicate status does not
// need to be considered. They are joinable.
if (hostname.Equals(GetHostName())) {
@ -444,16 +448,17 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
nsAutoCString hostnameFlat(PromiseFlatCString(hostname));
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY;
UniqueCERTCertList unusedBuiltChain;
SECStatus rv = certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
mozilla::pkix::Now(),
nullptr, // pinarg
hostnameFlat.get(),
unusedBuiltChain,
false, // save intermediates
flags);
if (rv != SECSuccess) {
mozilla::pkix::Result result =
certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
mozilla::pkix::Now(),
nullptr, // pinarg
hostnameFlat.get(),
unusedBuiltChain,
false, // save intermediates
flags);
if (result != mozilla::pkix::Success) {
return NS_OK;
}

View File

@ -745,7 +745,7 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
certList,
false, // don't store intermediates
flags)
!= SECSuccess) {
!= mozilla::pkix::Success) {
return NS_ERROR_FAILURE;
}

View File

@ -136,6 +136,9 @@ SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
if (NS_FAILED(rv)) {
policy->AddDir(rdwrcr, "/tmp");
}
// Bug 1308851: NVIDIA proprietary driver when using WebGL
policy->AddPrefix(rdwr, "/dev", "nvidia");
mCommonContentPolicy.reset(policy);
#endif
}

View File

@ -1,9 +1,9 @@
[
{
"size": 1379434,
"size": 1455774,
"visibility": "public",
"digest": "cafa23466e283bccb6f89db9606da281877a87e3f22439c25655767fedc118df6961d9c34fc52eb4e747d48a66acd8c15d7c8c07b8f36e2e7db4ad8826742bfd",
"digest": "0f552a18276ff98985b236cd5e2f8baa5e4edcbbee1d8c21e3e5a5f61f2a674e5fb355f8be3b1bfb2a1f34465721d7ded12f432a50877c78c4b039ce0fcf6b8d",
"algorithm": "sha512",
"filename": "geckodriver-v0.10.0-linux64.tar.gz"
"filename": "geckodriver-v0.11.1-linux64.tar.gz"
}
]

View File

@ -606,7 +606,7 @@ Tester.prototype = {
// Run the GC and CC a few times to make sure that as much
// as possible is freed.
let numCycles = 3;
for (i = 0; i < numCycles; i++) {
for (let i = 0; i < numCycles; i++) {
Cu.forceGC();
Cu.forceCC();
}

View File

@ -205,8 +205,8 @@ var PrintUtils = {
// collapse the browser here -- it will be shown in
// enterPrintPreview; this forces a reflow which fixes display
// issues in bug 267422.
this._sourceBrowser = this._listener.getPrintPreviewBrowser();
this._sourceBrowser.collapsed = true;
let ppBrowser = this._listener.getPrintPreviewBrowser();
ppBrowser.collapsed = true;
}
this._webProgressPP = {};
@ -531,15 +531,15 @@ var PrintUtils = {
// the original page. After we have parsed it, content will tell parent
// that the document is ready for print previewing.
spMM.sendAsyncMessage("Printing:Preview:ParseDocument", {
URL: this._listener.getSourceBrowser().currentURI.spec,
windowID: this._listener.getSourceBrowser().outerWindowID,
URL: this._sourceBrowser.currentURI.spec,
windowID: this._sourceBrowser.outerWindowID,
});
// Here we log telemetry data for when the user enters simplify mode.
this.logTelemetry("PRINT_PREVIEW_SIMPLIFY_PAGE_OPENED_COUNT");
}
} else {
sendEnterPreviewMessage(this._listener.getSourceBrowser(), false);
sendEnterPreviewMessage(this._sourceBrowser, false);
}
if (this._webProgressPP.value) {

View File

@ -128,7 +128,7 @@ public:
template<typename T>
static T* Cast(LookupCache* aThat) {
return (T::VER == aThat->Ver() ? reinterpret_cast<T*>(aThat) : nullptr);
return ((aThat && T::VER == aThat->Ver()) ? reinterpret_cast<T*>(aThat) : nullptr);
}
private:

View File

@ -374,8 +374,6 @@
prefsvc.getIntPref("accessibility.typeaheadfind.timeout");
this._flashFindBar =
prefsvc.getIntPref("accessibility.typeaheadfind.flashBar");
this._matchesCountLimit =
prefsvc.getIntPref("accessibility.typeaheadfind.matchesCountLimit");
this._useModalHighlight = prefsvc.getBoolPref("findbar.modalHighlight");
prefsvc.addObserver("accessibility.typeaheadfind",
@ -1309,9 +1307,9 @@
if (aResult.total !== 0) {
if (aResult.total == -1) {
this._foundMatches.value = this.pluralForm.get(
this._matchesCountLimit,
aResult.limit,
this.strBundle.GetStringFromName("FoundMatchesCountLimit")
).replace("#1", this._matchesCountLimit);
).replace("#1", aResult.limit);
} else {
this._foundMatches.value = this.pluralForm.get(
aResult.total,

View File

@ -394,7 +394,8 @@ Finder.prototype = {
_notifyMatchesCount: function(result = this._currentMatchesCountResult) {
// The `_currentFound` property is only used for internal bookkeeping.
delete result._currentFound;
if (result.total == this.matchesCountLimit)
result.limit = this.matchesCountLimit;
if (result.total == result.limit)
result.total = -1;
for (let l of this._listeners) {

View File

@ -37,17 +37,24 @@ const kRGBRE = /^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*/i;
// hard to clash with IDs content authors come up with.
const kModalIdPrefix = "cedee4d0-74c5-4f2d-ab43-4d37c0f9d463";
const kModalOutlineId = kModalIdPrefix + "-findbar-modalHighlight-outline";
const kOutlineBoxColor = "255,197,53";
const kModalStyles = {
outlineNode: [
["position", "absolute"],
["background", "#ffc535"],
["background-color", `rgb(${kOutlineBoxColor})`],
["background-clip", "padding-box"],
["border", "2px solid"],
["-moz-border-top-colors", `rgba(${kOutlineBoxColor},.1) rgba(${kOutlineBoxColor},.4) rgba(${kOutlineBoxColor},.7)`],
["-moz-border-right-colors", `rgba(${kOutlineBoxColor},.1) rgba(${kOutlineBoxColor},.4) rgba(${kOutlineBoxColor},.7)`],
["-moz-border-bottom-colors", `rgba(${kOutlineBoxColor},.1) rgba(${kOutlineBoxColor},.4) rgba(${kOutlineBoxColor},.7)`],
["-moz-border-left-colors", `rgba(${kOutlineBoxColor},.1) rgba(${kOutlineBoxColor},.4) rgba(${kOutlineBoxColor},.7)`],
["border-radius", "3px"],
["box-shadow", "0 2px 0 0 rgba(0,0,0,.1)"],
["color", "#000"],
["display", "-moz-box"],
["margin", "-2px 0 0 -2px !important"],
["padding", "2px !important"],
["overflow", "hidden"],
["pointer-events", "none"],
["position", "absolute"],
["white-space", "nowrap"],
["will-change", "transform"],
["z-index", 2]

View File

@ -2331,6 +2331,19 @@ var AddonManagerInternal = {
.installTemporaryAddon(aFile);
},
installAddonFromSources: function(aFile) {
if (!gStarted)
throw Components.Exception("AddonManager is not initialized",
Cr.NS_ERROR_NOT_INITIALIZED);
if (!(aFile instanceof Ci.nsIFile))
throw Components.Exception("aFile must be a nsIFile",
Cr.NS_ERROR_INVALID_ARG);
return AddonManagerInternal._getProviderByName("XPIProvider")
.installAddonFromSources(aFile);
},
/**
* Returns an Addon corresponding to an instance ID.
* @param aInstanceID
@ -3507,6 +3520,10 @@ this.AddonManager = {
return AddonManagerInternal.installTemporaryAddon(aDirectory);
},
installAddonFromSources: function(aDirectory) {
return AddonManagerInternal.installAddonFromSources(aDirectory);
},
getAddonByInstanceID: function(aInstanceID) {
return AddonManagerInternal.getAddonByInstanceID(aInstanceID);
},

View File

@ -412,6 +412,33 @@ function setFilePermissions(aFile, aPermissions) {
}
}
/**
* Write a given string to a file
*
* @param file
* The nsIFile instance to write into
* @param string
* The string to write
*/
function writeStringToFile(file, string) {
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
try {
stream.init(file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
0);
converter.init(stream, "UTF-8", 0, 0x0000);
converter.writeString(string);
}
finally {
converter.close();
stream.close();
}
}
/**
* A safe way to install a file or the contents of a directory to a new
* directory. The file or directory is moved or copied recursively and if
@ -3529,8 +3556,11 @@ this.XPIProvider = {
}
try {
addon._sourceBundle = location.installAddon(id, stageDirEntry,
existingAddonID);
addon._sourceBundle = location.installAddon({
id,
source: stageDirEntry,
existingAddonID
});
}
catch (e) {
logger.error("Failed to install staged add-on " + id + " in " + location.name,
@ -3671,7 +3701,7 @@ this.XPIProvider = {
// Install the add-on
try {
addon._sourceBundle = profileLocation.installAddon(id, entry, null, true);
addon._sourceBundle = profileLocation.installAddon({ id, source: entry, action: "copy" });
logger.debug("Installed distribution add-on " + id);
Services.prefs.setBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, true)
@ -4027,15 +4057,48 @@ this.XPIProvider = {
* @param aFile
* An nsIFile for the unpacked add-on directory or XPI file.
*
* @return See installAddonFromLocation return value.
*/
installTemporaryAddon: function(aFile) {
return this.installAddonFromLocation(aFile, TemporaryInstallLocation);
},
/**
* Permanently installs add-on from a local XPI file or directory.
* The signature is checked but the add-on persist on application restart.
*
* @param aFile
* An nsIFile for the unpacked add-on directory or XPI file.
*
* @return See installAddonFromLocation return value.
*/
installAddonFromSources: Task.async(function*(aFile) {
let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
return this.installAddonFromLocation(aFile, location, "proxy");
}),
/**
* Installs add-on from a local XPI file or directory.
*
* @param aFile
* An nsIFile for the unpacked add-on directory or XPI file.
* @param aInstallLocation
* Define a custom install location object to use for the install.
* @param aInstallAction
* Optional action mode to use when installing the addon
* (see MutableDirectoryInstallLocation.installAddon)
*
* @return a Promise that resolves to an Addon object on success, or rejects
* if the add-on is not a valid restartless add-on or if the
* same ID is already temporarily installed
* same ID is already installed.
*/
installTemporaryAddon: Task.async(function*(aFile) {
installAddonFromLocation: Task.async(function*(aFile, aInstallLocation, aInstallAction) {
if (aFile.exists() && aFile.isFile()) {
flushJarCache(aFile);
}
let addon = yield loadManifestFromFile(aFile, TemporaryInstallLocation);
let addon = yield loadManifestFromFile(aFile, aInstallLocation);
aInstallLocation.installAddon({ id: addon.id, source: aFile, action: aInstallAction });
if (addon.appDisabled) {
let message = `Add-on ${addon.id} is not compatible with application version.`;
@ -4054,7 +4117,7 @@ this.XPIProvider = {
if (!addon.bootstrap) {
throw new Error("Only restartless (bootstrap) add-ons"
+ " can be temporarily installed:", addon.id);
+ " can be installed from sources:", addon.id);
}
let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL;
let oldAddon = yield new Promise(
@ -4109,6 +4172,7 @@ this.XPIProvider = {
XPIStates.addAddon(addon);
XPIDatabase.saveChanges();
XPIStates.save();
AddonManagerPrivate.callAddonListeners("onInstalling", addon.wrapper,
false);
@ -6407,8 +6471,11 @@ AddonInstall.prototype = {
// Install the new add-on into its final location
let existingAddonID = this.existingAddon ? this.existingAddon.id : null;
let file = this.installLocation.installAddon(this.addon.id, stagedAddon,
existingAddonID);
let file = this.installLocation.installAddon({
id: this.addon.id,
source: stagedAddon,
existingAddonID
});
// Update the metadata in the database
this.addon._sourceBundle = file;
@ -6514,22 +6581,7 @@ AddonInstall.prototype = {
this.addon._sourceBundle = stagedAddon;
// Cache the AddonInternal as it may have updated compatibility info
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
try {
stream.init(stagedJSON, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
0);
converter.init(stream, "UTF-8", 0, 0x0000);
converter.writeString(JSON.stringify(this.addon));
}
finally {
converter.close();
stream.close();
}
writeStringToFile(stagedJSON, JSON.stringify(this.addon));
logger.debug("Staged install of " + this.addon.id + " from " + this.sourceURI.spec + " ready; waiting for restart.");
if (isUpgrade) {
@ -8321,18 +8373,24 @@ Object.assign(MutableDirectoryInstallLocation.prototype, {
/**
* Installs an add-on into the install location.
*
* @param aId
* @param id
* The ID of the add-on to install
* @param aSource
* @param source
* The source nsIFile to install from
* @param aExistingAddonID
* @param existingAddonID
* The ID of an existing add-on to uninstall at the same time
* @param aCopy
* If false the source files will be moved to the new location,
* otherwise they will only be copied
* @param action
* What to we do with the given source file:
* "move"
* Default action, the source files will be moved to the new
* location,
* "copy"
* The source files will be copied,
* "proxy"
* A "proxy file" is going to refer to the source file path
* @return an nsIFile indicating where the add-on was installed to
*/
installAddon: function(aId, aSource, aExistingAddonID, aCopy) {
installAddon: function({ id, source, existingAddonID, action = "move" }) {
let trashDir = this.getTrashDir();
let transaction = new SafeInstallOperation();
@ -8355,9 +8413,9 @@ Object.assign(MutableDirectoryInstallLocation.prototype, {
// If any of these operations fails the finally block will clean up the
// temporary directory
try {
moveOldAddon(aId);
if (aExistingAddonID && aExistingAddonID != aId) {
moveOldAddon(aExistingAddonID);
moveOldAddon(id);
if (existingAddonID && existingAddonID != id) {
moveOldAddon(existingAddonID);
{
// Move the data directories.
@ -8366,12 +8424,12 @@ Object.assign(MutableDirectoryInstallLocation.prototype, {
* for porting to OS.File.
*/
let oldDataDir = FileUtils.getDir(
KEY_PROFILEDIR, ["extension-data", aExistingAddonID], false, true
KEY_PROFILEDIR, ["extension-data", existingAddonID], false, true
);
if (oldDataDir.exists()) {
let newDataDir = FileUtils.getDir(
KEY_PROFILEDIR, ["extension-data", aId], false, true
KEY_PROFILEDIR, ["extension-data", id], false, true
);
if (newDataDir.exists()) {
let trashData = trashDir.clone();
@ -8384,15 +8442,16 @@ Object.assign(MutableDirectoryInstallLocation.prototype, {
}
}
if (aCopy) {
transaction.copy(aSource, this._directory);
if (action == "copy") {
transaction.copy(source, this._directory);
}
else {
if (aSource.isFile())
flushJarCache(aSource);
else if (action == "move") {
if (source.isFile())
flushJarCache(source);
transaction.moveUnder(aSource, this._directory);
transaction.moveUnder(source, this._directory);
}
// Do nothing for the proxy file as we sideload an addon permanently
}
finally {
// It isn't ideal if this cleanup fails but it isn't worth rolling back
@ -8401,23 +8460,33 @@ Object.assign(MutableDirectoryInstallLocation.prototype, {
recursiveRemove(trashDir);
}
catch (e) {
logger.warn("Failed to remove trash directory when installing " + aId, e);
logger.warn("Failed to remove trash directory when installing " + id, e);
}
}
let newFile = this._directory.clone();
newFile.append(aSource.leafName);
if (action == "proxy") {
// When permanently installing sideloaded addon, we just put a proxy file
// refering to the addon sources
newFile.append(id);
writeStringToFile(newFile, source.path);
} else {
newFile.append(source.leafName);
}
try {
newFile.lastModifiedTime = Date.now();
} catch (e) {
logger.warn("failed to set lastModifiedTime on " + newFile.path, e);
}
this._IDToFileMap[aId] = newFile;
XPIProvider._addURIMapping(aId, newFile);
this._IDToFileMap[id] = newFile;
XPIProvider._addURIMapping(id, newFile);
if (aExistingAddonID && aExistingAddonID != aId &&
aExistingAddonID in this._IDToFileMap) {
delete this._IDToFileMap[aExistingAddonID];
if (existingAddonID && existingAddonID != id &&
existingAddonID in this._IDToFileMap) {
delete this._IDToFileMap[existingAddonID];
}
return newFile;
@ -8824,9 +8893,7 @@ WinRegInstallLocation.prototype = {
for (let i = 0; i < count; ++i) {
let id = aKey.getValueName(i);
let file = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsIFile);
file.initWithPath(aKey.readStringValue(id));
let file = new nsIFile(aKey.readStringValue(id));
if (!file.exists()) {
logger.warn("Ignoring missing add-on in " + file.path);

View File

@ -0,0 +1 @@
Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>bootstrap1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:bootstrap>true</em:bootstrap>
<!-- Front End MetaData -->
<em:name>Test Bootstrap 1</em:name>
<em:description>Test Description</em:description>
<em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
<em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
<em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>1</em:minVersion>
<em:maxVersion>1</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -0,0 +1,80 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const ID = "bootstrap1@tests.mozilla.org";
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
startupManager();
BootstrapMonitor.init();
// Partial list of bootstrap reasons from XPIProvider.jsm
const BOOTSTRAP_REASONS = {
ADDON_INSTALL: 5,
ADDON_UPGRADE: 7,
ADDON_DOWNGRADE: 8,
};
// Install an unsigned add-on with no existing add-on present.
// Restart and make sure it is still around.
add_task(function*() {
let extInstallCalled = false;
AddonManager.addInstallListener({
onExternalInstall: (aInstall) => {
do_check_eq(aInstall.id, ID);
do_check_eq(aInstall.version, "1.0");
extInstallCalled = true;
},
});
let installingCalled = false;
let installedCalled = false;
AddonManager.addAddonListener({
onInstalling: (aInstall) => {
do_check_eq(aInstall.id, ID);
do_check_eq(aInstall.version, "1.0");
installingCalled = true;
},
onInstalled: (aInstall) => {
do_check_eq(aInstall.id, ID);
do_check_eq(aInstall.version, "1.0");
installedCalled = true;
},
onInstallStarted: (aInstall) => {
do_throw("onInstallStarted called unexpectedly");
}
});
yield AddonManager.installAddonFromSources(do_get_file("data/from_sources/"));
do_check_true(extInstallCalled);
do_check_true(installingCalled);
do_check_true(installedCalled);
let install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
let addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
do_check_eq(addon.version, "1.0");
do_check_eq(addon.name, "Test Bootstrap 1");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
yield promiseRestartManager();
install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
yield promiseRestartManager();
});

View File

@ -34,6 +34,7 @@ skip-if = appname != "firefox"
[test_XPIStates.js]
[test_temporary.js]
tags = webextensions
[test_install_from_sources.js]
[test_proxies.js]
[test_proxy.js]
[test_pass_symbol.js]