Merge inbound to central, a=merge

MozReview-Commit-ID: AWwXRG9NEWV
This commit is contained in:
Wes Kocher 2017-06-01 17:26:55 -07:00
Родитель 7ca8bf2c5e 6283f668ef
Коммит 2b3f76f494
37 изменённых файлов: 1229 добавлений и 256 удалений

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

@ -5,12 +5,26 @@
"use strict";
const {
UPDATE_SHOW_GRID_AREAS,
UPDATE_SHOW_GRID_LINE_NUMBERS,
UPDATE_SHOW_INFINITE_LINES,
} = require("./index");
module.exports = {
/**
* Update the grid highlighter's show grid areas preference.
*
* @param {Boolean} enabled
* Whether or not the grid highlighter should show the grid areas.
*/
updateShowGridAreas(enabled) {
return {
type: UPDATE_SHOW_GRID_AREAS,
enabled,
};
},
/**
* Update the grid highlighter's show grid line numbers preference.
*

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

@ -17,6 +17,9 @@ createEnum([
// Update the entire grids state with the new list of grids.
"UPDATE_GRIDS",
// Update the grid highlighter's show grid areas state.
"UPDATE_SHOW_GRID_AREAS",
// Update the grid highlighter's show grid line numbers state.
"UPDATE_SHOW_GRID_LINE_NUMBERS",

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

@ -31,6 +31,7 @@ module.exports = createClass({
onShowGridCellHighlight: PropTypes.func.isRequired,
onShowGridLineNamesHighlight: PropTypes.func.isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
onToggleShowGridAreas: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
onToggleShowInfiniteLines: PropTypes.func.isRequired,
},
@ -47,11 +48,12 @@ module.exports = createClass({
onHideBoxModelHighlighter,
onSetGridOverlayColor,
onShowBoxModelHighlighterForNode,
onShowGridAreaHighlight,
onShowGridCellHighlight,
onToggleShowGridAreas,
onToggleGridHighlighter,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
onShowGridAreaHighlight,
onShowGridCellHighlight,
} = this.props;
return grids.length ?
@ -74,6 +76,7 @@ module.exports = createClass({
}),
GridDisplaySettings({
highlighterSettings,
onToggleShowGridAreas,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
})

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

@ -16,12 +16,22 @@ module.exports = createClass({
propTypes: {
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
onToggleShowGridAreas: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
onToggleShowInfiniteLines: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
onShowGridAreasCheckboxClick() {
let {
highlighterSettings,
onToggleShowGridAreas,
} = this.props;
onToggleShowGridAreas(!highlighterSettings.showGridAreasOverlay);
},
onShowGridLineNumbersCheckboxClick() {
let {
highlighterSettings,
@ -88,6 +98,23 @@ module.exports = createClass({
),
getStr("layout.displayNumbersOnLines")
)
),
dom.li(
{
className: "grid-settings-item",
},
dom.label(
{},
dom.input(
{
id: "grid-setting-show-grid-areas",
type: "checkbox",
checked: highlighterSettings.showGridAreasOverlay,
onChange: this.onShowGridAreasCheckboxClick,
}
),
getStr("layout.displayGridAreas")
)
)
)
);

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

@ -15,10 +15,12 @@ const {
updateGrids,
} = require("./actions/grids");
const {
updateShowGridAreas,
updateShowGridLineNumbers,
updateShowInfiniteLines,
} = require("./actions/highlighter-settings");
const SHOW_GRID_AREAS = "devtools.gridinspector.showGridAreas";
const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
@ -54,6 +56,7 @@ function GridInspector(inspector, window) {
this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
this.onSidebarSelect = this.onSidebarSelect.bind(this);
this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
@ -124,6 +127,7 @@ GridInspector.prototype = {
onShowGridCellHighlight: this.onShowGridCellHighlight,
onShowGridLineNamesHighlight: this.onShowGridLineNamesHighlight,
onToggleGridHighlighter: this.onToggleGridHighlighter,
onToggleShowGridAreas: this.onToggleShowGridAreas,
onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
};
@ -215,9 +219,11 @@ GridInspector.prototype = {
loadHighlighterSettings() {
let { dispatch } = this.store;
let showGridAreas = Services.prefs.getBoolPref(SHOW_GRID_AREAS);
let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
dispatch(updateShowGridAreas(showGridAreas));
dispatch(updateShowGridLineNumbers(showGridLineNumbers));
dispatch(updateShowInfiniteLines(showInfinteLines));
},
@ -495,6 +501,28 @@ GridInspector.prototype = {
node !== this.highlighters.gridHighlighterShown));
},
/**
* Handler for a change in the show grid areas checkbox in the GridDisplaySettings
* component. Toggles on/off the option to show the grid areas 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 areas.
*/
onToggleShowGridAreas(enabled) {
this.store.dispatch(updateShowGridAreas(enabled));
Services.prefs.setBoolPref(SHOW_GRID_AREAS, enabled);
let { grids } = this.store.getState();
for (let grid of grids) {
if (grid.highlighted) {
let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
this.highlighters.showGridHighlighter(grid.nodeFront, 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

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

@ -5,17 +5,25 @@
"use strict";
const {
UPDATE_SHOW_GRID_AREAS,
UPDATE_SHOW_GRID_LINE_NUMBERS,
UPDATE_SHOW_INFINITE_LINES
} = require("../actions/index");
const INITIAL_HIGHLIGHTER_SETTINGS = {
showGridAreasOverlay: false,
showGridLineNumbers: false,
showInfiniteLines: false,
};
let reducers = {
[UPDATE_SHOW_GRID_AREAS](highlighterSettings, { enabled }) {
return Object.assign({}, highlighterSettings, {
showGridAreasOverlay: enabled,
});
},
[UPDATE_SHOW_GRID_LINE_NUMBERS](highlighterSettings, { enabled }) {
return Object.assign({}, highlighterSettings, {
showGridLineNumbers: enabled,

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

@ -13,6 +13,7 @@ support-files =
[browser_grids_display-setting-extend-grid-lines.js]
[browser_grids_display-setting-show-grid-line-numbers.js]
[browser_grids_display-setting-show-grid-areas.js]
[browser_grids_grid-list-color-picker-on-ESC.js]
[browser_grids_grid-list-color-picker-on-RETURN.js]
[browser_grids_grid-list-element-rep.js]

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

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the 'Display grid areas' grid highlighter setting will update
// the redux store and pref setting.
const TEST_URI = `
<style type='text/css'>
#grid {
display: grid;
}
</style>
<div id="grid">
<div id="cell1">cell1</div>
<div id="cell2">cell2</div>
</div>
`;
const SHOW_GRID_AREAS_PREF = "devtools.gridinspector.showGridAreas";
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let { inspector, gridInspector } = yield openLayoutView();
let { document: doc } = gridInspector;
let { store } = inspector;
yield selectNode("#grid", inspector);
let checkbox = doc.getElementById("grid-setting-show-grid-areas");
ok(!Services.prefs.getBoolPref(SHOW_GRID_AREAS_PREF),
"'Display grid areas' is pref off by default.");
info("Toggling ON the 'Display grid areas' setting.");
let onCheckboxChange = waitUntilState(store, state =>
state.highlighterSettings.showGridAreasOverlay);
checkbox.click();
yield onCheckboxChange;
info("Toggling OFF the 'Display grid areas' setting.");
onCheckboxChange = waitUntilState(store, state =>
!state.highlighterSettings.showGridAreasOverlay);
checkbox.click();
yield onCheckboxChange;
ok(!Services.prefs.getBoolPref(SHOW_GRID_AREAS_PREF),
"'Display grid areas' is pref off.");
Services.prefs.clearUserPref(SHOW_GRID_AREAS_PREF);
});

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

@ -51,6 +51,7 @@ LayoutView.prototype = {
onShowGridCellHighlight,
onShowGridLineNamesHighlight,
onToggleGridHighlighter,
onToggleShowGridAreas,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
} = this.inspector.gridInspector.getComponentProps();
@ -80,6 +81,7 @@ LayoutView.prototype = {
onShowGridLineNamesHighlight,
onToggleGeometryEditor,
onToggleGridHighlighter,
onToggleShowGridAreas,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
});

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

@ -51,8 +51,7 @@ Converter.prototype = {
* 2. onStartRequest fires, initializes stuff, modifies the listener
* to match our output type
* 3. onDataAvailable spits it back to the listener
* 4. onStopRequest spits it back to the listener and initializes
the JSON Viewer
* 4. onStopRequest spits it back to the listener
* 5. convert does nothing, it's just the synchronous version
* of asyncConvertData
*/
@ -69,97 +68,168 @@ Converter.prototype = {
},
onStartRequest: function (request, context) {
this.channel = request;
// Let "save as" save the original JSON, not the viewer.
// To save with the proper extension we need the original content type,
// which has been replaced by application/vnd.mozilla.json.view
let originalType;
if (request instanceof Ci.nsIHttpChannel) {
try {
let header = request.getResponseHeader("Content-Type");
originalType = header.split(";")[0];
} catch (err) {
// Handled below
}
} else {
let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
let match = uri.match(/^data:(.*?)[,;]/);
if (match) {
originalType = match[1];
}
}
const JSON_TYPES = ["application/json", "application/manifest+json"];
if (!JSON_TYPES.includes(originalType)) {
originalType = JSON_TYPES[0];
}
request.QueryInterface(Ci.nsIWritablePropertyBag);
request.setProperty("contentType", originalType);
// Parse source as JSON. This is like text/plain, but enforcing
// UTF-8 charset (see bug 741776).
// Set the content type to HTML in order to parse the doctype, styles
// and scripts, but later a <plaintext> element will switch the tokenizer
// to the plaintext state in order to parse the JSON.
request.QueryInterface(Ci.nsIChannel);
request.contentType = JSON_TYPES[0];
this.charset = request.contentCharset = "UTF-8";
request.contentType = "text/html";
// JSON enforces UTF-8 charset (see bug 741776).
request.contentCharset = "UTF-8";
// Changing the content type breaks saving functionality. Fix it.
fixSave(request);
// Because content might still have a reference to this window,
// force setting it to a null principal to avoid it being same-
// origin with (other) content.
request.loadInfo.resetPrincipalToInheritToNullPrincipal();
// Start the request.
this.listener.onStartRequest(request, context);
// Initialize stuff.
let win = NetworkHelper.getWindowForRequest(request);
exportData(win, request);
win.addEventListener("DOMContentLoaded", event => {
win.addEventListener("contentMessage", onContentMessage, false, true);
}, {once: true});
// Insert the initial HTML code.
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let stream = converter.convertToInputStream(initialHTML(win.document));
this.listener.onDataAvailable(request, context, stream, 0, stream.available());
},
onStopRequest: function (request, context, statusCode) {
let Locale = {
$STR: key => {
try {
return jsonViewStrings.GetStringFromName(key);
} catch (err) {
console.error(err);
return undefined;
}
}
};
let headers = {
response: [],
request: []
};
// The request doesn't have to be always nsIHttpChannel
// (e.g. in case of data: URLs)
if (request instanceof Ci.nsIHttpChannel) {
request.visitResponseHeaders({
visitHeader: function (name, value) {
headers.response.push({name: name, value: value});
}
});
request.visitRequestHeaders({
visitHeader: function (name, value) {
headers.request.push({name: name, value: value});
}
});
}
let win = NetworkHelper.getWindowForRequest(request);
JsonViewUtils.exportIntoContentScope(win, Locale, "Locale");
JsonViewUtils.exportIntoContentScope(win, headers, "headers");
win.addEventListener("DOMContentLoaded", event => {
win.addEventListener("contentMessage",
onContentMessage.bind(this), false, true);
loadJsonViewer(win.document);
}, {once: true});
this.listener.onStopRequest(this.channel, context, statusCode);
this.listener.onStopRequest(request, context, statusCode);
this.listener = null;
}
};
// Lets "save as" save the original JSON, not the viewer.
// To save with the proper extension we need the original content type,
// which has been replaced by application/vnd.mozilla.json.view
function fixSave(request) {
let originalType;
if (request instanceof Ci.nsIHttpChannel) {
try {
let header = request.getResponseHeader("Content-Type");
originalType = header.split(";")[0];
} catch (err) {
// Handled below
}
} else {
let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
let match = uri.match(/^data:(.*?)[,;]/);
if (match) {
originalType = match[1];
}
}
const JSON_TYPES = ["application/json", "application/manifest+json"];
if (!JSON_TYPES.includes(originalType)) {
originalType = JSON_TYPES[0];
}
request.QueryInterface(Ci.nsIWritablePropertyBag);
request.setProperty("contentType", originalType);
}
// Exports variables that will be accessed by the non-privileged scripts.
function exportData(win, request) {
let Locale = {
$STR: key => {
try {
return jsonViewStrings.GetStringFromName(key);
} catch (err) {
console.error(err);
return undefined;
}
}
};
JsonViewUtils.exportIntoContentScope(win, Locale, "Locale");
let headers = {
response: [],
request: []
};
// The request doesn't have to be always nsIHttpChannel
// (e.g. in case of data: URLs)
if (request instanceof Ci.nsIHttpChannel) {
request.visitResponseHeaders({
visitHeader: function (name, value) {
headers.response.push({name: name, value: value});
}
});
request.visitRequestHeaders({
visitHeader: function (name, value) {
headers.request.push({name: name, value: value});
}
});
}
JsonViewUtils.exportIntoContentScope(win, headers, "headers");
}
// Serializes a qualifiedName and an optional set of attributes into an HTML
// start tag. Be aware qualifiedName and attribute names are not validated.
// Attribute values are escaped with escapingString algorithm in attribute mode
// (https://html.spec.whatwg.org/multipage/syntax.html#escapingString).
function startTag(qualifiedName, attributes = {}) {
return Object.entries(attributes).reduce(function (prev, [attr, value]) {
return prev + " " + attr + "=\"" +
value.replace(/&/g, "&amp;")
.replace(/\u00a0/g, "&nbsp;")
.replace(/"/g, "&quot;") +
"\"";
}, "<" + qualifiedName) + ">";
}
// Builds an HTML string that will be used to load stylesheets and scripts,
// and switch the parser to plaintext state.
function initialHTML(doc) {
let os;
let platform = Services.appinfo.OS;
if (platform.startsWith("WINNT")) {
os = "win";
} else if (platform.startsWith("Darwin")) {
os = "mac";
} else {
os = "linux";
}
let base = doc.createElement("base");
base.href = "resource://devtools/client/jsonview/";
let style = doc.createElement("link");
style.rel = "stylesheet";
style.type = "text/css";
style.href = "css/main.css";
let script = doc.createElement("script");
script.src = "lib/require.js";
script.dataset.main = "viewer-config";
script.defer = true;
let head = doc.createElement("head");
head.append(base, style, script);
return "<!DOCTYPE html>\n" +
startTag("html", {
"platform": os,
"class": "theme-" + JsonViewUtils.getCurrentTheme(),
"dir": Services.locale.isAppLocaleRTL ? "rtl" : "ltr"
}) +
head.outerHTML +
startTag("body") +
startTag("div", {"id": "content"}) +
startTag("plaintext", {"id": "json"});
}
// Chrome <-> Content communication
function onContentMessage(e) {
// Do not handle events from different documents.
let win = NetworkHelper.getWindowForRequest(this.channel);
let win = this;
if (win != e.target) {
return;
}
@ -183,53 +253,6 @@ function onContentMessage(e) {
}
}
// Loads the JSON Viewer into a text/plain document
function loadJsonViewer(doc) {
function addStyleSheet(url) {
let link = doc.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
doc.head.appendChild(link);
}
let os;
let platform = Services.appinfo.OS;
if (platform.startsWith("WINNT")) {
os = "win";
} else if (platform.startsWith("Darwin")) {
os = "mac";
} else {
os = "linux";
}
doc.documentElement.setAttribute("platform", os);
doc.documentElement.dataset.contentType = doc.contentType;
doc.documentElement.classList.add("theme-" + JsonViewUtils.getCurrentTheme());
doc.documentElement.dir = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
let base = doc.createElement("base");
base.href = "resource://devtools/client/jsonview/";
doc.head.appendChild(base);
addStyleSheet("../themes/variables.css");
addStyleSheet("../themes/common.css");
addStyleSheet("../themes/toolbars.css");
addStyleSheet("css/main.css");
let json = doc.querySelector("pre");
json.id = "json";
let content = doc.createElement("div");
content.id = "content";
content.appendChild(json);
doc.body.appendChild(content);
let script = doc.createElement("script");
script.src = "lib/require.js";
script.dataset.main = "viewer-config";
doc.body.appendChild(script);
}
function copyHeaders(win, headers) {
let value = "";
let eol = (Services.appinfo.OS !== "WINNT") ? "\n" : "\r\n";

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

@ -33,6 +33,7 @@ pre {
#json {
margin: 8px;
white-space: pre-wrap;
}
/******************************************************************************/
@ -46,10 +47,3 @@ body.theme-dark {
.theme-dark pre {
background-color: var(--theme-body-background);
}
/******************************************************************************/
/* Fixes for quirks mode */
table {
font: inherit;
}

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

@ -3,7 +3,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@import "resource://devtools/client/shared/components/reps/reps.css";
@import "resource://devtools/client/themes/variables.css";
@import "resource://devtools/client/themes/common.css";
@import "resource://devtools/client/themes/toolbars.css";
@import "resource://devtools/client/shared/components/tree/tree-view.css";
@import "resource://devtools/client/shared/components/tabs/tabs.css";
@ -53,4 +56,3 @@
.theme-firebug .tabs .tabs-navigation {
font-size: 14px;
}

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

@ -12,6 +12,10 @@
layout.cannotShowGridOutline=Cannot show outline for this grid
layout.cannotShowGridOutline.title=The selected grids outline cannot effectively fit inside the layout panel for it to be usable.
# LOCALIZATION NOTE (layout.displayGridAreas): Label of the display grid areas setting
# option in the CSS Grid pane.
layout.displayGridAreas=Display grid areas
# LOCALIZATION NOTE (layout.displayNumbersOnLines): Label of the display numbers on lines
# setting option in the CSS Grid pane.
layout.displayNumbersOnLines=Display numbers on lines

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

@ -73,6 +73,7 @@ pref("devtools.fontinspector.enabled", true);
pref("devtools.layoutview.enabled", false);
// Grid highlighter preferences
pref("devtools.gridinspector.showGridAreas", false);
pref("devtools.gridinspector.showGridLineNumbers", false);
pref("devtools.gridinspector.showGridOutline", false);
pref("devtools.gridinspector.showInfiniteLines", false);

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

@ -40,6 +40,7 @@ const ROWS = "rows";
const GRID_FONT_SIZE = 10;
const GRID_FONT_FAMILY = "sans-serif";
const GRID_AREA_NAME_FONT_SIZE = "20";
const GRID_LINES_PROPERTIES = {
"edge": {
@ -53,6 +54,11 @@ const GRID_LINES_PROPERTIES = {
"implicit": {
lineDash: [2, 2],
alpha: 0.5,
},
"areaEdge": {
lineDash: [0, 0],
alpha: 1,
lineWidth: 3,
}
};
@ -197,6 +203,10 @@ function drawRoundedRect(ctx, x, y, width, height, radius) {
* - showGridArea(areaName)
* @param {String} areaName
* Shows the grid area highlight for the given area name.
* - showGridAreasOverlay(isShown)
* @param {Boolean} isShown
* Displays an overlay of all the grid areas for the current grid container if
* isShown is true.
* - showGridCell({ gridFragmentIndex: Number, rowNumber: Number, columnNumber: Number })
* @param {Object} { gridFragmentIndex: Number, rowNumber: Number,
* columnNumber: Number }
@ -951,7 +961,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
},
/**
* Updates the <canvas> element's style in accordance with the current window's
* Updates the <canvas> element's style in accordance with the current window's
* devicePixelRatio, and the position calculated in `calculateCanvasPosition`; it also
* clears the drawing context.
*/
@ -1066,6 +1076,10 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
this.getFirstColLinePos(fragment),
this.getLastColLinePos(fragment));
if (this.options.showGridAreasOverlay) {
this.renderGridAreaOverlay();
}
// Line numbers are rendered in a 2nd step to avoid overlapping with existing lines.
if (this.options.showGridLineNumbers) {
this.renderLineNumbers(fragment.cols, COLUMNS, "left", "top",
@ -1075,6 +1089,101 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
}
},
/**
* Renders the grid area overlay on the css grid highlighter canvas.
*/
renderGridAreaOverlay() {
let padding = 1;
for (let i = 0; i < this.gridData.length; i++) {
let fragment = this.gridData[i];
for (let area of fragment.areas) {
let { rowStart, rowEnd, columnStart, columnEnd } = area;
// Draw the line edges for the grid area
const areaColStart = fragment.cols.lines[columnStart - 1];
const areaColEnd = fragment.cols.lines[columnEnd - 1];
const areaRowStart = fragment.rows.lines[rowStart - 1];
const areaRowEnd = fragment.rows.lines[rowEnd - 1];
const areaColStartLinePos = areaColStart.start + areaColStart.breadth;
const areaRowStartLinePos = areaRowStart.start + areaRowStart.breadth;
this.renderLine(areaColStartLinePos + padding,
areaRowStartLinePos, areaRowEnd.start,
COLUMNS, "areaEdge");
this.renderLine(areaColEnd.start - padding,
areaRowStartLinePos, areaRowEnd.start,
COLUMNS, "areaEdge");
this.renderLine(areaRowStartLinePos + padding,
areaColStartLinePos, areaColEnd.start,
ROWS, "areaEdge");
this.renderLine(areaRowEnd.start - padding,
areaColStartLinePos, areaColEnd.start,
ROWS, "areaEdge");
this.renderGridAreaName(fragment, area);
}
}
this.ctx.restore();
},
/**
* Render grid area name on the containing grid area cell.
*
* @param {Object} fragment
* The grid fragment of the grid container.
* @param {Object} area
* The area overlay to render on the CSS highlighter canvas.
*/
renderGridAreaName(fragment, area) {
let { rowStart, rowEnd, columnStart, columnEnd } = area;
let { devicePixelRatio } = this.win;
let displayPixelRatio = getDisplayPixelRatio(this.win);
let currentZoom = getCurrentZoom(this.win);
let offset = (displayPixelRatio / 2) % 1;
let fontSize = (GRID_AREA_NAME_FONT_SIZE * displayPixelRatio);
this.ctx.save();
let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
this.ctx.translate(offset - canvasX, offset - canvasY);
this.ctx.font = (fontSize * currentZoom) + "px " + GRID_FONT_FAMILY;
this.ctx.strokeStyle = this.color;
this.ctx.fillStyle = this.color;
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
// Draw the text for the grid area name.
for (let rowNumber = rowStart; rowNumber < rowEnd; rowNumber++) {
for (let columnNumber = columnStart; columnNumber < columnEnd; columnNumber++) {
let row = fragment.rows.tracks[rowNumber - 1];
let column = fragment.cols.tracks[columnNumber - 1];
// Check if the font size is exceeds the bounds of the containing grid cell.
if (fontSize > column.breadth || fontSize > row.breadth) {
fontSize = (column.breadth + row.breadth) / 2;
this.ctx.font = (fontSize * currentZoom) + "px " + GRID_FONT_FAMILY;
}
let x = column.start + column.breadth / 2;
let y = row.start + row.breadth / 2;
[x, y] = apply(this.currentMatrix, [x, y]);
this.ctx.fillText(area.name, x, y);
}
}
this.ctx.restore();
},
/**
* Render the grid lines given the grid dimension information of the
* column or row lines.
@ -1180,7 +1289,6 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
this.ctx.beginPath();
this.ctx.translate(offset - x, offset - y);
this.ctx.lineWidth = lineWidth;
if (dimensionType === COLUMNS) {
if (isFinite(endPos)) {
@ -1203,6 +1311,12 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
this.ctx.strokeStyle = this.color;
this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha;
if (GRID_LINES_PROPERTIES[lineType].lineWidth) {
this.ctx.lineWidth = GRID_LINES_PROPERTIES[lineType].lineWidth * devicePixelRatio;
} else {
this.ctx.lineWidth = lineWidth;
}
this.ctx.stroke();
this.ctx.restore();
},

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

@ -143,10 +143,12 @@ AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
uint32_t aOutputChannels,
AudioDataValue* aOutput)
{
uint32_t numChannelsToCopy = std::min(aOutputChannels,
static_cast<uint32_t>(aChunk.mChannelData.Length()));
switch(aChunk.mBufferFormat) {
case AUDIO_FORMAT_S16: {
AutoTArray<const int16_t*, 2> array;
array.SetLength(aOutputChannels);
array.SetLength(numChannelsToCopy);
for (uint32_t i = 0; i < array.Length(); i++) {
array[i] = static_cast<const int16_t*>(aChunk.mChannelData[i]);
}
@ -155,7 +157,7 @@ AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
}
case AUDIO_FORMAT_FLOAT32: {
AutoTArray<const float*, 2> array;
array.SetLength(aOutputChannels);
array.SetLength(numChannelsToCopy);
for (uint32_t i = 0; i < array.Length(); i++) {
array[i] = static_cast<const float*>(aChunk.mChannelData[i]);
}

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

@ -2436,15 +2436,11 @@ already_AddRefed<ScaledFont>
gfxPlatform::GetScaledFontForFontWithCairoSkia(DrawTarget* aTarget, gfxFont* aFont)
{
NativeFont nativeFont;
if (aTarget->GetBackendType() == BackendType::CAIRO || aTarget->GetBackendType() == BackendType::SKIA) {
nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
nativeFont.mFont = aFont->GetCairoScaledFont();
return Factory::CreateScaledFontForNativeFont(nativeFont,
aFont->GetUnscaledFont(),
aFont->GetAdjustedSize());
}
return nullptr;
nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
nativeFont.mFont = aFont->GetCairoScaledFont();
return Factory::CreateScaledFontForNativeFont(nativeFont,
aFont->GetUnscaledFont(),
aFont->GetAdjustedSize());
}
/* static */ bool

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

@ -595,21 +595,15 @@ gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target)
already_AddRefed<ScaledFont>
gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
switch (aTarget->GetBackendType()) {
case BackendType::CAIRO:
case BackendType::SKIA:
if (aFont->GetType() == gfxFont::FONT_TYPE_FONTCONFIG) {
gfxFontconfigFontBase* fcFont = static_cast<gfxFontconfigFontBase*>(aFont);
return Factory::CreateScaledFontForFontconfigFont(
fcFont->GetCairoScaledFont(),
fcFont->GetPattern(),
fcFont->GetUnscaledFont(),
fcFont->GetAdjustedSize());
}
MOZ_FALLTHROUGH;
default:
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
}
if (aFont->GetType() == gfxFont::FONT_TYPE_FONTCONFIG) {
gfxFontconfigFontBase* fcFont = static_cast<gfxFontconfigFontBase*>(aFont);
return Factory::CreateScaledFontForFontconfigFont(
fcFont->GetCairoScaledFont(),
fcFont->GetPattern(),
fcFont->GetUnscaledFont(),
fcFont->GetAdjustedSize());
}
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
}
#ifdef GL_PROVIDER_GLX

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

@ -17291,8 +17291,7 @@ CSSParserImpl::ParsePaint(nsCSSPropertyID aPropID)
{
nsCSSValue x, y;
if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
VARIANT_OPENTYPE_SVG_KEYWORD,
if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | VARIANT_KEYWORD,
nsCSSProps::kContextPatternKTable) != CSSParseResult::Ok) {
return false;
}

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

@ -80,7 +80,7 @@ to mochitest command.
* Unsupported values
* SVG-in-OpenType values not supported servo/servo#15211 bug 1355412
* test_value_storage.html `context-` [7]
* test_bug798843_pref.html [7]
* test_bug798843_pref.html [5]
* Incorrect parsing
* different parsing bug 1364260
* test_supports_rules.html [6]

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

@ -13,8 +13,6 @@
<script>
var props = {
"fill" : "context-stroke none",
"stroke" : "context-fill none",
"fillOpacity" : "context-stroke-opacity",
"strokeOpacity" : "context-fill-opacity",
"strokeDasharray" : "context-value",

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

@ -11,5 +11,7 @@
// bug 1177298, uniqlo.com
"uniqlo.com": "\\)\\s#) Mobile Safari ",
// bug 1338260, directv.com
"directv.com": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
"directv.com": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
// bug 1310951, m.canadiantire.ca
"m.canadiantire.ca": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
}

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

@ -1841,7 +1841,7 @@ public abstract class GeckoApp extends GeckoActivity
startActivity(settingsIntent);
}
mPromptService = new PromptService(this);
mPromptService = new PromptService(this, getAppEventDispatcher());
// Trigger the completion of the telemetry timer that wraps activity startup,
// then grab the duration to give to FHR.

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

@ -182,7 +182,8 @@ class GlobalHistory {
/* protected */ void checkVisited(final String uri) {
final String storedURI = ReaderModeUtils.stripAboutReaderUrl(uri);
final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getContext());
final NotifierRunnable runnable =
new NotifierRunnable(GeckoAppShell.getApplicationContext());
mHandler.post(new Runnable() {
@Override
public void run() {

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

@ -6,8 +6,6 @@
package org.mozilla.gecko.prompts;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
@ -19,17 +17,19 @@ import android.util.Log;
public class PromptService implements BundleEventListener {
private static final String LOGTAG = "GeckoPromptService";
private final GeckoApp mGeckoApp;
private final Context mContext;
private final EventDispatcher mDispatcher;
public PromptService(GeckoApp geckoApp) {
mGeckoApp = geckoApp;
mGeckoApp.getAppEventDispatcher().registerUiThreadListener(this,
public PromptService(final Context context, final EventDispatcher dispatcher) {
mContext = context;
mDispatcher = dispatcher;
mDispatcher.registerUiThreadListener(this,
"Prompt:Show",
"Prompt:ShowTop");
}
public void destroy() {
mGeckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
mDispatcher.unregisterUiThreadListener(this,
"Prompt:Show",
"Prompt:ShowTop");
}
@ -39,7 +39,7 @@ public class PromptService implements BundleEventListener {
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
Prompt p;
p = new Prompt(mGeckoApp, new Prompt.PromptCallback() {
p = new Prompt(mContext, new Prompt.PromptCallback() {
@Override
public void onPromptFinished(final GeckoBundle result) {
callback.sendSuccess(result);

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

@ -7,6 +7,7 @@
var Cc = Components.classes;
var Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Messaging.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
this.EXPORTED_SYMBOLS = ["Prompt"];
@ -15,14 +16,24 @@ function log(msg) {
Services.console.logStringMessage(msg);
}
function getRootWindow(win) {
// Get the root xul window.
return win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
function Prompt(aOptions) {
this.window = "window" in aOptions ? aOptions.window : null;
this.msg = { async: true };
if (this.window) {
let window = Services.wm.getMostRecentWindow("navigator:browser");
var tab = window.BrowserApp.getTabForWindow(this.window);
let window = getRootWindow(this.window);
var tab = window &&
window.BrowserApp &&
window.BrowserApp.getTabForWindow(this.window);
if (tab) {
this.msg.tabId = tab.id;
}
@ -174,11 +185,24 @@ Prompt.prototype = {
this._innerShow();
},
_getDispatcher: function(win) {
let root = win && getRootWindow(win);
try {
return root && (root.WindowEventDispatcher || EventDispatcher.for(root));
} catch (e) {
// No EventDispatcher for this window.
return null;
}
},
_innerShow: function() {
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequestForResult(this.msg).then((data) => {
if (this.callback)
let dispatcher =
this._getDispatcher(this.window) ||
this._getDispatcher(Services.wm.getMostRecentWindow("navigator:browser"));
dispatcher.sendRequestForResult(this.msg).then((data) => {
if (this.callback) {
this.callback(data);
}
});
},

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

@ -2086,11 +2086,15 @@ pref("network.auth.subresource-img-cross-origin-http-auth-allow", true);
// in that case default credentials will always be used.
pref("network.auth.private-browsing-sso", false);
// Control how the throttling service works - number of ms that each
// Control how throttling of http responses works - number of ms that each
// suspend and resume period lasts (prefs named appropriately)
pref("network.throttle.suspend-for", 3000);
pref("network.throttle.resume-for", 200);
pref("network.throttle.enable", true);
pref("network.http.throttle.enable", true);
pref("network.http.throttle.suspend-for", 3000);
pref("network.http.throttle.resume-for", 200);
// Delay we resume throttled background responses after the last unthrottled
// response has finished. Prevents resuming too soon during an active page load
// at which sub-resource reqeusts quickly come and go.
pref("network.http.throttle.resume-background-in", 400);
pref("permissions.default.image", 1); // 1-Accept, 2-Deny, 3-dontAcceptForeign

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

@ -404,6 +404,7 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
}
aHttpTransaction->SetConnection(this);
aHttpTransaction->OnActivated(true);
if (aUseTunnel) {
LOG3(("Http2Session::AddStream session=%p trans=%p OnTunnel",

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

@ -45,6 +45,10 @@ public:
// called by the connection when it takes ownership of the transaction.
virtual void SetConnection(nsAHttpConnection *) = 0;
// called by the connection after a successfull activation of this transaction
// in other words, tells the transaction it transitioned to the "active" state.
virtual void OnActivated(bool h2) {}
// used to obtain the connection associated with this transaction
virtual nsAHttpConnection *Connection() = 0;

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

@ -711,10 +711,6 @@ nsHttpChannel::ContinueConnect()
while (suspendCount--)
mTransactionPump->Suspend();
if (mClassOfService & nsIClassOfService::Throttleable) {
gHttpHandler->ThrottleTransaction(mTransaction, true);
}
return NS_OK;
}
@ -6622,10 +6618,8 @@ nsHttpChannel::ContinueBeginConnect()
void
nsHttpChannel::OnClassOfServiceUpdated()
{
bool throttleable = !!(mClassOfService & nsIClassOfService::Throttleable);
if (mTransaction) {
gHttpHandler->ThrottleTransaction(mTransaction, throttleable);
gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction, mClassOfService);
}
}
@ -8009,10 +8003,6 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
while (suspendCount--)
mTransactionPump->Suspend();
if (mSuspendCount && mClassOfService & nsIClassOfService::Throttleable) {
gHttpHandler->ThrottleTransaction(mTransaction, true);
}
return NS_OK;
}
@ -8719,10 +8709,6 @@ nsHttpChannel::SuspendInternal()
if (mSuspendCount == 1) {
mSuspendTimestamp = TimeStamp::NowLoRes();
if (mClassOfService & nsIClassOfService::Throttleable && mTransaction) {
gHttpHandler->ThrottleTransaction(mTransaction, true);
}
}
nsresult rvTransaction = NS_OK;
@ -8748,10 +8734,6 @@ nsHttpChannel::ResumeInternal()
mSuspendTotalTime += (TimeStamp::NowLoRes() - mSuspendTimestamp).
ToMilliseconds();
if (mClassOfService & nsIClassOfService::Throttleable && mTransaction) {
gHttpHandler->ThrottleTransaction(mTransaction, false);
}
if (mCallOnResume) {
nsresult rv = AsyncCall(mCallOnResume);
mCallOnResume = nullptr;

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

@ -664,6 +664,8 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
mTransaction = mTLSFilter;
}
trans->OnActivated(false);
rv = OnOutputStreamReady(mSocketOut);
failed_activation:

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

@ -115,6 +115,11 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
, mMaxConns(0)
, mMaxPersistConnsPerHost(0)
, mMaxPersistConnsPerProxy(0)
, mMaxRequestDelay(0)
, mThrottleEnabled(false)
, mThrottleSuspendFor(0)
, mThrottleResumeFor(0)
, mThrottleResumeIn(0)
, mIsShuttingDown(false)
, mNumActiveConns(0)
, mNumIdleConns(0)
@ -125,6 +130,9 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
, mTimeoutTickArmed(false)
, mTimeoutTickNext(1)
, mCurrentTopLevelOuterContentWindowId(0)
, mThrottlingInhibitsReading(false)
, mActiveTabTransactionsExist(false)
, mActiveTabUnthrottledTransactionsExist(false)
{
LOG(("Creating nsHttpConnectionMgr @%p\n", this));
}
@ -164,7 +172,11 @@ nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
uint16_t maxConns,
uint16_t maxPersistConnsPerHost,
uint16_t maxPersistConnsPerProxy,
uint16_t maxRequestDelay)
uint16_t maxRequestDelay,
bool throttleEnabled,
uint32_t throttleSuspendFor,
uint32_t throttleResumeFor,
uint32_t throttleResumeIn)
{
LOG(("nsHttpConnectionMgr::Init\n"));
@ -177,6 +189,11 @@ nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
mMaxRequestDelay = maxRequestDelay;
mThrottleEnabled = throttleEnabled;
mThrottleSuspendFor = throttleSuspendFor;
mThrottleResumeFor = throttleResumeFor;
mThrottleResumeIn = throttleResumeIn;
mIsShuttingDown = false;
}
@ -344,13 +361,15 @@ nsHttpConnectionMgr::Observe(nsISupports *subject,
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
if (timer == mTimer) {
Unused << PruneDeadConnections();
}
else if (timer == mTimeoutTick) {
} else if (timer == mTimeoutTick) {
TimeoutTick();
} else if (timer == mTrafficTimer) {
Unused << PruneNoTraffic();
}
else {
} else if (timer == mThrottleTicker) {
ThrottlerTick();
} else if (timer == mDelayedResumeReadTimer) {
ResumeBackgroundThrottledTransactions();
} else {
MOZ_ASSERT(false, "unexpected timer-callback");
LOG(("Unexpected timer object\n"));
return NS_ERROR_UNEXPECTED;
@ -378,12 +397,12 @@ nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t pri
}
void
nsHttpConnectionMgr::ThrottleTransaction(nsHttpTransaction *trans, bool throttle)
nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction(nsHttpTransaction *trans, uint32_t classOfService)
{
LOG(("nsHttpConnectionMgr::ThrottleTransaction [trans=%p throttle=%" PRIx32 "]\n",
trans, static_cast<uint32_t>(throttle)));
Unused << PostEvent(&nsHttpConnectionMgr::OnMsgThrottleTransaction,
static_cast<int32_t>(throttle), trans);
LOG(("nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction [trans=%p classOfService=%" PRIu32 "]\n",
trans, static_cast<uint32_t>(classOfService)));
Unused << PostEvent(&nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction,
static_cast<int32_t>(classOfService), trans);
}
nsresult
@ -2157,6 +2176,10 @@ nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
mTrafficTimer->Cancel();
mTrafficTimer = nullptr;
}
DestroyThrottleTicker();
mActiveTransactions[false].Clear();
mActiveTransactions[true].Clear();
mCoalescingHash.Clear();
// signal shutdown complete
@ -2224,15 +2247,15 @@ nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
}
}
void nsHttpConnectionMgr::OnMsgThrottleTransaction(int32_t arg, ARefBase *param)
void nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction(int32_t arg, ARefBase *param)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG(("nsHttpConnectionMgr::OnMsgThrottleTransaction [trans=%p]\n", param));
LOG(("nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction [trans=%p]\n", param));
bool throttle = static_cast<bool>(arg);
uint32_t cos = static_cast<uint32_t>(arg);
nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
trans->ThrottleResponse(throttle);
trans->SetClassOfService(cos);
}
void
@ -2730,6 +2753,18 @@ nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
case MAX_REQUEST_DELAY:
mMaxRequestDelay = value;
break;
case THROTTLING_ENABLED:
SetThrottlingEnabled(!!value);
break;
case THROTTLING_SUSPEND_FOR:
mThrottleSuspendFor = value;
break;
case THROTTLING_RESUME_FOR:
mThrottleResumeFor = value;
break;
case THROTTLING_RESUME_IN:
mThrottleResumeIn = value;
break;
default:
NS_NOTREACHED("unexpected parameter name");
}
@ -2803,16 +2838,480 @@ nsHttpConnectionMgr::UpdateCurrentTopLevelOuterContentWindowId(
windowIdWrapper);
}
void nsHttpConnectionMgr::SetThrottlingEnabled(bool aEnable)
{
LOG(("nsHttpConnectionMgr::SetThrottlingEnabled enable=%d", aEnable));
mThrottleEnabled = aEnable;
if (mThrottleEnabled) {
EnsureThrottleTickerIfNeeded();
} else {
DestroyThrottleTicker();
ResumeReadOf(mActiveTransactions[false]);
ResumeReadOf(mActiveTransactions[true]);
}
}
void nsHttpConnectionMgr::LogActiveTransactions(char operation)
{
if (!LOG_ENABLED()) {
return;
}
nsTArray<RefPtr<nsHttpTransaction>> *trs = nullptr;
uint32_t au, at, bu = 0, bt = 0;
trs = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
au = trs ? trs->Length() : 0;
trs = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
at = trs ? trs->Length() : 0;
for (auto iter = mActiveTransactions[false].Iter(); !iter.Done(); iter.Next()) {
bu += iter.UserData()->Length();
}
bu -= au;
for (auto iter = mActiveTransactions[true].Iter(); !iter.Done(); iter.Next()) {
bt += iter.UserData()->Length();
}
bt -= at;
// Shows counts of:
// - unthrottled transaction for the active tab
// - throttled transaction for the active tab
// - unthrottled transaction for background tabs
// - throttled transaction for background tabs
LOG(("Active transactions %c[%u,%u,%u,%u]", operation, au, at, bu, bt));
}
void
nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
int32_t, ARefBase *param)
nsHttpConnectionMgr::AddActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
mCurrentTopLevelOuterContentWindowId =
static_cast<UINT64Wrapper*>(param)->GetValue();
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
nsTArray<RefPtr<nsHttpTransaction>> *transactions =
mActiveTransactions[aThrottled].LookupOrAdd(tabId);
MOZ_ASSERT(!transactions->Contains(aTrans));
transactions->AppendElement(aTrans);
LOG(("nsHttpConnectionMgr::AddActiveTransaction t=%p tabid=%" PRIx64 "(%d) thr=%d",
aTrans, tabId, tabId == mCurrentTopLevelOuterContentWindowId, aThrottled));
LogActiveTransactions('+');
if (tabId == mCurrentTopLevelOuterContentWindowId) {
mActiveTabTransactionsExist = true;
if (!aThrottled) {
mActiveTabUnthrottledTransactionsExist = true;
}
}
if (!mThrottleEnabled) {
return;
}
EnsureThrottleTickerIfNeeded();
}
void
nsHttpConnectionMgr::RemoveActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
bool forActiveTab = tabId == mCurrentTopLevelOuterContentWindowId;
nsTArray<RefPtr<nsHttpTransaction>> *transactions =
mActiveTransactions[aThrottled].Get(tabId);
if (!transactions || !transactions->RemoveElement(aTrans)) {
// Was not tracked as active, probably just ignore.
return;
}
LOG(("nsHttpConnectionMgr::RemoveActiveTransaction t=%p tabid=%" PRIx64 "(%d) thr=%d",
aTrans, tabId, forActiveTab, aThrottled));
if (!transactions->IsEmpty()) {
// There are still transactions of the type, hence nothing in the throttling conditions
// has changed and we don't need to update "Exists" caches nor we need to wake any now
// throttled transactions.
LogActiveTransactions('-');
return;
}
// To optimize the following logic, always remove the entry when the array is empty.
mActiveTransactions[aThrottled].Remove(tabId);
LogActiveTransactions('-');
if (forActiveTab) {
// Update caches of the active tab transaction existence, since it's now affected
if (!aThrottled) {
mActiveTabUnthrottledTransactionsExist = false;
}
if (mActiveTabTransactionsExist) {
mActiveTabTransactionsExist = mActiveTransactions[!aThrottled].Contains(tabId);
}
}
if (!mThrottleEnabled) {
return;
}
bool unthrottledExist = !mActiveTransactions[false].IsEmpty();
bool throttledExist = !mActiveTransactions[true].IsEmpty();
if (!unthrottledExist && !throttledExist) {
// Nothing active globally, just get rid of the timer completely and we are done.
MOZ_ASSERT(!mActiveTabUnthrottledTransactionsExist);
MOZ_ASSERT(!mActiveTabTransactionsExist);
DestroyThrottleTicker();
return;
}
if (!mThrottlingInhibitsReading) {
// There is then nothing to wake up. Affected transactions will not be put
// to sleep automatically on next tick.
LOG((" reading not currently inhibited"));
return;
}
if (mActiveTabUnthrottledTransactionsExist) {
// There are still unthrottled transactions for the active tab, hence the state
// is unaffected and we don't need to do anything (nothing to wake).
LOG((" there are unthrottled for the active tab"));
return;
}
if (mActiveTabTransactionsExist) {
// There are only trottled transactions for the active tab.
// If the last transaction we just removed was a non-throttled for the active tab
// we can wake the throttled transactions for the active tab.
if (forActiveTab && !aThrottled) {
LOG((" resuming throttled for active tab"));
ResumeReadOf(mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId));
}
return;
}
if (!unthrottledExist) {
// There are no unthrottled transactions for any tab. Resume all throttled,
// all are only for background tabs.
LOG((" delay resuming throttled for background tabs"));
DelayedResumeBackgroundThrottledTransactions();
return;
}
if (forActiveTab) {
// Removing the last transaction for the active tab frees up the unthrottled
// background tabs transactions.
LOG((" resuming unthrottled for background tabs"));
ResumeReadOf(mActiveTransactions[false]);
return;
}
LOG((" not resuming anything"));
}
void
nsHttpConnectionMgr::MoveActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
{
LOG(("nsHttpConnectionMgr::MoveActiveTransaction ENTER t=%p", aTrans));
AddActiveTransaction(aTrans, aThrottled);
RemoveActiveTransaction(aTrans, !aThrottled);
LOG(("nsHttpConnectionMgr::MoveActiveTransaction EXIT t=%p", aTrans));
}
bool
nsHttpConnectionMgr::ShouldStopReading(nsHttpTransaction * aTrans, bool aThrottled)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!mThrottlingInhibitsReading || !mThrottleEnabled) {
return false;
}
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
if (mActiveTabTransactionsExist) {
if (!tabId) {
// Chrome initiated and unidentified transactions just respect
// their throttle flag, when something for the active tab is happening.
return aThrottled;
}
if (tabId != mCurrentTopLevelOuterContentWindowId) {
// This is a background tab request, we want them to always throttle.
return true;
}
if (mActiveTabUnthrottledTransactionsExist) {
// Unthrottled transactions for the active tab take precedence
return aThrottled;
}
// This is a throttled transaction for the active tab and there are no
// unthrottled for the active tab, just let go on full fuel.
return false;
}
if (!mActiveTransactions[false].IsEmpty()) {
// This means there are unthrottled active transactions for background tabs.
// If we are here, there can't be any transactions for the active tab.
// (If there is no transaction for a tab id, there is no entry for it in the hashtable.)
return aThrottled;
}
// There are only unthrottled transactions for background tabs: don't throttle.
return false;
}
bool nsHttpConnectionMgr::IsThrottleTickerNeeded()
{
LOG(("nsHttpConnectionMgr::IsThrottleTickerNeeded"));
if (mActiveTabUnthrottledTransactionsExist &&
mActiveTransactions[false].Count() > 1) {
LOG((" there are unthrottled transactions for both active and bck"));
return true;
}
if (mActiveTabTransactionsExist &&
mActiveTransactions[true].Count() > 1) {
LOG((" there are throttled transactions for both active and bck"));
return true;
}
if (!mActiveTransactions[true].IsEmpty() &&
!mActiveTransactions[false].IsEmpty()) {
LOG((" there are both throttled and unthrottled transactions"));
return true;
}
LOG((" nothing to throttle"));
return false;
}
void
nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG(("nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded"));
if (!IsThrottleTickerNeeded()) {
return;
}
// There is a new demand to throttle, hence unschedule delayed resume
// of background throttled transastions.
CancelDelayedResumeBackgroundThrottledTransactions();
if (mThrottleTicker) {
return;
}
MOZ_ASSERT(!mThrottlingInhibitsReading);
mThrottleTicker = do_CreateInstance("@mozilla.org/timer;1");
if (mThrottleTicker) {
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
mThrottlingInhibitsReading = true;
}
LogActiveTransactions('^');
}
void
nsHttpConnectionMgr::DestroyThrottleTicker()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
// Nothing to throttle, hence no need for this timer anymore.
CancelDelayedResumeBackgroundThrottledTransactions();
MOZ_ASSERT(!mThrottleEnabled || !IsThrottleTickerNeeded());
if (!mThrottleTicker) {
return;
}
LOG(("nsHttpConnectionMgr::DestroyThrottleTicker"));
mThrottleTicker->Cancel();
mThrottleTicker = nullptr;
mThrottlingInhibitsReading = false;
LogActiveTransactions('v');
}
void
nsHttpConnectionMgr::ThrottlerTick()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
mThrottlingInhibitsReading = !mThrottlingInhibitsReading;
LOG(("nsHttpConnectionMgr::ThrottlerTick inhibit=%d", mThrottlingInhibitsReading));
if (!mThrottlingInhibitsReading && !IsThrottleTickerNeeded()) {
mThrottleTicker = nullptr;
} else {
mThrottleTicker = do_CreateInstance("@mozilla.org/timer;1");
}
if (mThrottlingInhibitsReading) {
if (mThrottleTicker) {
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
}
} else {
// Resume by the ticker happens sooner than delayed resume, no need
// for the delayed resume timer
CancelDelayedResumeBackgroundThrottledTransactions();
if (mThrottleTicker) {
mThrottleTicker->Init(this, mThrottleResumeFor, nsITimer::TYPE_ONE_SHOT);
}
ResumeReadOf(mActiveTransactions[false], true);
ResumeReadOf(mActiveTransactions[true]);
}
}
void
nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions()
{
if (mDelayedResumeReadTimer) {
return;
}
mDelayedResumeReadTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mDelayedResumeReadTimer) {
return;
}
LOG(("nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions"));
mDelayedResumeReadTimer->Init(this, mThrottleResumeIn, nsITimer::TYPE_ONE_SHOT);
}
void
nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions()
{
if (!mDelayedResumeReadTimer) {
return;
}
LOG(("nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions"));
mDelayedResumeReadTimer->Cancel();
mDelayedResumeReadTimer = nullptr;
}
void
nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG(("nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions"));
mDelayedResumeReadTimer = nullptr;
// We can destroy the ticker, since only transactions to resume now
// are background throttled. If 'higher class' transactions have
// been added, we don't get here - the ticker has been scheduled
// and hence the delayed resume timer canceled.
MOZ_ASSERT(mActiveTransactions[false].IsEmpty() &&
!mActiveTabTransactionsExist);
DestroyThrottleTicker();
ResumeReadOf(mActiveTransactions[true], true);
}
void
nsHttpConnectionMgr::ResumeReadOf(
nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>& hashtable,
bool excludeForActiveTab)
{
for (auto iter = hashtable.Iter(); !iter.Done(); iter.Next()) {
if (excludeForActiveTab && iter.Key() == mCurrentTopLevelOuterContentWindowId) {
// These have never been throttled (never stopped reading)
continue;
}
ResumeReadOf(iter.UserData());
}
}
void
nsHttpConnectionMgr::ResumeReadOf(nsTArray<RefPtr<nsHttpTransaction>>* transactions)
{
MOZ_ASSERT(transactions);
for (auto trans : *transactions) {
trans->ResumeReading();
}
}
void
nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
int32_t aLoading, ARefBase *param)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
uint64_t winId = static_cast<UINT64Wrapper*>(param)->GetValue();
if (mCurrentTopLevelOuterContentWindowId == winId) {
// duplicate notification
return;
}
bool activeTabWasLoading = mActiveTabTransactionsExist;
bool activeTabIdChanged = mCurrentTopLevelOuterContentWindowId != winId;
mCurrentTopLevelOuterContentWindowId = winId;
LOG(("nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId"
" id=%" PRIu64 "\n",
" id=%" PRIx64 "\n",
mCurrentTopLevelOuterContentWindowId));
nsTArray<RefPtr<nsHttpTransaction>> *transactions = nullptr;
if (activeTabIdChanged) {
// Update the "Exists" caches and resume any transactions that now deserve it,
// changing the active tab changes the conditions for throttling.
transactions = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
mActiveTabUnthrottledTransactionsExist = !!transactions;
if (!mActiveTabUnthrottledTransactionsExist) {
transactions = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
}
mActiveTabTransactionsExist = !!transactions;
}
if (transactions) {
// This means there are some transactions for this newly activated tab, resume them
// but anything else.
LOG((" resuming newly activated tab transactions"));
ResumeReadOf(transactions);
return;
}
if (!activeTabWasLoading) {
// There were no transactions for the previously active tab, hence
// all remaning transactions, if there were, were all unthrottled,
// no need to wake them.
return;
}
if (!mActiveTransactions[false].IsEmpty()) {
LOG((" resuming unthrottled background transactions"));
ResumeReadOf(mActiveTransactions[false]);
return;
}
if (!mActiveTransactions[true].IsEmpty()) {
LOG((" delayed resuming throttled background transactions"));
DelayedResumeBackgroundThrottledTransactions();
return;
}
DestroyThrottleTicker();
}
void

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

@ -55,7 +55,11 @@ public:
MAX_CONNECTIONS,
MAX_PERSISTENT_CONNECTIONS_PER_HOST,
MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
MAX_REQUEST_DELAY
MAX_REQUEST_DELAY,
THROTTLING_ENABLED,
THROTTLING_SUSPEND_FOR,
THROTTLING_RESUME_FOR,
THROTTLING_RESUME_IN
};
//-------------------------------------------------------------------------
@ -68,7 +72,11 @@ public:
uint16_t maxConnections,
uint16_t maxPersistentConnectionsPerHost,
uint16_t maxPersistentConnectionsPerProxy,
uint16_t maxRequestDelay);
uint16_t maxRequestDelay,
bool throttleEnabled,
uint32_t throttleSuspendFor,
uint32_t throttleResumeFor,
uint32_t throttleResumeIn);
MOZ_MUST_USE nsresult Shutdown();
//-------------------------------------------------------------------------
@ -95,13 +103,9 @@ public:
MOZ_MUST_USE nsresult RescheduleTransaction(nsHttpTransaction *,
int32_t priority);
// tells the transaction to stop receiving the response when |throttle|
// is true. to start receiving again, this must be called with |throttle|
// set to false. calling multiple times with the same value of |throttle|
// has no effect. called by nsHttpChannels with the Throttleable class set
// and controlled by net::ThrottlingService.
// there is nothing to do when this fails, hence the void result.
void ThrottleTransaction(nsHttpTransaction *, bool throttle);
// TOOD
void UpdateClassOfServiceOnTransaction(nsHttpTransaction *,
uint32_t classOfService);
// cancels a transaction w/ the given reason.
MOZ_MUST_USE nsresult CancelTransaction(nsHttpTransaction *,
@ -211,6 +215,17 @@ public:
nsresult UpdateCurrentTopLevelOuterContentWindowId(uint64_t aWindowId);
// tracks and untracks active transactions according their throttle status
void AddActiveTransaction(nsHttpTransaction* aTrans, bool aThrottled);
void RemoveActiveTransaction(nsHttpTransaction* aTrans, bool aThrottled);
void MoveActiveTransaction(nsHttpTransaction* aTrans, bool aThrottled);
// called by nsHttpTransaction::WriteSegments. decides whether the transaction
// should stop reading data based on: the throttling ticker status, overall
// status of all active transactions regarding active tab and respective
// throttling state.
bool ShouldStopReading(nsHttpTransaction* aTrans, bool aThrottled);
private:
virtual ~nsHttpConnectionMgr();
@ -486,6 +501,10 @@ private:
uint16_t mMaxPersistConnsPerHost;
uint16_t mMaxPersistConnsPerProxy;
uint16_t mMaxRequestDelay; // in seconds
bool mThrottleEnabled;
uint32_t mThrottleSuspendFor;
uint32_t mThrottleResumeFor;
uint32_t mThrottleResumeIn;
Atomic<bool, mozilla::Relaxed> mIsShuttingDown;
//-------------------------------------------------------------------------
@ -579,7 +598,7 @@ private:
void OnMsgShutdownConfirm (int32_t, ARefBase *);
void OnMsgNewTransaction (int32_t, ARefBase *);
void OnMsgReschedTransaction (int32_t, ARefBase *);
void OnMsgThrottleTransaction (int32_t, ARefBase *);
void OnMsgUpdateClassOfServiceOnTransaction (int32_t, ARefBase *);
void OnMsgCancelTransaction (int32_t, ARefBase *);
void OnMsgCancelTransactions (int32_t, ARefBase *);
void OnMsgProcessPendingQ (int32_t, ARefBase *);
@ -640,6 +659,58 @@ private:
nsCString mLogData;
uint64_t mCurrentTopLevelOuterContentWindowId;
// Called on a pref change
void SetThrottlingEnabled(bool aEnable);
// Two hashtalbes keeping track of active transactions regarding window id and throttling.
// Used by the throttling algorithm to obtain number of transactions for the active tab
// and for inactive tabs according their throttle status.
// mActiveTransactions[0] are all unthrottled transactions, mActiveTransactions[1] throttled.
nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>> mActiveTransactions[2];
// Whether we are inside the "stop reading" interval, altered by the throttle ticker
bool mThrottlingInhibitsReading;
// ticker for the 'stop reading'/'resume reading' signal
nsCOMPtr<nsITimer> mThrottleTicker;
// Checks if the combination of active transactions requires the ticker.
bool IsThrottleTickerNeeded();
// The method also unschedules the delayed resume of background tabs timer
// if the ticker was about to be scheduled.
void EnsureThrottleTickerIfNeeded();
// Drops also the mThrottlingInhibitsReading flag. Immediate or delayed resume
// of currently throttled transactions is not affected by this method.
void DestroyThrottleTicker();
// Handler for the ticker: alters the mThrottlingInhibitsReading flag.
void ThrottlerTick();
// mechanism to delay immediate resume of background tabs and chrome initiated
// throttled transactions after the last transaction blocking their unthrottle
// has been removed. Needs to be delayed because during a page load there is
// a number of intervals when there is no transaction that would cause throttling.
// Hence, throttling of long standing responses, like downloads, would be mostly
// ineffective if resumed during every such interval.
nsCOMPtr<nsITimer> mDelayedResumeReadTimer;
// Schedule the resume
void DelayedResumeBackgroundThrottledTransactions();
// Simply destroys the timer
void CancelDelayedResumeBackgroundThrottledTransactions();
// Handler for the timer: resumes all background throttled transactions
void ResumeBackgroundThrottledTransactions();
// Simple helpers, iterates the given hash/array and resume.
// @param excludeActive: skip active tabid transactions.
void ResumeReadOf(nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>&,
bool excludeActive = false);
void ResumeReadOf(nsTArray<RefPtr<nsHttpTransaction>>*);
// Cached status of the active tab active transactions existence,
// saves a lot of hashtable lookups
bool mActiveTabTransactionsExist;
bool mActiveTabUnthrottledTransactionsExist;
void LogActiveTransactions(char);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnectionMgr::nsHalfOpenSocket, NS_HALFOPENSOCKET_IID)

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

