зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-central to autoland. r=merge a=merge
This commit is contained in:
Коммит
2b41ef3155
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче