зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to central, a=merge
MozReview-Commit-ID: AWwXRG9NEWV
This commit is contained in:
Коммит
2b3f76f494
|
@ -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, "&")
|
||||
.replace(/\u00a0/g, " ")
|
||||
.replace(/"/g, """) +
|
||||
"\"";
|
||||
}, "<" + 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 grid’s 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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче