merge mozilla-central to autoland. r=merge a=merge

This commit is contained in:
Sebastian Hengst 2017-08-31 14:37:58 +02:00
Родитель dffd9ec2a9 030c11d7dd
Коммит 2b41ef3155
83 изменённых файлов: 1845 добавлений и 239 удалений

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

@ -197,14 +197,15 @@ a11y::IsHandlerRegistered()
return false;
}
nsAutoString subKey;
subKey.AppendLiteral("CLSID\\");
nsAutoString iid;
GUIDToString(CLSID_AccessibleHandler, iid);
subKey.Append(iid);
subKey.AppendLiteral("\\InprocHandler32");
nsAutoString clsid;
GUIDToString(CLSID_AccessibleHandler, clsid);
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, subKey,
nsAutoString subKey;
subKey.AppendLiteral(u"SOFTWARE\\Classes\\CLSID\\");
subKey.Append(clsid);
subKey.AppendLiteral(u"\\InprocHandler32");
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, subKey,
nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
return false;

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

@ -18,5 +18,7 @@ STRIP_FLAGS="--strip-debug"
ac_add_options --with-branding=browser/branding/aurora
mk_add_options MOZ_PGO=1
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -19,5 +19,7 @@ ac_add_options --disable-stdcxx-compat
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
# Bug 1283438
mk_add_options AUTOCLOBBER=
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -18,5 +18,7 @@ STRIP_FLAGS="--strip-debug"
ac_add_options --with-branding=browser/branding/aurora
mk_add_options MOZ_PGO=1
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -15,4 +15,7 @@ ac_add_options --disable-stdcxx-compat
# Bug 1283438
mk_add_options AUTOCLOBBER=
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -24,4 +24,7 @@ fi
ac_add_options --with-branding=browser/branding/aurora
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -26,4 +26,7 @@ export MOZ_TELEMETRY_REPORTING=1
# Bug 1283438
mk_add_options AUTOCLOBBER=
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -15,4 +15,7 @@ ac_add_options --with-branding=browser/branding/aurora
mk_add_options MOZ_PGO=1
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -14,4 +14,7 @@ export MOZ_TELEMETRY_REPORTING=1
# Bug 1283438
mk_add_options AUTOCLOBBER=
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -16,4 +16,7 @@ ac_add_options --with-branding=browser/branding/aurora
mk_add_options MOZ_PGO=1
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -15,4 +15,7 @@ export MOZ_TELEMETRY_REPORTING=1
# Bug 1283438
mk_add_options AUTOCLOBBER=
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
. "$topsrcdir/build/mozconfig.common.override"

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

@ -33,6 +33,7 @@
/* Set to none so no grey contrast background in the high-contrast mode */
background: none;
/* make sure the icon stay above the activity-stream searchbar */
/* We want this always under #onboarding-overlay */
z-index: 10;
}

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

@ -30,6 +30,25 @@ add_task(async function test_onboarding_overlay_button() {
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_onboarding_overlay_button_no_activity_steam() {
/* https://bugzilla.mozilla.org/show_bug.cgi?id=1393564 */
resetOnboardingDefaultState();
Preferences.set("browser.newtabpage.activity-stream.enabled", false);
info("Wait for onboarding overlay loaded");
let tab = await openTab(ABOUT_NEWTAB_URL);
let browser = tab.linkedBrowser;
await promiseOnboardingOverlayLoaded(browser);
info("Click on overlay button and ensure dialog opens");
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button",
{}, browser);
await promiseOnboardingOverlayOpened(browser);
Preferences.reset("browser.newtabpage.activity-stream.enabled");
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_onboarding_notification_bar() {
resetOnboardingDefaultState();
skipMuteNotificationOnFirstSession();

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

@ -55,10 +55,20 @@ module.exports = createClass({
onFocus: () => focusButton(id),
onClick: () => selectTool(id),
},
span(
{
className: "devtools-tab-line"
}
),
...this.renderIcon(panelDefinition, isHighlighted),
iconOnly ? null : span({
className: "devtools-tab-label"
}, label)
iconOnly ?
null :
span(
{
className: "devtools-tab-label"
},
label
)
);
}
});

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