@ -191,6 +191,10 @@ nsHttpHandler::nsHttpHandler()
, mMaxConnections(24)
, mMaxPersistentConnectionsPerServer(2)
, mMaxPersistentConnectionsPerProxy(4)
, mThrottleEnabled(true)
, mThrottleSuspendFor(3000)
, mThrottleResumeFor(200)
, mThrottleResumeIn(400)
, mRedirectionLimit(10)
, mPhishyUserPassLength(1)
, mQoSBits(0x00)
@ -549,7 +553,11 @@ nsHttpHandler::InitConnectionMgr()
mMaxConnections,
mMaxPersistentConnectionsPerServer,
mMaxPersistentConnectionsPerProxy,
mMaxRequestDelay);
mMaxRequestDelay,
mThrottleEnabled,
mThrottleSuspendFor,
mThrottleResumeFor,
mThrottleResumeIn);
return rv;
}
@ -1546,6 +1554,41 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
}
}
if (PREF_CHANGED("network.http.throttle.enable")) {
rv = prefs->GetBoolPref("network.http.throttle.enable", &mThrottleEnabled);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_ENABLED,
static_cast<int32_t>(mThrottleEnabled));
}
}
if (PREF_CHANGED("network.http.throttle.suspend-for")) {
rv = prefs->GetIntPref("network.http.throttle.suspend-for", &val);
mThrottleSuspendFor = (uint32_t)clamped(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_SUSPEND_FOR,
mThrottleSuspendFor);
}
}
if (PREF_CHANGED("network.http.throttle.resume-for")) {
rv = prefs->GetIntPref("network.http.throttle.resume-for", &val);
mThrottleResumeFor = (uint32_t)clamped(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_RESUME_FOR,
mThrottleResumeFor);
}
}
if (PREF_CHANGED("network.http.throttle.resume-background-in")) {
rv = prefs->GetIntPref("network.http.throttle.resume-background-in", &val);
mThrottleResumeIn = (uint32_t)clamped(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_RESUME_IN,
mThrottleResumeIn);
}
}
if (PREF_CHANGED(HTTP_PREF("focused_window_transaction_ratio"))) {
float ratio = 0;
rv = prefs->GetFloatPref(HTTP_PREF("focused_window_transaction_ratio"), &ratio);

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

@ -224,10 +224,10 @@ public:
return mConnMgr->RescheduleTransaction(trans, priority);
}
void ThrottleTransaction(nsHttpTransaction *trans,
bool throttle)
void UpdateClassOfServiceOnTransaction(nsHttpTransaction *trans,
uint32_t classOfService)
{
mConnMgr->ThrottleTransaction(trans, throttle);
mConnMgr->UpdateClassOfServiceOnTransaction(trans, classOfService);
}
// Called to cancel a transaction, which may or may not be assigned to
@ -448,6 +448,11 @@ private:
uint8_t mMaxPersistentConnectionsPerServer;
uint8_t mMaxPersistentConnectionsPerProxy;
bool mThrottleEnabled;
uint32_t mThrottleSuspendFor;
uint32_t mThrottleResumeFor;
uint32_t mThrottleResumeIn;
uint8_t mRedirectionLimit;
// we'll warn the user if we load an URL containing a userpass field

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

@ -31,6 +31,7 @@
#include "nsIHttpActivityObserver.h"
#include "nsSocketTransportService2.h"
#include "nsICancelable.h"
#include "nsIClassOfService.h"
#include "nsIEventTarget.h"
#include "nsIHttpChannelInternal.h"
#include "nsIInputStream.h"
@ -105,9 +106,11 @@ nsHttpTransaction::nsHttpTransaction()
, mCurrentHttpResponseHeaderSize(0)
, mCapsToClear(0)
, mResponseIsComplete(false)
, mThrottleResponse(false)
, mReadingStopped(false)
, mClosed(false)
, mConnected(false)
, mActivated(false)
, mActivatedAsH2(false)
, mHaveStatusLine(false)
, mHaveAllHeaders(false)
, mTransactionDone(false)
@ -151,11 +154,43 @@ nsHttpTransaction::nsHttpTransaction()
mPeerAddr.raw.family = PR_AF_UNSPEC;
}
void nsHttpTransaction::ThrottleResponse(bool aThrottle)
void nsHttpTransaction::ResumeReading()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
mThrottleResponse = aThrottle;
if (!mReadingStopped) {
return;
}
LOG(("nsHttpTransaction::ResumeReading %p", this));
mReadingStopped = false;
if (mConnection) {
nsresult rv = mConnection->ResumeRecv();
if (NS_FAILED(rv)) {
LOG((" resume failed with rv=%" PRIx32, static_cast<uint32_t>(rv)));
}
}
}
void nsHttpTransaction::SetClassOfService(uint32_t cos)
{
bool wasThrottling = mClassOfService & nsIClassOfService::Throttleable;
mClassOfService = cos;
bool isThrottling = mClassOfService & nsIClassOfService::Throttleable;
if (mConnection && wasThrottling != isThrottling) {
// Do nothing until we are actually activated. For now
// only remember the throttle flag. Call to MoveActiveTransaction
// would add this transaction to the list too early.
gHttpHandler->ConnMgr()->MoveActiveTransaction(this, isThrottling);
if (mReadingStopped && !isThrottling) {
ResumeReading();
}
}
}
nsHttpTransaction::~nsHttpTransaction()
@ -472,6 +507,21 @@ nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
}
}
void
nsHttpTransaction::OnActivated(bool h2)
{
MOZ_ASSERT(OnSocketThread());
mActivatedAsH2 = h2;
if (mActivated) {
return;
}
mActivated = true;
gHttpHandler->ConnMgr()->AddActiveTransaction(
this, mClassOfService & nsIClassOfService::Throttleable);
}
void
nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
{
@ -774,18 +824,30 @@ nsresult
nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
uint32_t count, uint32_t *countWritten)
{
static bool reentrantFlag = false;
LOG(("nsHttpTransaction::WriteSegments %p reentrantFlag=%d",
this, reentrantFlag));
MOZ_DIAGNOSTIC_ASSERT(!reentrantFlag);
reentrantFlag = true;
LOG(("nsHttpTransaction::WriteSegments %p", this));
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (mTransactionDone) {
reentrantFlag = false;
return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
}
// Throttling feature is now disabled for http/2 transactions
// because of bug 1367861. The logic around mActivatedAsH2
// will be removed when that is fixed
if (!mActivatedAsH2 &&
gHttpHandler->ConnMgr()->ShouldStopReading(this,
mClassOfService & nsIClassOfService::Throttleable)) {
LOG(("nsHttpTransaction::WriteSegments %p response throttled", this));
// Must remember that we have to call ResumeRecv() on our connection when
// called back by the conn manager to resume reading.
mReadingStopped = true;
// This makes the underlaying connection or stream wait for explicit resume.
// For h1 this means we stop reading from the socket.
// For h2 this means we stop updating recv window for the stream.
return NS_BASE_STREAM_WOULD_BLOCK;
}
mWriter = writer;
#ifdef WIN32 // bug 1153929
@ -795,7 +857,6 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
#endif // WIN32
if (!mPipeOut) {
reentrantFlag = false;
return NS_ERROR_UNEXPECTED;
}
@ -827,7 +888,6 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
}
}
reentrantFlag = false;
return rv;
}
@ -837,6 +897,12 @@ nsHttpTransaction::Close(nsresult reason)
LOG(("nsHttpTransaction::Close [this=%p reason=%" PRIx32 "]\n",
this, static_cast<uint32_t>(reason)));
if (!mClosed) {
gHttpHandler->ConnMgr()->RemoveActiveTransaction(
this, mClassOfService & nsIClassOfService::Throttleable);
mActivated = false;
}
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (reason == NS_BINDING_RETARGETED) {
LOG((" close %p skipped due to ERETARGETED\n", this));

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

@ -89,6 +89,8 @@ public:
uint64_t topLevelOuterContentWindowId,
nsIAsyncInputStream **responseBody);
void OnActivated(bool h2) override;
// attributes
nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nullptr; }
nsISupports *SecurityInfo() { return mSecurityInfo; }
@ -307,14 +309,17 @@ private:
Atomic<uint32_t> mCapsToClear;
Atomic<bool, ReleaseAcquire> mResponseIsComplete;
// If true, this transaction was asked to stop receiving the response.
// NOTE: this flag is currently unused. A useful remnant of an old throttling algorithm.
bool mThrottleResponse;
// True iff WriteSegments was called while this transaction should be throttled (stop reading)
// Used to resume read on unblock of reading. Conn manager is responsible for calling back
// to resume reading.
bool mReadingStopped;
// state flags, all logically boolean, but not packed together into a
// bitfield so as to avoid bitfield-induced races. See bug 560579.
bool mClosed;
bool mConnected;
bool mActivated;
bool mActivatedAsH2;
bool mHaveStatusLine;
bool mHaveAllHeaders;
bool mTransactionDone;
@ -373,9 +378,9 @@ public:
// but later can be dispatched via spdy (not subject to rate pacing).
void CancelPacing(nsresult reason);
// Called only on the socket thread. Updates the flag whether the transaction
// should make the underlying connection or session stop reading from the socket.
void ThrottleResponse(bool aThrottle);
// Called by the connetion manager on the socket thread when reading for this
// previously throttled transaction has to be resumed.
void ResumeReading();
private:
bool mSubmittedRatePacing;
@ -383,7 +388,7 @@ private:
bool mSynchronousRatePaceRequest;
nsCOMPtr<nsICancelable> mTokenBucketCancel;
public:
void SetClassOfService(uint32_t cos) { mClassOfService = cos; }
void SetClassOfService(uint32_t cos);
uint32_t ClassOfService() { return mClassOfService; }
private:
uint32_t mClassOfService;