@ -10,7 +10,7 @@ const promise = require("promise");
const Rule = require("devtools/client/inspector/rules/models/rule");
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {getCssProperties, isCssVariable} = require("devtools/shared/fronts/css-properties");
/**
* ElementStyle is responsible for the following:
@ -40,6 +40,7 @@ function ElementStyle(element, ruleView, store, pageStyle,
this.showUserAgentStyles = showUserAgentStyles;
this.rules = [];
this.cssProperties = getCssProperties(this.ruleView.inspector.toolbox);
this.variables = new Map();
// We don't want to overwrite this.store.userProperties so we only create it
// if it doesn't already exist.
@ -199,7 +200,9 @@ ElementStyle.prototype = {
* Calls markOverridden with all supported pseudo elements
*/
markOverriddenAll: function () {
this.variables.clear();
this.markOverridden();
for (let pseudo of this.cssProperties.pseudoElements) {
this.markOverridden(pseudo);
}
@ -288,6 +291,10 @@ ElementStyle.prototype = {
computedProp.overridden = overridden;
if (!computedProp.overridden && computedProp.textProp.enabled) {
taken[computedProp.name] = computedProp;
if (isCssVariable(computedProp.name)) {
this.variables.set(computedProp.name, computedProp.value);
}
}
}
@ -328,7 +335,20 @@ ElementStyle.prototype = {
dirty = (!!prop.overridden !== overridden) || dirty;
prop.overridden = overridden;
return dirty;
}
},
/**
* Returns the current value of a CSS variable; or null if the
* variable is not defined.
*
* @param {String} name
* The name of the variable.
* @return {String} the variable's value or null if the variable is
* not defined.
*/
getVariable: function (name) {
return this.variables.get(name);
},
};
/**

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

@ -36,6 +36,8 @@ support-files =
doc_test_image.png
doc_urls_clickable.css
doc_urls_clickable.html
doc_variables_1.html
doc_variables_2.html
head.js
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
@ -95,6 +97,8 @@ support-files =
[browser_rules_completion-popup-hidden-after-navigation.js]
[browser_rules_content_01.js]
[browser_rules_content_02.js]
[browser_rules_variables_01.js]
[browser_rules_variables_02.js]
skip-if = e10s && debug # Bug 1250058 - Docshell leak on debug e10s
[browser_rules_context-menu-show-mdn-docs-01.js]
[browser_rules_context-menu-show-mdn-docs-02.js]

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

@ -0,0 +1,35 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for variables in rule view.
const TEST_URI = URL_ROOT + "doc_variables_1.html";
add_task(function* () {
yield addTab(TEST_URI);
let {inspector, view} = yield openRuleView();
yield selectNode("#target", inspector);
info("Tests basic support for CSS Variables for both single variable " +
"and double variable. Formats tested: var(x, constant), var(x, var(y))");
let unsetColor = getRuleViewProperty(view, "div", "color").valueSpan
.querySelector(".ruleview-variable-unmatched");
let setColor = unsetColor.previousElementSibling;
is(unsetColor.textContent, " red", "red is unmatched in color");
is(setColor.textContent, "--color", "--color is not set correctly");
is(setColor.title, "--color = chartreuse", "--color's title is not set correctly");
let unsetVar = getRuleViewProperty(view, "div", "background-color").valueSpan
.querySelector(".ruleview-variable-unmatched");
let setVar = unsetVar.nextElementSibling;
let setVarName = setVar.firstElementChild.firstElementChild;
is(unsetVar.textContent, "--not-set",
"--not-set is unmatched in background-color");
is(setVar.textContent, " var(--bg)", "var(--bg) parsed incorrectly");
is(setVarName.textContent, "--bg", "--bg is not set correctly");
is(setVarName.title, "--bg = seagreen", "--bg's title is not set correctly");
});

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

@ -0,0 +1,190 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for variables in rule view.
const TEST_URI = URL_ROOT + "doc_variables_2.html";
add_task(function* () {
yield addTab(TEST_URI);
let {inspector, view} = yield openRuleView();
yield testBasic(inspector, view);
yield testNestedCssFunctions(inspector, view);
yield testBorderShorthandAndInheritance(inspector, view);
yield testSingleLevelVariable(inspector, view);
yield testDoubleLevelVariable(inspector, view);
yield testTripleLevelVariable(inspector, view);
});
function* testBasic(inspector, view) {
info("Test support for basic variable functionality for var() with 2 variables." +
"Format: var(--var1, var(--var2))");
yield selectNode("#a", inspector);
let unsetVar = getRuleViewProperty(view, "#a", "font-size").valueSpan
.querySelector(".ruleview-variable-unmatched");
let setVarParent = unsetVar.nextElementSibling;
let setVar = getVarFromParent(setVarParent);
is(unsetVar.textContent, "--var-not-defined",
"--var-not-defined is not set correctly");
is(unsetVar.title, "--var-not-defined is not set",
"--var-not-defined's title is not set correctly");
is(setVarParent.textContent, " var(--var-defined-font-size)",
"var(--var-defined-font-size) parsed incorrectly");
is(setVar.textContent, "--var-defined-font-size",
"--var-defined-font-size is not set correctly");
is(setVar.title, "--var-defined-font-size = 60px",
"--bg's title is not set correctly");
}
function* testNestedCssFunctions(inspector, view) {
info("Test support for variable functionality for a var() nested inside " +
"another CSS function. Format: rgb(0, 0, var(--var1, var(--var2)))");
yield selectNode("#b", inspector);
let unsetVarParent = getRuleViewProperty(view, "#b", "color").valueSpan
.querySelector(".ruleview-variable-unmatched");
let unsetVar = getVarFromParent(unsetVarParent);
let setVar = unsetVarParent.previousElementSibling;
is(unsetVarParent.textContent, " var(--var-defined-r-2)",
"var(--var-defined-r-2) not parsed correctly");
is(unsetVar.textContent, "--var-defined-r-2",
"--var-defined-r-2 is not set correctly");
is(unsetVar.title, "--var-defined-r-2 = 0",
"--var-defined-r-2's title is not set correctly");
is(setVar.textContent, "--var-defined-r-1",
"--var-defined-r-1 is not set correctly");
is(setVar.title, "--var-defined-r-1 = 255",
"--var-defined-r-1's title is not set correctly");
}
function* testBorderShorthandAndInheritance(inspector, view) {
info("Test support for variable functionality for shorthands/CSS styles with spaces " +
"like \"margin: w x y z\". Also tests functionality for inherticance of CSS" +
" variables. Format: var(l, var(m)) var(x) rgb(var(r) var(g) var(b))");
yield selectNode("#c", inspector);
let unsetVarL = getRuleViewProperty(view, "#c", "border").valueSpan
.querySelector(".ruleview-variable-unmatched");
let setVarMParent = unsetVarL.nextElementSibling;
// var(x) is the next sibling of the parent of M
let setVarXParent = setVarMParent.parentNode.nextElementSibling;
// var(r) is the next sibling of var(x), and var(g) is the next sibling of var(r), etc.
let setVarRParent = setVarXParent.nextElementSibling;
let setVarGParent = setVarRParent.nextElementSibling;
let setVarBParent = setVarGParent.nextElementSibling;
let setVarM = getVarFromParent(setVarMParent);
let setVarX = setVarXParent.firstElementChild;
let setVarR = setVarRParent.firstElementChild;
let setVarG = setVarGParent.firstElementChild;
let setVarB = setVarBParent.firstElementChild;
is(unsetVarL.textContent, "--var-undefined",
"--var-undefined is not set correctly");
is(unsetVarL.title, "--var-undefined is not set",
"--var-undefined's title is not set correctly");
is(setVarM.textContent, "--var-border-px",
"--var-border-px is not set correctly");
is(setVarM.title, "--var-border-px = 10px",
"--var-border-px's title is not set correctly");
is(setVarX.textContent, "--var-border-style",
"--var-border-style is not set correctly");
is(setVarX.title, "--var-border-style = solid",
"var-border-style's title is not set correctly");
is(setVarR.textContent, "--var-border-r",
"--var-defined-r is not set correctly");
is(setVarR.title, "--var-border-r = 255",
"--var-defined-r's title is not set correctly");
is(setVarG.textContent, "--var-border-g",
"--var-defined-g is not set correctly");
is(setVarG.title, "--var-border-g = 0",
"--var-defined-g's title is not set correctly");
is(setVarB.textContent, "--var-border-b",
"--var-defined-b is not set correctly");
is(setVarB.title, "--var-border-b = 0",
"--var-defined-b's title is not set correctly");
}
function* testSingleLevelVariable(inspector, view) {
info("Test support for variable functionality of a single level of " +
"undefined variables. Format: var(x, constant)");
yield selectNode("#d", inspector);
let unsetVar = getRuleViewProperty(view, "#d", "font-size").valueSpan
.querySelector(".ruleview-variable-unmatched");
is(unsetVar.textContent, "--var-undefined",
"--var-undefined is not set correctly");
is(unsetVar.title, "--var-undefined is not set",
"--var-undefined's title is not set correctly");
}
function* testDoubleLevelVariable(inspector, view) {
info("Test support for variable functionality of double level of " +
"undefined variables. Format: var(x, var(y, constant))");
yield selectNode("#e", inspector);
let allUnsetVars = getRuleViewProperty(view, "#e", "color").valueSpan
.querySelectorAll(".ruleview-variable-unmatched");
is(allUnsetVars.length, 2, "The number of unset variables is mismatched.");
let unsetVar1 = allUnsetVars[0];
let unsetVar2 = allUnsetVars[1];
is(unsetVar1.textContent, "--var-undefined",
"--var-undefined is not set correctly");
is(unsetVar1.title, "--var-undefined is not set",
"--var-undefined's title is not set correctly");
is(unsetVar2.textContent, "--var-undefined-2",
"--var-undefined is not set correctly");
is(unsetVar2.title, "--var-undefined-2 is not set",
"--var-undefined-2's title is not set correctly");
}
function* testTripleLevelVariable(inspector, view) {
info("Test support for variable functionality of triple level of " +
"undefined variables. Format: var(x, var(y, var(z, constant)))");
yield selectNode("#f", inspector);
let allUnsetVars = getRuleViewProperty(view, "#f", "border-style").valueSpan
.querySelectorAll(".ruleview-variable-unmatched");
is(allUnsetVars.length, 3, "The number of unset variables is mismatched.");
let unsetVar1 = allUnsetVars[0];
let unsetVar2 = allUnsetVars[1];
let unsetVar3 = allUnsetVars[2];
is(unsetVar1.textContent, "--var-undefined",
"--var-undefined is not set correctly");
is(unsetVar1.title, "--var-undefined is not set",
"--var-undefined's title is not set correctly");
is(unsetVar2.textContent, "--var-undefined-2",
"--var-undefined-2 is not set correctly");
is(unsetVar2.title, "--var-undefined-2 is not set",
"--var-defined-r-2's title is not set correctly");
is(unsetVar3.textContent, "--var-undefined-3",
"--var-undefined-3 is not set correctly");
is(unsetVar3.title, "--var-undefined-3 is not set",
"--var-defined-r-3's title is not set correctly");
}
function getVarFromParent(varParent) {
return varParent.firstElementChild.firstElementChild;
}

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

@ -0,0 +1,23 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>variables test</title>
<style>
* {
--color: tomato;
--bg: violet;
}
div {
--color: chartreuse;
color: var(--color, red);
background-color: var(--not-set, var(--bg));
}
</style>
</head>
<body>
<div id="target" style="--bg: seagreen;"> the ocean </div>
</body>
</html>

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

@ -0,0 +1,45 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>variables test</title>
<style>
:root {
--var-border-px: 10px;
--var-border-style: solid;
--var-border-r: 255;
--var-border-g: 0;
--var-border-b: 0;
}
#a {
--var-defined-font-size: 60px;
font-size: var(--var-not-defined, var(--var-defined-font-size));
}
#b {
--var-defined-r-1: 255;
--var-defined-r-2: 0;
color: rgb(var(--var-defined-r-1, var(--var-defined-r-2)), 0, 0);
}
#c {
border: var(--var-undefined, var(--var-border-px)) var(--var-border-style) rgb(var(--var-border-r), var(--var-border-g), var(--var-border-b))
}
#d {
font-size: var(--var-undefined, 30px);
}
#e {
color: var(--var-undefined, var(--var-undefined-2, blue));
}
#f {
border-style: var(--var-undefined, var(--var-undefined-2, var(--var-undefined-3, solid)));
}
</style>
</head>
<body>
<div id="a">A</div><br>
<div id="b">B</div><br>
<div id="c">C</div><br>
<div id="d">D</div><br>
<div id="e">E</div><br>
<div id="f">F</div>
</body>
</html>

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

@ -363,7 +363,9 @@ TextPropertyEditor.prototype = {
shapeClass: "ruleview-shape",
defaultColorType: !propDirty,
urlClass: "theme-link",
baseURI: this.sheetHref
baseURI: this.sheetHref,
unmatchedVariableClass: "ruleview-variable-unmatched",
isVariableInUse: varName => this.rule.elementStyle.getVariable(varName),
};
let frag = outputParser.parseCssProperty(name, val, parserOptions);
this.valueSpan.innerHTML = "";

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

@ -81,10 +81,6 @@
.theme-light .tabs .tabs-menu-item {
margin: 0;
padding: 0;
border-style: solid;
border-width: 0;
border-inline-start-width: 1px;
border-color: var(--theme-splitter-color);
color: var(--theme-content-color1);
}
@ -98,8 +94,8 @@
padding: 3px 15px;
}
.theme-dark .tabs .tabs-menu-item:hover:not(.is-active),
.theme-light .tabs .tabs-menu-item:hover:not(.is-active) {
.theme-dark .tabs .tabs-menu-item:hover,
.theme-light .tabs .tabs-menu-item:hover {
background-color: var(--theme-toolbar-hover);
}
@ -108,12 +104,6 @@
background-color: var(--theme-toolbar-hover-active);
}
.theme-dark .tabs .tabs-menu-item.is-active,
.theme-light .tabs .tabs-menu-item.is-active {
background-color: var(--theme-selection-background);
color: var(--theme-selection-color);
}
/* Dark Theme */
.theme-dark .tabs .tabs-menu-item {

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

@ -263,6 +263,7 @@ define(function (require, exports, module) {
ref,
role: "presentation",
},
DOM.span({className: "devtools-tab-line"}),
DOM.a({
id: id ? id + "-tab" : "tab-" + index,
tabIndex: isTabSelected ? 0 : -1,

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

@ -18,6 +18,10 @@ const {
const {appendText} = require("devtools/client/inspector/shared/utils");
const Services = require("Services");
const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
const HTML_NS = "http://www.w3.org/1999/xhtml";
const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
const CSS_SHAPES_ENABLED_PREF = "devtools.inspector.shapesHighlighter.enabled";
@ -96,24 +100,37 @@ OutputParser.prototype = {
},
/**
* Given an initial FUNCTION token, read tokens from |tokenStream|
* and collect all the (non-comment) text. Return the collected
* text. The function token and the close paren are included in the
* result.
* Read tokens from |tokenStream| and collect all the (non-comment)
* text. Return the collected texts and variable data (if any).
* Stop when an unmatched closing paren is seen.
* If |stopAtComma| is true, then also stop when a top-level
* (unparenthesized) comma is seen.
*
* @param {CSSToken} initialToken
* The FUNCTION token.
* @param {String} text
* The original CSS text.
* The original source text.
* @param {CSSLexer} tokenStream
* The token stream from which to read.
* @return {String}
* The text of body of the function call.
* @param {Object} options
* The options object in use; @see _mergeOptions.
* @param {Boolean} stopAtComma
* If true, stop at a comma.
* @return {Object}
* An object of the form {tokens, functionData, sawComma, sawVariable}.
* |tokens| is a list of the non-comment, non-whitespace tokens
* that were seen. The stopping token (paren or comma) will not
* be included.
* |functionData| is a list of parsed strings and nodes that contain the
* data between the matching parenthesis. The stopping token's text will
* not be included.
* |sawComma| is true if the stop was due to a comma, or false otherwise.
* |sawVariable| is true if a variable was seen while parsing the text.
*/
_collectFunctionText: function (initialToken, text, tokenStream) {
let result = text.substring(initialToken.startOffset,
initialToken.endOffset);
_parseMatchingParens: function (text, tokenStream, options, stopAtComma) {
let depth = 1;
let functionData = [];
let tokens = [];
let sawVariable = false;
while (depth > 0) {
let token = tokenStream.nextToken();
if (!token) {
@ -122,37 +139,142 @@ OutputParser.prototype = {
if (token.tokenType === "comment") {
continue;
}
result += text.substring(token.startOffset, token.endOffset);
if (token.tokenType === "symbol") {
if (token.text === "(") {
if (stopAtComma && depth === 1 && token.text === ",") {
return { tokens, functionData, sawComma: true, sawVariable };
} else if (token.text === "(") {
++depth;
} else if (token.text === ")") {
--depth;
if (depth === 0) {
break;
}
}
} else if (token.tokenType === "function" && token.text === "var" &&
options.isVariableInUse) {
sawVariable = true;
let variableNode = this._parseVariable(token, text, tokenStream, options);
functionData.push(variableNode);
} else if (token.tokenType === "function") {
++depth;
}
if (token.tokenType !== "function" || token.text !== "var" ||
!options.isVariableInUse) {
functionData.push(text.substring(token.startOffset, token.endOffset));
}
if (token.tokenType !== "whitespace") {
tokens.push(token);
}
}
return result;
return { tokens, functionData, sawComma: false, sawVariable };
},
/**
* Parse a string.
* Parse var() use and return a variable node to be added to the output state.
* This will read tokens up to and including the ")" that closes the "var("
* invocation.
*
* @param {CSSToken} initialToken
* The "var(" token that was already seen.
* @param {String} text
* The original input text.
* @param {CSSLexer} tokenStream
* The token stream from which to read.
* @param {Object} options
* The options object in use; @see _mergeOptions.
* @return {Object}
* A node for the variable, with the appropriate text and
* title. Eg. a span with "var(--var1)" as the textContent
* and a title for --var1 like "--var1 = 10" or
* "--var1 is not set".
*/
_parseVariable: function (initialToken, text, tokenStream, options) {
// Handle the "var(".
let varText = text.substring(initialToken.startOffset,
initialToken.endOffset);
let variableNode = this._createNode("span", {}, varText);
// Parse the first variable name within the parens of var().
let {tokens, functionData, sawComma, sawVariable} =
this._parseMatchingParens(text, tokenStream, options, true);
let result = sawVariable ? "" : functionData.join("");
// Display options for the first and second argument in the var().
let firstOpts = {};
let secondOpts = {};
let varValue;
// Get the variable value if it is in use.
if (tokens && tokens.length === 1) {
varValue = options.isVariableInUse(tokens[0].text);
}
// Get the variable name.
let varName = text.substring(tokens[0].startOffset, tokens[0].endOffset);
if (typeof varValue === "string") {
// The variable value is valid, set the variable name's title of the first argument
// in var() to display the variable name and value.
firstOpts.title =
STYLE_INSPECTOR_L10N.getFormatStr("rule.variableValue", varName, varValue);
secondOpts.class = options.unmatchedVariableClass;
} else {
// The variable name is not valid, mark it unmatched.
firstOpts.class = options.unmatchedVariableClass;
firstOpts.title = STYLE_INSPECTOR_L10N.getFormatStr("rule.variableUnset",
varName);
}
variableNode.appendChild(this._createNode("span", firstOpts, result));
// If we saw a ",", then append it and show the remainder using
// the correct highlighting.
if (sawComma) {
variableNode.appendChild(this.doc.createTextNode(","));
// Parse the text up until the close paren, being sure to
// disable the special case for filter.
let subOptions = Object.assign({}, options);
subOptions.expectFilter = false;
let saveParsed = this.parsed;
this.parsed = [];
let rest = this._doParse(text, subOptions, tokenStream, true);
this.parsed = saveParsed;
let span = this._createNode("span", secondOpts);
span.appendChild(rest);
variableNode.appendChild(span);
}
variableNode.appendChild(this.doc.createTextNode(")"));
return variableNode;
},
/* eslint-disable complexity */
/**
* The workhorse for @see _parse. This parses some CSS text,
* stopping at EOF; or optionally when an umatched close paren is
* seen.
*
* @param {String} text
* Text to parse.
* @param {Object} [options]
* Options object. For valid options and default values see
* _mergeOptions().
* The original input text.
* @param {Object} options
* The options object in use; @see _mergeOptions.
* @param {CSSLexer} tokenStream
* The token stream from which to read
* @param {Boolean} stopAtCloseParen
* If true, stop at an umatched close paren.
* @return {DocumentFragment}
* A document fragment.
*/
_parse: function (text, options = {}) {
text = text.trim();
this.parsed.length = 0;
let tokenStream = getCSSLexer(text);
let parenDepth = 0;
_doParse: function (text, options, tokenStream, stopAtCloseParen) {
let parenDepth = stopAtCloseParen ? 1 : 0;
let outerMostFunctionTakesColor = false;
let colorOK = function () {
@ -166,12 +288,17 @@ OutputParser.prototype = {
};
let spaceNeeded = false;
let token = tokenStream.nextToken();
while (token) {
let done = false;
while (!done) {
let token = tokenStream.nextToken();
if (!token) {
break;
}
if (token.tokenType === "comment") {
// This doesn't change spaceNeeded, because we didn't emit
// anything to the output.
token = tokenStream.nextToken();
continue;
}
@ -190,21 +317,44 @@ OutputParser.prototype = {
token.text);
}
++parenDepth;
} else if (token.text === "var" && options.isVariableInUse) {
let variableNode = this._parseVariable(token, text, tokenStream, options);
this.parsed.push(variableNode);
} else {
let functionText = this._collectFunctionText(token, text,
tokenStream);
let {functionData, sawVariable} = this._parseMatchingParens(text, tokenStream,
options);
if (options.expectCubicBezier && token.text === "cubic-bezier") {
this._appendCubicBezier(functionText, options);
} else if (colorOK() &&
colorUtils.isValidCSSColor(functionText, this.cssColor4)) {
this._appendColor(functionText, options);
} else if (options.expectShape &&
Services.prefs.getBoolPref(CSS_SHAPES_ENABLED_PREF) &&
BASIC_SHAPE_FUNCTIONS.includes(token.text)) {
this._appendShape(functionText, options);
let functionName = text.substring(token.startOffset, token.endOffset);
if (sawVariable) {
// If function contains variable, we need to add both strings
// and nodes.
this._appendTextNode(functionName);
for (let data of functionData) {
if (typeof data === "string") {
this._appendTextNode(data);
} else if (data) {
this.parsed.push(data);
}
}
this._appendTextNode(")");
} else {
this._appendTextNode(functionText);
// If no variable in function, join the text together and add
// to DOM accordingly.
let functionText = functionName + functionData.join("") + ")";
if (options.expectCubicBezier && token.text === "cubic-bezier") {
this._appendCubicBezier(functionText, options);
} else if (colorOK() &&
colorUtils.isValidCSSColor(functionText, this.cssColor4)) {
this._appendColor(functionText, options);
} else if (options.expectShape &&
Services.prefs.getBoolPref(CSS_SHAPES_ENABLED_PREF) &&
BASIC_SHAPE_FUNCTIONS.includes(token.text)) {
this._appendShape(functionText, options);
} else {
this._appendTextNode(functionText);
}
}
}
break;
@ -262,6 +412,12 @@ OutputParser.prototype = {
++parenDepth;
} else if (token.text === ")") {
--parenDepth;
if (stopAtCloseParen && parenDepth === 0) {
done = true;
break;
}
if (parenDepth === 0) {
outerMostFunctionTakesColor = false;
}
@ -279,8 +435,6 @@ OutputParser.prototype = {
token.tokenType === "id" || token.tokenType === "hash" ||
token.tokenType === "number" || token.tokenType === "dimension" ||
token.tokenType === "percentage" || token.tokenType === "dimension");
token = tokenStream.nextToken();
}
let result = this._toDOM();
@ -291,6 +445,26 @@ OutputParser.prototype = {
return result;
},
/* eslint-enable complexity */
/**
* Parse a string.
*
* @param {String} text
* Text to parse.
* @param {Object} [options]
* Options object. For valid options and default values see
* _mergeOptions().
* @return {DocumentFragment}
* A document fragment.
*/
_parse: function (text, options = {}) {
text = text.trim();
this.parsed.length = 0;
let tokenStream = getCSSLexer(text);
return this._doParse(text, options, tokenStream, false);
},
/**
* Return true if it's a display:[inline-]grid token.
@ -1242,6 +1416,14 @@ OutputParser.prototype = {
* - urlClass: "" // The class to be used for url() links.
* - baseURI: undefined // A string used to resolve
* // relative links.
* - isVariableInUse // A function taking a single
* // argument, the name of a variable.
* // This should return the variable's
* // value, if it is in use; or null.
* - unmatchedVariableClass: ""
* // The class to use for a component
* // of a "var(...)" that is not in
* // use.
* @return {Object}
* Overridden options object
*/
@ -1260,6 +1442,8 @@ OutputParser.prototype = {
supportsColor: false,
urlClass: "",
baseURI: undefined,
isVariableInUse: null,
unmatchedVariableClass: null,
};
for (let item in overrides) {

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

@ -29,6 +29,7 @@ function* performTest() {
testParseFilter(doc, parser);
testParseAngle(doc, parser);
testParseShape(doc, parser);
testParseVariable(doc, parser);
host.destroy();
}
@ -413,3 +414,50 @@ function testParseShape(doc, parser) {
is(frag.textContent, definition, desc + " text content");
}
}
function testParseVariable(doc, parser) {
let TESTS = [
{
text: "var(--seen)",
variables: {"--seen": "chartreuse" },
expected: "<span>var(<span title=\"--seen = chartreuse\">--seen</span>)</span>"
},
{
text: "var(--not-seen)",
variables: {},
expected: "<span>var(<span class=\"unmatched-class\" " +
"title=\"--not-seen is not set\">--not-seen</span>)</span>"
},
{
text: "var(--seen, seagreen)",
variables: {"--seen": "chartreuse" },
expected: "<span>var(<span title=\"--seen = chartreuse\">--seen</span>," +
"<span class=\"unmatched-class\"> <span data-color=\"seagreen\"><span>seagreen" +
"</span></span></span>)</span>"
},
{
text: "var(--not-seen, var(--seen))",
variables: {"--seen": "chartreuse" },
expected: "<span>var(<span class=\"unmatched-class\" " +
"title=\"--not-seen is not set\">--not-seen</span>,<span> <span>var(<span " +
"title=\"--seen = chartreuse\">--seen</span>)</span></span>)</span>"
},
];
for (let test of TESTS) {
let getValue = function (varName) {
return test.variables[varName];
};
let frag = parser.parseCssProperty("color", test.text, {
isVariableInUse: getValue,
unmatchedVariableClass: "unmatched-class"
});
let target = doc.querySelector("div");
target.appendChild(frag);
is(target.innerHTML, test.expected, test.text);
target.innerHTML = "";
}
}

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

@ -7,6 +7,16 @@
:root {
font: message-box;
--tab-line-selected-color: highlight;
}
:root.theme-light {
--tab-line-hover-color: rgba(0,0,0,.2);
}
:root.theme-dark {
--tab-line-hover-color: rgba(255,255,255,.2);
}
:root[platform="mac"] {
@ -683,6 +693,30 @@ checkbox:-moz-focusring {
background-position: center;
}
.devtools-tab-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 2px;
background: transparent;
}
.devtools-tab:hover .devtools-tab-line,
.tabs-menu-item:hover .devtools-tab-line {
background: var(--tab-line-hover-color);
}
.devtools-tab.selected .devtools-tab-line,
.tabs-menu-item.is-active .devtools-tab-line {
background: var(--tab-line-selected-color);
}
/* Hide the tab line in the firebug theme */
.theme-firebug .devtools-tab-line {
background: transparent !important;
}
/* No result message styles */
.devtools-sidepanel-no-result {

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

@ -566,7 +566,8 @@
}
.ruleview-selector-separator,
.ruleview-selector-unmatched {
.ruleview-selector-unmatched,
.ruleview-variable-unmatched {
color: #888;
}

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

@ -89,6 +89,7 @@
}
/* Toolbox tabs */
.devtools-tab {
position: relative;
display: flex;
@ -97,16 +98,13 @@
min-height: 24px;
margin: 0;
padding: 0;
border-style: solid;
border-width: 0;
border-inline-start-width: 1px;
border: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background-color: transparent;
}
.devtools-tab-label {
mask-image: linear-gradient(to left, transparent 0, black 6px);
/* Set the end padding on the label to make sure the label gets faded out properly */
@ -196,12 +194,6 @@
flex-shrink: 0;
}
/* Support invertable icon flags and make icon white when it's on a blue background */
.theme-light .devtools-tab.icon-invertable-light-theme:not(.selected) > img,
.devtools-tab.icon-invertable-dark-theme.selected > img {
filter: invert(1);
}
/* Don't apply any filter to non-invertable command button icons */
.command-button:not(.command-button-invertable),
/* icon-invertable-light-theme icons are white, so do not invert them for the dark theme */
@ -222,12 +214,6 @@
opacity: 1;
}
.devtools-tabbar .devtools-tab.selected,
.devtools-tabbar .devtools-tab.selected:hover:active {
color: var(--theme-selection-color);
background-color: var(--theme-selection-background);
}
.toolbox-tabs .devtools-tab.selected,
.toolbox-tabs .devtools-tab.highlighted,
.toolbox-tabs .devtools-tab.selected + .devtools-tab,

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

@ -149,7 +149,8 @@ CssProperties.prototype = {
* @return {Boolean}
*/
isInherited(property) {
return this.properties[property] && this.properties[property].isInherited;
return (this.properties[property] && this.properties[property].isInherited) ||
isCssVariable(property);
},
/**
@ -350,5 +351,6 @@ module.exports = {
CssProperties,
getCssProperties,
getClientCssProperties,
initCssProperties
initCssProperties,
isCssVariable,
};

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

@ -67,6 +67,18 @@ rule.filterProperty.title=Filter rules containing this property
# first opened and there's no node selected in the rule view.
rule.empty=No element selected.
# LOCALIZATION NOTE (rule.variableValue): Text displayed in a tooltip
# when the mouse is over a variable use (like "var(--something)") in
# the rule view. The first argument is the variable name and the
# second argument is the value.
rule.variableValue=%S = %S
# LOCALIZATION NOTE (rule.variableUnset): Text displayed in a tooltip
# when the mouse is over a variable use (like "var(--something)"),
# where the variable is not set. the rule view. The argument is the
# variable name.
rule.variableUnset=%S is not set
# LOCALIZATION NOTE (ruleView.selectorHighlighter.tooltip): Text displayed in a
# tooltip when the mouse is over a selector highlighter icon in the rule view.
rule.selectorHighlighter.tooltip=Highlight all elements matching this selector

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

@ -3,6 +3,7 @@ support-files =
file_abort_controller.html
file_abort_controller_fetch.html
worker_abort_controller_fetch.js
slow.sjs
[test_abort_controller.html]
[test_abort_controller_fetch.html]

11
dom/abort/tests/slow.sjs Normal file
Просмотреть файл

@ -0,0 +1,11 @@
function handleRequest(request, response)
{
response.processAsync();
timer = Components.classes["@mozilla.org/timer;1"].
createInstance(Components.interfaces.nsITimer);
timer.init(function() {
response.write("Here the content. But slowly.");
response.finish();
}, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

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

@ -99,6 +99,7 @@
#include "nsIWebNavigation.h"
#include "nsIScriptError.h"
#include "nsISimpleEnumerator.h"
#include "nsIRequestContext.h"
#include "nsStyleSheetService.h"
#include "nsNetUtil.h" // for NS_NewURI
@ -2330,6 +2331,18 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
// XXXbz what does "just fine" mean exactly? And given that there
// is no nsDocShell::SetDocument, what is this talking about?
// Inform the associated request context about this load start so
// any of its internal load progress flags gets reset.
nsCOMPtr<nsIRequestContextService> rcsvc =
do_GetService("@mozilla.org/network/request-context-service;1");
if (rcsvc) {
nsCOMPtr<nsIRequestContext> rc;
rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
if (rc) {
rc->BeginLoad();
}
}
}
mLastModified.Truncate();

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

@ -379,6 +379,9 @@ FetchDriver::HttpFetch()
}
if (mIsTrackingFetch && nsContentUtils::IsLowerNetworkPriority()) {
cos->AddClassFlags(nsIClassOfService::Throttleable |
nsIClassOfService::Tail);
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan);
if (p) {
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);

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

@ -505,6 +505,14 @@ public:
return NS_OK;
}
void Dispatch() {
if (nsCOMPtr<nsIGlobalObject> global = mCue->GetOwnerGlobal()) {
global->Dispatch(TaskCategory::Other, do_AddRef(this));
} else {
NS_DispatchToMainThread(do_AddRef(this));
}
}
private:
nsString mName;
double mTime;
@ -850,7 +858,7 @@ TextTrackManager::TimeMarchesOn()
// Fire the eventList
for (uint32_t i = 0; i < eventList.Length(); ++i) {
NS_DispatchToMainThread(eventList[i].forget());
eventList[i]->Dispatch();
}
// Step 16.

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

@ -3633,6 +3633,9 @@ ContentChild::GetSpecificMessageEventTarget(const Message& aMsg)
case PContent::Msg_DataStoragePut__ID:
case PContent::Msg_DataStorageRemove__ID:
case PContent::Msg_DataStorageClear__ID:
case PContent::Msg_PIPCBlobInputStreamConstructor__ID:
case PContent::Msg_BlobURLRegistration__ID:
case PContent::Msg_BlobURLUnregistration__ID:
return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other));
default:
return nullptr;

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

@ -3072,7 +3072,8 @@ QuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate)
MOZ_ASSERT(!lock->GetGroup().IsEmpty());
MOZ_ASSERT(lock->GetOriginScope().IsOrigin());
MOZ_ASSERT(!lock->GetOriginScope().GetOrigin().IsEmpty());
MOZ_ASSERT(lock->GetOriginScope().GetOrigin() != mOriginInfo->mOrigin,
MOZ_ASSERT(!(lock->GetOriginScope().GetOrigin() == mOriginInfo->mOrigin &&
lock->GetPersistenceType().Value() == groupInfo->mPersistenceType),
"Deleted itself!");
quotaManager->LockedRemoveQuotaForOrigin(

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

@ -137,10 +137,13 @@ ScriptLoader::ScriptLoader(nsIDocument *aDocument)
mGiveUpEncoding(false),
mReporter(new ConsoleReportCollector())
{
LOG(("ScriptLoader::ScriptLoader %p", this));
}
ScriptLoader::~ScriptLoader()
{
LOG(("ScriptLoader::~ScriptLoader %p", this));
mObservers.Clear();
if (mParserBlockingRequest) {
@ -1055,15 +1058,32 @@ ScriptLoader::StartLoad(ScriptLoadRequest* aRequest)
bool async = script ? script->GetScriptAsync() : aRequest->mPreloadAsAsync;
bool defer = script ? script->GetScriptDeferred() : aRequest->mPreloadAsDefer;
LOG(("ScriptLoadRequest (%p): async=%d defer=%d tracking=%d",
aRequest, async, defer, aRequest->IsTracking()));
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
if (cos) {
if (aRequest->mScriptFromHead && !async && !defer) {
// synchronous head scripts block loading of most other non js/css
// content such as images
// content such as images, Leader implicitely disallows tailing
cos->AddClassFlags(nsIClassOfService::Leader);
} else if (!defer) {
// other scripts are neither blocked nor prioritized unless marked deferred
} else if (defer && !async) {
// head/body deferred scripts are blocked by leaders but are not
// allowed tailing because they block DOMContentLoaded
cos->AddClassFlags(nsIClassOfService::TailForbidden);
} else {
// other scripts (=body sync or head/body async) are neither blocked
// nor prioritized
cos->AddClassFlags(nsIClassOfService::Unblocked);
if (async) {
// async scripts are allowed tailing, since those and only those
// don't block DOMContentLoaded; this flag doesn't enforce tailing,
// just overweights the Unblocked flag when the channel is found
// to be a thrird-party tracker and thus set the Tail flag to engage
// tailing.
cos->AddClassFlags(nsIClassOfService::TailAllowed);
}
}
}
@ -1206,8 +1226,11 @@ ScriptLoader::CreateLoadRequest(ScriptKind aKind,
const SRIMetadata& aIntegrity)
{
if (aKind == ScriptKind::Classic) {
return new ScriptLoadRequest(aKind, aElement, aVersion, aCORSMode,
ScriptLoadRequest* slr = new ScriptLoadRequest(aKind, aElement, aVersion, aCORSMode,
aIntegrity);
LOG(("ScriptLoader %p creates ScriptLoadRequest %p", this, slr));
return slr;
}
MOZ_ASSERT(aKind == ScriptKind::Module);

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

@ -13,7 +13,8 @@
<script class="testbody" type="text/javascript">
SpecialPowers.pushPrefEnv({"set": [["dom.fetchObserver.enabled", true ],
["dom.abortController.enabled", true ]]}, () => {
["dom.abortController.enabled", true ],
["dom.abortController.fetch.enabled", true ]]}, () => {
let ifr = document.createElement('iframe');
ifr.src = "file_fetch_observer.html";
document.body.appendChild(ifr);

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

@ -102,9 +102,9 @@ var legacyMozPrefixedInterfaces =
var interfaceNamesInGlobalScope =
[
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortController",
{name: "AbortController", nightly: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortSignal",
{name: "AbortSignal", nightly: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"AnalyserNode",
// IMPORTANT: Do not change this list without review from a DOM peer!

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

@ -80,9 +80,9 @@ var ecmaGlobals =
var interfaceNamesInGlobalScope =
[
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortController",
{ name: "AbortController", nightly: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortSignal",
{ name: "AbortSignal", nightly: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"Blob",
// IMPORTANT: Do not change this list without review from a DOM peer!

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

@ -80,9 +80,9 @@ var ecmaGlobals =
var interfaceNamesInGlobalScope =
[
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortController",
{ name: "AbortController", nightly: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortSignal",
{ name: "AbortSignal", nightly: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"Blob",
// IMPORTANT: Do not change this list without review from a DOM peer!

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

@ -45,6 +45,7 @@
#include "nsStringStream.h"
#include "nsIAuthPrompt.h"
#include "nsIAuthPrompt2.h"
#include "nsIClassOfService.h"
#include "nsIOutputStream.h"
#include "nsISupportsPrimitives.h"
#include "nsISupportsPriority.h"
@ -2620,12 +2621,19 @@ XMLHttpRequestMainThread::MaybeLowerChannelPriority()
return;
}
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
if (!p) {
return;
nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(mChannel);
if (cos) {
// Adding TailAllowed to overrule the Unblocked flag, but to preserve
// the effect of Unblocked when tailing is off.
cos->AddClassFlags(nsIClassOfService::Throttleable |
nsIClassOfService::Tail |
nsIClassOfService::TailAllowed);
}
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
if (p) {
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
}
}
nsresult

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

@ -57,6 +57,8 @@ static const char* sEGLExtensionNames[] = {
"EGL_EXT_device_query",
"EGL_NV_stream_consumer_gltexture_yuv",
"EGL_ANGLE_stream_producer_d3d_texture_nv12",
"EGL_ANGLE_device_creation",
"EGL_ANGLE_device_creation_d3d11",
};
#if defined(ANDROID)
@ -653,6 +655,18 @@ GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId
}
}
if (IsExtensionSupported(ANGLE_device_creation)) {
const GLLibraryLoader::SymLoadStruct createDeviceSymbols[] = {
SYMBOL(CreateDeviceANGLE),
SYMBOL(ReleaseDeviceANGLE),
END_OF_SYMBOLS
};
if (!fnLoadSymbols(createDeviceSymbols)) {
NS_ERROR("EGL supports ANGLE_device_creation without exposing its functions!");
MarkExtensionUnsupported(ANGLE_device_creation);
}
}
mInitialized = true;
reporter.SetSuccessful();
return true;

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

@ -111,6 +111,8 @@ public:
EXT_device_query,
NV_stream_consumer_gltexture_yuv,
ANGLE_stream_producer_d3d_texture_nv12,
ANGLE_device_creation,
ANGLE_device_creation_d3d11,
Extensions_Max
};
@ -322,6 +324,13 @@ public:
EGLBoolean fStreamPostD3DTextureNV12ANGLE(EGLDisplay dpy, EGLStreamKHR stream, void* texture, const EGLAttrib* attrib_list) const
WRAP( fStreamPostD3DTextureNV12ANGLE(dpy, stream, texture, attrib_list) )
// ANGLE_device_creation
EGLDeviceEXT fCreateDeviceANGLE(EGLint device_type, void* native_device, const EGLAttrib* attrib_list) const
WRAP( fCreateDeviceANGLE(device_type, native_device, attrib_list) )
EGLBoolean fReleaseDeviceANGLE(EGLDeviceEXT device)
WRAP( fReleaseDeviceANGLE(device) )
void fANGLEPlatformInitialize(angle::Platform* platform) const
VOID_WRAP( fANGLEPlatformInitialize(platform) )
@ -481,6 +490,12 @@ private:
EGLStreamKHR stream,
void* texture,
const EGLAttrib* attrib_list);
// ANGLE_device_creation
EGLDeviceEXT (GLAPIENTRY * fCreateDeviceANGLE) (EGLint device_type,
void* native_device,
const EGLAttrib* attrib_list);
EGLBoolean (GLAPIENTRY * fReleaseDeviceANGLE) (EGLDeviceEXT device);
void (GLAPIENTRY * fANGLEPlatformInitialize)(angle::Platform* platform);
void (GLAPIENTRY * fANGLEPlatformShutdown)();
} mSymbols;

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

@ -64,11 +64,19 @@ nsLanguageAtomService::GetLocaleLanguage()
{
do {
if (!mLocaleLanguage) {
nsAutoCString locale;
OSPreferences::GetInstance()->GetSystemLocale(locale);
AutoTArray<nsCString, 10> regionalPrefsLocales;
if (OSPreferences::GetInstance()->GetRegionalPrefsLocales(
regionalPrefsLocales)) {
// use lowercase for all language atoms
ToLowerCase(regionalPrefsLocales[0]);
mLocaleLanguage = NS_Atomize(regionalPrefsLocales[0]);
} else {
nsAutoCString locale;
OSPreferences::GetInstance()->GetSystemLocale(locale);
ToLowerCase(locale); // use lowercase for all language atoms
mLocaleLanguage = NS_Atomize(locale);
ToLowerCase(locale); // use lowercase for all language atoms
mLocaleLanguage = NS_Atomize(locale);
}
}
} while (0);

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

@ -193,13 +193,23 @@ Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
}
REFIID
Interceptor::MarshalAs(REFIID aIid) const
{
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
return IsCallerExternalProcess() ? aIid : mEventSink->MarshalAs(aIid);
#else
return mEventSink->MarshalAs(aIid);
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
}
HRESULT
Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid)
{
return mStdMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext,
mshlflags, pCid);
return mStdMarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext,
pvDestContext, mshlflags, pCid);
}
HRESULT
@ -207,7 +217,7 @@ Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize)
{
HRESULT hr = mStdMarshal->GetMarshalSizeMax(riid, pv, dwDestContext,
HRESULT hr = mStdMarshal->GetMarshalSizeMax(MarshalAs(riid), pv, dwDestContext,
pvDestContext, mshlflags, pSize);
if (FAILED(hr)) {
return hr;
@ -420,7 +430,7 @@ Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLock,
unkInterceptor,
aTarget.release()));
if (mEventSink->MarshalAs(aTargetIid) == aTargetIid) {
if (MarshalAs(aTargetIid) == aTargetIid) {
return unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
}
@ -449,7 +459,7 @@ Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor)
return S_OK;
}
REFIID interceptorIid = mEventSink->MarshalAs(aIid);
REFIID interceptorIid = MarshalAs(aIid);
RefPtr<IUnknown> unkInterceptor;
IUnknown* interfaceForQILog = nullptr;

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

@ -129,6 +129,7 @@ private:
HRESULT ThreadSafeQueryInterface(REFIID aIid,
IUnknown** aOutInterface) override;
HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
REFIID MarshalAs(REFIID aIid) const;
private:
InterceptorTargetPtr<IUnknown> mTarget;

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

@ -8,6 +8,7 @@
#define mozilla_mscom_PassthruProxy_h
#include "mozilla/Atomics.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/NotNull.h"
#include <objbase.h>

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

@ -90,7 +90,11 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
// correctness with IPDL. We'll request an IUnknown and then QI the
// actual interface later.
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
auto marshalFn = [this, &strActCtx, &unmarshalResult, &aIID]() -> void
#else
auto marshalFn = [this, &unmarshalResult, &aIID]() -> void
#endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
{
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
auto curActCtx = ActivationContext::GetCurrent();

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

@ -49,6 +49,7 @@ static const char16_t kFlags[] = u"FLAGS";
static const char16_t kProxyStubClsid32[] = u"\\ProxyStubClsid32";
static const char16_t kClsid[] = u"\\CLSID\\";
static const char16_t kInprocServer32[] = u"\\InprocServer32";
static const char16_t kInprocHandler32[] = u"\\InprocHandler32";
static const char16_t kTypeLib[] = u"\\TypeLib";
static const char16_t kVersion[] = u"Version";
static const char16_t kWin32[] = u"Win32";
@ -171,6 +172,24 @@ AnnotateClsidRegistrationForHive(JSONWriter& aJson, HKEY aHive,
if (GetStringValue(aHive, inprocServerSubkey, kThreadingModel, apartment)) {
aJson.StringProperty("ThreadingModel", NS_ConvertUTF16toUTF8(apartment).get());
}
nsAutoString inprocHandlerSubkey(clsidSubkey);
inprocHandlerSubkey.AppendLiteral(kInprocHandler32);
nsAutoString pathToHandlerDll;
if (GetStringValue(aHive, inprocHandlerSubkey, kDefaultValue, pathToHandlerDll)) {
aJson.StringProperty("HandlerPath", NS_ConvertUTF16toUTF8(pathToHandlerDll).get());
if (GetLoadedPath(pathToHandlerDll)) {
aJson.StringProperty("LoadedHandlerPath",
NS_ConvertUTF16toUTF8(pathToHandlerDll).get());
}
}
nsAutoString handlerApartment;
if (GetStringValue(aHive, inprocHandlerSubkey, kThreadingModel,
handlerApartment)) {
aJson.StringProperty("HandlerThreadingModel",
NS_ConvertUTF16toUTF8(handlerApartment).get());
}
}
static void

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

@ -242,31 +242,6 @@ GUIDToString(REFGUID aGuid, nsAString& aOutString)
}
}
bool
IsCallerExternalProcess()
{
MOZ_ASSERT(XRE_IsContentProcess());
/**
* CoGetCallerTID() gives us the caller's thread ID when that thread resides
* in a single-threaded apartment. Since our chrome main thread does live
* inside an STA, we will therefore be able to check whether the caller TID
* equals our chrome main thread TID. This enables us to distinguish
* between our chrome thread vs other out-of-process callers. We check for
* S_FALSE to ensure that the caller is a different process from ours, which
* is the only scenario that we care about.
*/
DWORD callerTid;
if (::CoGetCallerTID(&callerTid) != S_FALSE) {
return false;
}
// Now check whether the caller is our parent process main thread.
const DWORD parentMainTid =
dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
return callerTid != parentMainTid;
}
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
@ -317,6 +292,31 @@ IsVtableIndexFromParentInterface(REFIID aInterface, unsigned long aVtableIndex)
#if defined(MOZILLA_INTERNAL_API)
bool
IsCallerExternalProcess()
{
MOZ_ASSERT(XRE_IsContentProcess());
/**
* CoGetCallerTID() gives us the caller's thread ID when that thread resides
* in a single-threaded apartment. Since our chrome main thread does live
* inside an STA, we will therefore be able to check whether the caller TID
* equals our chrome main thread TID. This enables us to distinguish
* between our chrome thread vs other out-of-process callers. We check for
* S_FALSE to ensure that the caller is a different process from ours, which
* is the only scenario that we care about.
*/
DWORD callerTid;
if (::CoGetCallerTID(&callerTid) != S_FALSE) {
return false;
}
// Now check whether the caller is our parent process main thread.
const DWORD parentMainTid =
dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
return callerTid != parentMainTid;
}
bool
IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
unsigned long aVtableIndexHint)

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

@ -48,7 +48,6 @@ uint32_t CopySerializedProxy(IStream* aInStream, IStream** aOutStream);
#if defined(MOZILLA_INTERNAL_API)
void GUIDToString(REFGUID aGuid, nsAString& aOutString);
bool IsCallerExternalProcess();
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
@ -56,6 +55,8 @@ bool IsVtableIndexFromParentInterface(REFIID aInterface,
unsigned long aVtableIndex);
#if defined(MOZILLA_INTERNAL_API)
bool IsCallerExternalProcess();
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
unsigned long aVtableIndexHint);
#endif // defined(MOZILLA_INTERNAL_API)

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

@ -11,7 +11,6 @@ EXPORTS.mozilla.mscom += [
'COMApartmentRegion.h',
'COMPtrHolder.h',
'EnsureMTA.h',
'FastMarshaler.h',
'MainThreadClientInfo.h',
'MainThreadRuntime.h',
'Objref.h',
@ -28,7 +27,6 @@ SOURCES += [
UNIFIED_SOURCES += [
'AgileReference.cpp',
'EnsureMTA.cpp',
'FastMarshaler.cpp',
'MainThreadClientInfo.cpp',
'MainThreadRuntime.cpp',
'Objref.cpp',
@ -50,6 +48,7 @@ if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.mscom += [
'ActivationContext.h',
'DispatchForwarder.h',
'FastMarshaler.h',
'IHandlerProvider.h',
'Interceptor.h',
'InterceptorLog.h',
@ -71,6 +70,7 @@ if CONFIG['ACCESSIBILITY']:
UNIFIED_SOURCES += [
'ActivationContext.cpp',
'DispatchForwarder.cpp',
'FastMarshaler.cpp',
'InterceptorLog.cpp',
'MainThreadHandoff.cpp',
'MainThreadInvoker.cpp',

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

@ -107,8 +107,8 @@ Handler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid)
{
return mUnmarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext,
mshlflags, pCid);
return mUnmarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext,
pvDestContext, mshlflags, pCid);
}
HRESULT
@ -187,13 +187,10 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
if (FAILED(hr)) {
return hr;
}
// When marshaling without a handler, we just use the riid as passed in.
REFIID marshalAs = riid;
#else
REFIID marshalAs = MarshalAs(riid);
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
REFIID marshalAs = MarshalAs(riid);
hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
if (FAILED(hr)) {
return hr;
@ -266,17 +263,20 @@ template <size_t N>
static HRESULT
BuildClsidPath(wchar_t (&aPath)[N], REFCLSID aClsid)
{
const wchar_t kClsid[] = {L'C', L'L', L'S', L'I', L'D', L'\\'};
const wchar_t kSubkey[] = L"SOFTWARE\\Classes\\CLSID\\";
// We exclude kSubkey's null terminator in the length because we include
// the stringified GUID's null terminator.
constexpr uint32_t kSubkeyLen = mozilla::ArrayLength(kSubkey) - 1;
const size_t kReqdGuidLen = 39;
static_assert(N >= kReqdGuidLen + mozilla::ArrayLength(kClsid),
"aPath array is too short");
if (wcsncpy_s(aPath, kClsid, mozilla::ArrayLength(kClsid))) {
static_assert(N >= kReqdGuidLen + kSubkeyLen, "aPath array is too short");
if (wcsncpy_s(aPath, kSubkey, kSubkeyLen)) {
return E_INVALIDARG;
}
int guidConversionResult =
StringFromGUID2(aClsid, &aPath[mozilla::ArrayLength(kClsid)],
N - mozilla::ArrayLength(kClsid));
StringFromGUID2(aClsid, &aPath[kSubkeyLen], N - kSubkeyLen);
if (!guidConversionResult) {
return E_INVALIDARG;
}
@ -293,7 +293,7 @@ Handler::Unregister(REFCLSID aClsid)
return hr;
}
hr = HRESULT_FROM_WIN32(SHDeleteKey(HKEY_CLASSES_ROOT, path));
hr = HRESULT_FROM_WIN32(SHDeleteKey(HKEY_LOCAL_MACHINE, path));
if (FAILED(hr)) {
return hr;
}
@ -312,7 +312,7 @@ Handler::Register(REFCLSID aClsid)
HKEY rawClsidKey;
DWORD disposition;
LONG result = RegCreateKeyEx(HKEY_CLASSES_ROOT, path, 0, nullptr,
LONG result = RegCreateKeyEx(HKEY_LOCAL_MACHINE, path, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
nullptr, &rawClsidKey, &disposition);
if (result != ERROR_SUCCESS) {
@ -325,7 +325,7 @@ Handler::Register(REFCLSID aClsid)
}
HKEY rawInprocHandlerKey;
result = RegCreateKeyEx(HKEY_CLASSES_ROOT, path, 0, nullptr,
result = RegCreateKeyEx(HKEY_LOCAL_MACHINE, path, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
nullptr, &rawInprocHandlerKey, &disposition);
if (result != ERROR_SUCCESS) {

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

@ -8,12 +8,20 @@
#define jit_BacktrackingAllocator_h
#include "mozilla/Array.h"
#include "mozilla/Attributes.h"
#include "ds/PriorityQueue.h"
#include "ds/SplayTree.h"
#include "jit/RegisterAllocator.h"
#include "jit/StackSlotAllocator.h"
// Gives better traces in Nightly/debug builds (could be EARLY_BETA_OR_EARLIER)
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
#define AVOID_INLINE_FOR_DEBUGGING MOZ_NEVER_INLINE
#else
#define AVOID_INLINE_FOR_DEBUGGING
#endif
// Backtracking priority queue based register allocator based on that described
// in the following blog post:
//
@ -755,7 +763,7 @@ class BacktrackingAllocator : protected RegisterAllocator
MOZ_MUST_USE bool splitAndRequeueBundles(LiveBundle* bundle,
const LiveBundleVector& newBundles);
MOZ_MUST_USE bool spill(LiveBundle* bundle);
MOZ_MUST_USE bool tryAllocatingRegistersForSpillBundles();
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool tryAllocatingRegistersForSpillBundles();
bool isReusedInput(LUse* use, LNode* ins, bool considerCopy);
bool isRegisterUse(UsePosition* use, LNode* ins, bool considerCopy = false);
@ -764,12 +772,12 @@ class BacktrackingAllocator : protected RegisterAllocator
MOZ_MUST_USE bool insertAllRanges(LiveRangeSet& set, LiveBundle* bundle);
// Reification methods.
MOZ_MUST_USE bool pickStackSlots();
MOZ_MUST_USE bool resolveControlFlow();
MOZ_MUST_USE bool reifyAllocations();
MOZ_MUST_USE bool populateSafepoints();
MOZ_MUST_USE bool annotateMoveGroups();
MOZ_MUST_USE bool deadRange(LiveRange* range);
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool pickStackSlots();
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool resolveControlFlow();
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool reifyAllocations();
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool populateSafepoints();
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool annotateMoveGroups();
AVOID_INLINE_FOR_DEBUGGING MOZ_MUST_USE bool deadRange(LiveRange* range);
size_t findFirstNonCallSafepoint(CodePosition from);
size_t findFirstSafepoint(CodePosition pos, size_t startFrom);
void addLiveRegistersForRange(VirtualRegister& reg, LiveRange* range);

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

@ -3182,7 +3182,18 @@ class WrappedJSHolder : public nsISupports
private:
virtual ~WrappedJSHolder() {}
};
NS_IMPL_ISUPPORTS0(WrappedJSHolder);
NS_IMPL_ADDREF(WrappedJSHolder)
NS_IMPL_RELEASE(WrappedJSHolder)
// nsINamed is always supported by nsXPCWrappedJSClass.
// We expose this interface only for the identity in telemetry analysis.
NS_INTERFACE_TABLE_HEAD(WrappedJSHolder)
if (aIID.Equals(NS_GET_IID(nsINamed))) {
return mWrappedJS->QueryInterface(aIID, aInstancePtr);
}
NS_INTERFACE_TABLE0(WrappedJSHolder)
NS_INTERFACE_TABLE_TAIL
NS_IMETHODIMP
nsXPCComponents_Utils::GenerateXPCWrappedJS(HandleValue aObj, HandleValue aScope,

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

@ -2248,6 +2248,19 @@ pref("network.http.throttle.time-window", 3000);
// like click-to-play, image fancy-box zoom, navigation.
pref("network.http.on_click_priority", true);
// Some requests during a page load are marked as "tail", mainly trackers, but not only.
// This pref controls whether such requests are put to the tail, behind other requests
// emerging during page loading process.
pref("network.http.tailing.enabled", true);
// When the page load has not yet reached DOMContentLoaded point, tail requestes are delayed
// by (non-tailed requests count + 1) * delay-quantum milliseconds.
pref("network.http.tailing.delay-quantum", 600);
// The same as above, but applied after the document load reached DOMContentLoaded event.
pref("network.http.tailing.delay-quantum-after-domcontentloaded", 100);
// Upper limit for the calculated delay, prevents long standing and comet-like requests
// tail forever. This is in milliseconds as well.
pref("network.http.tailing.delay-max", 6000);
pref("permissions.default.image", 1); // 1-Accept, 2-Deny, 3-dontAcceptForeign
pref("network.proxy.type", 5);

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

@ -14,9 +14,12 @@
#include "mozilla/Atomics.h"
#include "mozilla/Logging.h"
#include "mozilla/Services.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/net/PSpdyPush.h"
#include "../protocol/http/nsHttpHandler.h"
namespace mozilla {
namespace net {
@ -26,36 +29,112 @@ LazyLogModule gRequestContextLog("RequestContext");
// nsIRequestContext
class RequestContext final : public nsIRequestContext
, public nsITimerCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTCONTEXT
NS_DECL_NSITIMERCALLBACK
explicit RequestContext(const uint64_t id);
private:
virtual ~RequestContext();
void ProcessTailQueue(nsresult aResult);
// Reschedules the timer if needed
void ScheduleUnblock();
// Hard-reschedules the timer
void RescheduleUntailTimer(TimeStamp const& now);
uint64_t mID;
Atomic<uint32_t> mBlockingTransactionCount;
nsAutoPtr<SpdyPushCache> mSpdyCache;
nsCString mUserAgentOverride;
typedef nsCOMPtr<nsIRequestTailUnblockCallback> PendingTailRequest;
// Number of known opened non-tailed requets
uint32_t mNonTailRequests;
// Queue of requests that have been tailed, when conditions are met
// we call each of them to unblock and drop the reference
nsTArray<PendingTailRequest> mTailQueue;
// Loosly scheduled timer, never scheduled further to the future than
// mUntailAt time
nsCOMPtr<nsITimer> mUntailTimer;
// Timestamp when the timer is expected to fire,
// always less than or equal to mUntailAt
TimeStamp mTimerScheduledAt;
// Timestamp when we want to actually untail queued requets based on
// the number of request count change in the past; iff this timestamp
// is set, we tail requests
TimeStamp mUntailAt;
// This member is true only between DOMContentLoaded notification and
// next document load beginning for this request context.
// Top level request contexts are recycled.
bool mAfterDOMContentLoaded;
};
NS_IMPL_ISUPPORTS(RequestContext, nsIRequestContext)
NS_IMPL_ISUPPORTS(RequestContext, nsIRequestContext, nsITimerCallback)
RequestContext::RequestContext(const uint64_t aID)
: mID(aID)
, mBlockingTransactionCount(0)
, mNonTailRequests(0)
, mAfterDOMContentLoaded(false)
{
LOG(("RequestContext::RequestContext this=%p id=%" PRIx64, this, mID));
}
RequestContext::~RequestContext()
{
MOZ_ASSERT(mTailQueue.Length() == 0);
LOG(("RequestContext::~RequestContext this=%p blockers=%u",
this, static_cast<uint32_t>(mBlockingTransactionCount)));
}
NS_IMETHODIMP
RequestContext::BeginLoad()
{
MOZ_ASSERT(NS_IsMainThread());
LOG(("RequestContext::BeginLoad %p", this));
if (IsNeckoChild() && gNeckoChild) {
// Tailing is not supported on the child process
gNeckoChild->SendRequestContextLoadBegin(mID);
return NS_OK;
}
mAfterDOMContentLoaded = false;
return NS_OK;
}
NS_IMETHODIMP
RequestContext::DOMContentLoaded()
{
MOZ_ASSERT(NS_IsMainThread());
LOG(("RequestContext::DOMContentLoaded %p", this));
if (IsNeckoChild() && gNeckoChild) {
// Tailing is not supported on the child process
gNeckoChild->SendRequestContextAfterDOMContentLoaded(mID);
return NS_OK;
}
if (mAfterDOMContentLoaded) {
// There is a possibility of a duplicate notification
return NS_OK;
}
mAfterDOMContentLoaded = true;
// Conditions for the delay calculation has changed.
ScheduleUnblock();
return NS_OK;
}
NS_IMETHODIMP
RequestContext::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount)
{
@ -120,6 +199,217 @@ RequestContext::SetUserAgentOverride(const nsACString& aUserAgentOverride)
return NS_OK;
}
NS_IMETHODIMP
RequestContext::AddNonTailRequest()
{
MOZ_ASSERT(NS_IsMainThread());
++mNonTailRequests;
LOG(("RequestContext::AddNonTailRequest this=%p, cnt=%u",
this, mNonTailRequests));
ScheduleUnblock();
return NS_OK;
}
NS_IMETHODIMP
RequestContext::RemoveNonTailRequest()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mNonTailRequests > 0);
LOG(("RequestContext::RemoveNonTailRequest this=%p, cnt=%u",
this, mNonTailRequests - 1));
--mNonTailRequests;
ScheduleUnblock();
return NS_OK;
}
void
RequestContext::ScheduleUnblock()
{
MOZ_ASSERT(!IsNeckoChild());
MOZ_ASSERT(NS_IsMainThread());
if (!gHttpHandler) {
return;
}
uint32_t quantum = gHttpHandler->TailBlockingDelayQuantum(mAfterDOMContentLoaded);
uint32_t delayMax = gHttpHandler->TailBlockingDelayMax();
CheckedInt<uint32_t> delay = quantum * mNonTailRequests;
if (!mAfterDOMContentLoaded) {
// Before DOMContentLoaded notification we want to make sure that tailed
// requests don't start when there is a short delay during which we may
// not have any active requests on the page happening.
delay += quantum;
}
if (!delay.isValid() || delay.value() > delayMax) {
delay = delayMax;
}
LOG(("RequestContext::ScheduleUnblock this=%p non-tails=%u tail-queue=%zu delay=%u after-DCL=%d",
this, mNonTailRequests, mTailQueue.Length(), delay.value(), mAfterDOMContentLoaded));
TimeStamp now = TimeStamp::NowLoRes();
mUntailAt = now + TimeDuration::FromMilliseconds(delay.value());
if (mTimerScheduledAt.IsNull() || mUntailAt < mTimerScheduledAt) {
LOG(("RequestContext %p timer would fire too late, rescheduling", this));
RescheduleUntailTimer(now);
}
}
void
RequestContext::RescheduleUntailTimer(TimeStamp const& now)
{
MOZ_ASSERT(mUntailAt >= now);
if (mUntailTimer) {
mUntailTimer->Cancel();
}
if (!mTailQueue.Length()) {
mUntailTimer = nullptr;
mTimerScheduledAt = TimeStamp();
return;
}
TimeDuration interval = mUntailAt - now;
if (!mTimerScheduledAt.IsNull() && mUntailAt < mTimerScheduledAt) {
// When the number of untailed requests goes down,
// let's half the interval, since it's likely we would
// reschedule for a shorter time again very soon.
// This will likely save rescheduling this timer.
interval = interval / int64_t(2);
mTimerScheduledAt = mUntailAt - interval;
} else {
mTimerScheduledAt = mUntailAt;
}
uint32_t delay = interval.ToMilliseconds();
mUntailTimer = do_CreateInstance("@mozilla.org/timer;1");
mUntailTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
LOG(("RequestContext::RescheduleUntailTimer %p in %d", this, delay));
}
NS_IMETHODIMP
RequestContext::Notify(nsITimer *timer)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(timer == mUntailTimer);
MOZ_ASSERT(!mTimerScheduledAt.IsNull());
MOZ_ASSERT(mTailQueue.Length());
mUntailTimer = nullptr;
TimeStamp now = TimeStamp::NowLoRes();
if (mUntailAt > mTimerScheduledAt && mUntailAt > now) {
LOG(("RequestContext %p timer fired too soon, rescheduling", this));
RescheduleUntailTimer(now);
return NS_OK;
}
// Must drop to allow re-engage of the timer
mTimerScheduledAt = TimeStamp();
ProcessTailQueue(NS_OK);
return NS_OK;
}
NS_IMETHODIMP
RequestContext::IsContextTailBlocked(nsIRequestTailUnblockCallback * aRequest,
bool *aBlocked)
{
MOZ_ASSERT(NS_IsMainThread());
LOG(("RequestContext::IsContextTailBlocked this=%p, request=%p, queued=%zu",
this, aRequest, mTailQueue.Length()));
*aBlocked = false;
if (mUntailAt.IsNull() || mUntailAt <= TimeStamp::NowLoRes()) {
LOG((" untail time passed"));
// To save the expansive compare to now next time
mUntailAt = TimeStamp();
return NS_OK;
}
if (mAfterDOMContentLoaded && !mNonTailRequests) {
LOG((" after DOMContentLoaded and no untailed requests"));
return NS_OK;
}
if (!gHttpHandler) {
// Xpcshell tests may not have http handler
LOG((" missing gHttpHandler?"));
return NS_OK;
}
*aBlocked = true;
mTailQueue.AppendElement(aRequest);
LOG((" request queued"));
if (!mUntailTimer) {
ScheduleUnblock();
}
return NS_OK;
}
NS_IMETHODIMP
RequestContext::CancelTailedRequest(nsIRequestTailUnblockCallback * aRequest)
{
MOZ_ASSERT(NS_IsMainThread());
bool removed = mTailQueue.RemoveElement(aRequest);
LOG(("RequestContext::CancelTailedRequest %p req=%p removed=%d",
this, aRequest, removed));
return NS_OK;
}
void
RequestContext::ProcessTailQueue(nsresult aResult)
{
LOG(("RequestContext::ProcessTailQueue this=%p, queued=%zu, rv=%" PRIx32,
this, mTailQueue.Length(), static_cast<uint32_t>(aResult)));
if (mUntailTimer) {
mUntailTimer->Cancel();
mUntailTimer = nullptr;
}
// Must drop to stop tailing requests
mUntailAt = TimeStamp();
nsTArray<PendingTailRequest> queue;
queue.SwapElements(mTailQueue);
for (auto request : queue) {
LOG((" untailing %p", request.get()));
request->OnTailUnblock(aResult);
}
}
NS_IMETHODIMP
RequestContext::CancelTailPendingRequests(nsresult aResult)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(NS_FAILED(aResult));
ProcessTailQueue(aResult);
return NS_OK;
}
//nsIRequestContextService
RequestContextService *RequestContextService::sSelf = nullptr;
@ -147,13 +437,24 @@ RequestContextService::~RequestContextService()
nsresult
RequestContextService::Init()
{
nsresult rv;
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_NOT_AVAILABLE;
}
return obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
if (NS_FAILED(rv)) {
return rv;
}
obs->AddObserver(this, "content-document-interactive", false);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
void
@ -194,6 +495,20 @@ RequestContextService::GetRequestContext(const uint64_t rcID, nsIRequestContext
return NS_OK;
}
NS_IMETHODIMP
RequestContextService::GetRequestContextFromLoadGroup(nsILoadGroup *aLoadGroup, nsIRequestContext **rc)
{
nsresult rv;
uint64_t rcID;
rv = aLoadGroup->GetRequestContextID(&rcID);
if (NS_FAILED(rv)) {
return rv;
}
return GetRequestContext(rcID, rc);
}
NS_IMETHODIMP
RequestContextService::NewRequestContext(nsIRequestContext **rc)
{
@ -213,6 +528,10 @@ RequestContextService::NewRequestContext(nsIRequestContext **rc)
NS_IMETHODIMP
RequestContextService::RemoveRequestContext(const uint64_t rcID)
{
if (IsNeckoChild() && gNeckoChild) {
gNeckoChild->SendRemoveRequestContext(rcID);
}
MOZ_ASSERT(NS_IsMainThread());
mTable.Remove(rcID);
return NS_OK;
@ -225,8 +544,41 @@ RequestContextService::Observe(nsISupports *subject, const char *topic,
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
Shutdown();
return NS_OK;
}
if (!strcmp("content-document-interactive", topic)) {
nsCOMPtr<nsIDocument> document(do_QueryInterface(subject));
MOZ_ASSERT(document);
// We want this be triggered also for iframes, since those track their
// own request context ids.
if (!document) {
return NS_OK;
}
nsIDocShell* ds = document->GetDocShell();
// XML documents don't always have a docshell assigned
if (!ds) {
return NS_OK;
}
nsCOMPtr<nsIDocumentLoader> dl(do_QueryInterface(ds));
if (!dl) {
return NS_OK;
}
nsCOMPtr<nsILoadGroup> lg;
dl->GetLoadGroup(getter_AddRefs(lg));
if (!lg) {
return NS_OK;
}
nsCOMPtr<nsIRequestContext> rc;
GetRequestContextFromLoadGroup(lg, getter_AddRefs(rc));
if (rc) {
rc->DOMContentLoaded();
}
return NS_OK;
}
MOZ_ASSERT(false, "Unexpected observer topic");
return NS_OK;
}

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

@ -31,7 +31,8 @@ typedef enum {
TFO_FAILED_NET_TIMEOUT__NO_TFO_FAILED_TOO,
TFO_FAILED_UNKNOW_ERROR_NO_TFO_FAILED_TOO,
TFO_FAILED_BACKUP_CONNECTION_NO_TFO_FAILED_TOO,
TFO_FAILED
TFO_FAILED,
TFO_HTTP // TFO is disabled for non-secure connections.
} TFOResult;
nsresult AttachTCPFastOpenIOLayer(PRFileDesc *fd);

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

@ -206,20 +206,29 @@ LowerPriorityHelper(nsIChannel* aChannel)
{
MOZ_ASSERT(aChannel);
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aChannel);
if (p) {
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
}
}
static void
SetThrottleableHelper(nsIChannel* aChannel)
{
MOZ_ASSERT(aChannel);
bool isBlockingResource = false;
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(aChannel));
if (cos) {
cos->AddClassFlags(nsIClassOfService::Throttleable);
uint32_t cosFlags = 0;
cos->GetClassFlags(&cosFlags);
isBlockingResource = cosFlags & (nsIClassOfService::UrgentStart |
nsIClassOfService::Leader |
nsIClassOfService::Unblocked);
// Requests not allowed to be tailed are usually those with higher
// prioritization. That overweights being a tracker: don't throttle
// them when not in background.
if (!(cosFlags & nsIClassOfService::TailForbidden)) {
cos->AddClassFlags(nsIClassOfService::Throttleable);
}
}
if (!isBlockingResource) {
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aChannel);
if (p) {
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
}
}
}
@ -1025,7 +1034,6 @@ IsTrackerBlacklistedCallback::OnClassifyComplete(nsresult aErrorCode,
if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
LowerPriorityHelper(channel);
}
SetThrottleableHelper(channel);
// We don't want to disable speculative connection when tracking protection
// is disabled. So, change the status to NS_OK.
@ -1072,7 +1080,6 @@ IsTrackerBlacklistedCallback::OnClassifyCompleteInternal(nsresult aErrorCode,
if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
LowerPriorityHelper(channel);
}
SetThrottleableHelper(channel);
return mChannelCallback->OnClassifyComplete(
NS_OK, aLists, aProvider, aPrefix);

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

@ -45,4 +45,13 @@ interface nsIClassOfService : nsISupports
const unsigned long Throttleable = 1 << 5;
const unsigned long UrgentStart = 1 << 6;
const unsigned long DontThrottle = 1 << 7;
// Enforce tailing on this load; any of Leader, Unblocked, UrgentStart, TailForbidden
// overrule this flag (disable tailing.)
const unsigned long Tail = 1 << 8;
// Tailing may be engaged regardless if the load is marked Unblocked when
// some other conditions are met later, like when the load is found to be
// a tracker.
const unsigned long TailAllowed = 1 << 9;
// Tailing not allowed under any circumstances or combination of flags.
const unsigned long TailForbidden = 1 << 10;
};

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

@ -14,8 +14,32 @@ class SpdyPushCache;
}
%}
interface nsILoadGroup;
interface nsIChannel;
interface nsIStreamListener;
[ptr] native SpdyPushCachePtr(mozilla::net::SpdyPushCache);
/**
* Requests capable of tail-blocking must implement this
* interfaces (typically channels).
* If the request is tail-blocked, it will be held in its request
* context queue until unblocked.
*/
[scriptable, uuid(7EB361D4-37A5-42C9-AFAE-F6C88FE7C394)]
interface nsIRequestTailUnblockCallback : nsISupports
{
/**
* Called when the requests is unblocked and proceed.
* @param result
* NS_OK - the request is OK to go, unblocking is not
* caused by cancelation of the request.
* any error - the request must behave as it were canceled
* with the result as status.
*/
void onTailUnblock(in nsresult aResult);
};
/**
* The nsIRequestContext is used to maintain state about connections
* that are in some way associated with each other (often by being part
@ -32,6 +56,17 @@ interface nsIRequestContext : nsISupports
*/
[noscript] readonly attribute unsigned long long ID;
/**
* Called by the associated document when its load starts. This resets
* context's internal states.
*/
void beginLoad();
/**
* Called when the associated document notified the DOMContentLoaded event.
*/
void DOMContentLoaded();
/**
* Number of active blocking transactions associated with this context
*/
@ -62,6 +97,35 @@ interface nsIRequestContext : nsISupports
* This holds a cached value of the user agent override.
*/
[noscript] attribute ACString userAgentOverride;
/**
* Increases/decrease the number of non-tailed requests in this context.
* If the count drops to zero, all tail-blocked callbacks are notified
* shortly after that to be unblocked.
*/
void addNonTailRequest();
void removeNonTailRequest();
/**
* If the request context is in tail-blocked state, the callback
* is queued and result is true. The callback will be notified
* about tail-unblocking or when the request context is canceled.
*/
[must_use] boolean isContextTailBlocked(in nsIRequestTailUnblockCallback callback);
/**
* Called when the request is sitting in the tail queue but has been
* canceled before untailing. This just removes the request from the
* queue so that it is not notified on untail and not referenced.
*/
void cancelTailedRequest(in nsIRequestTailUnblockCallback request);
/**
* This notifies all queued tail-blocked requests, they will be notified
* aResult and released afterwards. Called by the load group when
* it's canceled.
*/
void cancelTailPendingRequests(in nsresult aResult);
};
/**
@ -82,6 +146,10 @@ interface nsIRequestContextService : nsISupports
* Get an existing request context from its ID
*/
nsIRequestContext getRequestContext(in unsigned long long id);
/**
* Shorthand to get request context from a load group
*/
nsIRequestContext getRequestContextFromLoadGroup(in nsILoadGroup lg);
/**
* Create a new request context

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

@ -23,8 +23,6 @@
#include "MainThreadUtils.h"
#include "mozilla/Unused.h"
#include "mozilla/net/NeckoChild.h"
namespace mozilla {
namespace net {
@ -129,12 +127,7 @@ nsLoadGroup::~nsLoadGroup()
if (mRequestContext) {
uint64_t rcid;
mRequestContext->GetID(&rcid);
if (IsNeckoChild() && gNeckoChild) {
gNeckoChild->SendRemoveRequestContext(rcid);
} else {
mRequestContextService->RemoveRequestContext(rcid);
}
mRequestContextService->RemoveRequestContext(rcid);
}
LOG(("LOADGROUP [%p]: Destroyed.\n", this));
@ -274,6 +267,10 @@ nsLoadGroup::Cancel(nsresult status)
NS_RELEASE(request);
}
if (mRequestContext) {
Unused << mRequestContext->CancelTailPendingRequests(status);
}
#if defined(DEBUG)
NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty.");
NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");

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

@ -898,6 +898,40 @@ NeckoParent::RecvPredReset()
return IPC_OK();
}
mozilla::ipc::IPCResult
NeckoParent::RecvRequestContextLoadBegin(const uint64_t& rcid)
{
nsCOMPtr<nsIRequestContextService> rcsvc =
do_GetService("@mozilla.org/network/request-context-service;1");
if (!rcsvc) {
return IPC_OK();
}
nsCOMPtr<nsIRequestContext> rc;
rcsvc->GetRequestContext(rcid, getter_AddRefs(rc));
if (rc) {
rc->BeginLoad();
}
return IPC_OK();
}
mozilla::ipc::IPCResult
NeckoParent::RecvRequestContextAfterDOMContentLoaded(const uint64_t& rcid)
{
nsCOMPtr<nsIRequestContextService> rcsvc =
do_GetService("@mozilla.org/network/request-context-service;1");
if (!rcsvc) {
return IPC_OK();
}
nsCOMPtr<nsIRequestContext> rc;
rcsvc->GetRequestContext(rcid, getter_AddRefs(rc));
if (rc) {
rc->DOMContentLoaded();
}
return IPC_OK();
}
mozilla::ipc::IPCResult
NeckoParent::RecvRemoveRequestContext(const uint64_t& rcid)
{

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

@ -228,6 +228,8 @@ protected:
const OriginAttributes& aOriginAttributes) override;
virtual mozilla::ipc::IPCResult RecvPredReset() override;
virtual mozilla::ipc::IPCResult RecvRequestContextLoadBegin(const uint64_t& rcid) override;
virtual mozilla::ipc::IPCResult RecvRequestContextAfterDOMContentLoaded(const uint64_t& rcid) override;
virtual mozilla::ipc::IPCResult RecvRemoveRequestContext(const uint64_t& rcid) override;
/* WebExtensions */

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

@ -116,6 +116,8 @@ parent:
nsString password, nsString domain);
async OnAuthCancelled(uint64_t callbackId, bool userCancel);
async RequestContextLoadBegin(uint64_t rcid);
async RequestContextAfterDOMContentLoaded(uint64_t rcid);
async RemoveRequestContext(uint64_t rcid);
async PAltDataOutputStream(nsCString type, PHttpChannel channel);

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

@ -183,6 +183,7 @@ HttpBaseChannel::HttpBaseChannel()
, mResponseCouldBeSynthesized(false)
, mBlockAuthPrompt(false)
, mAllowStaleCacheContent(false)
, mAddedAsNonTailRequest(false)
, mTlsFlags(0)
, mSuspendCount(0)
, mInitialRwin(0)
@ -235,12 +236,38 @@ HttpBaseChannel::~HttpBaseChannel()
ReleaseMainThreadOnlyReferences();
}
namespace { // anon
class NonTailRemover : public nsISupports
{
NS_DECL_THREADSAFE_ISUPPORTS
explicit NonTailRemover(nsIRequestContext* rc)
: mRequestContext(rc)
{
}
private:
virtual ~NonTailRemover()
{
MOZ_ASSERT(NS_IsMainThread());
mRequestContext->RemoveNonTailRequest();
}
nsCOMPtr<nsIRequestContext> mRequestContext;
};
NS_IMPL_ISUPPORTS0(NonTailRemover)
} // anon
void
HttpBaseChannel::ReleaseMainThreadOnlyReferences()
{
if (NS_IsMainThread()) {
// Already on main thread, let dtor to
// take care of releasing references
RemoveAsNonTailRequest();
return;
}
@ -262,6 +289,14 @@ HttpBaseChannel::ReleaseMainThreadOnlyReferences()
arrayToRelease.AppendElement(mListenerContext.forget());
arrayToRelease.AppendElement(mCompressListener.forget());
if (mAddedAsNonTailRequest) {
// RemoveNonTailRequest() on our request context must be called on the main thread
MOZ_RELEASE_ASSERT(mRequestContext, "Someone released rc or set flags w/o having it?");
nsCOMPtr<nsISupports> nonTailRemover(new NonTailRemover(mRequestContext));
arrayToRelease.AppendElement(nonTailRemover.forget());
}
NS_DispatchToMainThread(new ProxyReleaseRunnable(Move(arrayToRelease)));
}
@ -3014,6 +3049,38 @@ HttpBaseChannel::ShouldIntercept(nsIURI* aURI)
return shouldIntercept;
}
void
HttpBaseChannel::AddAsNonTailRequest()
{
MOZ_ASSERT(NS_IsMainThread());
if (EnsureRequestContext()) {
LOG(("HttpBaseChannel::AddAsNonTailRequest this=%p, rc=%p, already added=%d",
this, mRequestContext.get(), (bool)mAddedAsNonTailRequest));
if (!mAddedAsNonTailRequest) {
mRequestContext->AddNonTailRequest();
mAddedAsNonTailRequest = true;
}
}
}
void
HttpBaseChannel::RemoveAsNonTailRequest()
{
MOZ_ASSERT(NS_IsMainThread());
if (mRequestContext) {
LOG(("HttpBaseChannel::RemoveAsNonTailRequest this=%p, rc=%p, already added=%d",
this, mRequestContext.get(), (bool)mAddedAsNonTailRequest));
if (mAddedAsNonTailRequest) {
mRequestContext->RemoveNonTailRequest();
mAddedAsNonTailRequest = false;
}
}
}
#ifdef DEBUG
void HttpBaseChannel::AssertPrivateBrowsingId()
{
@ -3175,8 +3242,12 @@ HttpBaseChannel::DoNotifyListener()
mOnStopRequestCalled = true;
}
// This channel has finished its job, potentially release any tail-blocked
// requests with this.
RemoveAsNonTailRequest();
// We have to make sure to drop the references to listeners and callbacks
// no longer needed
// no longer needed.
ReleaseListeners();
DoNotifyListenerCleanup();
@ -4094,7 +4165,7 @@ HttpBaseChannel::EnsureRequestContextID()
return false;
}
// Set the load group connection scope on the transaction
// Set the load group connection scope on this channel and its transaction
rootLoadGroup->GetRequestContextID(&mRequestContextID);
LOG(("HttpBaseChannel::EnsureRequestContextID this=%p id=%" PRIx64,
@ -4103,6 +4174,31 @@ HttpBaseChannel::EnsureRequestContextID()
return true;
}
bool
HttpBaseChannel::EnsureRequestContext()
{
if (mRequestContext) {
// Already have a request context, no need to do the rest of this work
return true;
}
if (!EnsureRequestContextID()) {
return false;
}
nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService();
if (!rcsvc) {
return false;
}
rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(mRequestContext));
if (!mRequestContext) {
return false;
}
return true;
}
void
HttpBaseChannel::EnsureTopLevelOuterContentWindowId()
{

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

@ -540,6 +540,10 @@ protected:
// Used to enforce that flag's behavior but not expose it externally.
uint32_t mAllowStaleCacheContent : 1;
// True iff this request has been calculated in its request context as
// a non tail request. We must remove it again when this channel is done.
uint32_t mAddedAsNonTailRequest : 1;
// An opaque flags for non-standard behavior of the TLS system.
// It is unlikely this will need to be set outside of telemetry studies
// relating to the TLS implementation.
@ -615,6 +619,14 @@ protected:
uint64_t mRequestContextID;
bool EnsureRequestContextID();
nsCOMPtr<nsIRequestContext> mRequestContext;
bool EnsureRequestContext();
// Adds/removes this channel as a non-tailed request in its request context
// these helpers ensure we add it only once and remove it only when added
// via mAddedAsNonTailRequest member tracking.
void AddAsNonTailRequest();
void RemoveAsNonTailRequest();
// ID of the top-level document's inner window this channel is being
// originated from.

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

@ -276,6 +276,10 @@ AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
mChannel->mRedirectChannel = nullptr;
if (succeeded) {
mChannel->RemoveAsNonTailRequest();
}
nsCOMPtr<nsIRedirectResultListener> vetoHook;
NS_QueryNotificationCallbacks(mChannel,
NS_GET_IID(nsIRedirectResultListener),
@ -336,6 +340,7 @@ nsHttpChannel::nsHttpChannel()
, mReqContentLength(0U)
, mPushedStream(nullptr)
, mLocalBlocklist(false)
, mOnTailUnblock(nullptr)
, mWarningReporter(nullptr)
, mIsReadingFromCache(false)
, mFirstResponseSource(RESPONSE_PENDING)
@ -536,17 +541,40 @@ nsHttpChannel::Connect()
{
LOG(("nsHttpChannel::Connect [this=%p]\n", this));
// Consider opening a TCP connection right away.
SpeculativeConnect();
// Don't allow resuming when cache must be used
if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
LOG(("Resuming from cache is not supported yet"));
return NS_ERROR_DOCUMENT_NOT_CACHED;
}
// open a cache entry for this channel...
bool isTrackingResource = mIsTrackingResource; // is atomic
LOG(("nsHttpChannel %p tracking resource=%d, local blocklist=%d, cos=%u",
this, isTrackingResource, mLocalBlocklist, mClassOfService));
if (isTrackingResource || mLocalBlocklist) {
AddClassFlags(nsIClassOfService::Tail);
}
if (WaitingForTailUnblock()) {
MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock;
return NS_OK;
}
return ConnectOnTailUnblock();
}
nsresult
nsHttpChannel::ConnectOnTailUnblock()
{
nsresult rv;
LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this));
// Consider opening a TCP connection right away.
SpeculativeConnect();
// open a cache entry for this channel...
bool isHttps = false;
rv = mURI->SchemeIs("https", &isHttps);
NS_ENSURE_SUCCESS(rv,rv);
@ -1064,30 +1092,6 @@ nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv)
return rv;
}
void
nsHttpChannel::SetupTransactionRequestContext()
{
if (!EnsureRequestContextID()) {
return;
}
nsIRequestContextService *rcsvc =
gHttpHandler->GetRequestContextService();
if (!rcsvc) {
return;
}
nsCOMPtr<nsIRequestContext> rc;
nsresult rv = rcsvc->GetRequestContext(mRequestContextID,
getter_AddRefs(rc));
if (NS_FAILED(rv)) {
return;
}
mTransaction->SetRequestContext(rc);
}
nsresult
nsHttpChannel::SetupTransaction()
{
@ -1317,7 +1321,9 @@ nsHttpChannel::SetupTransaction()
}
mTransaction->SetClassOfService(mClassOfService);
SetupTransactionRequestContext();
if (EnsureRequestContext()) {
mTransaction->SetRequestContext(mRequestContext);
}
rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
responseStream);
@ -5967,6 +5973,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIHstsPrimingCallback)
NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback)
// we have no macro that covers this case.
if (aIID.Equals(NS_GET_IID(nsHttpChannel)) ) {
AddRef();
@ -6008,6 +6015,10 @@ nsHttpChannel::Cancel(nsresult status)
mAuthProvider->Cancel(status);
if (mPreflightChannel)
mPreflightChannel->Cancel(status);
if (mRequestContext && mOnTailUnblock) {
mOnTailUnblock = nullptr;
mRequestContext->CancelTailedRequest(this);
}
return NS_OK;
}
@ -6129,6 +6140,21 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
return rv;
}
if (WaitingForTailUnblock()) {
// This channel is marked as Tail and is part of a request context
// that has positive number of non-tailed requestst, hence this channel
// has been put to a queue.
// When tail is unblocked, OnTailUnblock on this channel will be called
// to continue AsyncOpen.
mListener = listener;
mListenerContext = context;
MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
mOnTailUnblock = &nsHttpChannel::AsyncOpenOnTailUnblock;
LOG((" put on hold until tail is unblocked"));
return NS_OK;
}
if (mInterceptCache != INTERCEPTED && ShouldIntercept()) {
mInterceptCache = MAYBE_INTERCEPT;
SetCouldBeSynthesized();
@ -6193,6 +6219,12 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
return NS_OK;
}
nsresult
nsHttpChannel::AsyncOpenOnTailUnblock()
{
return AsyncOpen(mListener, mListenerContext);
}
namespace {
class InitLocalBlockListXpcCallback final : public nsIURIClassifierCallback {
@ -6533,7 +6565,12 @@ nsHttpChannel::BeginConnectContinue()
// will return false and then we can BeginConnectActual() right away.
RefPtr<nsHttpChannel> self = this;
bool willCallback = InitLocalBlockList([self](bool aLocalBlockList) -> void {
MOZ_ASSERT(self->mLocalBlocklist <= aLocalBlockList, "Unmarking local block-list flag?");
self->mLocalBlocklist = aLocalBlockList;
LOG(("nsHttpChannel %p on-local-blacklist=%d", self.get(), aLocalBlockList));
nsresult rv = self->BeginConnectActual();
if (NS_FAILED(rv)) {
// Since this error is thrown asynchronously so that the caller
@ -6737,6 +6774,8 @@ nsHttpChannel::ContinueBeginConnectWithResult()
void
nsHttpChannel::ContinueBeginConnect()
{
LOG(("nsHttpChannel::ContinueBeginConnect this=%p", this));
nsresult rv = ContinueBeginConnectWithResult();
if (NS_FAILED(rv)) {
CloseCacheEntry(false);
@ -6751,9 +6790,17 @@ nsHttpChannel::ContinueBeginConnect()
void
nsHttpChannel::OnClassOfServiceUpdated()
{
LOG(("nsHttpChannel::OnClassOfServiceUpdated this=%p, cos=%u",
this, mClassOfService));
if (mTransaction) {
gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction, mClassOfService);
}
if (EligibleForTailing()) {
RemoveAsNonTailRequest();
} else {
AddAsNonTailRequest();
}
}
NS_IMETHODIMP
@ -7525,8 +7572,11 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
"We should not call OnStopRequest twice");
mListener->OnStopRequest(this, mListenerContext, status);
mOnStopRequestCalled = true;
}
RemoveAsNonTailRequest();
// If a preferred alt-data type was set, this signals the consumer is
// interested in reading and/or writing the alt-data representation.
// We need to hold a reference to the cache entry in case the listener calls
@ -9332,6 +9382,98 @@ nsHttpChannel::Notify(nsITimer *aTimer)
return NS_OK;
}
bool
nsHttpChannel::EligibleForTailing()
{
if (!(mClassOfService & nsIClassOfService::Tail)) {
return false;
}
if (mClassOfService & (nsIClassOfService::UrgentStart |
nsIClassOfService::Leader |
nsIClassOfService::TailForbidden)) {
return false;
}
if (mClassOfService & nsIClassOfService::Unblocked &&
!(mClassOfService & nsIClassOfService::TailAllowed)) {
return false;
}
if (IsNavigation()) {
return false;
}
return true;
}
bool
nsHttpChannel::WaitingForTailUnblock()
{
nsresult rv;
if (!gHttpHandler->IsTailBlockingEnabled()) {
LOG(("nsHttpChannel %p tail-blocking disabled", this));
return false;
}
if (!EligibleForTailing()) {
LOG(("nsHttpChannel %p not eligible for tail-blocking", this));
AddAsNonTailRequest();
return false;
}
if (!EnsureRequestContext()) {
LOG(("nsHttpChannel %p no request context", this));
return false;
}
LOG(("nsHttpChannel::WaitingForTailUnblock this=%p, rc=%p",
this, mRequestContext.get()));
bool blocked;
rv = mRequestContext->IsContextTailBlocked(this, &blocked);
if (NS_FAILED(rv)) {
return false;
}
LOG((" blocked=%d", blocked));
return blocked;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIRequestTailUnblockCallback
//-----------------------------------------------------------------------------
// Must be implemented in the leaf class because we don't have
// AsyncAbort in HttpBaseChannel.
NS_IMETHODIMP
nsHttpChannel::OnTailUnblock(nsresult rv)
{
LOG(("nsHttpChannel::OnTailUnblock this=%p rv=%" PRIx32 " rc=%p",
this, static_cast<uint32_t>(rv), mRequestContext.get()));
MOZ_RELEASE_ASSERT(mOnTailUnblock);
if (NS_FAILED(mStatus)) {
rv = mStatus;
}
if (NS_SUCCEEDED(rv)) {
auto callback = mOnTailUnblock;
mOnTailUnblock = nullptr;
rv = (this->*callback)();
}
if (NS_FAILED(rv)) {
CloseCacheEntry(false);
return AsyncAbort(rv);
}
return NS_OK;
}
void
nsHttpChannel::SetWarningReporter(HttpChannelSecurityWarningReporter *aReporter)
{

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

@ -85,6 +85,7 @@ class nsHttpChannel final : public HttpBaseChannel
, public nsIChannelWithDivertableParentListener
, public nsIHstsPrimingCallback
, public nsIRaceCacheWithNetwork
, public nsIRequestTailUnblockCallback
, public nsITimerCallback
{
public:
@ -109,6 +110,7 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTPCHANNEL_IID)
NS_DECL_NSIRACECACHEWITHNETWORK
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIREQUESTTAILUNBLOCKCALLBACK
// nsIHttpAuthenticableChannel. We can't use
// NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
@ -325,7 +327,6 @@ private:
MOZ_MUST_USE nsresult Connect();
void SpeculativeConnect();
MOZ_MUST_USE nsresult SetupTransaction();
void SetupTransactionRequestContext();
MOZ_MUST_USE nsresult CallOnStartRequest();
MOZ_MUST_USE nsresult ProcessResponse();
void AsyncContinueProcessResponse();
@ -673,6 +674,25 @@ private:
void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
// If this resource is eligible for tailing based on class-of-service flags
// and load flags. We don't tail Leaders/Unblocked/UrgentStart and top-level
// loads.
bool EligibleForTailing();
// Called exclusively only from AsyncOpen or after all classification callbacks.
// If this channel is 1) Tail, 2) assigned a request context, 3) the context is
// still in the tail-blocked phase, then the method will queue this channel.
// OnTailUnblock will be called after the context is tail-unblocked or canceled.
bool WaitingForTailUnblock();
// A function we trigger when untail callback is triggered by our request
// context in case this channel was tail-blocked.
nsresult (nsHttpChannel::*mOnTailUnblock)();
// Called on untail when tailed during AsyncOpen execution.
nsresult AsyncOpenOnTailUnblock();
// Called on untail when tailed because of being a tracking resource.
nsresult ConnectOnTailUnblock();
nsCString mUsername;
// If non-null, warnings should be reported to this object.

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

@ -126,8 +126,9 @@ nsHttpConnection::~nsHttpConnection()
}
if ((mFastOpenStatus != TFO_FAILED) &&
(mFastOpenStatus != TFO_HTTP) &&
((mFastOpenStatus != TFO_NOT_TRIED) ||
#if defined(_WIN64) && defined(WIN95)
#if defined(_WIN64) && defined(WIN95)
(gHttpHandler->UseFastOpen() &&
gSocketTransportService &&
gSocketTransportService->HasFileDesc2PlatformOverlappedIOHandleFunc()))) {

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

@ -3632,12 +3632,6 @@ nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI
if (!specificEnt) {
RefPtr<nsHttpConnectionInfo> clone(specificCI->Clone());
specificEnt = new nsConnectionEntry(clone);
#if defined(_WIN64) && defined(WIN95)
specificEnt->mUseFastOpen = gHttpHandler->UseFastOpen() &&
gSocketTransportService->HasFileDesc2PlatformOverlappedIOHandleFunc();
#else
specificEnt->mUseFastOpen = gHttpHandler->UseFastOpen();
#endif
mCT.Put(clone->HashKey(), specificEnt);
}
return specificEnt;
@ -3771,7 +3765,6 @@ nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
, mFreeToUse(true)
, mPrimaryStreamStatus(NS_OK)
, mFastOpenInProgress(false)
, mFastOpenStatus(TFO_NOT_TRIED)
, mEnt(ent)
{
MOZ_ASSERT(ent && trans, "constructor with null arguments");
@ -3788,6 +3781,11 @@ nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
}
}
if (mEnt->mConnInfo->FirstHopSSL()) {
mFastOpenStatus = TFO_NOT_TRIED;
} else {
mFastOpenStatus = TFO_HTTP;
}
MOZ_ASSERT(mEnt);
}
@ -4960,7 +4958,18 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
, mDoNotDestroy(false)
{
MOZ_COUNT_CTOR(nsConnectionEntry);
mUseFastOpen = gHttpHandler->UseFastOpen();
if (mConnInfo->FirstHopSSL()) {
#if defined(_WIN64) && defined(WIN95)
mUseFastOpen = gHttpHandler->UseFastOpen() &&
gSocketTransportService->HasFileDesc2PlatformOverlappedIOHandleFunc();
#else
mUseFastOpen = gHttpHandler->UseFastOpen();
#endif
} else {
// Only allow the TCP fast open on a secure connection.
mUseFastOpen = false;
}
LOG(("nsConnectionEntry::nsConnectionEntry this=%p key=%s",
this, ci->HashKey().get()));

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

@ -198,6 +198,10 @@ nsHttpHandler::nsHttpHandler()
, mThrottleResumeIn(400)
, mThrottleTimeWindow(3000)
, mUrgentStartEnabled(true)
, mTailBlockingEnabled(true)
, mTailDelayQuantum(600)
, mTailDelayQuantumAfterDCL(100)
, mTailDelayMax(6000)
, mRedirectionLimit(10)
, mPhishyUserPassLength(1)
, mQoSBits(0x00)
@ -1653,6 +1657,22 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
Unused << prefs->GetBoolPref(HTTP_PREF("on_click_priority"), &mUrgentStartEnabled);
}
if (PREF_CHANGED(HTTP_PREF("tailing.enabled"))) {
Unused << prefs->GetBoolPref(HTTP_PREF("tailing.enabled"), &mTailBlockingEnabled);
}
if (PREF_CHANGED(HTTP_PREF("tailing.delay-quantum"))) {
Unused << prefs->GetIntPref(HTTP_PREF("tailing.delay-quantum"), &val);
mTailDelayQuantum = (uint32_t)clamped(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF("tailing.delay-quantum-after-domcontentloaded"))) {
Unused << prefs->GetIntPref(HTTP_PREF("tailing.delay-quantum-after-domcontentloaded"), &val);
mTailDelayQuantumAfterDCL = (uint32_t)clamped(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF("tailing.delay-max"))) {
Unused << prefs->GetIntPref(HTTP_PREF("tailing.delay-max"), &val);
mTailDelayMax = (uint32_t)clamped(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF("focused_window_transaction_ratio"))) {
float ratio = 0;
rv = prefs->GetFloatPref(HTTP_PREF("focused_window_transaction_ratio"), &ratio);

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

@ -136,6 +136,11 @@ public:
bool PromptTempRedirect() { return mPromptTempRedirect; }
bool IsUrgentStartEnabled() { return mUrgentStartEnabled; }
bool IsTailBlockingEnabled() { return mTailBlockingEnabled; }
uint32_t TailBlockingDelayQuantum(bool aAfterDOMContentLoaded) {
return aAfterDOMContentLoaded ? mTailDelayQuantumAfterDCL : mTailDelayQuantum;
}
uint32_t TailBlockingDelayMax() { return mTailDelayMax; }
// TCP Keepalive configuration values.
@ -464,6 +469,10 @@ private:
uint32_t mThrottleTimeWindow;
bool mUrgentStartEnabled;
bool mTailBlockingEnabled;
uint32_t mTailDelayQuantum;
uint32_t mTailDelayQuantumAfterDCL;
uint32_t mTailDelayMax;
uint8_t mRedirectionLimit;

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

@ -1,4 +1,4 @@
#!/usr/bin/python3
#!/usr/bin/env python3
import sys, subprocess, json, statistics

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

@ -0,0 +1 @@
prefs: [dom.abortController.enabled:true]

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

@ -0,0 +1 @@
prefs: [dom.abortController.enabled:true]

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

@ -1,2 +1,4 @@
prefs: [javascript.options.streams:true,
dom.abortController.enabled:true,
dom.abortController.fetch.enabled:true,
dom.streams.enabled:true]

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

@ -8,6 +8,7 @@
#include "nsICacheEntry.h"
#include "nsICachingChannel.h"
#include "nsIClassOfService.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIPrincipal.h"
@ -593,6 +594,12 @@ AsyncFetchAndSetIconForPage::FetchFromNetwork() {
priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
}
nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel);
if (cos) {
cos->AddClassFlags(nsIClassOfService::Tail |
nsIClassOfService::Throttleable);
}
rv = channel->AsyncOpen2(this);
if (NS_SUCCEEDED(rv)) {
mRequest = channel;

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

@ -45,8 +45,8 @@ function _setTimeoutOrIsInterval(aCallback, aMilliseconds, aIsInterval,
},
// nsINamed
name: aIsInterval ? "setInterval() in Timer.jsm"
: "setTimeout() in Timer.jsm",
name: (aIsInterval ? "setInterval() for " : "setTimeout() for ") +
Cu.generateXPCWrappedJS(aCallback).QueryInterface(Ci.nsINamed).name,
};
timer.initWithCallback(callback, aMilliseconds,

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

@ -87,6 +87,9 @@ function run_test_2() {
name: "Test Addon 3",
}, profileDir);
// Disable rcwn to make cache behavior deterministic.
Services.prefs.setBoolPref("network.http.rcwn.enabled", false);
// Background update uses a different pref, if set
Services.prefs.setCharPref("extensions.update.background.url",
"http://localhost:" + gPort + "/data/test_backgroundupdate.rdf");

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

@ -67,10 +67,10 @@ NESTED(_NS_InvokeByIndex, FRAMESZ, ra)
jal invoke_copy_to_stack
REG_L t3, 8(sp) # get previous a0
REG_L sp, 0(sp) # get orig sp back
REG_L s0, 0(sp) # get orig sp back and save away our stack pointer
REG_L a0, A0OFF(sp) # a0 - that
REG_L a1, A1OFF(sp) # a1 - methodIndex
REG_L a0, A0OFF(s0) # a0 - that
REG_L a1, A1OFF(s0) # a1 - methodIndex
# t1 = methodIndex * pow(2, PTRLOG)
# (use shift instead of mult)
@ -105,13 +105,12 @@ NESTED(_NS_InvokeByIndex, FRAMESZ, ra)
l.d $f18, 40(t1)
l.d $f19, 48(t1)
# save away our stack pointer and create
# the stack pointer for the function
move s0, sp
# create the stack pointer for the function
move sp, t3
jalr t9
## restore stack pointer.
move sp, s0
RESTORE_GP64